Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
106 lines
3.3 KiB
ArmAsm
106 lines
3.3 KiB
ArmAsm
/*
|
|
* switch.S (aarch64) - Cooperative VM context switch primitive.
|
|
*
|
|
* FABRIC-3.md §XXVIII (Stage 2, preemptive-context-switch save/restore
|
|
* primitive, 2026-09-13). Deliberately NOT isr.S's interrupt trap frame
|
|
* (Stage 0) -- that saves everything because an interrupt has no call
|
|
* site. sk_vm_switch_to() is an ordinary `bl`, so AAPCS64 already makes
|
|
* every caller-saved register (x0-x18, v0-v7, v16-v31) the caller's own
|
|
* problem across the call. Only the callee-saved set needs explicit
|
|
* handling: x19-x28, x29 (fp), x30 (lr), and d8-d15 (only the low 64
|
|
* bits of v8-v15 are callee-saved per AAPCS64).
|
|
*
|
|
* void sk_vm_switch_to(uint64_t *save_sp_out, uint64_t new_sp);
|
|
* x0 = save_sp_out, x1 = new_sp
|
|
*
|
|
* For a VM that has never run before, new_sp must point at a synthetic
|
|
* 160-byte frame built by the C glue (switch.c) to this exact layout,
|
|
* with x30 (lr) set to the trampoline entry address and everything else
|
|
* zeroed, so the final `ret` below (which branches to lr) jumps there.
|
|
*/
|
|
|
|
.section .text
|
|
.balign 4
|
|
|
|
.equ SW_X19, 0
|
|
.equ SW_X21, 16
|
|
.equ SW_X23, 32
|
|
.equ SW_X25, 48
|
|
.equ SW_X27, 64
|
|
.equ SW_X29, 80 /* x29 (fp), x30 (lr) */
|
|
.equ SW_D8, 96
|
|
.equ SW_D10, 112
|
|
.equ SW_D12, 128
|
|
.equ SW_D14, 144
|
|
.equ SW_SIZE, 160
|
|
|
|
/* .hidden is an ELF-only visibility directive with no COFF equivalent;
|
|
* this file is also assembled for the aarch64 loader, which clang targets
|
|
* at aarch64-pc-windows-msvc (PE/COFF) -- guard on _WIN32 (defined only by
|
|
* that target), matching isr.S's own precedent exactly. */
|
|
#ifndef _WIN32
|
|
.hidden sk_vm_switch_to
|
|
#endif
|
|
|
|
.global sk_vm_switch_to
|
|
sk_vm_switch_to:
|
|
sub sp, sp, #SW_SIZE
|
|
|
|
stp x19, x20, [sp, #SW_X19]
|
|
stp x21, x22, [sp, #SW_X21]
|
|
stp x23, x24, [sp, #SW_X23]
|
|
stp x25, x26, [sp, #SW_X25]
|
|
stp x27, x28, [sp, #SW_X27]
|
|
stp x29, x30, [sp, #SW_X29]
|
|
stp d8, d9, [sp, #SW_D8]
|
|
stp d10, d11, [sp, #SW_D10]
|
|
stp d12, d13, [sp, #SW_D12]
|
|
stp d14, d15, [sp, #SW_D14]
|
|
|
|
mov x2, sp
|
|
str x2, [x0] /* *save_sp_out = sp (this context's parked SP) */
|
|
|
|
mov sp, x1 /* sp = new_sp (switch stacks) */
|
|
|
|
ldp x19, x20, [sp, #SW_X19]
|
|
ldp x21, x22, [sp, #SW_X21]
|
|
ldp x23, x24, [sp, #SW_X23]
|
|
ldp x25, x26, [sp, #SW_X25]
|
|
ldp x27, x28, [sp, #SW_X27]
|
|
ldp x29, x30, [sp, #SW_X29]
|
|
ldp d8, d9, [sp, #SW_D8]
|
|
ldp d10, d11, [sp, #SW_D10]
|
|
ldp d12, d13, [sp, #SW_D12]
|
|
ldp d14, d15, [sp, #SW_D14]
|
|
|
|
add sp, sp, #SW_SIZE
|
|
ret
|
|
|
|
/*
|
|
* uint64_t sk_vm_switch_prime(uint64_t stack_top, void *entry);
|
|
*
|
|
* Builds the synthetic frame above at the top of a fresh native stack, so
|
|
* the first ever sk_vm_switch_to() into it lands at `entry` (x30/lr) via
|
|
* its final `ret`. Kept in this file (not C) so the layout can never
|
|
* drift from sk_vm_switch_to() above -- one set of offsets, not two.
|
|
*/
|
|
#ifndef _WIN32
|
|
.hidden sk_vm_switch_prime
|
|
#endif
|
|
|
|
.global sk_vm_switch_prime
|
|
sk_vm_switch_prime:
|
|
sub x2, x0, #SW_SIZE
|
|
stp xzr, xzr, [x2, #SW_X19]
|
|
stp xzr, xzr, [x2, #SW_X21]
|
|
stp xzr, xzr, [x2, #SW_X23]
|
|
stp xzr, xzr, [x2, #SW_X25]
|
|
stp xzr, xzr, [x2, #SW_X27]
|
|
stp xzr, x1, [x2, #SW_X29] /* x29=0, x30(lr)=entry */
|
|
stp xzr, xzr, [x2, #SW_D8]
|
|
stp xzr, xzr, [x2, #SW_D10]
|
|
stp xzr, xzr, [x2, #SW_D12]
|
|
stp xzr, xzr, [x2, #SW_D14]
|
|
mov x0, x2
|
|
ret
|