Skip to main content

Crate patina_debugger

Crate patina_debugger 

Source
Expand description

Patina Debugger

This crate provides a debugger implementation that will install itself in the exception handlers and communicate with debugger software using the GDB Remote protocol. The debugger is intended to be used in the boot phase cores.

This crate is under construction and may be missing functionality, documentation, and testing.

§Getting Started

For more details on using the debugger on a device, see the readme.

§Examples and Usage

The debugger consists of the static access routines and the underlying debugger struct. The top level platform code should initialize the static PatinaDebugger struct with the appropriate serial transport and default configuration. The platform has the option of setting static configuration, or enabling the debugger in runtime code based on platform policy. During entry, the platform should use the set_debugger routine to set the global instance of the debugger.

Core code should use the static routines to interact with the debugger. If the debugger is either not set or not enabled, the static routines will be no-ops.

extern crate patina;

static DEBUGGER: patina_debugger::PatinaDebugger<patina::serial::uart::UartNull> =
    patina_debugger::PatinaDebugger::new(patina::serial::uart::UartNull{})
        .with_timeout(30); // Set initial break timeout to 30 seconds.

fn entry() {

    // Configure the debugger. This is used for dynamic configuration of the debugger.
    DEBUGGER.enable(true);

    // Set the global debugger instance. This can only be done once.
    patina_debugger::set_debugger(&DEBUGGER);

    // Setup a custom monitor command for this platform.
    patina_debugger::add_monitor_command("my_command", "Description of my_command", |args, writer| {
        // Parse the arguments from _args, which is a SplitWhitespace iterator.
        let _ = write!(writer, "Executed my_command with args: {:?}", args);
    });

    // Call the core entry. The core can then initialize and access the debugger
    // through the static routines.
    start();
}

fn start() {
    // Initialize the debugger. This will cause a debug break because of the
    // initial break configuration set above.
    patina_debugger::initialize(&mut Interrupts::default(), Some(&ExampleTimer));

    // Notify the debugger of a module load.
    patina_debugger::notify_module_load("module.efi", 0x420000, 0x10000);

    // Poll the debugger for any pending interrupts.
    patina_debugger::poll_debugger();

    // Break into the debugger if the debugger is enabled and initialized.
    patina_debugger::breakpoint();

    // Cause a debug break unconditionally. This will crash the system
    // if the debugger is not enabled or initialized. This should be used with extreme caution.
    patina_debugger::breakpoint_unchecked();
}

The debugger can be further configured by using various functions on the initialization of the debugger struct. See the definition for debugger::PatinaDebugger for more details. Notably, if the device is using the same transport for logging and debugger, it is advisable to use .without_log_init().

§Features

alloc - Uses allocated buffers rather than static buffers for all memory. This provides additional functionality but prevents debugging prior to allocations being available. This is intended for use by the core crate, and not for platform use.

§License

Copyright (C) Microsoft Corporation.

SPDX-License-Identifier: Apache-2.0

Structs§

PatinaDebugger
Patina Debugger

Enums§

DebuggerLoggingPolicy
Policy for how the debugger will handle logging on the system.

Functions§

add_monitor_command
Adds a monitor command to the debugger. This may be called before initialization, but should not be called before memory allocations are available. See MonitorCommandFn for more details on the callback function expectations.
breakpoint
Invokes a debug break instruction if the debugger is enabled and initialized. This will cause the debugger to break in. If the debugger is not enabled and initialized, this routine will have no effect.
breakpoint_unchecked
Invokes a debug break instruction unconditionally. If this routine is invoked when the debugger is not enabled and initialized, it will cause an unhandled exception.
enabled
Checks if the debugger is enabled.
initialize
Initializes the debugger. This will install the debugger into the exception handlers using the provided interrupt manager. This routine may invoke a debug break depending on configuration.
initialized
Checks if the debugger is initialized.
notify_module_load
Notifies the debugger of a module load at the provided address and length. This should be invoked before the module has begun execution.
poll_debugger
Polls the debugger for any pending interrupts. The routine may cause a debug break.
set_debugger
Sets the global instance of the debugger.

Type Aliases§

MonitorCommandFn
Type for monitor command functions. This will be invoked by the debugger when the associated monitor command is invoked.