1 //! Provides support for the UEFI debugging protocol. 2 //! 3 //! This protocol is designed to allow debuggers to query the state of the firmware, 4 //! as well as set up callbacks for various events. 5 //! 6 //! It also defines a Debugport protocol for debugging over serial devices. 7 //! 8 //! An example UEFI debugger is Intel's [UDK Debugger Tool][udk]. 9 //! 10 //! [udk]: https://firmware.intel.com/develop/intel-uefi-tools-and-utilities/intel-uefi-development-kit-debugger-tool 11 12 use core::ffi::c_void; 13 14 use crate::proto::unsafe_protocol; 15 use crate::{Result, Status, StatusExt}; 16 17 // re-export for ease of use 18 pub use context::SystemContext; 19 pub use exception::ExceptionType; 20 21 mod context; 22 mod exception; 23 24 /// The debugging support protocol allows debuggers to connect to a UEFI machine. 25 /// It is expected that there will typically be two instances of the EFI Debug Support protocol in the system. 26 /// One associated with the native processor instruction set (IA-32, x64, ARM, RISC-V, or Itanium processor 27 /// family), and one for the EFI virtual machine that implements EFI byte code (EBC). 28 /// While multiple instances of the EFI Debug Support protocol are expected, there must never be more than 29 /// one for any given instruction set. 30 /// 31 /// NOTE: OVMF only implements this protocol interface for the virtual EBC processor 32 #[derive(Debug)] 33 #[repr(C)] 34 #[unsafe_protocol("2755590c-6f3c-42fa-9ea4-a3ba543cda25")] 35 pub struct DebugSupport { 36 isa: ProcessorArch, 37 get_maximum_processor_index: 38 extern "efiapi" fn(this: &mut DebugSupport, max_processor_index: &mut usize) -> Status, 39 register_periodic_callback: unsafe extern "efiapi" fn( 40 this: &mut DebugSupport, 41 processor_index: usize, 42 periodic_callback: Option<unsafe extern "efiapi" fn(SystemContext)>, 43 ) -> Status, 44 register_exception_callback: unsafe extern "efiapi" fn( 45 this: &mut DebugSupport, 46 processor_index: usize, 47 exception_callback: Option<unsafe extern "efiapi" fn(ExceptionType, SystemContext)>, 48 exception_type: ExceptionType, 49 ) -> Status, 50 invalidate_instruction_cache: unsafe extern "efiapi" fn( 51 this: &mut DebugSupport, 52 processor_index: usize, 53 start: *mut c_void, 54 length: u64, 55 ) -> Status, 56 } 57 58 impl DebugSupport { 59 /// Returns the processor architecture of the running CPU. 60 #[must_use] arch(&self) -> ProcessorArch61 pub const fn arch(&self) -> ProcessorArch { 62 self.isa 63 } 64 65 /// Returns the maximum value that may be used for the processor_index parameter in 66 /// `register_periodic_callback()` and `register_exception_callback()`. 67 /// 68 /// Note: Applications built with EDK2 (such as OVMF) always return `0` as of 2021-09-15 get_maximum_processor_index(&mut self) -> usize69 pub fn get_maximum_processor_index(&mut self) -> usize { 70 // initially set to a canary value for testing purposes 71 let mut max_processor_index: usize = usize::MAX; 72 73 // per the UEFI spec, this call should only return EFI_SUCCESS 74 let _ = (self.get_maximum_processor_index)(self, &mut max_processor_index); 75 76 max_processor_index 77 } 78 79 /// Registers a function to be called back periodically in interrupt context. 80 /// Pass `None` for `callback` to deregister the currently registered function for 81 /// a specified `processor_index`. Will return `Status::INVALID_PARAMETER` if 82 /// `processor_index` exceeds the current maximum from `Self::get_maximum_processor_index`. 83 /// 84 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter 85 /// 86 /// # Safety 87 /// No portion of the debug agent that runs in interrupt context may make any 88 /// calls to EFI services or other protocol interfaces. register_periodic_callback( &mut self, processor_index: usize, callback: Option<unsafe extern "efiapi" fn(SystemContext)>, ) -> Result89 pub unsafe fn register_periodic_callback( 90 &mut self, 91 processor_index: usize, 92 callback: Option<unsafe extern "efiapi" fn(SystemContext)>, 93 ) -> Result { 94 if processor_index > self.get_maximum_processor_index() { 95 return Err(Status::INVALID_PARAMETER.into()); 96 } 97 98 // Safety: As we've validated the `processor_index`, this should always be safe 99 (self.register_periodic_callback)(self, processor_index, callback).to_result() 100 } 101 102 /// Registers a function to be called when a given processor exception occurs. 103 /// Pass `None` for `callback` to deregister the currently registered function for a 104 /// given `exception_type` and `processor_index`. Will return `Status::INVALID_PARAMETER` 105 /// if `processor_index` exceeds the current maximum from `Self::get_maximum_processor_index`. 106 /// 107 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter 108 /// 109 /// # Safety 110 /// No portion of the debug agent that runs in interrupt context may make any 111 /// calls to EFI services or other protocol interfaces. register_exception_callback( &mut self, processor_index: usize, callback: Option<unsafe extern "efiapi" fn(ExceptionType, SystemContext)>, exception_type: ExceptionType, ) -> Result112 pub unsafe fn register_exception_callback( 113 &mut self, 114 processor_index: usize, 115 callback: Option<unsafe extern "efiapi" fn(ExceptionType, SystemContext)>, 116 exception_type: ExceptionType, 117 ) -> Result { 118 if processor_index > self.get_maximum_processor_index() { 119 return Err(Status::INVALID_PARAMETER.into()); 120 } 121 122 // Safety: As we've validated the `processor_index`, this should always be safe 123 (self.register_exception_callback)(self, processor_index, callback, exception_type) 124 .to_result() 125 } 126 127 /// Invalidates processor instruction cache for a memory range for a given `processor_index`. 128 /// 129 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter 130 /// 131 /// # Safety 132 /// `start` must be a c_void ptr to a valid memory address invalidate_instruction_cache( &mut self, processor_index: usize, start: *mut c_void, length: u64, ) -> Result133 pub unsafe fn invalidate_instruction_cache( 134 &mut self, 135 processor_index: usize, 136 start: *mut c_void, 137 length: u64, 138 ) -> Result { 139 if processor_index > self.get_maximum_processor_index() { 140 return Err(Status::INVALID_PARAMETER.into()); 141 } 142 143 // per the UEFI spec, this call should only return EFI_SUCCESS 144 // Safety: As we've validated the `processor_index`, this should always be safe 145 (self.invalidate_instruction_cache)(self, processor_index, start, length).to_result() 146 } 147 } 148 149 newtype_enum! { 150 /// The instruction set architecture of the running processor. 151 /// 152 /// UEFI can be and has been ported to new CPU architectures in the past, 153 /// therefore modeling this C enum as a Rust enum (where the compiler must know 154 /// about every variant in existence) would _not_ be safe. 155 pub enum ProcessorArch: u32 => { 156 /// 32-bit x86 PC 157 X86_32 = 0x014C, 158 /// 64-bit x86 PC 159 X86_64 = 0x8664, 160 /// Intel Itanium 161 ITANIUM = 0x200, 162 /// UEFI Interpreter bytecode 163 EBC = 0x0EBC, 164 /// ARM Thumb / Mixed 165 ARM = 0x01C2, 166 /// ARM 64-bit 167 AARCH_64 = 0xAA64, 168 /// RISC-V 32-bit 169 RISCV_32 = 0x5032, 170 /// RISC-V 64-bit 171 RISCV_64 = 0x5064, 172 /// RISC-V 128-bit 173 RISCV_128 = 0x5128, 174 }} 175 176 /// The debug port protocol abstracts the underlying debug port 177 /// hardware, whether it is a regular Serial port or something else. 178 #[derive(Debug)] 179 #[repr(C)] 180 #[unsafe_protocol("eba4e8d2-3858-41ec-a281-2647ba9660d0")] 181 pub struct DebugPort { 182 reset: extern "efiapi" fn(this: &DebugPort) -> Status, 183 write: extern "efiapi" fn( 184 this: &DebugPort, 185 timeout: u32, 186 buffer_size: &mut usize, 187 buffer: *const c_void, 188 ) -> Status, 189 read: extern "efiapi" fn( 190 this: &DebugPort, 191 timeout: u32, 192 buffer_size: &mut usize, 193 buffer: *mut c_void, 194 ) -> Status, 195 poll: extern "efiapi" fn(this: &DebugPort) -> Status, 196 } 197 198 impl DebugPort { 199 /// Resets the debugport device. reset(&self) -> Result200 pub fn reset(&self) -> Result { 201 (self.reset)(self).to_result() 202 } 203 204 /// Write data to the debugport device. 205 /// 206 /// Note: `timeout` is given in microseconds write(&self, timeout: u32, data: &[u8]) -> Result<(), usize>207 pub fn write(&self, timeout: u32, data: &[u8]) -> Result<(), usize> { 208 let mut buffer_size = data.len(); 209 210 (self.write)( 211 self, 212 timeout, 213 &mut buffer_size, 214 data.as_ptr().cast::<c_void>(), 215 ) 216 .to_result_with( 217 || debug_assert_eq!(buffer_size, data.len()), 218 |_| buffer_size, 219 ) 220 } 221 222 /// Read data from the debugport device. 223 /// 224 /// Note: `timeout` is given in microseconds read(&self, timeout: u32, data: &mut [u8]) -> Result<(), usize>225 pub fn read(&self, timeout: u32, data: &mut [u8]) -> Result<(), usize> { 226 let mut buffer_size = data.len(); 227 228 (self.read)( 229 self, 230 timeout, 231 &mut buffer_size, 232 data.as_mut_ptr().cast::<c_void>(), 233 ) 234 .to_result_with( 235 || debug_assert_eq!(buffer_size, data.len()), 236 |_| buffer_size, 237 ) 238 } 239 240 /// Check to see if any data is available to be read from the debugport device. poll(&self) -> Result241 pub fn poll(&self) -> Result { 242 (self.poll)(self).to_result() 243 } 244 } 245