Skip to main content

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}