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
12use core::ffi::c_void;
13
14use crate::proto::unsafe_protocol;
15use crate::{Result, Status};
16
17// re-export for ease of use
18pub use self::context::SystemContext;
19pub use self::exception::ExceptionType;
20
21mod context;
22mod 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#[repr(C)]
33#[unsafe_protocol("2755590c-6f3c-42fa-9ea4-a3ba543cda25")]
34pub struct DebugSupport {
35 isa: ProcessorArch,
36 get_maximum_processor_index:
37 extern "efiapi" fn(this: &mut DebugSupport, max_processor_index: &mut usize) -> Status,
38 register_periodic_callback: unsafe extern "efiapi" fn(
39 this: &mut DebugSupport,
40 processor_index: usize,
41 periodic_callback: Option<unsafe extern "efiapi" fn(SystemContext)>,
42 ) -> Status,
43 register_exception_callback: unsafe extern "efiapi" fn(
44 this: &mut DebugSupport,
45 processor_index: usize,
46 exception_callback: Option<unsafe extern "efiapi" fn(ExceptionType, SystemContext)>,
47 exception_type: ExceptionType,
48 ) -> Status,
49 invalidate_instruction_cache: unsafe extern "efiapi" fn(
50 this: &mut DebugSupport,
51 processor_index: usize,
52 start: *mut c_void,
53 length: u64,
54 ) -> Status,
55}
56
57impl DebugSupport {
58 /// Returns the processor architecture of the running CPU.
59 #[must_use]
60 pub const fn arch(&self) -> ProcessorArch {
61 self.isa
62 }
63
64 /// Returns the maximum value that may be used for the processor_index parameter in
65 /// `register_periodic_callback()` and `register_exception_callback()`.
66 ///
67 /// Note: Applications built with EDK2 (such as OVMF) always return `0` as of 2021-09-15
68 pub fn get_maximum_processor_index(&mut self) -> usize {
69 // initially set to a canary value for testing purposes
70 let mut max_processor_index: usize = usize::MAX;
71
72 // per the UEFI spec, this call should only return EFI_SUCCESS
73 let _ = (self.get_maximum_processor_index)(self, &mut max_processor_index);
74
75 max_processor_index
76 }
77
78 /// Registers a function to be called back periodically in interrupt context.
79 /// Pass `None` for `callback` to deregister the currently registered function for
80 /// a specified `processor_index`. Will return `Status::INVALID_PARAMETER` if
81 /// `processor_index` exceeds the current maximum from `Self::get_maximum_processor_index`.
82 ///
83 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter
84 ///
85 /// # Safety
86 /// No portion of the debug agent that runs in interrupt context may make any
87 /// calls to EFI services or other protocol interfaces.
88 pub unsafe fn register_periodic_callback(
89 &mut self,
90 processor_index: usize,
91 callback: Option<unsafe extern "efiapi" fn(SystemContext)>,
92 ) -> Result {
93 if processor_index > self.get_maximum_processor_index() {
94 return Err(Status::INVALID_PARAMETER.into());
95 }
96
97 // Safety: As we've validated the `processor_index`, this should always be safe
98 (self.register_periodic_callback)(self, processor_index, callback).into()
99 }
100
101 /// Registers a function to be called when a given processor exception occurs.
102 /// Pass `None` for `callback` to deregister the currently registered function for a
103 /// given `exception_type` and `processor_index`. Will return `Status::INVALID_PARAMETER`
104 /// if `processor_index` exceeds the current maximum from `Self::get_maximum_processor_index`.
105 ///
106 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter
107 ///
108 /// # Safety
109 /// No portion of the debug agent that runs in interrupt context may make any
110 /// calls to EFI services or other protocol interfaces.
111 pub unsafe fn register_exception_callback(
112 &mut self,
113 processor_index: usize,
114 callback: Option<unsafe extern "efiapi" fn(ExceptionType, SystemContext)>,
115 exception_type: ExceptionType,
116 ) -> Result {
117 if processor_index > self.get_maximum_processor_index() {
118 return Err(Status::INVALID_PARAMETER.into());
119 }
120
121 // Safety: As we've validated the `processor_index`, this should always be safe
122 (self.register_exception_callback)(self, processor_index, callback, exception_type).into()
123 }
124
125 /// Invalidates processor instruction cache for a memory range for a given `processor_index`.
126 ///
127 /// Note: Applications built with EDK2 (such as OVMF) ignore the `processor_index` parameter
128 ///
129 /// # Safety
130 /// `start` must be a c_void ptr to a valid memory address
131 pub unsafe fn invalidate_instruction_cache(
132 &mut self,
133 processor_index: usize,
134 start: *mut c_void,
135 length: u64,
136 ) -> Result {
137 if processor_index > self.get_maximum_processor_index() {
138 return Err(Status::INVALID_PARAMETER.into());
139 }
140
141 // per the UEFI spec, this call should only return EFI_SUCCESS
142 // Safety: As we've validated the `processor_index`, this should always be safe
143 (self.invalidate_instruction_cache)(self, processor_index, start, length).into()
144 }
145}
146
147newtype_enum! {
148/// The instruction set architecture of the running processor.
149///
150/// UEFI can be and has been ported to new CPU architectures in the past,
151/// therefore modeling this C enum as a Rust enum (where the compiler must know
152/// about every variant in existence) would _not_ be safe.
153pub enum ProcessorArch: u32 => {
154 /// 32-bit x86 PC
155 X86_32 = 0x014C,
156 /// 64-bit x86 PC
157 X86_64 = 0x8664,
158 /// Intel Itanium
159 ITANIUM = 0x200,
160 /// UEFI Interpreter bytecode
161 EBC = 0x0EBC,
162 /// ARM Thumb / Mixed
163 ARM = 0x01C2,
164 /// ARM 64-bit
165 AARCH_64 = 0xAA64,
166 /// RISC-V 32-bit
167 RISCV_32 = 0x5032,
168 /// RISC-V 64-bit
169 RISCV_64 = 0x5064,
170 /// RISC-V 128-bit
171 RISCV_128 = 0x5128,
172}}
173