aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorLukas Oesch <lukas.oesch@ost.ch>2026-06-10 10:40:46 +0200
committerLukas Oesch <lukas.oesch@ost.ch>2026-06-10 10:40:46 +0200
commit33abd5cf264cb9e34121082105b0bc17b3cf7a36 (patch)
tree36b15d53fea04f4f9d9af817100f7ad013bd9b5c /scripts
parentd01caf1c4aef3c89c68b9d1cc9fe56445f0860b5 (diff)
parent7e27130c342b7299a1d2188a7192a7f17b5ac2ad (diff)
downloadkernel-33abd5cf264cb9e34121082105b0bc17b3cf7a36.tar.xz
kernel-33abd5cf264cb9e34121082105b0bc17b3cf7a36.zip
Merge branch 'develop-BA-FS26' into 'develop'HEADdevelop
Merge of BA-FS26 branch into develop See merge request teachos/kernel!49
Diffstat (limited to 'scripts')
-rw-r--r--scripts/ci/parse_clang_tidy.py54
-rw-r--r--scripts/gdb/teachos.py47
-rw-r--r--scripts/gdb/teachos/__init__.py8
-rw-r--r--scripts/gdb/teachos/dump_mb2i.py241
-rw-r--r--scripts/gdb/toolchain.py35
-rwxr-xr-xscripts/qemu-wrapper.sh30
6 files changed, 413 insertions, 2 deletions
diff --git a/scripts/ci/parse_clang_tidy.py b/scripts/ci/parse_clang_tidy.py
new file mode 100644
index 0000000..ec51d77
--- /dev/null
+++ b/scripts/ci/parse_clang_tidy.py
@@ -0,0 +1,54 @@
+import hashlib
+import json
+import os
+import re
+import sys
+
+
+def parse_clang_tidy(log_file_path: str) -> list[dict]:
+ issues = []
+ pattern = re.compile(
+ r"^(.*?):(\d+):(\d+):\s+(warning|error|note):\s+(.*?)\s+\[(.*?)\]$"
+ )
+ repo_root = os.environ.get("CI_PROJECT_DIR", os.getcwd())
+
+ with open(log_file_path, "r") as f:
+ for line in f:
+ match = pattern.match(line)
+ if not match:
+ continue
+
+ path, line, column, severity, message, name = match.groups()
+ severity = "minor" if severity == "warning" else "major"
+
+ try:
+ path = os.path.relpath(path, repo_root)
+ except ValueError:
+ pass
+
+ fingerprint_data = f"{path}:{line}:{column}:{message}:{name}"
+ fingerprint = hashlib.sha256(fingerprint_data.encode()).hexdigest()
+
+ issues.append(
+ {
+ "description": f"{message} ({name})",
+ "fingerprint": fingerprint,
+ "severity": severity,
+ "location": {
+ "path": path,
+ "lines": {
+ "begin": int(line),
+ },
+ },
+ }
+ )
+
+ return issues
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ sys.exit("Usage: python3 parse_clang_tidy.py <CLANG_TIDY_LOG_PATH>")
+
+ issues = parse_clang_tidy(sys.argv[1])
+ print(json.dumps(issues, indent=2))
diff --git a/scripts/gdb/teachos.py b/scripts/gdb/teachos.py
new file mode 100644
index 0000000..355f6b9
--- /dev/null
+++ b/scripts/gdb/teachos.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/teachos/__init__.py b/scripts/gdb/teachos/__init__.py
new file mode 100644
index 0000000..a5eca92
--- /dev/null
+++ b/scripts/gdb/teachos/__init__.py
@@ -0,0 +1,8 @@
+def format_size(size):
+ for unit in ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"]:
+ if size < 1024.0:
+ if unit == "Bytes":
+ return f"{int(size)} {unit}"
+ return f"{size:.2f} {unit}"
+ size /= 1024.0
+ return f"{size:.2f} PiB"
diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py
new file mode 100644
index 0000000..0657ebd
--- /dev/null
+++ b/scripts/gdb/teachos/dump_mb2i.py
@@ -0,0 +1,241 @@
+import gdb
+import struct
+from enum import IntEnum
+from teachos import format_size
+
+
+class TagType(IntEnum):
+ END = 0
+ CMDLINE = 1
+ BOOT_LOADER_NAME = 2
+ MODULE = 3
+ BASIC_MEMINFO = 4
+ BOOTDEV = 5
+ MMAP = 6
+ VBE = 7
+ FRAMEBUFFER = 8
+ ELF_SECTIONS = 9
+ APM = 10
+ EFI32_SYSTEM_TABLE_POINTER = 11
+ EFI64_SYSTEM_TABLE_POINTER = 12
+ SMBIOS_TABLES = 13
+ ACPI_OLD = 14
+ ACPI_NEW = 15
+ NETWORKING = 16
+ EFI_MEMORY_MAP = 17
+ EFI_BOOT_SERVICES_NOT_TERMINATED = 18
+ EFI32_IMAGE_HANDLE_POINTER = 19
+ EFI64_IMAGE_HANDLE_POINTER = 20
+ LOAD_BASE_ADDR = 21
+
+
+class MemoryType(IntEnum):
+ AVAILABLE = 1
+ RESERVED = 2
+ ACPI_RECLAIM = 3
+ NVS = 4
+ BAD = 5
+
+
+class FramebufferType(IntEnum):
+ INDEXED = 0
+ RGB = 1
+ TEXT = 2
+
+
+TAG_NAMES = {
+ TagType.END: "End of Tags",
+ TagType.CMDLINE: "Boot Command Line",
+ TagType.BOOT_LOADER_NAME: "Boot Loader Name",
+ TagType.MODULE: "Boot Module",
+ TagType.BASIC_MEMINFO: "Basic Memory Information",
+ TagType.BOOTDEV: "BIOS Boot Device",
+ TagType.MMAP: "Memory Map",
+ TagType.VBE: "VBE Info",
+ TagType.FRAMEBUFFER: "Framebuffer Info",
+ TagType.ELF_SECTIONS: "ELF Symbols",
+ TagType.APM: "APM Table",
+ TagType.EFI32_SYSTEM_TABLE_POINTER: "EFI 32-bit System Table Pointer",
+ TagType.EFI64_SYSTEM_TABLE_POINTER: "EFI 64-bit System Table Pointer",
+ TagType.SMBIOS_TABLES: "SMBIOS Tables",
+ TagType.ACPI_OLD: "ACPI old RSDP (1.0)",
+ TagType.ACPI_NEW: "ACPI new RSDP (2.0+)",
+ TagType.NETWORKING: "Networking Information",
+ TagType.EFI_MEMORY_MAP: "EFI Memory Map",
+ TagType.EFI_BOOT_SERVICES_NOT_TERMINATED: "EFI Boot Services Not Terminated",
+ TagType.EFI32_IMAGE_HANDLE_POINTER: "EFI 32-bit Image Handle Pointer",
+ TagType.EFI64_IMAGE_HANDLE_POINTER: "EFI 64-bit Image Handle Pointer",
+ TagType.LOAD_BASE_ADDR: "Image Load Base Physical Address",
+}
+
+MEMORY_TYPES = {
+ MemoryType.AVAILABLE: "Available",
+ MemoryType.RESERVED: "Reserved",
+ MemoryType.ACPI_RECLAIM: "ACPI Reclaim",
+ MemoryType.NVS: "Non-volatile storage",
+ MemoryType.BAD: "Bad Memory",
+}
+
+FRAMEBUFFER_TYPES = {
+ FramebufferType.INDEXED: "Indexed Color",
+ FramebufferType.RGB: "RGB",
+ FramebufferType.TEXT: "Text Mode",
+}
+
+
+INDENT = f"{' '*11}"
+
+
+class DumpMB2I(gdb.Command):
+ """
+ Dump the information provided by the Multiboot2 loader.
+ Usage: dump_mb2i <address_expression>
+ 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("<II", header_data)
+ except gdb.MemoryError:
+ gdb.write(f"Error: Cannot read memory at {address:#018x}\n")
+ return
+
+ if total_size < 8 or total_size > 1024 * 1024:
+ gdb.write(
+ f"Warning: suspicious total size ({total_size} bytes). Aborting.\n"
+ )
+ 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("<II", tag_header)
+ except gdb.MemoryError:
+ gdb.write(
+ f"Error: Cannot read memory at {tag_address:#018x} (offset {offset})\n"
+ )
+ break
+
+ if tag_type == TagType.END and tag_size == 8:
+ break
+
+ self._print_tag(inferior, tag_address, tag_type, tag_size)
+
+ offset += (tag_size + 7) & ~7
+
+ def _print_tag(self, inferior, tag_address, tag_type, tag_size):
+ name = TAG_NAMES.get(tag_type, f"Unknown Tag")
+ size = format_size(tag_size)
+ payload = format_size(tag_size - 8)
+ gdb.write(f"[Tag {tag_type:#04x}] {name} (size: {size} | payload: {payload})\n")
+
+ if tag_size <= 8 and tag_type != TagType.END:
+ return
+
+ try:
+ data = inferior.read_memory(tag_address + 8, tag_size - 8).tobytes()
+
+ handlers = {
+ TagType.CMDLINE: self._write_string_tag,
+ TagType.BOOT_LOADER_NAME: self._write_string_tag,
+ TagType.MODULE: self._write_module_tag,
+ TagType.BASIC_MEMINFO: self._write_basic_memory_info_tag,
+ TagType.BOOTDEV: self._write_boot_device_tag,
+ TagType.MMAP: self._write_memory_map_tag,
+ TagType.LOAD_BASE_ADDR: self._write_image_load_base_physical_address_tag,
+ TagType.FRAMEBUFFER: self._write_framebuffer_info_tag,
+ }
+
+ handler = handlers.get(tag_type, self._write_unknown_tag)
+ handler(data)
+
+ except Exception as e:
+ gdb.write(
+ f"{INDENT}<failed to decode tag: {e} ({tag_type:#04x} | {tag_size:#06x})>\n"
+ )
+
+ def _write_string_tag(self, data):
+ text = data.split(b"\x00")[0].decode("ascii", "replace")
+ gdb.write(f'{INDENT}"{text}"\n')
+
+ def _write_module_tag(self, data):
+ start, end = struct.unpack_from("<II", data)
+ string = data[8:].split(b"\x00")[0].decode("ascii", "replace")
+ gdb.write(
+ f"{INDENT}start: {start:#010x} | end: {end:#010x} | size: {format_size(end - start)}\n"
+ )
+ if string:
+ gdb.write(f'{INDENT}string: "{string}"\n')
+
+ def _write_basic_memory_info_tag(self, data):
+ lower, upper = struct.unpack_from("<II", data)
+ gdb.write(
+ f"{INDENT}upper memory: {format_size(upper * 1024)} | lower memory: {format_size(lower * 1024)}\n"
+ )
+
+ def _write_boot_device_tag(self, data):
+ device, partition, sub_partition = struct.unpack_from("<III", data)
+ gdb.write(
+ f"{INDENT}device: {device:#04x} | partition: {partition:#06x} | sub-partition: {sub_partition:#06x}\n"
+ )
+
+ def _write_memory_map_tag(self, data):
+ entry_size, entry_version = struct.unpack_from("<II", data)
+ if entry_size < 24:
+ return
+ num_entries = (len(data) - 8) // entry_size
+ for i in range(num_entries):
+ entry_offset = 8 + i * entry_size
+ base, length, memory_type = struct.unpack_from("<QQI", data, entry_offset)
+ type_string = MEMORY_TYPES.get(memory_type, "Unknown")
+ gdb.write(
+ f"{INDENT}[{i:02d}] {base:#018x} - {base + length:#018x} | {format_size(length)} | type: {type_string}\n"
+ )
+
+ def _write_image_load_base_physical_address_tag(self, data):
+ base = struct.unpack("<I", data)[0]
+ gdb.write(f"{INDENT}address: {base:#010x}\n")
+
+ def _write_framebuffer_info_tag(self, data):
+ address, pitch, width, height, bpp, type = struct.unpack_from("<QIIIBB", data)
+ type_string = FRAMEBUFFER_TYPES.get(type, "Unknown")
+ gdb.write(f"{INDENT}address: {address:#010x} | type: {type_string}\n")
+ gdb.write(f"{INDENT}depth: {bpp} | pitch: {format_size(pitch)}\n")
+ if type == FramebufferType.TEXT:
+ gdb.write(f"{INDENT}width: {width} chars | height: {height} chars\n")
+ else:
+ gdb.write(f"{INDENT}width: {width} px | height: {height} px\n")
+
+ def _write_unknown_tag(self, data):
+ for i in range(0, len(data), 16):
+ chunk = data[i : i + 16]
+ hex_chunk = " ".join([f"{b:02x}" for b in chunk])
+ gdb.write(f"{INDENT}{i:#010x}: {hex_chunk}\n")
diff --git a/scripts/gdb/toolchain.py b/scripts/gdb/toolchain.py
new file mode 100644
index 0000000..bbb7810
--- /dev/null
+++ b/scripts/gdb/toolchain.py
@@ -0,0 +1,35 @@
+import os
+import subprocess
+import sys
+
+
+def setup_toolchain_debugging():
+ try:
+ gcc_path = (
+ subprocess.check_output(["which", "x86_64-pc-elf-g++"])
+ .decode("utf-8")
+ .strip()
+ )
+ python_dir = None
+
+ share_path = os.path.join(os.path.dirname(os.path.dirname(gcc_path)), "share")
+ for root, dirs, _ in os.walk(share_path):
+ if "libstdcxx" in dirs:
+ python_dir = root
+ break
+
+ if python_dir:
+ sys.path.insert(0, python_dir)
+
+ from libstdcxx.v6.printers import register_libstdcxx_printers
+ from libstdcxx.v6.xmethods import register_libstdcxx_xmethods
+
+ register_libstdcxx_printers(None)
+ register_libstdcxx_xmethods(None)
+
+ print(f"Loaded Printers & Xmethods from: {python_dir}")
+ except Exception as e:
+ print(f"Debug setup failed: {e}")
+
+
+setup_toolchain_debugging()
diff --git a/scripts/qemu-wrapper.sh b/scripts/qemu-wrapper.sh
index fa89ac5..dd0200d 100755
--- a/scripts/qemu-wrapper.sh
+++ b/scripts/qemu-wrapper.sh
@@ -1,4 +1,30 @@
#!/bin/bash
+# scripts/qemu-wrapper.sh
-echo "QEMU WRAPPER"
-qemu-system-x86_64 $@
+WORKSPACE_DIR=${1:-.}
+BUILD_TYPE=${2:-Debug}
+ISO_PATH=$3
+IS_DEBUG=${4:-0}
+
+if [ -z "$ISO_PATH" ]; then
+ echo "Usage: $0 <workspace_dir> <build_type> <iso_path> [is_debug]"
+ exit 1
+fi
+
+ARGS=(
+ "-m" "64M"
+ "-machine" "q35"
+ "-smp" "4,sockets=1,cores=4,threads=1"
+ "-display" "curses"
+ "-debugcon" "file:${WORKSPACE_DIR}/qemu-debugcon-${BUILD_TYPE}.log"
+ "-cdrom" "${ISO_PATH}"
+)
+
+if [ "$IS_DEBUG" == "1" ]; then
+ ARGS=( "-s" "-no-reboot" "-d" "int,cpu_reset" "${ARGS[@]}" )
+fi
+
+echo "QEMU WRAPPER: Executing TeachOS"
+echo "Arguments: ${ARGS[@]}"
+
+qemu-system-x86_64 "${ARGS[@]}" 2> "${WORKSPACE_DIR}/qemu-stderr-${BUILD_TYPE}.log"