1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::io; 6 use std::io::Write; 7 8 use base::error; 9 #[cfg(windows)] 10 use base::named_pipes; 11 use base::Event; 12 use base::FileSync; 13 use base::RawDescriptor; 14 use base::Result; 15 use hypervisor::ProtectionType; 16 17 use crate::pci::CrosvmDeviceId; 18 use crate::serial_device::SerialInput; 19 use crate::serial_device::SerialOptions; 20 use crate::BusAccessInfo; 21 use crate::BusDevice; 22 use crate::DeviceId; 23 use crate::SerialDevice; 24 use crate::Suspendable; 25 26 const BOCHS_DEBUGCON_READBACK: u8 = 0xe9; 27 28 pub struct Debugcon { 29 out: Option<Box<dyn io::Write + Send>>, 30 } 31 32 impl SerialDevice for Debugcon { 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>, ) -> Debugcon33 fn new( 34 _protection_type: ProtectionType, 35 _interrupt_evt: Event, 36 _input: Option<Box<dyn SerialInput>>, 37 out: Option<Box<dyn io::Write + Send>>, 38 _sync: Option<Box<dyn FileSync + Send>>, 39 _options: SerialOptions, 40 _keep_rds: Vec<RawDescriptor>, 41 ) -> Debugcon { 42 Debugcon { out } 43 } 44 45 #[cfg(windows)] 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>, ) -> Debugcon46 fn new_with_pipe( 47 _protection_type: ProtectionType, 48 _interrupt_evt: Event, 49 _pipe_in: named_pipes::PipeConnection, 50 _pipe_out: named_pipes::PipeConnection, 51 _options: SerialOptions, 52 _keep_rds: Vec<RawDescriptor>, 53 ) -> Debugcon { 54 unimplemented!("new_with_pipe unimplemented for Debugcon"); 55 } 56 } 57 58 impl BusDevice for Debugcon { device_id(&self) -> DeviceId59 fn device_id(&self) -> DeviceId { 60 CrosvmDeviceId::DebugConsole.into() 61 } 62 debug_label(&self) -> String63 fn debug_label(&self) -> String { 64 "debugcon".to_owned() 65 } 66 write(&mut self, _info: BusAccessInfo, data: &[u8])67 fn write(&mut self, _info: BusAccessInfo, data: &[u8]) { 68 if data.len() != 1 { 69 return; 70 } 71 if let Err(e) = self.handle_write(data) { 72 error!("debugcon failed write: {}", e); 73 } 74 } 75 read(&mut self, _info: BusAccessInfo, data: &mut [u8])76 fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) { 77 if data.len() != 1 { 78 return; 79 } 80 data[0] = BOCHS_DEBUGCON_READBACK; 81 } 82 } 83 84 impl Debugcon { handle_write(&mut self, data: &[u8]) -> Result<()>85 fn handle_write(&mut self, data: &[u8]) -> Result<()> { 86 if let Some(out) = self.out.as_mut() { 87 out.write_all(data)?; 88 out.flush()?; 89 } 90 Ok(()) 91 } 92 } 93 94 impl Suspendable for Debugcon { sleep(&mut self) -> anyhow::Result<()>95 fn sleep(&mut self) -> anyhow::Result<()> { 96 Ok(()) 97 } 98 wake(&mut self) -> anyhow::Result<()>99 fn wake(&mut self) -> anyhow::Result<()> { 100 Ok(()) 101 } 102 snapshot(&mut self) -> anyhow::Result<serde_json::Value>103 fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> { 104 Ok(serde_json::Value::Null) 105 } 106 restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>107 fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { 108 anyhow::ensure!( 109 data == serde_json::Value::Null, 110 "unexpected snapshot data: should be null, got {}", 111 data, 112 ); 113 Ok(()) 114 } 115 } 116 117 #[cfg(test)] 118 mod tests { 119 use std::io; 120 use std::sync::Arc; 121 122 use sync::Mutex; 123 124 use super::*; 125 126 const ADDR: BusAccessInfo = BusAccessInfo { 127 offset: 0, 128 address: 0, 129 id: 0, 130 }; 131 132 // XXX(gerow): copied from devices/src/serial.rs 133 #[derive(Clone)] 134 struct SharedBuffer { 135 buf: Arc<Mutex<Vec<u8>>>, 136 } 137 138 impl SharedBuffer { new() -> SharedBuffer139 fn new() -> SharedBuffer { 140 SharedBuffer { 141 buf: Arc::new(Mutex::new(Vec::new())), 142 } 143 } 144 } 145 146 impl io::Write for SharedBuffer { write(&mut self, buf: &[u8]) -> io::Result<usize>147 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 148 self.buf.lock().write(buf) 149 } flush(&mut self) -> io::Result<()>150 fn flush(&mut self) -> io::Result<()> { 151 self.buf.lock().flush() 152 } 153 } 154 155 #[test] write()156 fn write() { 157 let debugcon_out = SharedBuffer::new(); 158 let mut debugcon = Debugcon::new( 159 ProtectionType::Unprotected, 160 Event::new().unwrap(), 161 None, 162 Some(Box::new(debugcon_out.clone())), 163 None, 164 Default::default(), 165 Vec::new(), 166 ); 167 168 debugcon.write(ADDR, b"a"); 169 debugcon.write(ADDR, b"b"); 170 debugcon.write(ADDR, b"c"); 171 assert_eq!(debugcon_out.buf.lock().as_slice(), b"abc"); 172 } 173 174 #[test] read()175 fn read() { 176 let mut debugcon = Debugcon::new( 177 ProtectionType::Unprotected, 178 Event::new().unwrap(), 179 None, 180 None, 181 None, 182 Default::default(), 183 Vec::new(), 184 ); 185 186 let mut data = [0u8; 1]; 187 debugcon.read(ADDR, &mut data[..]); 188 assert_eq!(data[0], 0xe9); 189 } 190 } 191