kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 296361e67f879675a2e8f178cf6f9850a1a3e5ce
parent a8052633f6775e1cba26a4b7a6db19f237e98c22
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  4 Jun 2026 10:49:40 -0700

test: add freestanding qemu-system smoke

Diffstat:
Mmk/test.mk | 8++++++++
Msrc/asm/asm.c | 45+++++++++++++++++++++++++++++++++------------
Msrc/link/link_layout.c | 3++-
Msrc/obj/elf/link.c | 66+++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Atest/smoke/freestanding_system.sh | 393+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 485 insertions(+), 30 deletions(-)

diff --git a/mk/test.mk b/mk/test.mk @@ -894,6 +894,14 @@ test-smoke-rv64: test-smoke-rv32: bash test/smoke/rv32.sh +# test-smoke-freestanding-system: whole-toolchain bare-metal smoke for +# aarch64/x86_64/riscv64/riscv32 under qemu-system. Each lane compiles with kit, +# assembles its reset stub with kit, links with kit ld -T, then boots the image +# under the matching qemu-system binary. rv32 may need compiler-rt-style helpers +# for 64-bit operations, so build that freestanding runtime variant with kit too. +test-smoke-freestanding-system: bin rt-riscv32-elf-hardfloat + bash test/smoke/freestanding_system.sh + # test-parse-rv64-wide: end-to-end coverage of the rv64 128-bit scalar types # — __int128 (i128_*) and IEEE-754 binary128 long double (ldbl128_*) — built # with kit and run on riscv64. Exercises the soft-float / i128 lowering to diff --git a/src/asm/asm.c b/src/asm/asm.c @@ -142,22 +142,25 @@ static int starts_with(AsmDriver* d, Sym s, const char* prefix) { /* ---- section management ---- */ -static ObjSecId ensure_section(AsmDriver* d, Sym name, SecKind kind, u16 flags, - u32 align) { +static ObjSecId ensure_section_ex(AsmDriver* d, Sym name, SecKind kind, u16 sem, + u16 flags, u32 align) { ObjSecId* hit = SymSecMap_get(&d->sec_map, name); ObjSecId id; if (hit) return *hit; + id = obj_section_ex(d->ob, name, kind, sem, flags, align, 0, OBJ_SEC_NONE, 0); + SymSecMap_set(&d->sec_map, name, id); + return id; +} + +static ObjSecId ensure_section(AsmDriver* d, Sym name, SecKind kind, u16 flags, + u32 align) { /* A .bss section is NOBITS: it stores no bytes, only a size. Create it that * way (codegen does the same via obj_section_ex) so the ELF emitter writes * SHT_NOBITS and `.zero`/labels track bss_size, not a byte buffer — matching * `cc -c` so the round-tripped object isn't a writable-but-loaded .bss. */ - if (kind == SEC_BSS) - id = obj_section_ex(d->ob, name, kind, SSEM_NOBITS, flags, align, 0, - OBJ_SEC_NONE, 0); - else - id = obj_section(d->ob, name, kind, flags, align); - SymSecMap_set(&d->sec_map, name, id); - return id; + return ensure_section_ex(d, name, kind, + kind == SEC_BSS ? SSEM_NOBITS : SSEM_PROGBITS, flags, + align); } static void set_section(AsmDriver* d, Sym name, SecKind kind, u16 flags, @@ -746,6 +749,7 @@ static void do_directive(AsmDriver* d, Sym name) { d_panicf(d, "asm: .section: expected name"); } SecKind kind = SEC_OTHER; + u16 sem = SSEM_PROGBITS; u16 flags = 0; u32 entsize = 0; int have_flags = 0; @@ -834,11 +838,26 @@ static void do_directive(AsmDriver* d, Sym name) { (void)d_next(d); if (asm_driver_eat_comma(d)) { AsmTok ty = d_peek(d); + Sym tag = 0; if (tok_is_punct(ty, '@')) { (void)d_next(d); - (void)d_next(d); /* the @type ident (progbits/nobits) */ + AsmTok ti = d_next(d); /* the @type ident (progbits/nobits/note) */ + if (ti.kind == ASM_TOK_IDENT) tag = ti.v.ident; } else if (ty.kind == ASM_TOK_IDENT) { - (void)d_next(d); + tag = d_next(d).v.ident; + } + if (tag) { + if (sym_eq(d, tag, "note")) { + sem = SSEM_NOTE; + } else if (sym_eq(d, tag, "nobits")) { + sem = SSEM_NOBITS; + } else if (sym_eq(d, tag, "init_array")) { + sem = SSEM_INIT_ARRAY; + } else if (sym_eq(d, tag, "fini_array")) { + sem = SSEM_FINI_ARRAY; + } else if (sym_eq(d, tag, "preinit_array")) { + sem = SSEM_PREINIT_ARRAY; + } } if (asm_driver_eat_comma(d)) { AsmTok es = d_peek(d); @@ -911,11 +930,13 @@ static void do_directive(AsmDriver* d, Sym name) { } } } + if (kind == SEC_BSS) sem = SSEM_NOBITS; + if (sem == SSEM_NOTE) kind = SEC_OTHER; /* Consume any remaining operands (e.g. ,unique,N or group fields). */ d_skip_to_eol(d); { - ObjSecId sid = ensure_section(d, sname, kind, flags, 1); + ObjSecId sid = ensure_section_ex(d, sname, kind, sem, flags, 1); if (entsize) obj_section_set_entsize(d->ob, sid, entsize); d->cur_sec = sid; d->mc->set_section(d->mc, sid); diff --git a/src/link/link_layout.c b/src/link/link_layout.c @@ -46,7 +46,8 @@ int link_section_kept(const Section* s) { * symtab/strtab, group, and note sections are dropped — none of * them participate in a static ET_EXEC layout. */ if (!(s->flags & SF_ALLOC)) return 0; - if (s->sem == SSEM_PROGBITS || s->sem == SSEM_NOBITS) return 1; + if (s->sem == SSEM_PROGBITS || s->sem == SSEM_NOBITS || s->sem == SSEM_NOTE) + return 1; if (s->sem == SSEM_INIT_ARRAY || s->sem == SSEM_FINI_ARRAY) return 1; return 0; } diff --git a/src/obj/elf/link.c b/src/obj/elf/link.c @@ -839,7 +839,8 @@ void link_emit_elf(LinkImage* img, Writer* w) { int pie = img->pie; int scripted = img->scripted; /* Static ET_EXEC base: a `kit ld -Ttext ADDR` override (e.g. 0x80000000 for a - * qemu `virt` image) wins over IMAGE_BASE_STATIC; PIE/scripted keep base 0. */ + * qemu `virt` image) wins over IMAGE_BASE_STATIC; PIE/scripted keep base 0. + */ u64 img_base = (pie || scripted) ? 0ULL : img->text_base_set ? img->text_base : IMAGE_BASE_STATIC; @@ -853,11 +854,21 @@ void link_emit_elf(LinkImage* img, Writer* w) { * Scripted images skip the headers PT_LOAD and PT_NOTE: phdrs are * just the per-segment PT_LOADs. */ u32 has_tls = img->tls_memsz ? 1u : 0u; + u32 nphdr_section_notes = 0; + { + u32 i; + for (i = 0; i < img->nsections; ++i) { + const LinkSection* s = &img->sections[i]; + if (!s->file_only && s->sem == SSEM_NOTE && s->size) { + nphdr_section_notes++; + } + } + } u32 nphdr_extra_dyn = pie ? 4u : 0u; u32 nphdr_headers = scripted ? 0u : 1u; u32 nphdr_buildid = scripted ? 0u : 1u; - u32 nphdr_total = nphdr_headers + img->nsegments + nphdr_buildid + has_tls + - nphdr_extra_dyn; + u32 nphdr_total = nphdr_headers + img->nsegments + nphdr_buildid + + nphdr_section_notes + has_tls + nphdr_extra_dyn; u64 build_id_note_bytes = scripted ? 0ULL : BUILD_ID_NOTE_BYTES; /* Class-selected on-disk header sizes (ELF32: 52/32/40, ELF64: 64/56/64). */ u64 ehdr_sz = elf_ehdr_sz(class32); @@ -1057,7 +1068,8 @@ void link_emit_elf(LinkImage* img, Writer* w) { u32 outshdr_cap = img->nsections + 1u; outshdrs = (OutShdr*)heap->alloc(heap, sizeof(*outshdrs) * outshdr_cap, _Alignof(OutShdr)); - if (!outshdrs) compiler_panic(c, SRCLOC_NONE, "link_emit_elf: oom on outshdrs"); + if (!outshdrs) + compiler_panic(c, SRCLOC_NONE, "link_emit_elf: oom on outshdrs"); memset(outshdrs, 0, sizeof(*outshdrs) * outshdr_cap); { /* Build a sort index over LinkSection ids by (segment_id, vaddr). */ @@ -1116,7 +1128,8 @@ void link_emit_elf(LinkImage* img, Writer* w) { u32 sh_name_symtab = strb_add_cstr(&shstrtab, ".symtab"); u32 sh_name_strtab = strb_add_cstr(&shstrtab, ".strtab"); u32 sh_name_shstrtab = strb_add_cstr(&shstrtab, ".shstrtab"); - u32 sh_name_buildid = strb_add_cstr(&shstrtab, ".note.gnu.build-id"); + u32 sh_name_buildid = + scripted ? 0u : strb_add_cstr(&shstrtab, ".note.gnu.build-id"); /* Per-output-shdr names — interned strings from input section names. */ u32* outshdr_name_off = (u32*)heap->alloc(heap, sizeof(u32) * (noutshdr + 1u), _Alignof(u32)); @@ -1138,9 +1151,9 @@ void link_emit_elf(LinkImage* img, Writer* w) { } } - u32 nshdr = 1u + noutshdr + 4u; - u32 shndx_buildid = 1u + noutshdr; - u32 shndx_symtab = shndx_buildid + 1u; + u32 nbuildid_shdr = scripted ? 0u : 1u; + u32 nshdr = 1u + noutshdr + nbuildid_shdr + 3u; + u32 shndx_symtab = 1u + noutshdr + nbuildid_shdr; u32 shndx_strtab = shndx_symtab + 1u; u32 shndx_shstrtab = shndx_strtab + 1u; @@ -1317,6 +1330,23 @@ void link_emit_elf(LinkImage* img, Writer* w) { p->p_memsz = (seg->flags & SF_TLS) ? seg->file_size : seg->mem_size; p->p_align = seg->align ? seg->align : PAGE_SIZE; } + /* Allocatable SHT_NOTE sections can also be addressed by PT_NOTE. QEMU's + * x86 PVH loader uses this for XEN_ELFNOTE_PHYS32_ENTRY while the bytes + * still live in the normal read-only PT_LOAD. */ + for (i = 0; i < img->nsections; ++i) { + const LinkSection* s = &img->sections[i]; + Phdr64* p; + if (s->file_only || s->sem != SSEM_NOTE || s->size == 0) continue; + p = &phdrs[pi++]; + p->p_type = PT_NOTE; + p->p_flags = PF_R; + p->p_offset = s->file_offset; + p->p_vaddr = img_base + s->vaddr; + p->p_paddr = p->p_vaddr; + p->p_filesz = s->size; + p->p_memsz = s->size; + p->p_align = s->align ? s->align : 4; + } /* PT_NOTE for build-id. Scripted images skip the build-id entirely. */ if (!scripted) { phdrs[pi].p_type = PT_NOTE; @@ -1632,15 +1662,17 @@ void link_emit_elf(LinkImage* img, Writer* w) { write_shdr(w, &sh, class32); } /* shdr: .note.gnu.build-id (allocatable; in headers PT_LOAD) */ - memset(&sh, 0, sizeof(sh)); - sh.sh_name = sh_name_buildid; - sh.sh_type = SHT_NOTE; - sh.sh_flags = SHF_ALLOC; - sh.sh_addr = build_id_addr; - sh.sh_offset = build_id_off; - sh.sh_size = BUILD_ID_NOTE_BYTES; - sh.sh_addralign = 4; - write_shdr(w, &sh, class32); + if (!scripted) { + memset(&sh, 0, sizeof(sh)); + sh.sh_name = sh_name_buildid; + sh.sh_type = SHT_NOTE; + sh.sh_flags = SHF_ALLOC; + sh.sh_addr = build_id_addr; + sh.sh_offset = build_id_off; + sh.sh_size = BUILD_ID_NOTE_BYTES; + sh.sh_addralign = 4; + write_shdr(w, &sh, class32); + } /* shdr: .symtab */ memset(&sh, 0, sizeof(sh)); sh.sh_name = sh_name_symtab; diff --git a/test/smoke/freestanding_system.sh b/test/smoke/freestanding_system.sh @@ -0,0 +1,393 @@ +#!/usr/bin/env bash +# test/smoke/freestanding_system.sh - qemu-system bare-metal smoke for +# kit-compiled aarch64/x86_64/riscv64/riscv32 freestanding ELF images. +# +# Each lane builds the same small C payload with `kit cc -target *-none-elf`, +# assembles an arch-specific reset stub with `kit as`, links a static image with +# `kit ld -T`, then boots it under the matching qemu-system binary. This is a +# whole-toolchain smoke: no clang, ld.lld, hosted libc, or qemu-user path is in +# the image build. + +set -u + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +BUILD_DIR="$ROOT/build/test/freestanding-system" +mkdir -p "$BUILD_DIR" + +KIT_KIT_DIR="$ROOT/test/lib" +# shellcheck source=../lib/kit_sh_kit.sh +. "$ROOT/test/lib/kit_sh_kit.sh" +kit_report_init +KIT_SKIP_IS_FAILURE=1 + +KIT="${KIT:-$ROOT/build/kit}" +TIMEOUT="${TIMEOUT:-timeout}" +if ! command -v "$TIMEOUT" >/dev/null 2>&1; then + if command -v gtimeout >/dev/null 2>&1; then + TIMEOUT="gtimeout" + fi +fi + +if [ ! -x "$KIT" ]; then + skip_test "kit" "kit binary not found at $KIT" + kit_summary test-smoke-freestanding-system + kit_exit +fi + +if ! command -v "$TIMEOUT" >/dev/null 2>&1; then + skip_test "timeout" "timeout command unavailable" + kit_summary test-smoke-freestanding-system + kit_exit +fi + +cat > "$BUILD_DIR/app.c" <<'EOF' +static int calc(void) { + volatile unsigned long long a = 0x1122334455667788ull; + volatile unsigned long long b = 0x0102030405060708ull; + unsigned long long c = (a ^ b) + 0x10ull; + if (c != ((0x1122334455667788ull ^ 0x0102030405060708ull) + 0x10ull)) + return 2; + + int sum = 0; + for (int i = 1; i <= 10; ++i) sum += i; + if (sum != 55) return 3; + + volatile long long sx = -7; + if ((sx >> 1) != -4) return 4; + + unsigned x = 0x12345678u; + x = (x << 3) | (x >> 29); + if (x != 0x91a2b3c0u) return 5; + + return 0; +} + +int cmain(void) { return calc(); } +EOF + +have_qemu() { + command -v "$1" >/dev/null 2>&1 +} + +compile_obj() { # <arch-label> <target> <extra...> + local label="$1" target="$2" + shift 2 + local dir="$BUILD_DIR/$label" + mkdir -p "$dir" + "$KIT" cc -target "$target" "$@" -O1 -ffreestanding -c "$BUILD_DIR/app.c" \ + -o "$dir/app.o" 2>"$dir/cc.err" +} + +assemble_obj() { # <label> <target> <src> <extra...> + local label="$1" target="$2" src="$3" + shift 3 + "$KIT" as -target "$target" "$@" -o "$BUILD_DIR/$label/start.o" "$src" \ + 2>"$BUILD_DIR/$label/as.err" +} + +link_elf() { # <label> + local label="$1" dir="$BUILD_DIR/$1" + "$KIT" ld -T "$dir/link.ld" -e _start "$dir/start.o" "$dir/app.o" \ + -o "$dir/kernel.elf" 2>"$dir/ld.err" +} + +record_build_fail() { # <name> <log> + local name="$1" log="$2" + if [ -s "$log" ]; then + not_ok "$name" "$log" + else + not_ok "$name" "command failed with no stderr" + fi +} + +run_aa64() { + local label="aa64" dir="$BUILD_DIR/aa64" + if ! have_qemu qemu-system-aarch64; then + skip_test "$label" "qemu-system-aarch64 unavailable" + return + fi + mkdir -p "$dir" + cat > "$dir/start.S" <<'EOF' +.section .text.start,"ax",@progbits +.globl _start +_start: + adrp x0, stack_top + add x0, x0, :lo12:stack_top + mov sp, x0 + bl cmain + adrp x1, semihost_args + add x1, x1, :lo12:semihost_args + str x0, [x1, #8] + mov x0, #0x20 + hlt #0xf000 +.Lhang: + b .Lhang + +.section .data.semihost,"aw",@progbits +.balign 8 +semihost_args: + .quad 0x20026 + .quad 0 + +.section .bss.stack,"aw",@nobits +.balign 16 +stack_bottom: + .zero 65536 +stack_top: +EOF + cat > "$dir/link.ld" <<'EOF' +ENTRY(_start) +SECTIONS { + . = 0x40080000; + .text : { *(.text.start) *(.text*) } + .rodata : { *(.rodata*) } + .data : { *(.data*) } + .bss : { *(.bss*) *(COMMON) } + /DISCARD/ : { *(.comment) } +} +EOF + if ! compile_obj "$label" aarch64-none-elf; then + record_build_fail "$label cc" "$dir/cc.err"; return + fi + if ! assemble_obj "$label" aarch64-none-elf "$dir/start.S"; then + record_build_fail "$label as" "$dir/as.err"; return + fi + if ! link_elf "$label"; then + record_build_fail "$label ld" "$dir/ld.err"; return + fi + + local rc=0 + "$TIMEOUT" 20 qemu-system-aarch64 -machine virt -cpu cortex-a53 \ + -kernel "$dir/kernel.elf" -display none -serial none -monitor none \ + -semihosting-config enable=on,target=native -no-reboot \ + >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$? + if [ "$rc" -eq 0 ]; then + ok "$label qemu-system (rc=0)" + else + printf 'expected qemu rc 0, got %s; see %s\n' "$rc" "$dir/qemu.err" \ + > "$dir/qemu.diag" + not_ok "$label qemu-system" "$dir/qemu.diag" + fi +} + +run_riscv() { # <label> <qemu> <target> <march> <mabi> <stack> + local label="$1" qemu="$2" target="$3" march="$4" mabi="$5" stack="$6" + local dir="$BUILD_DIR/$label" + if ! have_qemu "$qemu"; then + skip_test "$label" "$qemu unavailable" + return + fi + mkdir -p "$dir" + cat > "$dir/start.S" <<EOF +.section .text.start,"ax",@progbits +.globl _start +_start: + li sp, $stack + li t0, 0x2000 + csrs mstatus, t0 + call cmain + li t0, 0x100000 + beqz a0, .Lpass + slli a0, a0, 16 + li t1, 0x3333 + or a0, a0, t1 + sw a0, 0(t0) +.Lhang: + j .Lhang +.Lpass: + li t1, 0x5555 + sw t1, 0(t0) + j .Lhang +EOF + cat > "$dir/link.ld" <<'EOF' +ENTRY(_start) +SECTIONS { + . = 0x80000000; + .text : { *(.text.start) *(.text*) } + .rodata : { *(.rodata*) } + .data : { *(.data*) } + .bss : { *(.bss*) *(COMMON) } + /DISCARD/ : { *(.riscv.attributes) *(.comment) } +} +EOF + if ! compile_obj "$label" "$target" -march="$march" -mabi="$mabi"; then + record_build_fail "$label cc" "$dir/cc.err"; return + fi + if ! assemble_obj "$label" "$target" "$dir/start.S" -march="$march" -mabi="$mabi"; then + record_build_fail "$label as" "$dir/as.err"; return + fi + if ! link_elf "$label"; then + record_build_fail "$label ld" "$dir/ld.err"; return + fi + + local rc=0 + "$TIMEOUT" 20 "$qemu" -machine virt -bios none \ + -kernel "$dir/kernel.elf" -nographic -no-reboot \ + >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$? + if [ "$rc" -eq 0 ]; then + ok "$label qemu-system (rc=0)" + else + printf 'expected qemu rc 0, got %s; see %s\n' "$rc" "$dir/qemu.err" \ + > "$dir/qemu.diag" + not_ok "$label qemu-system" "$dir/qemu.diag" + fi +} + +run_rv64() { + run_riscv rv64 qemu-system-riscv64 riscv64-none-elf \ + rv64imafd_zicsr_zifencei lp64 0x81000000 +} + +run_rv32() { + run_riscv rv32 qemu-system-riscv32 riscv32-none-elf \ + rv32imafc_zicsr_zifencei ilp32f 0x80100000 +} + +emit_x64_start() { + local out="$1" + cat > "$out" <<'EOF' +.section .multiboot,"a",@progbits +.balign 4 +.long 0x1badb002 +.long 0 +.long 0xe4524ffe + +.section .note.Xen,"a",@note +.balign 4 +.long 4 +.long 4 +.long 18 +.ascii "Xen" +.byte 0 +.long _start + +.section .text.start,"ax",@progbits +.globl _start +_start: + .byte 0xfa /* cli */ + .byte 0xbc /* mov $stack_top, %esp */ + .long stack_top + .byte 0xb8 /* mov $pml4, %eax */ + .long pml4 + .byte 0x0f, 0x22, 0xd8 /* mov %eax, %cr3 */ + .byte 0x0f, 0x20, 0xe0 /* mov %cr4, %eax */ + .byte 0x83, 0xc8, 0x20 /* or $CR4_PAE, %eax */ + .byte 0x0f, 0x22, 0xe0 /* mov %eax, %cr4 */ + .byte 0xb9, 0x80, 0x00, 0x00, 0xc0 /* mov $EFER, %ecx */ + .byte 0x0f, 0x32 /* rdmsr */ + .byte 0x0d, 0x00, 0x01, 0x00, 0x00 /* or $EFER_LME, %eax */ + .byte 0x0f, 0x30 /* wrmsr */ + .byte 0x0f, 0x20, 0xc0 /* mov %cr0, %eax */ + .byte 0x0d, 0x00, 0x00, 0x00, 0x80 /* or $CR0_PG, %eax */ + .byte 0x0f, 0x22, 0xc0 /* mov %eax, %cr0 */ + .byte 0x0f, 0x01, 0x15 /* lgdt gdt_desc */ + .long gdt_desc + .byte 0xea /* ljmp $0x08,$long_entry */ + .long long_entry + .hword 0x08 + +long_entry: + .byte 0x66, 0xb8, 0x10, 0x00 /* mov $0x10, %ax */ + .byte 0x8e, 0xd8 /* mov %ax, %ds */ + .byte 0x8e, 0xc0 /* mov %ax, %es */ + .byte 0x8e, 0xd0 /* mov %ax, %ss */ + .byte 0x48, 0xbc /* movabs $stack_top, %rsp */ + .quad stack_top + call cmain + .byte 0x66, 0xba, 0x01, 0x05 /* mov $0x501, %dx */ + .byte 0x66, 0xef /* outw %ax, %dx */ +.Lhang: + .byte 0xf4 /* hlt */ + jmp .Lhang + +.section .data.boot,"aw",@progbits +.balign 8 +gdt: + .quad 0 + .quad 0x00af9a000000ffff + .quad 0x00af92000000ffff +gdt_desc: + .hword 23 + .long gdt + +.balign 4096 +pml4: + .quad pdpt + 0x3 +EOF + for _ in $(seq 1 511); do printf ' .quad 0\n' >> "$out"; done + cat >> "$out" <<'EOF' +.balign 4096 +pdpt: + .quad pd + 0x3 +EOF + for _ in $(seq 1 511); do printf ' .quad 0\n' >> "$out"; done + printf '.balign 4096\npd:\n' >> "$out" + local i=0 + while [ "$i" -lt 512 ]; do + printf ' .quad 0x%016x\n' $((i * 0x200000 + 0x83)) >> "$out" + i=$((i + 1)) + done + cat >> "$out" <<'EOF' + +.section .bss.stack,"aw",@nobits +.balign 16 +stack_bottom: + .zero 65536 +stack_top: +EOF +} + +run_x64() { + local label="x64" dir="$BUILD_DIR/x64" + if ! have_qemu qemu-system-x86_64; then + skip_test "$label" "qemu-system-x86_64 unavailable" + return + fi + mkdir -p "$dir" + emit_x64_start "$dir/start.S" + cat > "$dir/link.ld" <<'EOF' +ENTRY(_start) +SECTIONS { + . = 0x100000; + .multiboot : { *(.multiboot) } + .note.Xen : { *(.note.Xen) } + .text : { *(.text.start) *(.text*) } + .rodata : { *(.rodata*) } + .data.boot : ALIGN(4096) { *(.data.boot) } + .data : { *(.data*) } + .bss.stack : ALIGN(16) { *(.bss.stack) } + .bss : { *(.bss*) *(COMMON) } + /DISCARD/ : { *(.comment) } +} +EOF + if ! compile_obj "$label" x86_64-none-elf; then + record_build_fail "$label cc" "$dir/cc.err"; return + fi + if ! assemble_obj "$label" x86_64-none-elf "$dir/start.S"; then + record_build_fail "$label as" "$dir/as.err"; return + fi + if ! link_elf "$label"; then + record_build_fail "$label ld" "$dir/ld.err"; return + fi + + local rc=0 want=1 + "$TIMEOUT" 20 qemu-system-x86_64 -kernel "$dir/kernel.elf" \ + -device isa-debug-exit,iobase=0x501,iosize=0x02 \ + -display none -serial none -monitor none -no-reboot \ + >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$? + if [ "$rc" -eq "$want" ] && ! grep -q 'Error loading' "$dir/qemu.err"; then + ok "$label qemu-system (guest rc=0)" + else + printf 'expected qemu rc %s (guest rc=0), got %s; see %s\n' \ + "$want" "$rc" "$dir/qemu.err" > "$dir/qemu.diag" + not_ok "$label qemu-system" "$dir/qemu.diag" + fi +} + +run_aa64 +run_x64 +run_rv64 +run_rv32 + +kit_summary test-smoke-freestanding-system +kit_exit