exec_bare.sh (25211B)
1 # test/lib/exec_bare.sh — bare-metal (freestanding) execution backend for 2 # test/lib/exec_target.sh, and the single owner of kit's per-arch boot 3 # scaffolding (reset stub + linker script + exit-code oracle + qemu-system 4 # invocation) for aarch64 / x86_64 / riscv64 / riscv32 / arm32 (Cortex-M). 5 # 6 # This consolidates what used to be three scripts: the rv32 corpus runner 7 # (exec_rv32_bare.sh), the aa64/rv64 ready-image runner (exec_kernel.sh, now a 8 # thin shim over this file), and the per-arch stubs inlined in 9 # test/smoke/freestanding_system.sh. 10 # 11 # Two contracts: 12 # 13 # exec_bare_run_image <arch> <kernel.elf> <out> <err> → sets RUN_RC 14 # Run a ready bootable image (the image owns its own _start + exit 15 # convention). What test/link's kernel_image cases need. 16 # 17 # exec_bare_setup <arch> <work> [entry] 18 # exec_bare_run <arch> <obj> <work> <rcfile> → writes <rcfile> 19 # Link a corpus object (entry default `main`, returns the exit code) with 20 # the per-arch reset stub + (rv32) runtime into a bootable image, run it, 21 # and write the decoded guest exit code. What toy/parse need. exec_bare_run 22 # echoes a one-line reason and returns 0 (ran; rc in <rcfile>) or 2 23 # (build/link failure; caller decides skip vs fail). 24 # 25 # exec_bare_supported <arch> true if qemu-system-<arch> is on PATH. 26 # 27 # Exit-code oracle (so callers compare rc == expected uniformly): 28 # aa64 ARM semihosting SYS_EXIT_EXTENDED → qemu rc = guest code 29 # rv* SiFive test finisher MMIO @0x100000 → qemu rc = guest code 30 # x64 isa-debug-exit @0x501 → qemu rc = (code<<1)|1, decoded here 31 # arm32 ARM semihosting BKPT #0xAB + SYS_EXIT_EXTENDED → qemu rc = guest code 32 # 33 # Corpus depth (exec_bare_setup/run) is fully wired for rv32 only; the aa64/x64/ 34 # rv64 stubs here are smoke-capable (single object, no TLS/rt). Hardening them 35 # for the toy/parse corpora is the tracked follow-up in doc/plan/PORT.md. 36 37 EXEC_BARE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" 38 EXEC_BARE_KIT="${KIT:-$EXEC_BARE_ROOT/build/kit}" 39 EXEC_BARE_TIMEOUT="timeout" 40 command -v "$EXEC_BARE_TIMEOUT" >/dev/null 2>&1 || { 41 command -v gtimeout >/dev/null 2>&1 && EXEC_BARE_TIMEOUT="gtimeout" 42 } 43 EXEC_BARE_TO="${EXEC_BARE_TO:-20}" 44 45 # ---- arch helpers ---------------------------------------------------------- 46 _bare_canon() { 47 case "$1" in 48 aa64|aarch64|arm64) echo aa64 ;; 49 x64|x86_64|amd64) echo x64 ;; 50 rv64|riscv64) echo rv64 ;; 51 rv32|riscv32) echo rv32 ;; 52 arm32|arm|armv7m|armv7em|cortex-m3|cortex-m4|cortex-m7) echo arm32 ;; 53 *) return 1 ;; 54 esac 55 } 56 _bare_qemu() { 57 case "$(_bare_canon "$1")" in 58 aa64) echo qemu-system-aarch64 ;; 59 x64) echo qemu-system-x86_64 ;; 60 rv64) echo qemu-system-riscv64 ;; 61 rv32) echo qemu-system-riscv32 ;; 62 arm32) echo qemu-system-arm ;; 63 esac 64 } 65 _bare_triple() { 66 case "$(_bare_canon "$1")" in 67 aa64) echo aarch64-none-elf ;; 68 x64) echo x86_64-none-elf ;; 69 rv64) echo riscv64-none-elf ;; 70 rv32) echo riscv32-none-elf ;; 71 arm32) echo arm-none-eabi ;; 72 esac 73 } 74 75 exec_bare_supported() { 76 local q; q="$(_bare_qemu "$1")" || return 1 77 [ -n "$q" ] && command -v "$q" >/dev/null 2>&1 78 } 79 80 # ---- qemu invocation + exit decode ----------------------------------------- 81 # Sets RUN_RC_RAW to the raw qemu-system exit status. 82 _bare_qemu_run() { 83 local arch elf out err q 84 arch="$(_bare_canon "$1")"; elf="$2"; out="$3"; err="$4" 85 q="$(_bare_qemu "$arch")" 86 case "$arch" in 87 aa64) 88 $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine virt -cpu cortex-a72 \ 89 -kernel "$elf" -nographic \ 90 -semihosting-config enable=on,target=native -no-reboot \ 91 >"$out" 2>"$err"; RUN_RC_RAW=$? ;; 92 x64) 93 $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -kernel "$elf" \ 94 -device isa-debug-exit,iobase=0x501,iosize=0x02 \ 95 -display none -serial none -monitor none -no-reboot \ 96 >"$out" 2>"$err"; RUN_RC_RAW=$? ;; 97 rv64|rv32) 98 $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine virt -bios none \ 99 -kernel "$elf" -nographic -no-reboot \ 100 >"$out" 2>"$err"; RUN_RC_RAW=$? ;; 101 arm32) 102 $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine mps2-an385 -cpu cortex-m3 \ 103 -kernel "$elf" -nographic -monitor none \ 104 -semihosting-config enable=on,target=native -no-reboot \ 105 >"$out" 2>"$err"; RUN_RC_RAW=$? ;; 106 *) RUN_RC_RAW=127 ;; 107 esac 108 } 109 110 # Decode a raw qemu exit into the guest exit code. Only x64's isa-debug-exit 111 # needs it (qemu maps a guest write of N to (N<<1)|1); for aa64/rv* qemu already 112 # returns the guest code, so identity. A non-odd x64 rc (e.g. 124 timeout) is a 113 # genuine failure and is passed through unchanged. 114 _bare_decode() { 115 local r="$2" 116 case "$(_bare_canon "$1")" in 117 x64) if [ "$r" -ge 1 ] && [ $((r & 1)) -eq 1 ]; then echo $(((r - 1) / 2)); else echo "$r"; fi ;; 118 *) echo "$r" ;; 119 esac 120 } 121 122 # ---- ready-image runner (exec_kernel contract) ----------------------------- 123 exec_bare_run_image() { # arch elf out err -> sets RUN_RC 124 local arch; arch="$(_bare_canon "$1")" || { RUN_RC=127; return; } 125 exec_bare_supported "$arch" || { RUN_RC=127; return; } 126 _bare_qemu_run "$arch" "$2" "$3" "$4" 127 RUN_RC="$(_bare_decode "$arch" "$RUN_RC_RAW")" 128 } 129 130 # ---- corpus runner: setup/link/run ----------------------------------------- 131 exec_bare_setup_build() { # arch work [entry] -> build per-arch stub 132 local arch work entry dir 133 arch="$(_bare_canon "$1")" || return 1 134 work="$2"; entry="${3:-${EXEC_BARE_ENTRY:-main}}" 135 [ -x "$EXEC_BARE_KIT" ] || return 1 136 dir="$work/_bare/$arch"; mkdir -p "$dir" 137 rm -f "$dir/.ok" 138 case "$arch" in 139 aa64) _bare_emit_aa64 "$dir" || return 1 ;; 140 x64) _bare_emit_x64 "$dir" || return 1 ;; 141 rv64) _bare_emit_rv64 "$dir" || return 1 ;; 142 rv32) _bare_emit_rv32 "$dir" "$entry" || return 1 ;; 143 arm32) _bare_emit_arm32 "$dir" "$entry" || return 1 ;; 144 esac 145 : > "$dir/.ok" 146 # Cache the stub dir per arch so a caller may pass a different work dir to 147 # exec_bare_run for the output image (toy/parse set up once under BUILD_DIR 148 # but link each case under its own KIT_WORK). 149 eval "EXEC_BARE_DIR_$arch=\$dir" 150 } 151 152 exec_bare_setup() { # arch work [entry] 153 local arch 154 arch="$(_bare_canon "$1")" || return 1 155 exec_bare_supported "$arch" || return 1 156 exec_bare_setup_build "$arch" "$2" "${3:-${EXEC_BARE_ENTRY:-main}}" 157 } 158 159 exec_bare_link() { # arch obj work elf -> echoes reason; 0 linked / 2 failed 160 local arch obj work elf dir lderr rt 161 arch="$(_bare_canon "$1")" || { echo "unknown bare arch '$1'"; return 2; } 162 obj="$2"; work="$3"; elf="$4" 163 eval "dir=\"\${EXEC_BARE_DIR_$arch:-$work/_bare/$arch}\"" 164 [ -f "$dir/.ok" ] || { echo "bare runner not set up for $arch"; return 2; } 165 lderr="$elf.ld.err" 166 if [ "$arch" = rv32 ]; then 167 rt="$EXEC_BARE_ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a" 168 if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \ 169 "$dir/start.o" "$dir/wrap.o" "$obj" "$rt" -o "$elf" 2>"$lderr"; then 170 echo "kit ld (rv32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2 171 fi 172 elif [ "$arch" = arm32 ]; then 173 # The Cortex-M reset stub IS the image entry (Reset_Handler == _start); 174 # i64 mul/div/shift and soft-double pull __aeabi_*/__*di3 helpers from the 175 # arm-eabi-thumb2 runtime variant (built on demand by _bare_emit_arm32). 176 rt="$EXEC_BARE_ROOT/build/rt/arm-eabi-thumb2/libkit_rt.a" 177 if [ -f "$rt" ]; then 178 if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \ 179 "$dir/start.o" "$obj" "$rt" -o "$elf" 2>"$lderr"; then 180 echo "kit ld (arm32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2 181 fi 182 else 183 # No runtime variant present: link the stub + corpus object alone. 184 # Inline-only i64/double cases still resolve; helper-needing cases 185 # surface as unresolved-symbol link failures (caller decides). 186 if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \ 187 "$dir/start.o" "$obj" -o "$elf" 2>"$lderr"; then 188 echo "kit ld (arm32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2 189 fi 190 fi 191 else 192 if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \ 193 "$dir/start.o" "$obj" -o "$elf" 2>"$lderr"; then 194 echo "kit ld ($arch) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2 195 fi 196 fi 197 return 0 198 } 199 200 exec_bare_run() { # arch obj work rcfile -> echoes reason; 0 ran / 2 build-fail 201 local arch obj work rcf elf 202 arch="$(_bare_canon "$1")" || { echo "unknown bare arch '$1'"; return 2; } 203 obj="$2"; work="$3"; rcf="$4" 204 elf="$work/$(basename "$obj").$arch.elf" 205 exec_bare_link "$arch" "$obj" "$work" "$elf" || return 2 206 _bare_qemu_run "$arch" "$elf" "$elf.out" "$elf.err" 207 _bare_decode "$arch" "$RUN_RC_RAW" > "$rcf" 208 return 0 209 } 210 211 # =========================================================================== 212 # Per-arch stub emitters. Each writes start.S + link.ld under <dir> and 213 # assembles start.o with kit-as. The stub sets up the stack (+ FPU on riscv), 214 # calls main, and reports main's return through the arch's exit oracle. 215 # =========================================================================== 216 217 _bare_emit_aa64() { 218 local dir="$1" 219 # The CPU comes out of reset at EL1 with the FPU trapping and the MMU off. 220 # Two things general C needs are therefore set up before calling main: 221 # * CPACR_EL1.FPEN = 0b11 — else the first float/double op traps. 222 # * an identity MMU map marking RAM as Normal memory — with the MMU off 223 # all data accesses are Device-nGnRnE, which enforces natural alignment, 224 # so the wide unaligned stores kit emits (e.g. an 8-byte aggregate-init 225 # store landing on a 4-byte-aligned slot) take an alignment fault. We 226 # have no exception vectors, so a fault spins at the vector base forever 227 # (timeout). Normal memory permits the unaligned access, like a real OS. 228 # Sysregs use the generic S<op0>_<op1>_C<crn>_C<crm>_<op2> spelling (kit-as 229 # only names a handful by mnemonic). 230 cat > "$dir/start.S" <<'EOF' 231 .section .text.start,"ax",@progbits 232 .globl _start 233 _start: 234 adrp x0, stack_top 235 add x0, x0, :lo12:stack_top 236 and x0, x0, #0xfffffffffffffff0 // AArch64 faults on a misaligned SP; the 237 mov sp, x0 // linker-script .bss lump may not 16-align 238 // stack_top, so force it here. 239 240 movz x0, #0x30, lsl #16 // CPACR_EL1.FPEN = 0b11 (don't trap FP/SIMD) 241 msr S3_0_C1_C0_2, x0 242 isb 243 244 movz x0, #0x00ff // MAIR_EL1 attr0 = 0xFF (Normal WB) 245 msr S3_0_C10_C2_0, x0 246 movz x0, #0x3519 // TCR_EL1: T0SZ=25, 4KB granule, WB/IS 247 movk x0, #0x0080, lsl #16 // table walks, EPD1=1 (no TTBR1), 248 movk x0, #0x0001, lsl #32 // IPS=36-bit 249 msr S3_0_C2_C0_2, x0 250 adrp x1, l1_table // fill the 512-entry L1 table with 1GB 251 add x1, x1, :lo12:l1_table // identity blocks, all Normal memory 252 mov x2, #0 253 movz x3, #0x0701 // block desc: AF | SH=inner | AttrIdx0 | 01 254 .Lmap: 255 lsl x4, x2, #30 // output PA = index * 1GB == input VA 256 orr x4, x4, x3 257 str x4, [x1, x2, lsl #3] 258 add x2, x2, #1 259 cmp x2, #512 260 b.lo .Lmap 261 msr S3_0_C2_C0_0, x1 // TTBR0_EL1 = l1_table 262 dsb sy 263 isb 264 mrs x0, S3_0_C1_C0_0 // SCTLR_EL1: enable MMU + caches 265 orr x0, x0, #(1 << 0) // M (MMU) 266 orr x0, x0, #(1 << 2) // C (data cache) 267 orr x0, x0, #(1 << 12) // I (instruction cache) 268 movn x5, #(1 << 1) // A=0: allow unaligned on Normal memory 269 and x0, x0, x5 270 msr S3_0_C1_C0_0, x0 271 isb 272 273 bl main 274 adrp x1, semihost_args 275 add x1, x1, :lo12:semihost_args 276 str x0, [x1, #8] 277 mov x0, #0x20 278 hlt #0xf000 279 .Lhang: 280 b .Lhang 281 282 .section .data.semihost,"aw",@progbits 283 .balign 8 284 semihost_args: 285 .quad 0x20026 286 .quad 0 287 288 .section .bss.pgtbl,"aw",@nobits 289 .balign 4096 290 l1_table: 291 .zero 4096 292 293 .section .bss.stack,"aw",@nobits 294 .balign 16 295 stack_bottom: 296 .zero 65536 297 stack_top: 298 EOF 299 cat > "$dir/link.ld" <<'EOF' 300 ENTRY(_start) 301 SECTIONS { 302 . = 0x40080000; 303 .text : ALIGN(8) { *(.text.start) *(.text*) } 304 .rodata : ALIGN(8) { *(.rodata*) } 305 .data : ALIGN(8) { *(.data*) } 306 .bss.pgtbl : ALIGN(4096) { *(.bss.pgtbl) } /* L1 table needs 4K alignment */ 307 .bss : ALIGN(16) { *(.bss*) *(COMMON) . = ALIGN(., 16); } 308 /DISCARD/ : { *(.comment) } 309 } 310 EOF 311 "$EXEC_BARE_KIT" as -target aarch64-none-elf \ 312 -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" 313 } 314 315 _bare_emit_rv64() { 316 local dir="$1" 317 cat > "$dir/start.S" <<'EOF' 318 .section .text.start,"ax",@progbits 319 .globl _start 320 _start: 321 li sp, 0x81000000 322 li t0, 0x2000 323 csrs mstatus, t0 324 call main 325 li t0, 0x100000 326 beqz a0, .Lpass 327 slli a0, a0, 16 328 li t1, 0x3333 329 or a0, a0, t1 330 sw a0, 0(t0) 331 .Lhang: 332 j .Lhang 333 .Lpass: 334 li t1, 0x5555 335 sw t1, 0(t0) 336 j .Lhang 337 EOF 338 cat > "$dir/link.ld" <<'EOF' 339 ENTRY(_start) 340 SECTIONS { 341 . = 0x80000000; 342 .text : { *(.text.start) *(.text*) } 343 .rodata : { *(.rodata*) } 344 .data : { *(.data*) } 345 .bss : { *(.bss*) *(COMMON) } 346 /DISCARD/ : { *(.riscv.attributes) *(.comment) } 347 } 348 EOF 349 "$EXEC_BARE_KIT" as -target riscv64-none-elf \ 350 -march=rv64imafd_zicsr_zifencei -mabi=lp64d \ 351 -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" 352 } 353 354 # rv32 is the corpus-capable arch: the reset stub seeds a static-TLS image and 355 # enables the FPU (ilp32f); the C wrapper maps the entry's return onto the 356 # SiFive finisher. The runtime archive (i64 + soft-double helpers) is linked in. 357 # Ported from the former exec_rv32_bare.sh; see doc/plan/PORT.md. 358 _bare_emit_rv32() { 359 local dir="$1" entry="${2:-main}" 360 local march="rv32imafc_zicsr_zifencei" mabi="ilp32f" 361 local rt="$EXEC_BARE_ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a" 362 if [ ! -f "$rt" ]; then 363 make -C "$EXEC_BARE_ROOT" rt-riscv32-elf-hardfloat >/dev/null 2>&1 || true 364 fi 365 [ -f "$rt" ] || return 1 366 cat > "$dir/start.S" <<'EOF' 367 .section .text.start,"ax",@progbits 368 .globl _start 369 _start: 370 li sp, 0x80100000 371 li t0, 0x2000 372 csrs mstatus, t0 # mstatus.FS = Initial (enable the FPU for ilp32f) 373 374 // ---- static thread-local storage setup ----------------------------------- 375 la t0, __rv32_tls_block # t0 = block base (TCB at +0) 376 addi t1, t0, 16 # t1 = dst = block + 16 (.tdata copy start) 377 la t2, __rv32_tdata_lma # t2 = src = .tdata load image 378 la t3, __rv32_tdata_size # t3 = .tdata byte count (abs symbol: la yields value) 379 .Lcopy: 380 beqz t3, .Lcopy_done 381 lbu t4, 0(t2) 382 sb t4, 0(t1) 383 addi t1, t1, 1 384 addi t2, t2, 1 385 addi t3, t3, -1 386 j .Lcopy 387 .Lcopy_done: 388 la t3, __rv32_tbss_size 389 .Lzero: 390 beqz t3, .Lzero_done 391 sb zero, 0(t1) 392 addi t1, t1, 1 393 addi t3, t3, -1 394 j .Lzero 395 .Lzero_done: 396 la t2, __rv32_tdata_lma 397 sub tp, t0, t2 # tp = block - __rv32_tdata_lma 398 // --------------------------------------------------------------------------- 399 400 call _rv32_cmain 401 .Lhang: j .Lhang 402 403 .section .bss.rv32tls,"aw",@nobits 404 .balign 16 405 __rv32_tls_block: 406 .zero 4096 407 EOF 408 cat > "$dir/wrap.c" <<EOF 409 #define FINISHER ((volatile unsigned int*)0x100000) 410 extern int ${entry}(void); 411 __attribute__((noreturn)) void _rv32_cmain(void) { 412 int code = ${entry}(); 413 *FINISHER = code ? (0x3333u | ((unsigned)code << 16)) : 0x5555u; 414 for (;;) {} 415 } 416 EOF 417 cat > "$dir/link.ld" <<'EOF' 418 ENTRY(_start) 419 SECTIONS { 420 . = 0x80000000; 421 .text : { *(.text.start) *(.text*) } 422 .rodata : { *(.rodata*) } 423 .data : { *(.data*) } 424 .tdata : { . = ALIGN(16); __rv32_tdata_lma = .; *(.tdata .tdata.*) } 425 .tdata_end : { __rv32_tdata_end = .; } 426 .tbss : { __rv32_tbss_start = .; *(.tbss .tbss.*) } 427 .tbss_end : { __rv32_tbss_end = .; } 428 .bss : { *(.bss*) *(COMMON) } 429 __rv32_tdata_size = __rv32_tdata_end - __rv32_tdata_lma; 430 __rv32_tbss_size = __rv32_tbss_end - __rv32_tbss_start; 431 /DISCARD/ : { *(.riscv.attributes) *(.comment) } 432 } 433 EOF 434 "$EXEC_BARE_KIT" as -target riscv32-none-elf -march="$march" -mabi="$mabi" \ 435 -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" || return 1 436 "$EXEC_BARE_KIT" cc -target riscv32-none-elf -march="$march" -mabi="$mabi" \ 437 -O1 -ffreestanding -c "$dir/wrap.c" -o "$dir/wrap.o" 2>"$dir/wrap.err" || return 1 438 } 439 440 # arm32 (Cortex-M3, Thumb-2) bare-metal reset stub for qemu-system-arm 441 # -machine mps2-an385. The M-profile boot contract is a vector table at flash 442 # address 0x0: word[0] = initial MSP (top of SRAM), word[1] = reset handler. 443 # The reset handler copies .data flash->SRAM, zeroes .bss, calls the corpus 444 # entry (default `main`), and reports its return through the ARM semihosting 445 # SYS_EXIT_EXTENDED oracle (r0=0x20, r1=&{ADP_Stopped_ApplicationExit, code}, 446 # BKPT #0xAB) — qemu's own process exit then == the guest exit code. 447 # 448 # The startup stub is assembled by kit-as. Keep it within the descriptor-driven 449 # Thumb-2 subset: MOVW/MOVT symbol materialization, explicit ADDs instead of 450 # post-indexed memory, and named local labels. 451 # 452 # `Reset_Handler` is the true M-profile reset entry (named in the vector table); 453 # `_start` is aliased to it so the shared `kit ld -e _start` link path resolves. 454 _bare_emit_arm32() { 455 local dir="$1" entry="${2:-main}" 456 # Build the arm-eabi-thumb2 runtime variant on demand for the i64/double 457 # helpers (best-effort; exec_bare_link degrades gracefully if absent). 458 local rt="$EXEC_BARE_ROOT/build/rt/arm-eabi-thumb2/libkit_rt.a" 459 if [ ! -f "$rt" ]; then 460 make -C "$EXEC_BARE_ROOT" rt-arm-eabi-thumb2 >/dev/null 2>&1 || true 461 fi 462 cat > "$dir/start.S" <<EOF 463 .syntax unified 464 .thumb 465 .cpu cortex-m3 466 467 /* ---- Vector table: must be FIRST, placed at address 0x0 ---- */ 468 .section .vectors, "a" 469 .balign 4 470 .global _vectors 471 _vectors: 472 .word _estack /* word[0]: initial MSP = top of SRAM */ 473 .word Reset_Handler /* word[1]: reset handler (Thumb bit auto-set) */ 474 475 /* ---- Reset handler (== _start for the shared kit-ld entry) ---- */ 476 .section .text.start, "ax" 477 .thumb_func 478 .balign 4 479 .global Reset_Handler 480 .global _start 481 .type Reset_Handler, @function 482 .type _start, @function 483 Reset_Handler: 484 _start: 485 /* Copy .data from flash LMA (_sidata) to SRAM VMA (_sdata.._edata). */ 486 movw r0, #:lower16:_sidata 487 movt r0, #:upper16:_sidata 488 movw r1, #:lower16:_sdata 489 movt r1, #:upper16:_sdata 490 movw r2, #:lower16:_edata 491 movt r2, #:upper16:_edata 492 .Lcopy_data: 493 cmp r1, r2 494 bcs .Lcopy_done 495 ldr r3, [r0] 496 str r3, [r1] 497 add.w r0, r0, #4 498 add.w r1, r1, #4 499 b .Lcopy_data 500 .Lcopy_done: 501 502 /* Zero .bss (_sbss.._ebss). */ 503 movw r0, #:lower16:_sbss 504 movt r0, #:upper16:_sbss 505 movw r1, #:lower16:_ebss 506 movt r1, #:upper16:_ebss 507 movs r2, #0 508 .Lzero_bss: 509 cmp r0, r1 510 bcs .Lzero_done 511 str r2, [r0] 512 add.w r0, r0, #4 513 b .Lzero_bss 514 .Lzero_done: 515 516 /* Call the corpus entry; its return value lands in r0 == exit code. */ 517 bl ${entry} 518 bl _exit_semihost 519 .Lhang_after_main: 520 b .Lhang_after_main 521 522 /* ---- Semihosting SYS_EXIT_EXTENDED (0x20): r0 = exit code on entry. ---- */ 523 .thumb_func 524 .balign 4 525 .global _exit_semihost 526 .type _exit_semihost, @function 527 _exit_semihost: 528 mov r2, r0 /* save exit code */ 529 sub sp, sp, #8 /* 2-word parameter block on the stack */ 530 movw r3, #0x0026 531 movt r3, #0x0002 /* ADP_Stopped_ApplicationExit */ 532 str r3, [sp, #0] /* block[0] = reason */ 533 str r2, [sp, #4] /* block[1] = exit code */ 534 mov r1, sp /* r1 -> parameter block */ 535 movs r0, #0x20 /* SYS_EXIT_EXTENDED */ 536 bkpt 0xAB /* semihosting call */ 537 add sp, sp, #8 538 .Lhang_after_exit: 539 b .Lhang_after_exit 540 EOF 541 # mps2-an385: code in FLASH @ 0x0 (vector table first), data in SRAM @ 542 # 0x20000000 with the .data init image at a flash LMA (AT> FLASH); the stub 543 # copies it across at reset. kit ld's linker-script front end supports 544 # MEMORY / AT> / LOADADDR / ORIGIN / LENGTH; the one unsupported GNU form is 545 # a *top-level* `sym = expr`, so _estack is assigned inside SECTIONS. 546 cat > "$dir/link.ld" <<'EOF' 547 ENTRY(_start) 548 MEMORY { 549 FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4M 550 SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4M 551 } 552 SECTIONS { 553 .text : { 554 *(.vectors) 555 *(.text.start) 556 *(.text*) 557 *(.rodata*) 558 . = ALIGN(4); 559 } > FLASH 560 _sidata = LOADADDR(.data); 561 .data : ALIGN(4) { 562 _sdata = .; 563 *(.data*) 564 /* Single-thread TLS-as-static: __thread initialized data is a plain static 565 * instance, seeded from flash by the reset stub like .data. */ 566 *(.tdata*) 567 . = ALIGN(4); 568 _edata = .; 569 } > SRAM AT> FLASH 570 .bss (NOLOAD) : ALIGN(4) { 571 _sbss = .; 572 *(.bss*) 573 /* Single-thread TLS-as-static: __thread zero data folds into .bss. */ 574 *(.tbss*) 575 *(COMMON) 576 . = ALIGN(4); 577 _ebss = .; 578 } > SRAM 579 _estack = ORIGIN(SRAM) + LENGTH(SRAM); 580 /DISCARD/ : { *(.ARM.exidx*) *(.ARM.extab*) *(.ARM.attributes) 581 *(.comment) *(.note*) } 582 } 583 EOF 584 "$EXEC_BARE_KIT" as -target arm-none-eabi \ 585 -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" || return 1 586 } 587 588 # x86_64 long-mode reset: a multiboot header, a 32-bit entry that sets up 589 # identity-mapped paging + long mode, then calls main and reports its return via 590 # isa-debug-exit (port 0x501). Ported from freestanding_system.sh. 591 _bare_emit_x64() { 592 local dir="$1" out="$dir/start.S" i 593 cat > "$out" <<'EOF' 594 .section .multiboot,"a",@progbits 595 .balign 4 596 .long 0x1badb002 597 .long 0 598 .long 0xe4524ffe 599 600 .section .note.Xen,"a",@note 601 .balign 4 602 .long 4 603 .long 4 604 .long 18 605 .ascii "Xen" 606 .byte 0 607 .long _start 608 609 .section .text.start,"ax",@progbits 610 .globl _start 611 _start: 612 .byte 0xfa /* cli */ 613 .byte 0xbc /* mov $stack_top, %esp */ 614 .long stack_top 615 .byte 0xb8 /* mov $pml4, %eax */ 616 .long pml4 617 .byte 0x0f, 0x22, 0xd8 /* mov %eax, %cr3 */ 618 .byte 0x0f, 0x20, 0xe0 /* mov %cr4, %eax */ 619 .byte 0x83, 0xc8, 0x20 /* or $CR4_PAE, %eax */ 620 .byte 0x0f, 0x22, 0xe0 /* mov %eax, %cr4 */ 621 .byte 0xb9, 0x80, 0x00, 0x00, 0xc0 /* mov $EFER, %ecx */ 622 .byte 0x0f, 0x32 /* rdmsr */ 623 .byte 0x0d, 0x00, 0x01, 0x00, 0x00 /* or $EFER_LME, %eax */ 624 .byte 0x0f, 0x30 /* wrmsr */ 625 .byte 0x0f, 0x20, 0xc0 /* mov %cr0, %eax */ 626 .byte 0x0d, 0x00, 0x00, 0x00, 0x80 /* or $CR0_PG, %eax */ 627 .byte 0x0f, 0x22, 0xc0 /* mov %eax, %cr0 */ 628 .byte 0x0f, 0x01, 0x15 /* lgdt gdt_desc */ 629 .long gdt_desc 630 .byte 0xea /* ljmp $0x08,$long_entry */ 631 .long long_entry 632 .hword 0x08 633 634 long_entry: 635 .byte 0x66, 0xb8, 0x10, 0x00 /* mov $0x10, %ax */ 636 .byte 0x8e, 0xd8 /* mov %ax, %ds */ 637 .byte 0x8e, 0xc0 /* mov %ax, %es */ 638 .byte 0x8e, 0xd0 /* mov %ax, %ss */ 639 .byte 0x48, 0xbc /* movabs $stack_top, %rsp */ 640 .quad stack_top 641 call main 642 .byte 0x66, 0xba, 0x01, 0x05 /* mov $0x501, %dx */ 643 .byte 0x66, 0xef /* outw %ax, %dx */ 644 .Lhang: 645 .byte 0xf4 /* hlt */ 646 jmp .Lhang 647 648 .section .data.boot,"aw",@progbits 649 .balign 8 650 gdt: 651 .quad 0 652 .quad 0x00af9a000000ffff 653 .quad 0x00af92000000ffff 654 gdt_desc: 655 .hword 23 656 .long gdt 657 658 .balign 4096 659 pml4: 660 .quad pdpt + 0x3 661 EOF 662 for _ in $(seq 1 511); do printf ' .quad 0\n' >> "$out"; done 663 cat >> "$out" <<'EOF' 664 .balign 4096 665 pdpt: 666 .quad pd + 0x3 667 EOF 668 for _ in $(seq 1 511); do printf ' .quad 0\n' >> "$out"; done 669 printf '.balign 4096\npd:\n' >> "$out" 670 i=0 671 while [ "$i" -lt 512 ]; do 672 printf ' .quad 0x%016x\n' $((i * 0x200000 + 0x83)) >> "$out" 673 i=$((i + 1)) 674 done 675 cat >> "$out" <<'EOF' 676 677 .section .bss.stack,"aw",@nobits 678 .balign 16 679 stack_bottom: 680 .zero 65536 681 stack_top: 682 EOF 683 cat > "$dir/link.ld" <<'EOF' 684 ENTRY(_start) 685 SECTIONS { 686 . = 0x100000; 687 .multiboot : { *(.multiboot) } 688 .note.Xen : { *(.note.Xen) } 689 .text : { *(.text.start) *(.text*) } 690 .rodata : { *(.rodata*) } 691 .data.boot : ALIGN(4096) { *(.data.boot) } 692 .data : { *(.data*) } 693 .bss.stack : ALIGN(16) { *(.bss.stack) } 694 .bss : { *(.bss*) *(COMMON) } 695 /DISCARD/ : { *(.comment) } 696 } 697 EOF 698 "$EXEC_BARE_KIT" as -target x86_64-none-elf \ 699 -o "$dir/start.o" "$out" 2>"$dir/as.err" 700 }