blob: a56e43e41e9938a8acab7c832f195b8bb9ce64f9 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io;
use std::io::Write;
use base::error;
#[cfg(windows)]
use base::named_pipes;
use base::Event;
use base::FileSync;
use base::RawDescriptor;
use base::Result;
use hypervisor::ProtectionType;
use crate::pci::CrosvmDeviceId;
use crate::serial_device::SerialInput;
use crate::serial_device::SerialOptions;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::DeviceId;
use crate::SerialDevice;
use crate::Suspendable;
const BOCHS_DEBUGCON_READBACK: u8 = 0xe9;
pub struct Debugcon {
out: Option<Box<dyn io::Write + Send>>,
}
impl SerialDevice for Debugcon {
fn new(
_protection_type: ProtectionType,
_interrupt_evt: Event,
_input: Option<Box<dyn SerialInput>>,
out: Option<Box<dyn io::Write + Send>>,
_sync: Option<Box<dyn FileSync + Send>>,
_options: SerialOptions,
_keep_rds: Vec<RawDescriptor>,
) -> Debugcon {
Debugcon { out }
}
#[cfg(windows)]
fn new_with_pipe(
_protection_type: ProtectionType,
_interrupt_evt: Event,
_pipe_in: named_pipes::PipeConnection,
_pipe_out: named_pipes::PipeConnection,
_options: SerialOptions,
_keep_rds: Vec<RawDescriptor>,
) -> Debugcon {
unimplemented!("new_with_pipe unimplemented for Debugcon");
}
}
impl BusDevice for Debugcon {
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::DebugConsole.into()
}
fn debug_label(&self) -> String {
"debugcon".to_owned()
}
fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
if data.len() != 1 {
return;
}
if let Err(e) = self.handle_write(data) {
error!("debugcon failed write: {}", e);
}
}
fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
if data.len() != 1 {
return;
}
data[0] = BOCHS_DEBUGCON_READBACK;
}
}
impl Debugcon {
fn handle_write(&mut self, data: &[u8]) -> Result<()> {
if let Some(out) = self.out.as_mut() {
out.write_all(data)?;
out.flush()?;
}
Ok(())
}
}
impl Suspendable for Debugcon {
fn sleep(&mut self) -> anyhow::Result<()> {
Ok(())
}
fn wake(&mut self) -> anyhow::Result<()> {
Ok(())
}
fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
Ok(serde_json::Value::Null)
}
fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
anyhow::ensure!(
data == serde_json::Value::Null,
"unexpected snapshot data: should be null, got {}",
data,
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::sync::Arc;
use sync::Mutex;
use super::*;
const ADDR: BusAccessInfo = BusAccessInfo {
offset: 0,
address: 0,
id: 0,
};
// XXX(gerow): copied from devices/src/serial.rs
#[derive(Clone)]
struct SharedBuffer {
buf: Arc<Mutex<Vec<u8>>>,
}
impl SharedBuffer {
fn new() -> SharedBuffer {
SharedBuffer {
buf: Arc::new(Mutex::new(Vec::new())),
}
}
}
impl io::Write for SharedBuffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.lock().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.buf.lock().flush()
}
}
#[test]
fn write() {
let debugcon_out = SharedBuffer::new();
let mut debugcon = Debugcon::new(
ProtectionType::Unprotected,
Event::new().unwrap(),
None,
Some(Box::new(debugcon_out.clone())),
None,
Default::default(),
Vec::new(),
);
debugcon.write(ADDR, &[b'a']);
debugcon.write(ADDR, &[b'b']);
debugcon.write(ADDR, &[b'c']);
assert_eq!(debugcon_out.buf.lock().as_slice(), [b'a', b'b', b'c']);
}
#[test]
fn read() {
let mut debugcon = Debugcon::new(
ProtectionType::Unprotected,
Event::new().unwrap(),
None,
None,
None,
Default::default(),
Vec::new(),
);
let mut data = [0u8; 1];
debugcon.read(ADDR, &mut data[..]);
assert_eq!(data[0], 0xe9);
}
}