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