kit

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

freestanding_system.sh (10413B)


      1 #!/usr/bin/env bash
      2 # test/smoke/freestanding_system.sh - qemu-system bare-metal smoke for
      3 # kit-compiled aarch64/x86_64/riscv64/riscv32 freestanding ELF images.
      4 #
      5 # Each lane builds the same small C payload with `kit cc -target *-none-elf`,
      6 # assembles an arch-specific reset stub with `kit as`, links a static image with
      7 # `kit ld -T`, then boots it under the matching qemu-system binary. This is a
      8 # whole-toolchain smoke: no clang, ld.lld, hosted libc, or qemu-user path is in
      9 # the image build.
     10 
     11 set -u
     12 
     13 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     14 BUILD_DIR="$ROOT/build/test/freestanding-system"
     15 mkdir -p "$BUILD_DIR"
     16 
     17 KIT_KIT_DIR="$ROOT/test/lib"
     18 # shellcheck source=../lib/kit_sh_kit.sh
     19 . "$ROOT/test/lib/kit_sh_kit.sh"
     20 kit_report_init
     21 KIT_SKIP_IS_FAILURE=1
     22 
     23 KIT="${KIT:-$ROOT/build/kit}"
     24 TIMEOUT="${TIMEOUT:-timeout}"
     25 if ! command -v "$TIMEOUT" >/dev/null 2>&1; then
     26   if command -v gtimeout >/dev/null 2>&1; then
     27     TIMEOUT="gtimeout"
     28   fi
     29 fi
     30 
     31 if [ ! -x "$KIT" ]; then
     32   skip_test "kit" "kit binary not found at $KIT"
     33   kit_summary test-smoke-freestanding-system
     34   kit_exit
     35 fi
     36 
     37 if ! command -v "$TIMEOUT" >/dev/null 2>&1; then
     38   skip_test "timeout" "timeout command unavailable"
     39   kit_summary test-smoke-freestanding-system
     40   kit_exit
     41 fi
     42 
     43 cat > "$BUILD_DIR/app.c" <<'EOF'
     44 static int calc(void) {
     45   volatile unsigned long long a = 0x1122334455667788ull;
     46   volatile unsigned long long b = 0x0102030405060708ull;
     47   unsigned long long c = (a ^ b) + 0x10ull;
     48   if (c != ((0x1122334455667788ull ^ 0x0102030405060708ull) + 0x10ull))
     49     return 2;
     50 
     51   int sum = 0;
     52   for (int i = 1; i <= 10; ++i) sum += i;
     53   if (sum != 55) return 3;
     54 
     55   volatile long long sx = -7;
     56   if ((sx >> 1) != -4) return 4;
     57 
     58   unsigned x = 0x12345678u;
     59   x = (x << 3) | (x >> 29);
     60   if (x != 0x91a2b3c0u) return 5;
     61 
     62   return 0;
     63 }
     64 
     65 int cmain(void) { return calc(); }
     66 EOF
     67 
     68 have_qemu() {
     69   command -v "$1" >/dev/null 2>&1
     70 }
     71 
     72 compile_obj() { # <arch-label> <target> <extra...>
     73   local label="$1" target="$2"
     74   shift 2
     75   local dir="$BUILD_DIR/$label"
     76   mkdir -p "$dir"
     77   "$KIT" cc -target "$target" "$@" -O1 -ffreestanding -c "$BUILD_DIR/app.c" \
     78     -o "$dir/app.o" 2>"$dir/cc.err"
     79 }
     80 
     81 assemble_obj() { # <label> <target> <src> <extra...>
     82   local label="$1" target="$2" src="$3"
     83   shift 3
     84   "$KIT" as -target "$target" "$@" -o "$BUILD_DIR/$label/start.o" "$src" \
     85     2>"$BUILD_DIR/$label/as.err"
     86 }
     87 
     88 link_elf() { # <label>
     89   local label="$1" dir="$BUILD_DIR/$1"
     90   "$KIT" ld -T "$dir/link.ld" -e _start "$dir/start.o" "$dir/app.o" \
     91     -o "$dir/kernel.elf" 2>"$dir/ld.err"
     92 }
     93 
     94 record_build_fail() { # <name> <log>
     95   local name="$1" log="$2"
     96   if [ -s "$log" ]; then
     97     not_ok "$name" "$log"
     98   else
     99     not_ok "$name" "command failed with no stderr"
    100   fi
    101 }
    102 
    103 run_aa64() {
    104   local label="aa64" dir="$BUILD_DIR/aa64"
    105   if ! have_qemu qemu-system-aarch64; then
    106     skip_test "$label" "qemu-system-aarch64 unavailable"
    107     return
    108   fi
    109   mkdir -p "$dir"
    110   cat > "$dir/start.S" <<'EOF'
    111 .section .text.start,"ax",@progbits
    112 .globl _start
    113 _start:
    114   adrp x0, stack_top
    115   add  x0, x0, :lo12:stack_top
    116   mov  sp, x0
    117   bl   cmain
    118   adrp x1, semihost_args
    119   add  x1, x1, :lo12:semihost_args
    120   str  x0, [x1, #8]
    121   mov  x0, #0x20
    122   hlt  #0xf000
    123 .Lhang:
    124   b .Lhang
    125 
    126 .section .data.semihost,"aw",@progbits
    127 .balign 8
    128 semihost_args:
    129   .quad 0x20026
    130   .quad 0
    131 
    132 .section .bss.stack,"aw",@nobits
    133 .balign 16
    134 stack_bottom:
    135   .zero 65536
    136 stack_top:
    137 EOF
    138   cat > "$dir/link.ld" <<'EOF'
    139 ENTRY(_start)
    140 SECTIONS {
    141   . = 0x40080000;
    142   .text   : { *(.text.start) *(.text*) }
    143   .rodata : { *(.rodata*) }
    144   .data   : { *(.data*) }
    145   .bss    : { *(.bss*) *(COMMON) }
    146   /DISCARD/ : { *(.comment) }
    147 }
    148 EOF
    149   if ! compile_obj "$label" aarch64-none-elf; then
    150     record_build_fail "$label cc" "$dir/cc.err"; return
    151   fi
    152   if ! assemble_obj "$label" aarch64-none-elf "$dir/start.S"; then
    153     record_build_fail "$label as" "$dir/as.err"; return
    154   fi
    155   if ! link_elf "$label"; then
    156     record_build_fail "$label ld" "$dir/ld.err"; return
    157   fi
    158 
    159   local rc=0
    160   "$TIMEOUT" 20 qemu-system-aarch64 -machine virt -cpu cortex-a53 \
    161     -kernel "$dir/kernel.elf" -display none -serial none -monitor none \
    162     -semihosting-config enable=on,target=native -no-reboot \
    163     >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$?
    164   if [ "$rc" -eq 0 ]; then
    165     ok "$label qemu-system (rc=0)"
    166   else
    167     printf 'expected qemu rc 0, got %s; see %s\n' "$rc" "$dir/qemu.err" \
    168       > "$dir/qemu.diag"
    169     not_ok "$label qemu-system" "$dir/qemu.diag"
    170   fi
    171 }
    172 
    173 run_riscv() { # <label> <qemu> <target> <march> <mabi> <stack>
    174   local label="$1" qemu="$2" target="$3" march="$4" mabi="$5" stack="$6"
    175   local dir="$BUILD_DIR/$label"
    176   if ! have_qemu "$qemu"; then
    177     skip_test "$label" "$qemu unavailable"
    178     return
    179   fi
    180   mkdir -p "$dir"
    181   cat > "$dir/start.S" <<EOF
    182 .section .text.start,"ax",@progbits
    183 .globl _start
    184 _start:
    185   li sp, $stack
    186   li t0, 0x2000
    187   csrs mstatus, t0
    188   call cmain
    189   li t0, 0x100000
    190   beqz a0, .Lpass
    191   slli a0, a0, 16
    192   li t1, 0x3333
    193   or a0, a0, t1
    194   sw a0, 0(t0)
    195 .Lhang:
    196   j .Lhang
    197 .Lpass:
    198   li t1, 0x5555
    199   sw t1, 0(t0)
    200   j .Lhang
    201 EOF
    202   cat > "$dir/link.ld" <<'EOF'
    203 ENTRY(_start)
    204 SECTIONS {
    205   . = 0x80000000;
    206   .text   : { *(.text.start) *(.text*) }
    207   .rodata : { *(.rodata*) }
    208   .data   : { *(.data*) }
    209   .bss    : { *(.bss*) *(COMMON) }
    210   /DISCARD/ : { *(.riscv.attributes) *(.comment) }
    211 }
    212 EOF
    213   if ! compile_obj "$label" "$target" -march="$march" -mabi="$mabi"; then
    214     record_build_fail "$label cc" "$dir/cc.err"; return
    215   fi
    216   if ! assemble_obj "$label" "$target" "$dir/start.S" -march="$march" -mabi="$mabi"; then
    217     record_build_fail "$label as" "$dir/as.err"; return
    218   fi
    219   if ! link_elf "$label"; then
    220     record_build_fail "$label ld" "$dir/ld.err"; return
    221   fi
    222 
    223   local rc=0
    224   "$TIMEOUT" 20 "$qemu" -machine virt -bios none \
    225     -kernel "$dir/kernel.elf" -nographic -no-reboot \
    226     >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$?
    227   if [ "$rc" -eq 0 ]; then
    228     ok "$label qemu-system (rc=0)"
    229   else
    230     printf 'expected qemu rc 0, got %s; see %s\n' "$rc" "$dir/qemu.err" \
    231       > "$dir/qemu.diag"
    232     not_ok "$label qemu-system" "$dir/qemu.diag"
    233   fi
    234 }
    235 
    236 run_rv64() {
    237   run_riscv rv64 qemu-system-riscv64 riscv64-none-elf \
    238     rv64imafd_zicsr_zifencei lp64 0x81000000
    239 }
    240 
    241 run_rv32() {
    242   run_riscv rv32 qemu-system-riscv32 riscv32-none-elf \
    243     rv32imafc_zicsr_zifencei ilp32f 0x80100000
    244 }
    245 
    246 emit_x64_start() {
    247   local out="$1"
    248   cat > "$out" <<'EOF'
    249 .section .multiboot,"a",@progbits
    250 .balign 4
    251 .long 0x1badb002
    252 .long 0
    253 .long 0xe4524ffe
    254 
    255 .section .note.Xen,"a",@note
    256 .balign 4
    257 .long 4
    258 .long 4
    259 .long 18
    260 .ascii "Xen"
    261 .byte 0
    262 .long _start
    263 
    264 .section .text.start,"ax",@progbits
    265 .globl _start
    266 _start:
    267   .byte 0xfa                         /* cli */
    268   .byte 0xbc                         /* mov $stack_top, %esp */
    269   .long stack_top
    270   .byte 0xb8                         /* mov $pml4, %eax */
    271   .long pml4
    272   .byte 0x0f, 0x22, 0xd8             /* mov %eax, %cr3 */
    273   .byte 0x0f, 0x20, 0xe0             /* mov %cr4, %eax */
    274   .byte 0x83, 0xc8, 0x20             /* or $CR4_PAE, %eax */
    275   .byte 0x0f, 0x22, 0xe0             /* mov %eax, %cr4 */
    276   .byte 0xb9, 0x80, 0x00, 0x00, 0xc0 /* mov $EFER, %ecx */
    277   .byte 0x0f, 0x32                   /* rdmsr */
    278   .byte 0x0d, 0x00, 0x01, 0x00, 0x00 /* or $EFER_LME, %eax */
    279   .byte 0x0f, 0x30                   /* wrmsr */
    280   .byte 0x0f, 0x20, 0xc0             /* mov %cr0, %eax */
    281   .byte 0x0d, 0x00, 0x00, 0x00, 0x80 /* or $CR0_PG, %eax */
    282   .byte 0x0f, 0x22, 0xc0             /* mov %eax, %cr0 */
    283   .byte 0x0f, 0x01, 0x15             /* lgdt gdt_desc */
    284   .long gdt_desc
    285   .byte 0xea                         /* ljmp $0x08,$long_entry */
    286   .long long_entry
    287   .hword 0x08
    288 
    289 long_entry:
    290   .byte 0x66, 0xb8, 0x10, 0x00       /* mov $0x10, %ax */
    291   .byte 0x8e, 0xd8                   /* mov %ax, %ds */
    292   .byte 0x8e, 0xc0                   /* mov %ax, %es */
    293   .byte 0x8e, 0xd0                   /* mov %ax, %ss */
    294   .byte 0x48, 0xbc                   /* movabs $stack_top, %rsp */
    295   .quad stack_top
    296   call cmain
    297   .byte 0x66, 0xba, 0x01, 0x05       /* mov $0x501, %dx */
    298   .byte 0x66, 0xef                   /* outw %ax, %dx */
    299 .Lhang:
    300   .byte 0xf4                         /* hlt */
    301   jmp .Lhang
    302 
    303 .section .data.boot,"aw",@progbits
    304 .balign 8
    305 gdt:
    306   .quad 0
    307   .quad 0x00af9a000000ffff
    308   .quad 0x00af92000000ffff
    309 gdt_desc:
    310   .hword 23
    311   .long gdt
    312 
    313 .balign 4096
    314 pml4:
    315   .quad pdpt + 0x3
    316 EOF
    317   for _ in $(seq 1 511); do printf '  .quad 0\n' >> "$out"; done
    318   cat >> "$out" <<'EOF'
    319 .balign 4096
    320 pdpt:
    321   .quad pd + 0x3
    322 EOF
    323   for _ in $(seq 1 511); do printf '  .quad 0\n' >> "$out"; done
    324   printf '.balign 4096\npd:\n' >> "$out"
    325   local i=0
    326   while [ "$i" -lt 512 ]; do
    327     printf '  .quad 0x%016x\n' $((i * 0x200000 + 0x83)) >> "$out"
    328     i=$((i + 1))
    329   done
    330   cat >> "$out" <<'EOF'
    331 
    332 .section .bss.stack,"aw",@nobits
    333 .balign 16
    334 stack_bottom:
    335   .zero 65536
    336 stack_top:
    337 EOF
    338 }
    339 
    340 run_x64() {
    341   local label="x64" dir="$BUILD_DIR/x64"
    342   if ! have_qemu qemu-system-x86_64; then
    343     skip_test "$label" "qemu-system-x86_64 unavailable"
    344     return
    345   fi
    346   mkdir -p "$dir"
    347   emit_x64_start "$dir/start.S"
    348   cat > "$dir/link.ld" <<'EOF'
    349 ENTRY(_start)
    350 SECTIONS {
    351   . = 0x100000;
    352   .multiboot : { *(.multiboot) }
    353   .note.Xen  : { *(.note.Xen) }
    354   .text      : { *(.text.start) *(.text*) }
    355   .rodata    : { *(.rodata*) }
    356   .data.boot : ALIGN(4096) { *(.data.boot) }
    357   .data      : { *(.data*) }
    358   .bss.stack : ALIGN(16) { *(.bss.stack) }
    359   .bss       : { *(.bss*) *(COMMON) }
    360   /DISCARD/  : { *(.comment) }
    361 }
    362 EOF
    363   if ! compile_obj "$label" x86_64-none-elf; then
    364     record_build_fail "$label cc" "$dir/cc.err"; return
    365   fi
    366   if ! assemble_obj "$label" x86_64-none-elf "$dir/start.S"; then
    367     record_build_fail "$label as" "$dir/as.err"; return
    368   fi
    369   if ! link_elf "$label"; then
    370     record_build_fail "$label ld" "$dir/ld.err"; return
    371   fi
    372 
    373   local rc=0 want=1
    374   "$TIMEOUT" 20 qemu-system-x86_64 -kernel "$dir/kernel.elf" \
    375     -device isa-debug-exit,iobase=0x501,iosize=0x02 \
    376     -display none -serial none -monitor none -no-reboot \
    377     >"$dir/qemu.out" 2>"$dir/qemu.err" || rc=$?
    378   if [ "$rc" -eq "$want" ] && ! grep -q 'Error loading' "$dir/qemu.err"; then
    379     ok "$label qemu-system (guest rc=0)"
    380   else
    381     printf 'expected qemu rc %s (guest rc=0), got %s; see %s\n' \
    382       "$want" "$rc" "$dir/qemu.err" > "$dir/qemu.diag"
    383     not_ok "$label qemu-system" "$dir/qemu.diag"
    384   fi
    385 }
    386 
    387 run_aa64
    388 run_x64
    389 run_rv64
    390 run_rv32
    391 
    392 kit_summary test-smoke-freestanding-system
    393 kit_exit