diff --git a/components/backtracer/LionsOS_Backtracer.py b/components/backtracer/LionsOS_Backtracer.py new file mode 100644 index 000000000..2e639037c --- /dev/null +++ b/components/backtracer/LionsOS_Backtracer.py @@ -0,0 +1,113 @@ +# Copyright 2026, UNSW +# SPDX-License-Identifier: BSD-2-Clause +import argparse +from dataclasses import dataclass +from typing import List +from sdfgen import SystemDescription, Sddf, DeviceTree, LionsOs +from importlib.metadata import version +from board import BOARDS +from subprocess import run +from copy import deepcopy + +# assert version("sdfgen").split(".")[1] == "28", "Unexpected sdfgen version" + +ProtectionDomain = SystemDescription.ProtectionDomain +ProtectionDomain.PRIORITY_MAX = 254 + +MemoryRegion = SystemDescription.MemoryRegion +Map = SystemDescription.Map +Channel = SystemDescription.Channel + + +def get_architecture_pointer_alignment(arch: SystemDescription.Arch): + match arch: + case ( + SystemDescription.Arch.AARCH64 + | SystemDescription.Arch.RISCV64 + | SystemDescription.Arch.X86_64 + ): + return 8 + case ( + SystemDescription.Arch.AARCH32 + | SystemDescription.Arch.RISCV32 + | SystemDescription.Arch.X86 + ): + return 4 + case _: + raise Exception(f"Alignment of architecture {arch} is unknown.") + + +def enable_backtracing( + sdf, arch, array_of_pds_or_single_pd, *, show_backtrace_func_list_addr=0xB00000, backtracer_stack_size=0x10000, + pd_callback_channel_id=61, endianness="little" +): + """ + Wrap an array or single pd as children into a backtracer parent, + capable of catching faults and then forcing prints of the backtrace + Remember to compile each of the children with "backtrace.o" + """ + backtracer = ProtectionDomain( + "backtracer", + "backtracer.elf", + priority=ProtectionDomain.PRIORITY_MAX, + stack_size=backtracer_stack_size, + ) + + pd_elf_paths = [] + if not isinstance(array_of_pds_or_single_pd, list): + array_of_pds_or_single_pd = [array_of_pds_or_single_pd] + + for i, child_pd in enumerate(array_of_pds_or_single_pd): + # Also add a channel for allowing a thread to pause completely + newChannel = Channel( + child_pd, + backtracer, + a_id=pd_callback_channel_id, + b_id=i, + pp_a=True, + pd_a_setvar_id="unwind_helper_channel_to_backtracer", + ) + sdf.add_channel(newChannel) + backtracer.add_child_pd(child_pd) + pd_elf_paths.append(child_pd.program_image) + + # Create a memory region at the predefined address, as an array + # Extract each of the addresses of the children's show_backtrace function + pd_show_backtrace_addrs = [] + for elf_path in pd_elf_paths: + # Not very stable but good enough for now. + shell_output = run( + f'set -o pipefail && nm {elf_path} | grep "show_backtrace" | cut --delimiter=" " -f 1', + capture_output=True, + shell=True, + text=True, + ) + if shell_output.returncode != 0: + raise Exception( + f"Failed to get addresses of 'show_backtrace' for file {elf_path}\n" + + f"stderr: {shell_output.stderr}\n" + + f"stdout: {shell_output.stdout}\n" + + f"exit code: {shell_output.returncode}\n" + ) + show_backtrace_addr = int(shell_output.stdout.strip(), 16) + print(f"'{elf_path}':show_backtrace @ {hex(show_backtrace_addr)}") + pd_show_backtrace_addrs.append(show_backtrace_addr) + + # now write a .data file containing the files spaced out by architectures pointer size. + alignment = get_architecture_pointer_alignment(arch) + print(f"Alignment for architecture {arch.name}: {alignment}") + frame = b"" + for backtrace_addr in pd_show_backtrace_addrs: + frame += bytes(backtrace_addr.to_bytes(alignment, endianness)) + BACKTRACER_FUNCTION_DATA_PATH = "backtrace_functions.data" + with open(BACKTRACER_FUNCTION_DATA_PATH, "wb") as dataFile: + dataFile.write(frame) + func_list_mr = MemoryRegion( + sdf, "backtracerFunctions", prefill_path=BACKTRACER_FUNCTION_DATA_PATH + ) + sdf.add_mr(func_list_mr) + func_list_map = Map( + func_list_mr, vaddr=0x20000, perms="r", setvar_vaddr="backtraceFunctions" + ) + backtracer.add_map(func_list_map) + return backtracer diff --git a/components/backtracer/README.md b/components/backtracer/README.md new file mode 100644 index 000000000..963c3acb5 --- /dev/null +++ b/components/backtracer/README.md @@ -0,0 +1,30 @@ + +# Usage +1. Include `backtracer.mk` file. +2. For all targets to be backtraced: + 1. Add `libunwind.a` and `unwind_helpers.o` as targets and add them to be compiled with the target. + 3. Add `-funwind-tables` to `CFLAGS` + 4. Add `--eh-frame-hdr -L{Directory of backtracer}` to `LDFLAGS` + 5. Add `-Tunwind.ld -lunwind` to `LIBS` + 6. Optionally add `--start-group` and `--end-group` to the beginning and end of `LIBS` respectively. +3. For `meta.py`: + 1. Make sure to add the path to this directory in to `PYTHONPATH` + 2. Import `LionsOS_Backtracer` + 3. For all PDs to be traced, add them to a list and pass them to the `enable_backtracing` function. + This function returns a parent PD containing all of the children given. + The function prototype is `enable_backtracing(sdf, architecture, PD_list) -> PD` + +See the `backtrace_test` example for more details. + +# Dependencies +- `libc` for `libunwind` +- llvm-project's `libunwind` +- `sddf` or some implementation of `printf` +- patched `sdfgen` with Memory region prefilling capabilities (updated sdfgen) + +# Cons +- Larger binary sizes +- Depending on `llvm-project` (sorry) diff --git a/components/backtracer/backtracer.c b/components/backtracer/backtracer.c new file mode 100644 index 000000000..3830d4c3f --- /dev/null +++ b/components/backtracer/backtracer.c @@ -0,0 +1,67 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +#include "monitor.h" +#include +#include + +void (**backtraceFunctions)() = NULL; + +void init() +{ + LOG("Backtracer initialised!\n"); + LOG("Backtracer table starting address: %p\n", backtraceFunctions); +} + +#if defined(__aarch64__) +static void callConvention_prologue(seL4_UserContext *ctxt, uintptr_t funcAddr) +{ + // Set the link register to old PC + ctxt->x30 = ctxt->pc; + // Set the PC to the next function + ctxt->pc = funcAddr; + LOG("Old PC: %p, New PC: %p\n", (void *)ctxt->x30, (void *)funcAddr); +} +#elif defined(__riscv__) +#error "Unimplemented backtracer for riscv" +#elif defined(__x86_64__) +#error "Unimplemented backtracer for x86_64" +#else +#error "Unsupported architecture for backtracing" +#endif + +seL4_Bool fault(microkit_child child, microkit_msginfo msginfo, microkit_msginfo *reply_msginfo) +{ + LOG("Child '%d' Faulted!\n", child); + print_fault_error(child, msginfo); + seL4_UserContext ctxt = { 0 }; + int readRegResult = seL4_TCB_ReadRegisters(BASE_TCB_CAP + child, seL4_True, 0, + sizeof(seL4_UserContext) / sizeof(seL4_Word), &ctxt); + if (readRegResult != 0) { + LOG("Failed to read registers for setting up backtrace jump! Got %d, " + "expected %d\n", + readRegResult, 0); + return seL4_False; + } + print_tcb_registers(&ctxt); + callConvention_prologue(&ctxt, (uintptr_t)(backtraceFunctions[child])); + int writeRegResult = seL4_TCB_WriteRegisters(BASE_TCB_CAP + child, seL4_True, 0, + sizeof(seL4_UserContext) / sizeof(seL4_Word), &ctxt); + if (writeRegResult != 0) { + LOG("Failed to write registers for setting up backtrace jump! Got error value %d, " + "expected %d\n", + writeRegResult, 0); + return seL4_False; + } + return seL4_True; +} + +void notified(microkit_channel ch) +{ +} +microkit_msginfo protected(microkit_channel ch, microkit_msginfo msginfo) +{ + microkit_pd_stop(ch); + return msginfo; +} diff --git a/components/backtracer/backtracer.mk b/components/backtracer/backtracer.mk new file mode 100644 index 000000000..ea1c203ea --- /dev/null +++ b/components/backtracer/backtracer.mk @@ -0,0 +1,79 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +TAR ?= tar + +BACKTRACER_DIR := $(LIONSOS)/components/backtracer +LLVM := llvm-project-22.1.8.src +LLVM_TAR := llvm-project-22.1.8.src.tar.xz +LLVM_URL := https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz +LIBUNWIND := $(LLVM)/libunwind +LIBUNWIND_BUILD_DIR := ./libunwind + +CFLAGS_backtracer := \ + $(CFLAGS)\ + -I$(LIONSOS)/include \ + -I$(SDDF)/include \ + -I$(SDDF)/include/microkit \ + -I$(LIBUNWIND)/include \ + -I$(BOARD_DIR)/include \ + -I$(LIONS_LIBC)/include -funwind-tables + +LDFLAGS_backtracer := -L$(BOARD_DIR)/lib -L$(LIONS_LIBC)/lib +LIBS_backtracer := -lmicrokit -Tmicrokit.ld libsddf_util_debug.a -lc + +LLVM_CMAKE_FLAGS := \ + -DLLVM_ENABLE_RUNTIMES=libunwind\ + -DCMAKE_SYSTEM_NAME=Generic\ + -DCMAKE_C_COMPILER_TARGET=$(TARGET)\ + -DCMAKE_CXX_COMPILER_TARGET=$(TARGET)\ + -DCMAKE_ASM_COMPILER_TARGET=$(TARGET)\ + -DLIBUNWIND_IS_BAREMETAL=ON\ + -DLIBUNWIND_ENABLE_SHARED=OFF\ + -DLIBUNWIND_ENABLE_THREADS=OFF\ + -DLIBUNWIND_USE_COMPILER_RT=ON\ + -DLIBUNWIND_ENABLE_PEDANTIC=OFF\ + -DLIBUNWIND_ENABLE_ASSERTIONS=OFF\ + -DCMAKE_BUILD_TYPE=Debug\ + -DLIBUNWIND_ENABLE_STATIC=ON\ + -DCMAKE_C_COMPILER=$(CC)\ + -DCMAKE_CXX_COMPILER=$(CXX)\ + -DCMAKE_ASM_COMPILER=$(CC)\ + -DCMAKE_C_FLAGS="-I$(LIONS_LIBC)/include"\ + -DCMAKE_CXX_FLAGS="-I$(LIONS_LIBC)/include -fno-exceptions"\ + -DCMAKE_C_COMPILER_WORKS=ON\ + -DCMAKE_CXX_COMPILER_WORKS=ON\ + -DCMAKE_ASM_COMPILER_WORKS=ON + +backtracer: + mkdir -p $@ + +unwind_helpers.o: $(BACKTRACER_DIR)/unwind_helpers.c | $(LIONS_LIBC)/include backtracer $(LLVM) + ${CC} ${CFLAGS_backtracer} -c -o $@ $< + +backtracer/backtracer.o: $(BACKTRACER_DIR)/backtracer.c | $(LIONS_LIBC)/include backtracer + ${CC} ${CFLAGS_backtracer} -c -o $@ $< + +backtracer.elf: backtracer/backtracer.o libunwind.a | backtracer + ${LD} ${LDFLAGS_backtracer} -o $@ $^ ${LIBS_backtracer} + +$(LLVM_TAR): + wget $(LLVM_URL) + +$(LLVM): $(LLVM_TAR) + ${TAR} xvf $< $@/{libunwind,runtimes,cmake,utils,third-party} $@/llvm/{cmake,utils} + +$(LIBUNWIND_BUILD_DIR): | $(LLVM) $(LIONS_LIBC)/include + cmake -B $(LIBUNWIND_BUILD_DIR) -S $(LLVM)/runtimes \ + $(LLVM_CMAKE_FLAGS) + +libunwind.a: | $(LIONS_LIBC)/include $(LIBUNWIND_BUILD_DIR) + ${MAKE} -C $(LIBUNWIND_BUILD_DIR) + cp $(LIBUNWIND_BUILD_DIR)/lib/libunwind.a $@ + +clean:: + ${RM} -rf backtracer backtracer.elf unwind_helpers.o libunwind.a libunwind + +-include unwind_helpers.d backtrace/backtracer.d diff --git a/components/backtracer/monitor.h b/components/backtracer/monitor.h new file mode 100644 index 000000000..86ea8c2fb --- /dev/null +++ b/components/backtracer/monitor.h @@ -0,0 +1,621 @@ +#pragma once +/* + * Copyright 2021, Breakaway Consulting Pty. Ltd. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#include +#include + +#define LOG(...) sddf_printf("BACKTRACER | " __VA_ARGS__) + +#define MAX_PDS 64 + +#define BASE_PD_TCB_CAP 10 +#define BASE_SCHED_CONTEXT_CAP 138 +#define BASE_NOTIFICATION_CAP 202 + +/* Sanity check that the architecture specific macro have been set. */ +#if defined(__aarch64__) +#elif defined(__x86_64__) +#elif defined(__riscv64__) +#else +#error "Unknown or unsupported architecture for backtracing" +#endif + +#ifdef __riscv64__ +/* + * Convert the fault status register given by the kernel into a string + * describing what fault happened. The FSR is the 'scause' register. + */ +static char *riscv_fsr_to_string(seL4_Word fsr) +{ + switch (fsr) { + case 0: + return "Instruction address misaligned"; + case 1: + return "Instruction access fault"; + case 2: + return "Illegal instruction"; + case 3: + return "Breakpoint"; + case 4: + return "Load address misaligned"; + case 5: + return "Load access fault"; + case 6: + return "Store/AMO address misaligned"; + case 7: + return "Store/AMO access fault"; + case 8: + return "Environment call from U-mode"; + case 9: + return "Environment call from S-mode"; + case 12: + return "Instruction page fault"; + case 13: + return "Load page fault"; + case 15: + return "Store/AMO page fault"; + case 18: + return "Software check"; + case 19: + return "Hardware error"; + default: + return ""; + } +} +#endif + +#ifdef __aarch64__ +static char *ec_to_string(uintptr_t ec) +{ + switch (ec) { + case 0: + return "Unknown reason"; + case 1: + return "Trapped WFI or WFE instruction execution"; + case 3: + return "Trapped MCR or MRC access with (coproc==0b1111) this is not " + "reported using EC 0b000000"; + case 4: + return "Trapped MCRR or MRRC access with (coproc==0b1111) this is not " + "reported using EC 0b000000"; + case 5: + return "Trapped MCR or MRC access with (coproc==0b1110)"; + case 6: + return "Trapped LDC or STC access"; + case 7: + return "Access to SVC, Advanced SIMD or floating-point functionality " + "trapped"; + case 12: + return "Trapped MRRC access with (coproc==0b1110)"; + case 13: + return "Branch Target Exception"; + case 17: + return "SVC instruction execution in AArch32 state"; + case 21: + return "SVC instruction execution in AArch64 state"; + case 24: + return "Trapped MSR, MRS or System instruction exuection in AArch64 state, " + "this is not reported using EC 0xb000000, 0b000001 or 0b000111"; + case 25: + return "Access to SVE functionality trapped"; + case 28: + return "Exception from a Pointer Authentication instruction authentication " + "failure"; + case 32: + return "Instruction Abort from a lower Exception level"; + case 33: + return "Instruction Abort taken without a change in Exception level"; + case 34: + return "PC alignment fault exception"; + case 36: + return "Data Abort from a lower Exception level"; + case 37: + return "Data Abort taken without a change in Exception level"; + case 38: + return "SP alignment faultr exception"; + case 40: + return "Trapped floating-point exception taken from AArch32 state"; + case 44: + return "Trapped floating-point exception taken from AArch64 state"; + case 47: + return "SError interrupt"; + case 48: + return "Breakpoint exception from a lower Exception level"; + case 49: + return "Breakpoint exception taken without a change in Exception level"; + case 50: + return "Software Step exception from a lower Exception level"; + case 51: + return "Software Step exception taken without a change in Exception level"; + case 52: + return "Watchpoint exception from a lower Exception level"; + case 53: + return "Watchpoint exception taken without a change in Exception level"; + case 56: + return "BKPT instruction execution in AArch32 state"; + case 60: + return "BRK instruction execution in AArch64 state"; + } + return ""; +} + +static char *data_abort_dfsc_to_string(uintptr_t dfsc) +{ + switch (dfsc) { + case 0x00: + return "address size fault, level 0"; + case 0x01: + return "address size fault, level 1"; + case 0x02: + return "address size fault, level 2"; + case 0x03: + return "address size fault, level 3"; + case 0x04: + return "translation fault, level 0"; + case 0x05: + return "translation fault, level 1"; + case 0x06: + return "translation fault, level 2"; + case 0x07: + return "translation fault, level 3"; + case 0x09: + return "access flag fault, level 1"; + case 0x0a: + return "access flag fault, level 2"; + case 0x0b: + return "access flag fault, level 3"; + case 0x0d: + return "permission fault, level 1"; + case 0x0e: + return "permission fault, level 2"; + case 0x0f: + return "permission fault, level 3"; + case 0x10: + return "synchronuos external abort"; + case 0x11: + return "synchronous tag check fault"; + case 0x14: + return "synchronous external abort, level 0"; + case 0x15: + return "synchronous external abort, level 1"; + case 0x16: + return "synchronous external abort, level 2"; + case 0x17: + return "synchronous external abort, level 3"; + case 0x18: + return "synchronous parity or ECC error"; + case 0x1c: + return "synchronous parity or ECC error, level 0"; + case 0x1d: + return "synchronous parity or ECC error, level 1"; + case 0x1e: + return "synchronous parity or ECC error, level 2"; + case 0x1f: + return "synchronous parity or ECC error, level 3"; + case 0x21: + return "alignment fault"; + case 0x30: + return "tlb conflict abort"; + case 0x31: + return "unsupported atomic hardware update fault"; + } + return ""; +} +#endif + +#ifdef __x86_64__ +static char *page_fault_to_string(seL4_Word fsr) +{ + // https://wiki.osdev.org/Exceptions#Page_Fault + switch (fsr) { + case 0 | 4: + return "read to a non-present page at ring 3"; + case 1 | 4: + return "page-protection violation from read at ring 3"; + case 2 | 4: + return "write to a non-present page at ring 3"; + case 3 | 4: + return "page-protection violation from write at ring 3"; + case 16: + // Note that seL4 currently does not implement the NX/XD bit + // to mark a page as non-executable so we will never see the below message. + return "instruction fetch from non-executable page"; + default: + return "invalid FSR or unimplemented decoding"; + } +} +#endif + +/* UBSAN decoding related functionality */ +#define UBSAN_ARM64_BRK_IMM 0x5500 +#define UBSAN_ARM64_BRK_MASK 0x00ff +#define ESR_COMMENT_MASK ((1 << 16) - 1) +#define ARM64_BRK_EC 60 + +/* + * ABI defined by Clang's UBSAN enum SanitizerHandler: + * https://github.com/llvm/llvm-project/blob/release/16.x/clang/lib/CodeGen/CodeGenFunction.h#L113 + */ +enum UBSAN_CHECKS { + UBSAN_ADD_OVERFLOW, + UBSAN_BUILTIN_UNREACHABLE, + UBSAN_CFI_CHECK_FAIL, + UBSAN_DIVREM_OVERFLOW, + UBSAN_DYNAMIC_TYPE_CACHE_MISS, + UBSAN_FLOAT_CAST_OVERFLOW, + UBSAN_FUNCTION_TYPE_MISMATCH, + UBSAN_IMPLICIT_CONVERSION, + UBSAN_INVALID_BUILTIN, + UBSAN_INVALID_OBJC_CAST, + UBSAN_LOAD_INVALID_VALUE, + UBSAN_MISSING_RETURN, + UBSAN_MUL_OVERFLOW, + UBSAN_NEGATE_OVERFLOW, + UBSAN_NULLABILITY_ARG, + UBSAN_NULLABILITY_RETURN, + UBSAN_NONNULL_ARG, + UBSAN_NONNULL_RETURN, + UBSAN_OUT_OF_BOUNDS, + UBSAN_POINTER_OVERFLOW, + UBSAN_SHIFT_OUT_OF_BOUNDS, + UBSAN_SUB_OVERFLOW, + UBSAN_TYPE_MISMATCH, + UBSAN_ALIGNMENT_ASSUMPTION, + UBSAN_VLA_BOUND_NOT_POSITIVE, +}; + +#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT +static char *usban_code_to_string(seL4_Word code) +{ + switch (code) { + case UBSAN_ADD_OVERFLOW: + return "add overflow"; + case UBSAN_BUILTIN_UNREACHABLE: + return "builtin unreachable"; + case UBSAN_CFI_CHECK_FAIL: + return "control-flow-integrity check fail"; + case UBSAN_DIVREM_OVERFLOW: + return "division remainder overflow"; + case UBSAN_DYNAMIC_TYPE_CACHE_MISS: + return "dynamic type cache miss"; + case UBSAN_FLOAT_CAST_OVERFLOW: + return "float case overflow"; + case UBSAN_FUNCTION_TYPE_MISMATCH: + return "function type mismatch"; + case UBSAN_IMPLICIT_CONVERSION: + return "implicit conversion"; + case UBSAN_INVALID_BUILTIN: + return "invalid builtin"; + case UBSAN_INVALID_OBJC_CAST: + return "invalid objc cast"; + case UBSAN_LOAD_INVALID_VALUE: + return "load invalid value"; + case UBSAN_MISSING_RETURN: + return "missing return"; + case UBSAN_MUL_OVERFLOW: + return "multiplication overflow"; + case UBSAN_NEGATE_OVERFLOW: + return "negate overflow"; + case UBSAN_NULLABILITY_ARG: + return "nullability argument"; + case UBSAN_NULLABILITY_RETURN: + return "nullability return"; + case UBSAN_NONNULL_ARG: + return "non-null argument"; + case UBSAN_NONNULL_RETURN: + return "non-null return"; + case UBSAN_OUT_OF_BOUNDS: + return "out of bounds access"; + case UBSAN_POINTER_OVERFLOW: + return "pointer overflow"; + case UBSAN_SHIFT_OUT_OF_BOUNDS: + return "shift out of bounds"; + case UBSAN_SUB_OVERFLOW: + return "subtraction overflow"; + case UBSAN_TYPE_MISMATCH: + return "type mismatch"; + case UBSAN_ALIGNMENT_ASSUMPTION: + return "alignment assumption"; + case UBSAN_VLA_BOUND_NOT_POSITIVE: + return "variable-length-array bound not positive"; + default: + return "unknown reason"; + } +} +#endif + +static void print_tcb_registers(seL4_UserContext *regs) +{ +#if defined(__riscv64__) + LOG("BACKTRACER | Registers: \n"); + LOG("BACKTRACER | pc : %#016lx\n", regs->pc); + LOG("ra : %#016lx\n", regs->ra); + LOG("s0 : %#016lx\n", regs->s0); + LOG("s1 : %#016lx\n", regs->s1); + LOG("s2 : %#016lx\n", regs->s2); + LOG("s3 : %#016lx\n", regs->s3); + LOG("s4 : %#016lx\n", regs->s4); + LOG("s5 : %#016lx\n", regs->s5); + LOG("s6 : %#016lx\n", regs->s6); + LOG("s7 : %#016lx\n", regs->s7); + LOG("s8 : %#016lx\n", regs->s8); + LOG("s9 : %#016lx\n", regs->s9); + LOG("s10 : %#016lx\n", regs->s10); + LOG("s11 : %#016lx\n", regs->s11); + LOG("a0 : %#016lx\n", regs->a0); + LOG("a1 : %#016lx\n", regs->a1); + LOG("a2 : %#016lx\n", regs->a2); + LOG("a3 : %#016lx\n", regs->a3); + LOG("a4 : %#016lx\n", regs->a4); + LOG("a5 : %#016lx\n", regs->a5); + LOG("a6 : %#016lx\n", regs->a6); + LOG("t0 : %#016lx\n", regs->t0); + LOG("t1 : %#016lx\n", regs->t1); + LOG("t2 : %#016lx\n", regs->t2); + LOG("t3 : %#016lx\n", regs->t3); + LOG("t4 : %#016lx\n", regs->t4); + LOG("t5 : %#016lx\n", regs->t5); + LOG("t6 : %#016lx\n", regs->t6); + LOG("tp : %#016lx\n", regs->tp); +#elif defined(__aarch64__) + LOG("Registers: \n"); + LOG("pc : %#016lx\n", regs->pc); + LOG("sp: %#016lx\n", regs->sp); + LOG("spsr : %#016lx\n", regs->spsr); + LOG("x0 : %#016lx\n", regs->x0); + LOG("x1 : %#016lx\n", regs->x1); + LOG("x2 : %#016lx\n", regs->x2); + LOG("x3 : %#016lx\n", regs->x3); + LOG("x4 : %#016lx\n", regs->x4); + LOG("x5 : %#016lx\n", regs->x5); + LOG("x6 : %#016lx\n", regs->x6); + LOG("x7 : %#016lx\n", regs->x7); + LOG("x8 : %#016lx\n", regs->x8); + LOG("x16 : %#016lx\n", regs->x16); + LOG("x17 : %#016lx\n", regs->x17); + LOG("x18 : %#016lx\n", regs->x18); + LOG("x29 : %#016lx\n", regs->x29); + LOG("x30 : %#016lx\n", regs->x30); + LOG("x9 : %#016lx\n", regs->x9); + LOG("x10 : %#016lx\n", regs->x10); + LOG("x11 : %#016lx\n", regs->x11); + LOG("x12 : %#016lx\n", regs->x12); + LOG("x13 : %#016lx\n", regs->x13); + LOG("x14 : %#016lx\n", regs->x14); + LOG("x15 : %#016lx\n", regs->x15); + LOG("x19 : %#016lx\n", regs->x19); + LOG("x20 : %#016lx\n", regs->x20); + LOG("x21 : %#016lx\n", regs->x21); + LOG("x22 : %#016lx\n", regs->x22); + LOG("x23 : %#016lx\n", regs->x23); + LOG("x24 : %#016lx\n", regs->x24); + LOG("x25 : %#016lx\n", regs->x25); + LOG("x26 : %#016lx\n", regs->x26); + LOG("x27 : %#016lx\n", regs->x27); + LOG("x28 : %#016lx\n", regs->x28); + LOG("tpidr_el0 : %#016lx\n", regs->tpidr_el0); + LOG("tpidrro_el0 : %#016lx\n", regs->tpidrro_el0); +#elif defined(__x86_64__) + LOG("Registers: \n"); + LOG("rip : %#016lx\n", regs->rip); + LOG("rsp : %#016lx\n", regs->rsp); + LOG("rflags : %#016lx\n", regs->rflags); + LOG("rax : %#016lx\n", regs->rax); + LOG("rbx : %#016lx\n", regs->rbx); + LOG("rcx : %#016lx\n", regs->rcx); + LOG("rdx : %#016lx\n", regs->rdx); + LOG("rsi : %#016lx\n", regs->rsi); + LOG("rdi : %#016lx\n", regs->rdi); + LOG("rbp : %#016lx\n", regs->rbp); + LOG("r8 : %#016lx\n", regs->r8); + LOG("r9 : %#016lx\n", regs->r9); + LOG("r10 : %#016lx\n", regs->r10); + LOG("r11 : %#016lx\n", regs->r11); + LOG("r12 : %#016lx\n", regs->r12); + LOG("r13 : %#016lx\n", regs->r13); + LOG("r14 : %#016lx\n", regs->r14); + LOG("r15 : %#016lx\n", regs->r15); + LOG("fs_base : %#016lx\n", regs->fs_base); + LOG("gs_base : %#016lx\n", regs->gs_base); +#endif +} + +#ifdef __riscv64__ +static void riscv_print_vm_fault() +{ + seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); + seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); + seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); + LOG("BACKTRACER | VMFault: ip=%#016lx\n", ip); + puthex64(fault_addr); + puts(" fsr=%#016lx\n", fsr); + puts(is_instruction ? "(instruction fault)" : "(data fault)"); + puts("\n"); + puts("BACKTRACER | description of fault: "); + puts(riscv_fsr_to_string(fsr)); + puts("\n"); +} +#endif + +#ifdef __x86_64__ +static void x86_64_print_vm_fault() +{ + seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); + seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); + seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); + puts("BACKTRACER | VMFault: ip="); + puthex64(ip); + puts(" fault_addr="); + puthex64(fault_addr); + puts(" fsr="); + puthex64(fsr); + puts(" "); + puts(is_instruction ? "(instruction fault)" : "(data fault)"); + puts("\n"); + + puts("BACKTRACER | description of fault: "); + puts(page_fault_to_string(fsr)); + puts("\n"); +} +#endif + +#ifdef __aarch64__ +static void aarch64_print_vm_fault() +{ + seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); + seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); + seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); + seL4_Word ec = fsr >> 26; + seL4_Word il = fsr >> 25 & 1; + seL4_Word iss = fsr & 0x1ffffffUL; + LOG("VMFault: ip=%#016lx fault_addr=%#016lx\n", ip, fault_addr); + LOG(" fsr=%#016lx %s\n", fsr, is_instruction ? "(instruction fault)" : "(data fault)"); + LOG(" ec: %#08lx %s\n", ec, ec_to_string(ec)); + LOG(" il: %s iss: %#08lx\n", il ? "1" : "0", iss); + + if (ec == 0x24) { + /* FIXME: Note, this is not a complete decoding of the fault! Just some of + the more common fields! + */ + seL4_Word dfsc = iss & 0x3f; + bool ea = (iss >> 9) & 1; + bool cm = (iss >> 8) & 1; + bool s1ptw = (iss >> 7) & 1; + bool wnr = (iss >> 6) & 1; + LOG(" dfsc = %s (%#08lx)", data_abort_dfsc_to_string(dfsc), dfsc); + if (ea) { + sddf_printf(" -- external abort"); + } + if (cm) { + sddf_printf(" -- cache maint"); + } + if (s1ptw) { + sddf_printf(" -- stage 2 fault for stage 1 page table walk"); + } + if (wnr) { + sddf_printf(" -- write not read"); + } + sddf_printf("\n"); + } +} +#endif + +static void print_fault_error(microkit_child child, microkit_msginfo msginfo) +{ + seL4_Word tcb_cap = BASE_PD_TCB_CAP + child; + seL4_Word label = microkit_msginfo_get_label(msginfo); + seL4_Word err = { 0 }; + seL4_Word badge = child + 1; + + if (label == seL4_Fault_NullFault && child < MAX_PDS) { + /* This is a request from our PD to become passive */ + err = seL4_SchedContext_Bind(BASE_SCHED_CONTEXT_CAP + child, BASE_NOTIFICATION_CAP + child); + if (err != seL4_NoError) { + LOG("could not bind scheduling context to notification " + "object\n"); + } else { + LOG("PD id: '%d' is now passive!\n", child); + } + return; + } + + LOG("received message %#08lx badge: %#016lx tcb cap: %#016lx\n", label, badge, tcb_cap); + + switch (label) { + case seL4_Fault_CapFault: { + seL4_Word ip = seL4_GetMR(seL4_CapFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_CapFault_Addr); + seL4_Word in_recv_phase = seL4_GetMR(seL4_CapFault_InRecvPhase); + seL4_Word lookup_failure_type = seL4_GetMR(seL4_CapFault_LookupFailureType); + seL4_Word bits_left = seL4_GetMR(seL4_CapFault_BitsLeft); + seL4_Word depth_bits_found = seL4_GetMR(seL4_CapFault_DepthMismatch_BitsFound); + seL4_Word guard_found = seL4_GetMR(seL4_CapFault_GuardMismatch_GuardFound); + seL4_Word guard_bits_found = seL4_GetMR(seL4_CapFault_GuardMismatch_BitsFound); + + LOG("CapFault: ip=%#016lx fault_addr=%#016lx in_recv_phase=%s", ip, fault_addr, in_recv_phase == 0 ? "false" : "true"); + sddf_printf(" lookup_failure_type="); + + switch (lookup_failure_type) { + case seL4_NoFailure: + sddf_printf("seL4_NoFailure"); + break; + case seL4_InvalidRoot: + sddf_printf("seL4_InvalidRoot"); + break; + case seL4_MissingCapability: + sddf_printf("seL4_MissingCapability"); + break; + case seL4_DepthMismatch: + sddf_printf("seL4_DepthMismatch"); + break; + case seL4_GuardMismatch: + sddf_printf("seL4_GuardMismatch"); + break; + default: + sddf_printf("%#016lx", lookup_failure_type); + } + + if (lookup_failure_type == seL4_MissingCapability || lookup_failure_type == seL4_DepthMismatch + || lookup_failure_type == seL4_GuardMismatch) { + sddf_printf(" bits_left=%#016lx", bits_left); + } + if (lookup_failure_type == seL4_DepthMismatch) { + sddf_printf(" depth_bits_found=%#016lx", depth_bits_found); + } + if (lookup_failure_type == seL4_GuardMismatch) { + sddf_printf(" guard_found=%#016lx", guard_found); + sddf_printf(" guard_bits_found=%#016lx", guard_bits_found); + } + sddf_printf("\n"); + break; + } + case seL4_Fault_UserException: { + LOG("UserException\n"); + break; + } + case seL4_Fault_VMFault: { +#if defined(__aarch64__) + aarch64_print_vm_fault(); +#elif defined(__riscv64__) + riscv_print_vm_fault(); +#elif defined(__x86_64__) + x86_64_print_vm_fault(); +#else +#error "Unknown architecture to print a VM fault for" +#endif + break; + } +#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT + case seL4_Fault_VCPUFault: { + seL4_Word esr = seL4_GetMR(seL4_VCPUFault_HSR); + seL4_Word ec = esr >> 26; + + LOG("received vCPU fault with ESR: %#016lx\n", esr); + + seL4_Word esr_comment = esr & ESR_COMMENT_MASK; + if (ec == ARM64_BRK_EC && ((esr_comment & ~UBSAN_ARM64_BRK_MASK) == UBSAN_ARM64_BRK_IMM)) { + /* We likely have a UBSAN check going off from a brk instruction */ + seL4_Word ubsan_code = esr_comment & UBSAN_ARM64_BRK_MASK; + LOG("potential undefined behaviour detected by UBSAN for: " + "'%s'\n", usban_code_to_string(ubsan_code)); + } else { + LOG("Unknown vCPU fault\n"); + } + break; + } +#endif + default: + LOG("Unknown fault: %#016lx\n", label); + break; + } +} diff --git a/components/backtracer/unwind.ld b/components/backtracer/unwind.ld new file mode 100644 index 000000000..0e8eaaca4 --- /dev/null +++ b/components/backtracer/unwind.ld @@ -0,0 +1,20 @@ +/* + * Copyright 2026, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +SECTIONS +{ + .eh_frame : + { + __eh_frame_start = .; + KEEP(*(.eh_frame)) + __eh_frame_end = .; + } + .eh_frame_hdr : + { + __eh_frame_hdr_start = .; + KEEP(*(.eh_frame_hdr)) + __eh_frame_hdr_end = .; + } +} +INSERT AFTER .text; diff --git a/components/backtracer/unwind_helpers.c b/components/backtracer/unwind_helpers.c new file mode 100644 index 000000000..dae1fb9ee --- /dev/null +++ b/components/backtracer/unwind_helpers.c @@ -0,0 +1,32 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#define UNW_LOCAL_ONLY +#include + +static seL4_MessageInfo_t empty_msg = { 0 }; +uintptr_t unwind_helper_channel_to_backtracer = 0; + +void show_backtrace(void) +{ + sddf_printf("SHOW_BACKTRACE | BEGIN_SHOW_BACKTRACE for '%s'\n", microkit_name); + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t ip, sp; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + + seL4_Word depth = 0; + while (unw_step(&cursor) > 0) { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + sddf_printf("SHOW_BACKTRACE | #%d: ip = %p, sp = %p\n", (int)depth++, (void *)ip, (void *)sp); + } + microkit_dbg_puts("SHOW_BACKTRACE | END_SHOW_BACKTRACE\n"); + microkit_ppcall(unwind_helper_channel_to_backtracer, empty_msg); + microkit_dbg_puts("You're not supposed to see this\n"); +} diff --git a/dep/sddf b/dep/sddf index d1f5252ea..e689f1d97 160000 --- a/dep/sddf +++ b/dep/sddf @@ -1 +1 @@ -Subproject commit d1f5252ea64edab6087552eb4220e23c019c2fbe +Subproject commit e689f1d97d5ef151906862683620132034082f62 diff --git a/examples/backtrace_test/Makefile b/examples/backtrace_test/Makefile new file mode 100644 index 000000000..c9553c6a0 --- /dev/null +++ b/examples/backtrace_test/Makefile @@ -0,0 +1,37 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif +override MICROKIT_SDK:=$(abspath ${MICROKIT_SDK}) + +export LIONSOS ?= $(abspath ../..) +export BACKTRACE_TEST_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +export MICROKIT_CONFIG ?= debug +export BUILD_DIR ?= $(abspath build) +export MICROKIT_BOARD ?= qemu_virt_aarch64 + +IMAGE_FILE := $(BUILD_DIR)/backtrace_test.img +REPORT_FILE := $(BUILD_DIR)/report.txt + +all: ${IMAGE_FILE} + +qemu qemu-gdb ${IMAGE_FILE} ${REPORT_FILE} clean clobber libunwind.a: ${BUILD_DIR}/Makefile FORCE + ${MAKE} -C ${BUILD_DIR} MICROKIT_SDK=${MICROKIT_SDK} $(notdir $@) + +${BUILD_DIR}/Makefile: backtrace_test.mk Makefile + mkdir -p ${BUILD_DIR} + cp backtrace_test.mk $@ + echo "export LIONSOS ?= ${LIONSOS}" > $@ + echo "export BACKTRACE_TEST_DIR ?= ${BACKTRACE_TEST_DIR}" >> $@ + echo "export BUILD_DIR := ${BUILD_DIR}" >> $@ + echo "export MICROKIT_BOARD ?= ${MICROKIT_BOARD}" >> $@ + echo "export MICROKIT_SDK ?= ${MICROKIT_SDK}" >> $@ + echo "export MICROKIT_CONFIG ?= ${MICROKIT_CONFIG}" >> $@ + cat backtrace_test.mk >> $@ + +FORCE: diff --git a/examples/backtrace_test/backtrace_test.mk b/examples/backtrace_test/backtrace_test.mk new file mode 100644 index 000000000..472fe5229 --- /dev/null +++ b/examples/backtrace_test/backtrace_test.mk @@ -0,0 +1,93 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +SUPPORTED_BOARDS := \ + qemu_virt_aarch64 \ + maaxboard + +IMAGES := \ + faulter.elf + +TOOLCHAIN ?= clang +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) +SDDF := $(LIONSOS)/dep/sddf +LLVM := $(LIONSOS)/dep/llvm-project/ +SYSTEM_FILE := backtrace_test.system +IMAGE_FILE := backtrace_test.img +REPORT_FILE := report.txt +BACKTRACER := $(LIONSOS)/components/backtracer + +all: ${IMAGE_FILE} + +include ${SDDF}/tools/make/board/common.mk + +METAPROGRAM := $(BACKTRACE_TEST_DIR)/meta.py + +FAT := $(LIONSOS)/components/fs/fat + +CFLAGS += \ + -Wno-bitwise-op-parentheses \ + -Wno-shift-op-parentheses \ + -Wno-unused-function \ + -Wno-tautological-constant-out-of-range-compare \ + -I$(LIONSOS)/include \ + -I$(SDDF)/include \ + -I$(SDDF)/include/microkit \ + -I$(LWIP)/include \ + -I$(LIBUNWIND)/include \ + -DMAX_FDS=8 \ + -funwind-tables -O0 -ggdb + +include $(LIONSOS)/lib/libc/libc.mk + +LDFLAGS := --eh-frame-hdr -L$(BOARD_DIR)/lib -L$(LIONS_LIBC)/lib -L$(BACKTRACE_TEST_DIR)/build -L$(BACKTRACER) +LIBS := --start-group -Tunwind.ld -lmicrokit -Tmicrokit.ld libsddf_util_debug.a -lc -lunwind --end-group + +SDDF_LIBC_INCLUDE := $(LIONS_LIBC)/include +include ${SDDF}/util/util.mk + +FAT_LIBC_LIB := $(LIONS_LIBC)/lib/libc.a +FAT_LIBC_INCLUDE := $(LIONS_LIBC)/include +include $(LIONSOS)/components/fs/fat/fat.mk + +include $(BACKTRACER)/backtracer.mk + +${IMAGES}: $(LIONS_LIBC)/lib/libc.a libsddf_util_debug.a + +faulter.o: $(BACKTRACE_TEST_DIR)/faulter.c | $(LIONS_LIBC)/include + ${CC} ${CFLAGS} -c -o $@ $< + +faulter.elf: faulter.o libunwind.a unwind_helpers.o + ${LD} ${LDFLAGS} -o $@ $^ ${LIBS} + +FORCE: + +$(SYSTEM_FILE): $(METAPROGRAM) $(IMAGES) $(DTB) backtracer.elf + PYTHONPATH="${SDDF}/tools/meta:${BACKTRACER}:$$PYTHONPATH:$(PYTHONPATH)" $(PYTHON) $(METAPROGRAM) --sddf $(SDDF) --board $(MICROKIT_BOARD) --output . --sdf $(SYSTEM_FILE) + # Add the unwind table to the memory region specified. + +$(IMAGE_FILE) $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) + $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) + +qemu_disk: + $(SDDF)/tools/mkvirtdisk $@ 1 512 16777216 GPT + +qemu: ${IMAGE_FILE} qemu_disk + $(QEMU) -machine virt,virtualization=on \ + -cpu cortex-a53 \ + -serial mon:stdio \ + -device loader,file=$(IMAGE_FILE),addr=0x70000000,cpu-num=0 \ + -m size=2G \ + -nographic \ + -global virtio-mmio.force-legacy=false \ + -d guest_errors \ + -drive file=qemu_disk,if=none,format=raw,id=hd \ + -device virtio-blk-device,drive=hd,bus=virtio-mmio-bus.1 \ + -device virtio-net-device,netdev=netdev0,bus=virtio-mmio-bus.0 \ + -netdev user,id=netdev0,hostfwd=tcp::5560-10.0.2.15:5560,hostfwd=tcp::5561-10.0.2.15:5561 + +clean:: + ${RM} -rf ${IMAGES} faulter.o faulter.elf diff --git a/examples/backtrace_test/faulter.c b/examples/backtrace_test/faulter.c new file mode 100644 index 000000000..53107e3fe --- /dev/null +++ b/examples/backtrace_test/faulter.c @@ -0,0 +1,35 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#define LOG(...) sddf_printf("FAULTER | " __VA_ARGS__) + +uintptr_t faulty_ptr = 0; + +void recurseFault(int depth) +{ + if (depth == 0) { + *(volatile int *)faulty_ptr; + return; + } + recurseFault(--depth); +} + +void init() +{ + LOG("Faulter initialised!\n"); + recurseFault(4); + LOG("After dereference\n"); +} +void notified(microkit_channel ch) +{ + LOG("Notified!\n"); +} +microkit_msginfo protected(microkit_channel ch, microkit_msginfo msginfo) +{ + LOG("Protected!\n"); +} diff --git a/examples/backtrace_test/meta.py b/examples/backtrace_test/meta.py new file mode 100644 index 000000000..cfbfe4d3d --- /dev/null +++ b/examples/backtrace_test/meta.py @@ -0,0 +1,48 @@ +# Copyright 2026, UNSW +# SPDX-License-Identifier: BSD-2-Clause +import argparse +from dataclasses import dataclass +from typing import List +from sdfgen import SystemDescription, Sddf, DeviceTree, LionsOs +from importlib.metadata import version +from board import BOARDS +from subprocess import run +from copy import deepcopy +import LionsOS_Backtracer + +# assert version("sdfgen").split(".")[1] == "28", "Unexpected sdfgen version" + +ProtectionDomain = SystemDescription.ProtectionDomain + +MemoryRegion = SystemDescription.MemoryRegion +Map = SystemDescription.Map +Channel = SystemDescription.Channel + + +def generate(sdf_path: str, output_dir: str): + domains = [ + ProtectionDomain(f"faulter{i}", "faulter.elf", priority=i, stack_size=0x100000) + for i in range(5) + ] + backtracer = LionsOS_Backtracer.enable_backtracing(sdf, board.arch, domains) + sdf.add_pd(backtracer) + + with open(f"{output_dir}/{sdf_path}", "w+") as f: + f.write(sdf.render()) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--sddf", required=True) + parser.add_argument("--board", required=True, choices=[b.name for b in BOARDS]) + parser.add_argument("--output", required=True) + parser.add_argument("--sdf", required=True) + + args = parser.parse_args() + + board = next(filter(lambda b: b.name == args.board, BOARDS)) + + sdf = SystemDescription(board.arch, board.paddr_top) + sddf = Sddf(args.sddf) + + generate(args.sdf, args.output) diff --git a/flake.lock b/flake.lock index 77bb57fac..f24903676 100644 --- a/flake.lock +++ b/flake.lock @@ -132,16 +132,16 @@ "zig-overlay": "zig-overlay" }, "locked": { - "lastModified": 1764823094, - "narHash": "sha256-Eeef5J0y31NH/F5Z0fayJ+4mroYfb8JPadu3cw6cNhc=", + "lastModified": 1781144170, + "narHash": "sha256-KcJ9hHnpG5Ho6gCxmhoU5iVonjLdB/OaA+L6CfYQeIQ=", "owner": "au-ts", "repo": "microkit_sdf_gen", - "rev": "13686a373b9189c7b1d778cd749d7d8bbd8a63f6", + "rev": "32e356aeaecbcd819c9b944fb55e37fad834c76c", "type": "github" }, "original": { "owner": "au-ts", - "ref": "0.28.1", + "ref": "mr_prefill_support", "repo": "microkit_sdf_gen", "type": "github" } @@ -216,11 +216,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1739707935, - "narHash": "sha256-YE+Zn03AWqDBmgxNrM50s7tpH+7fQYu1n9ZvXUWhznE=", + "lastModified": 1756037521, + "narHash": "sha256-nLOxILJqZtVCPCWppiDcEodTyguC6/EWvOprRuVoH4Q=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "18c6ec3d906c8f7e611687ca71705d6130e754c4", + "rev": "25d6cdf56abecb2b6a8e59905948ef41892aa371", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c6c2fcdb2..6399df1b1 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; zig-overlay.url = "github:mitchellh/zig-overlay"; - sdfgen.url = "github:au-ts/microkit_sdf_gen/0.28.1"; + sdfgen.url = "github:au-ts/microkit_sdf_gen/mr_prefill_support"; sdfgen.inputs.nixpkgs.follows = "nixpkgs"; };