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}; |
16 | |
17 | // re-export for ease of use |
18 | pub use self::context::SystemContext; |
19 | pub use self::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 | #[repr (C)] |
33 | #[unsafe_protocol ("2755590c-6f3c-42fa-9ea4-a3ba543cda25" )] |
34 | pub 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 | |
57 | impl 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 | |
147 | newtype_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. |
153 | pub 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 | |