From d8670d8eeb55bc0ea347cfe4a9a27640fe4e4e7e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 4 May 2026 19:40:01 +0200 Subject: debug: add multiboot2 information dump tool This patch introduces a new GDB tool `dump_mb2i` that dump the multiboot2 information provided by the bootloader. This tool can be invoked in the GDB console. For example in vscode: -exec dump_mb2i ((kapi::boot::information)bootstrap_information).mbi The tool expects the address of the loader provided MB2 information as its only argument. --- scripts/gdb/load.py | 47 +++++++++++++ scripts/gdb/pretty_printers.py | 41 ----------- scripts/gdb/teachos/dump_mb2i.py | 147 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 scripts/gdb/load.py delete mode 100644 scripts/gdb/pretty_printers.py create mode 100644 scripts/gdb/teachos/dump_mb2i.py (limited to 'scripts/gdb') diff --git a/scripts/gdb/load.py b/scripts/gdb/load.py new file mode 100644 index 0000000..355f6b9 --- /dev/null +++ b/scripts/gdb/load.py @@ -0,0 +1,47 @@ +import sys +import os +import gdb +import importlib.util + +script_path = os.path.abspath(__file__) +script_root = os.path.dirname(script_path) +repo_root = os.path.dirname(os.path.dirname(script_root)) + +if script_root not in sys.path: + sys.path.insert(0, script_root) + +from teachos.dump_mb2i import DumpMB2I + +components = { + "kstd": "libs/kstd/gdb", + "kapi": "kapi/gdb", +} + +for component, path in components.items(): + full_path = os.path.join(repo_root, *path.split("/")) + init_file = os.path.join(full_path, "__init__.py") + + if os.path.isfile(init_file): + try: + spec = importlib.util.spec_from_file_location(component, init_file) + module = importlib.util.module_from_spec(spec) + sys.modules[component] = module + spec.loader.exec_module(module) + + if hasattr(module, "register_printers"): + module.register_printers(gdb.current_objfile()) + gdb.write(f"Info: Registered pretty printers for '{component}'.\n") + else: + gdb.write( + f"Warning: '{component}' does not have 'register_printers' function\n" + ) + except Exception as e: + gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") + else: + gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") + +gdb.write("Info: Loaded TeachOS pretty printers.\n") + +DumpMB2I() + +gdb.write("Info: Loaded TeachOS tools.\n") \ No newline at end of file diff --git a/scripts/gdb/pretty_printers.py b/scripts/gdb/pretty_printers.py deleted file mode 100644 index c209328..0000000 --- a/scripts/gdb/pretty_printers.py +++ /dev/null @@ -1,41 +0,0 @@ -import sys -import os -import gdb -import importlib.util - -script_path = os.path.abspath(__file__) -script_root = os.path.dirname(script_path) -repo_root = os.path.dirname(os.path.dirname(script_root)) - -if script_root not in sys.path: - sys.path.insert(0, script_root) - -components = { - "kstd": "libs/kstd/gdb", - "kapi": "kapi/gdb", -} - -for component, path in components.items(): - full_path = os.path.join(repo_root, *path.split("/")) - init_file = os.path.join(full_path, "__init__.py") - - if os.path.isfile(init_file): - try: - spec = importlib.util.spec_from_file_location(component, init_file) - module = importlib.util.module_from_spec(spec) - sys.modules[component] = module - spec.loader.exec_module(module) - - if hasattr(module, "register_printers"): - module.register_printers(gdb.current_objfile()) - gdb.write(f"Info: Registered pretty printers for '{component}'.\n") - else: - gdb.write( - f"Warning: '{component}' does not have 'register_printers' function\n" - ) - except Exception as e: - gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") - else: - gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") - -gdb.write("Info: Loaded TeachOS pretty printers.\n") diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py new file mode 100644 index 0000000..ff15fde --- /dev/null +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -0,0 +1,147 @@ +import gdb +import struct + + +TAG_NAMES = { + 0: "End of Tags", + 1: "Boot Command Line", + 2: "Boot Loader Name", + 3: "Boot Module", + 4: "Basic Memory Information", + 5: "BIOS Boot Device", + 6: "Memory Map", + 7: "VBE Info", + 8: "Framebuffer Info", + 9: "ELF Symbols", + 10: "APM Table", + 14: "ACPI old RSDP (1.0)", + 15: "ACPI new RSDP (2.0+)", + 21: "Image Load Base Physical Address", +} + +MEMORY_TYPES = { + 1: "Available", + 2: "Reserved", + 3: "ACPI Reclaim", + 4: "Non-volatile storage", + 5: "Bad Memory", +} + +INDENT = f"{' '*11}" + + +class DumpMB2I(gdb.Command): + """ + Dump the information provided by the Multiboot2 loader. + Usage: dump_mb2i + Example: dump_mb2i $rbx + dump_mb2i 0x8000 + """ + + def __init__(self): + super(DumpMB2I, self).__init__("dump_mb2i", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + if not arg: + gdb.write( + "Error: please provide the address of the Multiboot2 information structure.\n" + ) + return + + try: + address = int(gdb.parse_and_eval(arg)) + except gdb.error as e: + gdb.write(f"Error: Invalid address expression: {e}\n") + return + + inferior = gdb.selected_inferior() + + try: + header_data = inferior.read_memory(address, 8).tobytes() + total_size, reserved = struct.unpack(" 1024 * 1024: + gdb.write("Warning: suspicious total size ({total_size} bytes). Aborting.") + return + + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"| Multiboot2 Boot Information Payload{' ':>34}|\n") + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"Base Address: {address:#018x}\nTotal Size: {total_size} Bytes\n\n") + + offset = 8 + + while offset < total_size: + tag_address = address + offset + + try: + tag_header = inferior.read_memory(tag_address, 8).tobytes() + tag_type, tag_size = struct.unpack("\n") -- cgit v1.2.3