patina_debugger 11.3.2

Debugger implementation for a Patina Core.
Documentation

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 statis 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;
extern crate patina_internal_cpu;

use patina_internal_cpu::interrupts::{Interrupts, InterruptManager};

static DEBUGGER: patina_debugger::PatinaDebugger<patina::serial::uart::UartNull> =
    patina_debugger::PatinaDebugger::new(patina::serial::uart::UartNull{});

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() {
    let mut interrupt_manager = Interrupts::default();

    // Initialize the debugger. This will cause a debug break because of the
    // initial break configuration set above.
    patina_debugger::initialize(&mut interrupt_manager);

    // 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.
    if patina_debugger::enabled() {
        patina_debugger::breakpoint();
    }
}

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

windbg_workarounds - (Default) Enables workarounds for Windbg compatibility.

alloc - Uses allocated buffers rather than static buffers for all memory. This provides additional functionality but prevents debugging prior to allocations being available.

License

Copyright (C) Microsoft Corporation.

SPDX-License-Identifier: Apache-2.0