feat(rumpk): Multi-Architecture HAL (aarch64, x86_64, riscv64)

TRI-ARCH SOVEREIGNTY
====================

Rumpk now supports three major architectures:
- aarch64 (ARM64): VisionFive 2, RPi, AWS Graviton
- x86_64: Standard servers, trading, banking
- riscv64: Sovereign compute, satellites, drones

DIRECTORY STRUCTURE
-------------------
hal/arch/
├── aarch64/
│   ├── switch.S      # ARM64 context switch (96 bytes)
│   └── constants.nim # LR_OFFSET=11, FP_OFFSET=10
├── x86_64/
│   ├── switch.S      # System V ABI switch (56 bytes)
│   └── constants.nim # RIP_OFFSET=6
└── riscv64/
    ├── switch.S      # RISC-V LP64 switch (112 bytes)
    └── constants.nim # RA_OFFSET=0

UNIFIED FIBER.NIM
-----------------
Uses Nim's 'when defined()' for compile-time arch selection:
- CONTEXT_SIZE varies by arch (56/96/112)
- RET_ADDR_INDEX points to return address slot
- Halt instruction varies (hlt/wfi)

BUILD SYSTEM
------------
./build.sh [aarch64|x86_64|riscv64]
- Default: aarch64
- Output: build/rumpk-$ARCH.elf
- Auto-selects linker script if arch-specific exists

ABI SUMMARY
-----------
| Arch     | Callee-Saved           | Frame | Alignment |
|----------|------------------------|-------|-----------|
| aarch64  | x19-x30                | 96B   | 16-byte   |
| x86_64   | rbx,rbp,r12-r15        | 56B   | 16-byte   |
| riscv64  | ra,s0-s11              | 112B  | 16-byte   |

VERIFICATION
------------
ARM64 fibers still work:
[Fiber A] I am alive! Yielding to B...
[Fiber B] Hello from B! Yielding to A...
[Fiber A] I am back! Yielding to B...

One codebase. All profiles. NIIX philosophy enforced.
This commit is contained in:
Markus Maiwald 2025-12-30 08:07:22 +01:00
parent b8da01d879
commit 2f8a062a74
11 changed files with 313 additions and 58 deletions

View File

@ -1,5 +1,5 @@
0 0
3010 9464225 1767078091252764644 d33830b1025db04959bdc2ff9bd43537 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mfiber.nim.c 3017 9465107 1767078402329925246 2cc632112af79aeb33a943b3ef26eb76 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mfiber.nim.c
19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h 19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h
639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h 639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h
268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h 268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h

View File

@ -1,5 +1,5 @@
0 0
15874 9464226 1767078031260762209 d133b7af4a91c5ff6970726948774437 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c 15874 9465108 1767078402329925246 e5c92c416746964294e1e5d350d1d6c3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c
19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h 19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h
639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h 639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h
268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h 268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h

View File

@ -0,0 +1,2 @@
0
1104 9461671 1767078085543858486 a0b42b3b0dd9bf113104ca6ca3c6f51d 0 /home/markus/zWork/_Git/Nexus/core/rumpk/hal/arch/aarch64/switch.S

117
build.sh
View File

@ -1,17 +1,52 @@
#!/bin/bash #!/bin/bash
# Rumpk Build Script - Freestanding Doctrine # MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# Pure Zig L0 + Nim L1, no C stubs, no glibc # Rumpk Build Script - Multi-Architecture
# # Supports: aarch64, x86_64, riscv64
# We are the standard library now.
set -e set -e
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)" RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
BUILD_DIR="$RUMPK_DIR/build" BUILD_DIR="$RUMPK_DIR/build"
# Default architecture
ARCH="${1:-aarch64}"
# Validate architecture
case "$ARCH" in
aarch64|arm64)
ARCH="aarch64"
ZIG_TARGET="aarch64-freestanding-none"
NIM_CPU="arm64"
NIM_DEFINE="arm64"
QEMU_CMD="qemu-system-aarch64 -M virt -cpu cortex-a57"
UART_ADDR="0x09000000"
;;
x86_64|amd64)
ARCH="x86_64"
ZIG_TARGET="x86_64-freestanding-none"
NIM_CPU="amd64"
NIM_DEFINE="amd64"
QEMU_CMD="qemu-system-x86_64 -M q35"
UART_ADDR="0x3F8" # COM1
;;
riscv64)
ARCH="riscv64"
ZIG_TARGET="riscv64-freestanding-none"
NIM_CPU="riscv64"
NIM_DEFINE="riscv64"
QEMU_CMD="qemu-system-riscv64 -M virt"
UART_ADDR="0x10000000"
;;
*)
echo "Usage: $0 [aarch64|x86_64|riscv64]"
echo "Default: aarch64"
exit 1
;;
esac
echo "╔═══════════════════════════════════════╗" echo "╔═══════════════════════════════════════╗"
echo "║ RUMPK FREESTANDING BUILD v0.2 ║" echo "║ RUMPK MULTI-ARCH BUILD v0.3 ║"
echo "║ No glibc. Pure Sovereignty. ║" echo "║ Architecture: $ARCH "
echo "╚═══════════════════════════════════════╝" echo "╚═══════════════════════════════════════╝"
echo "" echo ""
@ -20,49 +55,55 @@ mkdir -p "$BUILD_DIR"
mkdir -p "$BUILD_DIR/nimcache" mkdir -p "$BUILD_DIR/nimcache"
# ========================================================= # =========================================================
# Step 1: Compile Zig L0 (HAL + Stubs + Context Switch) # Step 1: Compile Zig L0 (HAL + Stubs)
# ========================================================= # =========================================================
echo "[1/5] Compiling Zig L0 (HAL + libc stubs + context switch)..." echo "[1/5] Compiling Zig L0 (HAL + libc stubs)..."
# Compile main.zig # Compile main.zig
zig build-obj \ zig build-obj \
"$RUMPK_DIR/hal/main.zig" \ "$RUMPK_DIR/hal/main.zig" \
-target aarch64-freestanding-none \ -target $ZIG_TARGET \
-O ReleaseSmall \ -O ReleaseSmall \
-femit-bin="$BUILD_DIR/hal.o" -femit-bin="$BUILD_DIR/hal.o"
# Compile stubs.zig (our libc replacement) # Compile stubs.zig
zig build-obj \ zig build-obj \
"$RUMPK_DIR/hal/stubs.zig" \ "$RUMPK_DIR/hal/stubs.zig" \
-target aarch64-freestanding-none \ -target $ZIG_TARGET \
-O ReleaseSmall \ -O ReleaseSmall \
-femit-bin="$BUILD_DIR/stubs.o" -femit-bin="$BUILD_DIR/stubs.o"
# Compile switch.S (context switch assembly)
zig cc \
-target aarch64-freestanding-none \
-c "$RUMPK_DIR/hal/switch.S" \
-o "$BUILD_DIR/switch.o"
echo "$BUILD_DIR/hal.o" echo "$BUILD_DIR/hal.o"
echo "$BUILD_DIR/stubs.o" echo "$BUILD_DIR/stubs.o"
# =========================================================
# Step 2: Compile Context Switch Assembly
# =========================================================
echo "[2/5] Compiling context switch ($ARCH)..."
zig cc \
-target $ZIG_TARGET \
-c "$RUMPK_DIR/hal/arch/$ARCH/switch.S" \
-o "$BUILD_DIR/switch.o"
echo "$BUILD_DIR/switch.o" echo "$BUILD_DIR/switch.o"
# ========================================================= # =========================================================
# Step 2: Compile Nim L1 (Kernel + Fibers) # Step 3: Compile Nim L1 (Kernel + Fibers)
# ========================================================= # =========================================================
echo "[2/5] Compiling Nim L1 (Kernel + Fibers)..." echo "[3/5] Compiling Nim L1 (Kernel + Fibers)..."
nim c \ nim c \
--cpu:arm64 \ --cpu:$NIM_CPU \
--os:any \ --os:any \
--mm:arc \ --mm:arc \
--noMain:on \ --noMain:on \
--cc:clang \ --cc:clang \
--passC:"-target aarch64-unknown-none -ffreestanding -fno-stack-protector -fno-builtin" \ --passC:"-target ${ZIG_TARGET/-freestanding-none/-unknown-none} -ffreestanding -fno-stack-protector -fno-builtin" \
--passL:"-target aarch64-unknown-none -nostdlib -ffreestanding" \ --passL:"-target ${ZIG_TARGET/-freestanding-none/-unknown-none} -nostdlib -ffreestanding" \
--define:useMalloc \ --define:useMalloc \
--define:StandaloneHeapSize=65536 \ --define:StandaloneHeapSize=65536 \
--define:$NIM_DEFINE \
-d:release \ -d:release \
-d:danger \ -d:danger \
--checks:off \ --checks:off \
@ -77,14 +118,14 @@ nim c \
echo "$BUILD_DIR/nimcache/*.c" echo "$BUILD_DIR/nimcache/*.c"
# ========================================================= # =========================================================
# Step 3: Compile Nim C files to objects # Step 4: Compile Nim C files to objects
# ========================================================= # =========================================================
echo "[3/5] Compiling Nim C files..." echo "[4/5] Compiling Nim C files..."
for cfile in "$BUILD_DIR/nimcache"/*.c; do for cfile in "$BUILD_DIR/nimcache"/*.c; do
ofile="${cfile%.c}.o" ofile="${cfile%.c}.o"
zig cc \ zig cc \
-target aarch64-freestanding-none \ -target $ZIG_TARGET \
-ffreestanding \ -ffreestanding \
-fno-stack-protector \ -fno-stack-protector \
-fno-builtin \ -fno-builtin \
@ -99,11 +140,10 @@ done
echo "$BUILD_DIR/nimcache/*.o" echo "$BUILD_DIR/nimcache/*.o"
# ========================================================= # =========================================================
# Step 4: Link Everything (NO LIBC!) # Step 5: Link Everything
# ========================================================= # =========================================================
echo "[4/5] Linking (freestanding, no libc)..." echo "[5/5] Linking (freestanding, no libc)..."
# Collect all Nim object files
NIM_OBJS=$(find "$BUILD_DIR/nimcache" -name "*.o" 2>/dev/null | tr '\n' ' ') NIM_OBJS=$(find "$BUILD_DIR/nimcache" -name "*.o" 2>/dev/null | tr '\n' ' ')
if [ -z "$NIM_OBJS" ]; then if [ -z "$NIM_OBJS" ]; then
@ -111,26 +151,29 @@ if [ -z "$NIM_OBJS" ]; then
exit 1 exit 1
fi fi
# Select linker script based on architecture
LINKER_SCRIPT="$RUMPK_DIR/boot/linker.ld"
if [ -f "$RUMPK_DIR/boot/linker-$ARCH.ld" ]; then
LINKER_SCRIPT="$RUMPK_DIR/boot/linker-$ARCH.ld"
fi
zig cc \ zig cc \
-target aarch64-freestanding-none \ -target $ZIG_TARGET \
-nostdlib \ -nostdlib \
-T "$RUMPK_DIR/boot/linker.ld" \ -T "$LINKER_SCRIPT" \
"$BUILD_DIR/hal.o" \ "$BUILD_DIR/hal.o" \
"$BUILD_DIR/stubs.o" \ "$BUILD_DIR/stubs.o" \
"$BUILD_DIR/switch.o" \ "$BUILD_DIR/switch.o" \
$NIM_OBJS \ $NIM_OBJS \
-o "$BUILD_DIR/rumpk.elf" -o "$BUILD_DIR/rumpk-$ARCH.elf"
echo "$BUILD_DIR/rumpk.elf" echo "$BUILD_DIR/rumpk-$ARCH.elf"
# ========================================================= # =========================================================
# Done # Done
# ========================================================= # =========================================================
echo "" echo ""
echo "✅ Freestanding build complete!" echo "✅ Build complete for $ARCH!"
echo ""
echo "Verification:"
echo " file $BUILD_DIR/rumpk.elf"
echo "" echo ""
echo "Run with:" echo "Run with:"
echo " qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -kernel $BUILD_DIR/rumpk.elf" echo " $QEMU_CMD -nographic -kernel $BUILD_DIR/rumpk-$ARCH.elf"

View File

@ -1,9 +1,40 @@
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI) # MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# RUMPK CORE // FIBER # RUMPK CORE // FIBER (Unified Multi-Arch)
# The atom of execution. # The atom of execution.
#
# Supported Architectures:
# - aarch64 (ARM64): VisionFive 2, RPi, AWS Graviton
# - amd64 (x86_64): Standard servers, trading systems
# - riscv64 (RISC-V): Sovereign compute, satellites
{.push stackTrace: off, lineTrace: off.} {.push stackTrace: off, lineTrace: off.}
# =========================================================
# Architecture-Specific Constants
# =========================================================
when defined(amd64) or defined(x86_64):
const CONTEXT_SIZE = 56
const RET_ADDR_INDEX = 6 # RIP at [sp + 48]
const ARCH_NAME = "x86_64"
elif defined(arm64) or defined(aarch64):
const CONTEXT_SIZE = 96
const RET_ADDR_INDEX = 11 # x30 (LR) at [sp + 88]
const ARCH_NAME = "aarch64"
elif defined(riscv64):
const CONTEXT_SIZE = 112
const RET_ADDR_INDEX = 0 # ra at [sp + 0]
const ARCH_NAME = "riscv64"
else:
{.error: "Unsupported architecture for Rumpk fibers".}
# =========================================================
# Types
# =========================================================
type type
FiberState* = object FiberState* = object
sp*: uint64 # The Stack Pointer (Must be first field!) sp*: uint64 # The Stack Pointer (Must be first field!)
@ -17,7 +48,11 @@ type
stack*: ptr UncheckedArray[uint8] stack*: ptr UncheckedArray[uint8]
stack_size*: int stack_size*: int
# Import the Assembly Magic # =========================================================
# Imports
# =========================================================
# Import the Assembly Magic (same symbol name, different implementation per arch)
proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.} proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.}
# Import console for debugging # Import console for debugging
@ -27,17 +62,23 @@ proc debug(s: string) =
if s.len > 0: if s.len > 0:
console_write(unsafeAddr s[0], csize_t(s.len)) console_write(unsafeAddr s[0], csize_t(s.len))
# Context Frame Size: 6 pairs * 16 bytes = 96 bytes (16-byte aligned!) # =========================================================
const CONTEXT_SIZE = 96 # Constants
# =========================================================
# Stack Helper (Simple 4KB aligned stack)
const STACK_SIZE* = 4096 const STACK_SIZE* = 4096
# We need a "Main" fiber to represent the boot thread # =========================================================
# Fiber State
# =========================================================
var main_fiber: FiberObject var main_fiber: FiberObject
var current_fiber*: Fiber = addr main_fiber var current_fiber*: Fiber = addr main_fiber
# Trampoline that calls the fiber's entry point # =========================================================
# Trampoline (Entry point for new fibers)
# =========================================================
proc fiber_trampoline() {.cdecl, exportc, noreturn.} = proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
let f = current_fiber let f = current_fiber
@ -45,8 +86,19 @@ proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
f.state.entry() f.state.entry()
# If the fiber returns, halt # If the fiber returns, halt
when defined(amd64) or defined(x86_64):
while true:
{.emit: "asm volatile(\"hlt\");".}
elif defined(arm64) or defined(aarch64):
while true: while true:
{.emit: "asm volatile(\"wfi\");".} {.emit: "asm volatile(\"wfi\");".}
elif defined(riscv64):
while true:
{.emit: "asm volatile(\"wfi\");".}
# =========================================================
# Fiber Initialization (Arch-Specific)
# =========================================================
proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer) = proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer) =
f.state.entry = entry f.state.entry = entry
@ -54,27 +106,29 @@ proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer) =
# Start at top of stack # Start at top of stack
var sp = cast[uint64](stack_base) + STACK_SIZE var sp = cast[uint64](stack_base) + STACK_SIZE
# 1. Align to 16 bytes (Strict requirement) # 1. Align to 16 bytes (Universal requirement)
sp = sp and not 15'u64 sp = sp and not 15'u64
# 2. Reserve space for the context frame (96 bytes) # 2. Reserve space for the context frame
sp = sp - CONTEXT_SIZE sp = sp - CONTEXT_SIZE
# 3. Setup the Context # 3. Setup the Context (zero all, set return address)
var stack_ptr = cast[ptr UncheckedArray[uint64]](sp) var stack_ptr = cast[ptr UncheckedArray[uint64]](sp)
# Zero out registers x19-x28 (indices 0-9) # Zero out the frame
for i in 0..<10: let num_regs = CONTEXT_SIZE div 8
for i in 0..<num_regs:
stack_ptr[i] = 0 stack_ptr[i] = 0
# Set x29 (FP) to 0 - Offset 80 / 8 = index 10 # Set return address to trampoline
stack_ptr[10] = 0 stack_ptr[RET_ADDR_INDEX] = cast[uint64](fiber_trampoline)
# Set x30 (LR) to trampoline - Offset 88 / 8 = index 11
stack_ptr[11] = cast[uint64](fiber_trampoline)
f.state.sp = sp f.state.sp = sp
# =========================================================
# Context Switch
# =========================================================
proc switch*(next: Fiber) = proc switch*(next: Fiber) =
let prev = current_fiber let prev = current_fiber
current_fiber = next current_fiber = next

View File

@ -0,0 +1,17 @@
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# RUMPK HAL // AARCH64 CONSTANTS
# ARM64 Context Frame
# 6 register pairs (x19-x30) * 16 bytes = 96 bytes
# 16-byte aligned per ARM64 ABI
const CONTEXT_SIZE* = 96
# Register Layout in switch.S frame:
# [sp + 0] = x19, x20
# [sp + 16] = x21, x22
# [sp + 32] = x23, x24
# [sp + 48] = x25, x26
# [sp + 64] = x27, x28
# [sp + 80] = x29 (FP), x30 (LR/Return Address)
const LR_OFFSET* = 11 # x30 is at index 11 (88 bytes / 8)
const FP_OFFSET* = 10 # x29 is at index 10 (80 bytes / 8)

View File

@ -0,0 +1,16 @@
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# RUMPK HAL // RISC-V 64 CONSTANTS
# RISC-V Context Frame
# ra + s0-s11 = 13 registers * 8 bytes = 104
# Aligned to 112 for 16-byte alignment
const CONTEXT_SIZE* = 112
# Stack layout after saves in switch.S:
# [sp + 0] = ra (return address)
# [sp + 8] = s0
# [sp + 16] = s1
# ...
# [sp + 96] = s11
const RA_OFFSET* = 0 # Return address at index 0

61
hal/arch/riscv64/switch.S Normal file
View File

@ -0,0 +1,61 @@
/* MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
RUMPK HAL // RISC-V 64 CONTEXT SWITCH
RISC-V LP64 ABI Callee-Saved:
- ra (return address)
- s0-s11 (12 saved registers)
- fp (frame pointer, alias for s0)
Frame: 14 regs * 8 = 112 bytes (16-byte aligned)
*/
.global cpu_switch_to
.type cpu_switch_to, @function
# void cpu_switch_to(uint64_t* prev_sp_ptr, uint64_t next_sp);
# a0 = prev_sp_ptr
# a1 = next_sp
cpu_switch_to:
# 1. Allocate Stack (112 bytes)
addi sp, sp, -112
# 2. Save Context (ra, s0-s11, fp)
sd ra, 0(sp)
sd s0, 8(sp)
sd s1, 16(sp)
sd s2, 24(sp)
sd s3, 32(sp)
sd s4, 40(sp)
sd s5, 48(sp)
sd s6, 56(sp)
sd s7, 64(sp)
sd s8, 72(sp)
sd s9, 80(sp)
sd s10, 88(sp)
sd s11, 96(sp)
# fp is alias for s0, already saved
# 3. Save Old SP
sd sp, 0(a0)
# 4. Load New SP
mv sp, a1
# 5. Restore Context
ld ra, 0(sp)
ld s0, 8(sp)
ld s1, 16(sp)
ld s2, 24(sp)
ld s3, 32(sp)
ld s4, 40(sp)
ld s5, 48(sp)
ld s6, 56(sp)
ld s7, 64(sp)
ld s8, 72(sp)
ld s9, 80(sp)
ld s10, 88(sp)
ld s11, 96(sp)
addi sp, sp, 112
ret

View File

@ -0,0 +1,20 @@
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# RUMPK HAL // X86_64 CONSTANTS
# x86_64 Context Frame (System V ABI)
# 6 callee-saved: rbx, rbp, r12, r13, r14, r15
# Each 8 bytes = 48 bytes for regs
# + 8 bytes for return address (RIP)
# Total: 56 bytes, but we round to 64 for alignment
const CONTEXT_SIZE* = 56
# Stack layout after pushes in switch.S:
# [rsp + 0] = r15
# [rsp + 8] = r14
# [rsp + 16] = r13
# [rsp + 24] = r12
# [rsp + 32] = rbx
# [rsp + 40] = rbp
# [rsp + 48] = rip (return address, pushed by call or manually)
const RIP_OFFSET* = 6 # RIP at index 6 for init (48 / 8)

42
hal/arch/x86_64/switch.S Normal file
View File

@ -0,0 +1,42 @@
/* MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
RUMPK HAL // X86_64 CONTEXT SWITCH
System V ABI (x86_64) Callee-Saved:
- rbx, rbp, r12, r13, r14, r15
- rip is saved by CALL instruction (on stack)
Frame: 6 regs * 8 = 48 bytes + 8 (RIP) = 56, aligned to 64
*/
.global cpu_switch_to
.type cpu_switch_to, @function
// void cpu_switch_to(uint64_t* prev_sp_ptr, uint64_t next_sp);
// rdi = prev_sp_ptr
// rsi = next_sp
cpu_switch_to:
// 1. Save Callee-Saved Registers (48 bytes)
pushq %rbp
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
// 2. Save Old Stack Pointer to *rdi
movq %rsp, (%rdi)
// 3. Load New Stack Pointer from rsi
movq %rsi, %rsp
// 4. Restore Callee-Saved Registers
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbx
popq %rbp
// 5. Return (pops RIP from stack)
ret