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