Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,28 @@ debug: all
@echo "Starting QEMU paused with GDB on :1234 and monitor on 127.0.0.1:55555"
QEMU_OPTS="-S -s" ./startQemu.sh headless

# Automated GDB session via pexpect (no manual address copying, no probe run).
# Uses the debug mailbox (physical 0x7000) — efi_main writes its own runtime
# address there on entry; GDB watches for it via a hardware watchpoint.
#
# Requires: pip install --break-system-packages pexpect
# Requires: tmux session named 'theseus' (created automatically if absent)
#
# Full interactive session (default):
# make debug-auto
#
# Non-interactive smoke-test (CI-friendly, exits after breakpoint check):
# make debug-auto-ci
.PHONY: debug-auto debug-auto-ci
debug-auto: all
@echo "Starting automated GDB session (mailbox watchpoint + pexpect)..."
python3 scripts/gdb-auto.py --tmux theseus

debug-auto-ci: all
@echo "Starting non-interactive GDB breakpoint smoke-test..."
python3 scripts/gdb-auto.py --tmux theseus \
--no-interactive --timeout-boot 180

# Print a short help message describing common targets and how to set PROFILE
.PHONY: help
help:
Expand Down
35 changes: 35 additions & 0 deletions bootloader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,41 @@ core::arch::global_asm!(".globl __chkstk", "__chkstk:", " jmp ___chkstk_ms",)
/// direct `ExitBootServices` → `kernel_entry` transfer.
#[entry]
fn efi_main() -> Status {
// -----------------------------------------------------------------------
// GDB debug mailbox — write runtime efi_main address to a fixed physical
// location so automated GDB tooling can discover it via a watchpoint.
//
// Must happen before any UEFI call so the address is visible as early as
// possible. The page is allocated via UEFI AllocateType::Address so the
// firmware records our ownership in the memory map.
//
// Layout at DEBUG_MAILBOX_PHYS:
// +0x00 u64 runtime efi_main address (written first)
// +0x08 u64 magic sentinel (written second → GDB trigger)
//
// See: shared/src/constants.rs :: debug_mailbox
// debug.gdb :: theseus-auto command
// -----------------------------------------------------------------------
use theseus_shared::constants::debug_mailbox;
use uefi::boot::{self as uefi_boot, AllocateType};
use uefi::mem::memory_map::MemoryType as UefiMemType;

// Allocate the mailbox page via UEFI so firmware records our ownership.
// Ignore errors — if the page is already allocated (e.g. by firmware) we
// fall back to a direct write; in QEMU/OVMF this range is always free.
let _ = uefi_boot::allocate_pages(
AllocateType::Address(debug_mailbox::PHYS),
UefiMemType::LOADER_DATA,
1,
);

unsafe {
let base = debug_mailbox::PHYS as *mut u64;
// Write address first, then magic — GDB watches the magic location.
base.add(0).write_volatile(efi_main as *const () as u64);
base.add(1).write_volatile(debug_mailbox::MAGIC);
}

// Install pre-exit allocators that forward to UEFI Boot Services
theseus_shared::allocator::install_pre_exit_allocators(pre_exit_alloc, pre_exit_dealloc);
// Initialize UEFI environment and global output driver
Expand Down
Loading