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