kernel: Save and restore trap contexts

17b40aa2448e7f4b3985612fe1e82ca9a00e349cabcb7e9b5ccc337172fbf2b8
Assisted-by: Codex:gpt-6
Alexis Sellier committed ago 1 parent 3661ec1f
Makefile +6 -2
21 21
22 22
# Verify the emulator binary exists.
23 23
EMU_PATH := $(shell command -v $(EMU) 2>/dev/null)
24 24
25 25
default: emulator $(RAD_BIN)
26 -
test: emulator seed-test std-test bin-test kernel-test package-test native-test shared-test sync-test kernel-boot-test
26 +
test: emulator seed-test std-test bin-test kernel-test package-test native-test shared-test sync-test kernel-boot-test trap-test
27 27
28 28
seed-test:
29 29
	@seed/test
30 30
31 31
# Emulator command check
66 66
		lib/std.test.rv64.s \
67 67
		lib/std.test.rv64.o
68 68
69 69
# Kernel modules and tests use a separate package and test entry point.
70 70
KERNEL_SRC := kernel/kernel.rad $(shell find kernel/kernel -name '*.rad' ! -name 'tests.rad' ! -path '*/tests/*' 2>/dev/null)
71 -
KERNEL_ASM := kernel/kernel/boot.ras kernel/kernel/sync.ras
71 +
KERNEL_ASM := kernel/kernel/boot.ras kernel/kernel/sync.ras kernel/kernel/trap.ras
72 72
KERNEL_TEST_SRC := kernel/kernel/tests.rad $(shell find kernel/kernel/tests -name '*.rad' 2>/dev/null)
73 73
KERNEL := -pkg kernel $(patsubst %,-mod %,$(sort $(KERNEL_SRC)))
74 74
KERNEL_TEST := $(BIN_DIR)/kernel.test.rv64
75 75
76 76
kernel-test: emulator $(KERNEL_TEST)
188 188
.PHONY: test clean default seed-test std-test bin-test kernel-test package-test native-test shared-test sync-test seed \
189 189
	clean-std-test clean-bin-test clean-rad emulator
190 190
.SUFFIXES:
191 191
.DELETE_ON_ERROR:
192 192
.SILENT:
193 +
194 +
# Integer trap contexts and nested machine interrupts.
195 +
trap-test: $(BIN_DIR)/sync.build.rv64
196 +
	@RAD_EMULATOR="$(EMU)" sh test/trap/run
kernel/kernel.rad +1 -0
8 8
export mod slots;
9 9
export mod range;
10 10
export mod sync;
11 11
export mod platform;
12 12
@test export mod tests;
13 +
export mod trap;
13 14
export mod boot;
kernel/kernel/boot.rad +15 -0
3 3
use super::platform;
4 4
use super::fdt;
5 5
use super::range;
6 6
use super::limits;
7 7
use super::sync;
8 +
use super::trap;
8 9
9 10
/// Platform data published by hart zero before secondary initialization.
10 11
export unsafe static PLATFORM: platform::Platform = undefined;
11 12
/// Release/acquire publication flag for PLATFORM.
12 13
static READY: u64 = 0;
13 14
/// Number of harts that validated their machine stack.
14 15
static ARRIVED: u64 = 0;
16 +
/// Trap anchors owned by their physical harts.
17 +
unsafe static HARTS: [trap::Hart; limits::HARTS] = undefined;
18 +
19 +
/// Stop on a trap before a domain or interrupt dispatcher is installed.
20 +
unsafe fn unexpected(frame: &mut trap::Frame, hart: &mut trap::Hart) {
21 +
    print("kernel: unexpected trap\n");
22 +
    trap::halt();
23 +
}
15 24
16 25
/// Read a byte from a device register with I/O ordering.
17 26
fn read8(address: u64) -> u8;
18 27
/// Write a byte to a device register with I/O ordering.
19 28
fn write8(address: u64, value: u8);
56 65
    } else {
57 66
        while sync::loadAcquire(&READY) == 0 {}
58 67
    }
59 68
    assert (PLATFORM.harts & (1 << hart as u32)) <> 0;
60 69
    assert stackTop == PLATFORM.stacks[hart as u32].end and (stackTop & 15) == 0;
70 +
    let stack = PLATFORM.stacks[hart as u32];
71 +
    set HARTS[hart as u32] = trap::Hart {
72 +
        stackTop: stack.end, stackBottom: stack.start, kernelGp: 0, handler: unexpected,
73 +
        savedT0: 0, savedT1: 0, savedSp: 0,
74 +
    };
75 +
    trap::install(&mut HARTS[hart as u32]);
61 76
    let mut count: u64 = 0;
62 77
    for id in 0..limits::HARTS { if (PLATFORM.harts & (1 << id)) <> 0 { set count += 1; } }
63 78
    return sync::fetchAdd(&mut ARRIVED, 1) + 1 == count;
64 79
}
kernel/kernel/tests.rad +1 -0
3 3
export mod range;
4 4
export mod abi;
5 5
export mod slots;
6 6
export mod fdt;
7 7
export mod platform;
8 +
export mod trap;
kernel/kernel/tests/trap.rad added +43 -0
1 +
//! Trap classification, frame layout, and resume-address boundaries.
2 +
3 +
use std::testing;
4 +
use kernel::trap;
5 +
6 +
/// The assembly frame includes x0-x31 and all four trap CSRs.
7 +
@test fn layout() throws (testing::TestError) {
8 +
    try testing::expect(@sizeOf(trap::Frame) == 288);
9 +
    try testing::expect(@sizeOf(trap::Hart) == 56);
10 +
    try testing::expect(trap::FRAME_SIZE % 16 == 0);
11 +
}
12 +
13 +
/// Interrupts retain their source identity; exceptions retain their fault class.
14 +
@test fn classification() throws (testing::TestError) {
15 +
    try testing::expect(trap::classify(0x8000000000000003) == trap::Cause::Software);
16 +
    try testing::expect(trap::classify(0x8000000000000007) == trap::Cause::Timer);
17 +
    try testing::expect(trap::classify(0x800000000000000b) == trap::Cause::External);
18 +
    try testing::expect(trap::classify(8) == trap::Cause::UserCall);
19 +
    try testing::expect(trap::classify(11) == trap::Cause::MachineCall);
20 +
    try testing::expect(trap::classify(2) == trap::Cause::IllegalInstruction);
21 +
    try testing::expect(trap::classify(3) == trap::Cause::Breakpoint);
22 +
    for code in &[0 as u64, 1, 4, 5, 6, 7, 12, 13, 15] {
23 +
        try testing::expect(trap::classify(code) == trap::Cause::MemoryFault);
24 +
    }
25 +
    try testing::expect(trap::classify(0x8000000000000008) == trap::Cause::Unknown);
26 +
    try testing::expect(trap::classify(9) == trap::Cause::Unknown);
27 +
}
28 +
29 +
/// Only an aligned ecall resume address can advance by one RV64 instruction.
30 +
@test unsafe fn resumeAddress() throws (testing::TestError) {
31 +
    let mut frame: trap::Frame = undefined;
32 +
    set frame.pc = 0x80010000; set frame.cause = 8; set frame.status = 0;
33 +
    try testing::expect(trap::fromUser(&frame));
34 +
    try testing::expect(trap::advanceCall(&mut frame) and frame.pc == 0x80010004);
35 +
    set frame.status = 0x1800;
36 +
    try testing::expect(not trap::fromUser(&frame));
37 +
    set frame.cause = 0x8000000000000007;
38 +
    try testing::expect(not trap::advanceCall(&mut frame) and frame.pc == 0x80010004);
39 +
    set frame.cause = 11; set frame.pc = 0xfffffffffffffffc;
40 +
    try testing::expect(not trap::advanceCall(&mut frame) and frame.pc == 0xfffffffffffffffc);
41 +
    set frame.pc = 2;
42 +
    try testing::expect(not trap::advanceCall(&mut frame) and frame.pc == 2);
43 +
}
kernel/kernel/trap.rad added +94 -0
1 +
//! Integer trap contexts and exception classification.
2 +
3 +
/// Frame bytes reserved by trap entry. This is a multiple of stack alignment.
4 +
export constant FRAME_SIZE: u32 = 288;
5 +
6 +
/// Saved architectural state. Assembly offsets follow field declaration order.
7 +
export record Frame: Copy {
8 +
    /// Integer registers indexed by architectural register number. x0 is zero.
9 +
    registers: [u64; 32],
10 +
    /// Instruction address captured from mepc.
11 +
    pc: u64,
12 +
    /// Trap-entry mstatus, including previous privilege and interrupt state.
13 +
    status: u64,
14 +
    /// Interrupt bit and exception code captured from mcause.
15 +
    cause: u64,
16 +
    /// Fault address or instruction bits captured from mtval.
17 +
    value: u64,
18 +
}
19 +
20 +
/// Per-hart trap entry state named by mscratch. Entry scratch fields are private
21 +
/// to the assembly prologue while machine interrupts are disabled.
22 +
export record Hart: Copy {
23 +
    /// Exclusive upper bound of the kernel stack.
24 +
    stackTop: u64,
25 +
    /// First valid kernel stack byte.
26 +
    stackBottom: u64,
27 +
    /// Package-state base used by kernel code.
28 +
    kernelGp: u64,
29 +
    /// Handler called with a complete frame and this hart's state.
30 +
    handler: unsafe fn(&mut Frame, &mut Hart),
31 +
    /// Temporary preservation of x5 during entry.
32 +
    savedT0: u64,
33 +
    /// Temporary preservation of x6 during entry.
34 +
    savedT1: u64,
35 +
    /// Interrupted stack pointer, before stack selection.
36 +
    savedSp: u64,
37 +
}
38 +
39 +
/// Trap classes consumed by kernel dispatch.
40 +
export union Cause: Copy {
41 +
    /// Machine software interrupt.
42 +
    Software,
43 +
    /// Machine timer interrupt.
44 +
    Timer,
45 +
    /// Machine external interrupt.
46 +
    External,
47 +
    /// Environment call from user mode.
48 +
    UserCall,
49 +
    /// Environment call from machine mode.
50 +
    MachineCall,
51 +
    /// Unsupported or privileged instruction.
52 +
    IllegalInstruction,
53 +
    /// Breakpoint instruction.
54 +
    Breakpoint,
55 +
    /// Instruction, load, or store address/access fault.
56 +
    MemoryFault,
57 +
    /// Unhandled architectural cause.
58 +
    Unknown,
59 +
}
60 +
61 +
/// Classify mcause without discarding its interrupt bit.
62 +
export fn classify(cause: u64) -> Cause {
63 +
    match cause {
64 +
        case 0x8000000000000003 => return Cause::Software,
65 +
        case 0x8000000000000007 => return Cause::Timer,
66 +
        case 0x800000000000000b => return Cause::External,
67 +
        case 8 => return Cause::UserCall,
68 +
        case 11 => return Cause::MachineCall,
69 +
        case 2 => return Cause::IllegalInstruction,
70 +
        case 3 => return Cause::Breakpoint,
71 +
        case 0, 1, 4, 5, 6, 7, 12, 13, 15 => return Cause::MemoryFault,
72 +
        else => return Cause::Unknown,
73 +
    }
74 +
}
75 +
76 +
/// Check the privilege captured in the frame's MPP field.
77 +
export fn fromUser(frame: &Frame) -> bool { return (frame.status & 0x1800) == 0; }
78 +
79 +
/// Advance a completed ecall. Leave interrupts, faults, and invalid PCs unchanged.
80 +
export fn advanceCall(frame: &mut Frame) -> bool {
81 +
    if frame.cause <> 8 and frame.cause <> 11 { return false; }
82 +
    if (frame.pc & 3) <> 0 or frame.pc > 0xffffffffffffffff - 4 { return false; }
83 +
    set frame.pc += 4;
84 +
    return true;
85 +
}
86 +
87 +
/// Install trap entry for the current hart with machine interrupts disabled.
88 +
export fn install(hart: *mut Hart);
89 +
/// Restore a complete integer context through mret. The current mscratch must
90 +
/// name an installed Hart, and the frame must have machine interrupts disabled.
91 +
export fn resume(frame: *Frame) -> !;
92 +
93 +
/// Disable machine interrupts and stop the current hart.
94 +
export fn halt() -> !;
kernel/kernel/trap.ras added +146 -0
1 +
//! RV64 integer trap entry. mscratch names the current hart's kernel state.
2 +
.text;
3 +
.export @kernel::trap::install;
4 +
.export @kernel::trap::resume;
5 +
.export @kernel::trap::vector;
6 +
.export @kernel::trap::halt;
7 +
8 +
// Install a direct trap vector and its per-hart stack anchor.
9 +
@kernel::trap::install
10 +
    csrw mie %zero;
11 +
    csrr %t0 mstatus;
12 +
    andi %t0 %t0 -9;
13 +
    csrw mstatus %t0;
14 +
    csrw mscratch %a0;
15 +
    la %t0 @kernel::trap::vector;
16 +
    csrw mtvec %t0;
17 +
    ret;
18 +
19 +
// Hardware has cleared MIE. Preserve entry scratch registers before stack selection.
20 +
@kernel::trap::vector
21 +
    csrrw %tp mscratch %tp;
22 +
    sd %t0 32(%tp);
23 +
    sd %t1 40(%tp);
24 +
    sd %sp 48(%tp);
25 +
    csrr %t0 mstatus;
26 +
    srli %t0 %t0 11;
27 +
    andi %t0 %t0 3;
28 +
    li %t1 3;
29 +
    beq %t0 %t1 @machineStack;
30 +
    ld %sp 0(%tp);
31 +
@machineStack
32 +
    andi %t0 %sp 15;
33 +
    bnez %t0 @stackFault;
34 +
    ld %t0 0(%tp);
35 +
    bltu %t0 %sp @stackFault;
36 +
    addi %t1 %sp -288;
37 +
    bltu %sp %t1 @stackFault;
38 +
    ld %t0 8(%tp);
39 +
    bltu %t1 %t0 @stackFault;
40 +
    mv %sp %t1;
41 +
    sd %zero 0(%sp);
42 +
    // SC clears the hart reservation. The saved x0 slot accepts a zero store.
43 +
    sc.d %zero %zero (%sp);
44 +
    sd %ra 8(%sp);
45 +
    sd %gp 24(%sp);
46 +
    sd %t2 56(%sp);
47 +
    sd %s0 64(%sp);
48 +
    sd %s1 72(%sp);
49 +
    sd %a0 80(%sp);
50 +
    sd %a1 88(%sp);
51 +
    sd %a2 96(%sp);
52 +
    sd %a3 104(%sp);
53 +
    sd %a4 112(%sp);
54 +
    sd %a5 120(%sp);
55 +
    sd %a6 128(%sp);
56 +
    sd %a7 136(%sp);
57 +
    sd %s2 144(%sp);
58 +
    sd %s3 152(%sp);
59 +
    sd %s4 160(%sp);
60 +
    sd %s5 168(%sp);
61 +
    sd %s6 176(%sp);
62 +
    sd %s7 184(%sp);
63 +
    sd %s8 192(%sp);
64 +
    sd %s9 200(%sp);
65 +
    sd %s10 208(%sp);
66 +
    sd %s11 216(%sp);
67 +
    sd %t3 224(%sp);
68 +
    sd %t4 232(%sp);
69 +
    sd %t5 240(%sp);
70 +
    sd %t6 248(%sp);
71 +
    ld %t0 48(%tp);
72 +
    sd %t0 16(%sp);
73 +
    csrr %t0 mscratch;
74 +
    sd %t0 32(%sp);
75 +
    ld %t0 32(%tp);
76 +
    sd %t0 40(%sp);
77 +
    ld %t0 40(%tp);
78 +
    sd %t0 48(%sp);
79 +
    csrr %t0 mepc;
80 +
    sd %t0 256(%sp);
81 +
    csrr %t0 mstatus;
82 +
    sd %t0 264(%sp);
83 +
    csrr %t0 mcause;
84 +
    sd %t0 272(%sp);
85 +
    csrr %t0 mtval;
86 +
    sd %t0 280(%sp);
87 +
    csrw mscratch %tp;
88 +
    ld %gp 16(%tp);
89 +
    mv %a0 %sp;
90 +
    mv %a1 %tp;
91 +
    ld %t0 24(%tp);
92 +
    jalr %ra %t0 0;
93 +
    mv %a0 %sp;
94 +
95 +
// Interrupts remain disabled until mret restores the saved MPIE state.
96 +
@kernel::trap::resume
97 +
    csrr %t0 mstatus;
98 +
    andi %t0 %t0 -9;
99 +
    csrw mstatus %t0;
100 +
    mv %sp %a0;
101 +
    // A resumed frame must not use a reservation from another execution context.
102 +
    sc.d %zero %zero (%sp);
103 +
    ld %t0 256(%sp);
104 +
    csrw mepc %t0;
105 +
    ld %t0 264(%sp);
106 +
    andi %t0 %t0 -9;
107 +
    csrw mstatus %t0;
108 +
    ld %ra 8(%sp);
109 +
    ld %gp 24(%sp);
110 +
    ld %tp 32(%sp);
111 +
    ld %t0 40(%sp);
112 +
    ld %t1 48(%sp);
113 +
    ld %t2 56(%sp);
114 +
    ld %s0 64(%sp);
115 +
    ld %s1 72(%sp);
116 +
    ld %a0 80(%sp);
117 +
    ld %a1 88(%sp);
118 +
    ld %a2 96(%sp);
119 +
    ld %a3 104(%sp);
120 +
    ld %a4 112(%sp);
121 +
    ld %a5 120(%sp);
122 +
    ld %a6 128(%sp);
123 +
    ld %a7 136(%sp);
124 +
    ld %s2 144(%sp);
125 +
    ld %s3 152(%sp);
126 +
    ld %s4 160(%sp);
127 +
    ld %s5 168(%sp);
128 +
    ld %s6 176(%sp);
129 +
    ld %s7 184(%sp);
130 +
    ld %s8 192(%sp);
131 +
    ld %s9 200(%sp);
132 +
    ld %s10 208(%sp);
133 +
    ld %s11 216(%sp);
134 +
    ld %t3 224(%sp);
135 +
    ld %t4 232(%sp);
136 +
    ld %t5 240(%sp);
137 +
    ld %t6 248(%sp);
138 +
    ld %sp 16(%sp);
139 +
    mret;
140 +
141 +
// A frame must never extend outside the reserved kernel stack.
142 +
@stackFault
143 +
@kernel::trap::halt
144 +
    csrw mie %zero;
145 +
    wfi;
146 +
    j @stackFault;
test/boot/run +1 -1
2 2
# Boot the kernel through its production initialization on each supported hart count.
3 3
set -eu
4 4
emulator=${RAD_EMULATOR:-emulator}
5 5
work=$(mktemp -d)
6 6
trap 'rm -rf "$work"' EXIT HUP INT TERM
7 -
cat test/boot/machine.ras kernel/kernel/boot.ras kernel/kernel/sync.ras > "$work/boot.ras"
7 +
cat test/boot/machine.ras kernel/kernel/boot.ras kernel/kernel/sync.ras kernel/kernel/trap.ras > "$work/boot.ras"
8 8
"$emulator" -run bin/kernel.build.rv64 -- bin/kernel.ril "$work/boot.ras" "$work/boot.rv64"
9 9
for harts in 1 2 8; do
10 10
    if ! "$emulator" -machine -max-steps=10000000 -harts="$harts" -run "$work/boot.rv64" > "$work/log" 2>&1; then
11 11
        cat "$work/log" >&2
12 12
        exit 1
test/sync/build.rad +1 -1
11 11
/// Assembly input storage.
12 12
static SOURCE: [u8; 65536] = [0; 65536];
13 13
/// Encoded text words.
14 14
static TEXT: [u32; 4096] = [0; 4096];
15 15
/// Assembly name and fixup storage.
16 -
static MEMORY: [u8; 4194304] = [0; 4194304];
16 +
static MEMORY: [u8; 16777216] = [0; 16777216];
17 17
/// Interned assembly identifiers.
18 18
unsafe static STRINGS: strings::Pool = strings::Pool { table: undefined, count: 0 };
19 19
20 20
/// Assemble startup and synchronization routines into one native code segment.
21 21
@default unsafe fn main(env: *sys::Env) -> i32 {
test/trap/machine.ras added +334 -0
1 +
//! Machine and user trap entry with a nested machine timer interrupt.
2 +
.text;
3 +
    li %s0 1073807360;
4 +
    slli %s0 %s0 1;
5 +
    sd %sp 0(%s0);
6 +
    li %t0 4096;
7 +
    sub %t0 %sp %t0;
8 +
    sd %t0 8(%s0);
9 +
    li %t0 0x12345678;
10 +
    sd %t0 16(%s0);
11 +
    la %t0 @handler;
12 +
    sd %t0 24(%s0);
13 +
    mv %a0 %s0;
14 +
    call @kernel::trap::install;
15 +
    li %ra 17;
16 +
    li %gp 51;
17 +
    li %tp 68;
18 +
    li %t0 85;
19 +
    li %t1 102;
20 +
    li %t2 119;
21 +
    li %s1 153;
22 +
    li %a0 170;
23 +
    li %a1 187;
24 +
    li %a2 204;
25 +
    li %a3 221;
26 +
    li %a4 238;
27 +
    li %a5 255;
28 +
    li %a6 272;
29 +
    li %a7 289;
30 +
    li %s2 306;
31 +
    li %s3 323;
32 +
    li %s4 340;
33 +
    li %s5 357;
34 +
    li %s6 374;
35 +
    li %s7 391;
36 +
    li %s8 408;
37 +
    li %s9 425;
38 +
    li %s10 442;
39 +
    li %s11 459;
40 +
    li %t3 476;
41 +
    li %t4 493;
42 +
    li %t5 510;
43 +
    li %t6 527;
44 +
    ecall;
45 +
    addi %t0 %t0 -85;
46 +
    bnez %t0 @fail;
47 +
    li %t0 17;
48 +
    bne %ra %t0 @fail;
49 +
    li %t0 51;
50 +
    bne %gp %t0 @fail;
51 +
    li %t0 68;
52 +
    bne %tp %t0 @fail;
53 +
    li %t0 102;
54 +
    bne %t1 %t0 @fail;
55 +
    li %t0 119;
56 +
    bne %t2 %t0 @fail;
57 +
    li %t0 153;
58 +
    bne %s1 %t0 @fail;
59 +
    li %t0 170;
60 +
    bne %a0 %t0 @fail;
61 +
    li %t0 187;
62 +
    bne %a1 %t0 @fail;
63 +
    li %t0 204;
64 +
    bne %a2 %t0 @fail;
65 +
    li %t0 221;
66 +
    bne %a3 %t0 @fail;
67 +
    li %t0 238;
68 +
    bne %a4 %t0 @fail;
69 +
    li %t0 255;
70 +
    bne %a5 %t0 @fail;
71 +
    li %t0 272;
72 +
    bne %a6 %t0 @fail;
73 +
    li %t0 289;
74 +
    bne %a7 %t0 @fail;
75 +
    li %t0 306;
76 +
    bne %s2 %t0 @fail;
77 +
    li %t0 323;
78 +
    bne %s3 %t0 @fail;
79 +
    li %t0 340;
80 +
    bne %s4 %t0 @fail;
81 +
    li %t0 357;
82 +
    bne %s5 %t0 @fail;
83 +
    li %t0 374;
84 +
    bne %s6 %t0 @fail;
85 +
    li %t0 391;
86 +
    bne %s7 %t0 @fail;
87 +
    li %t0 408;
88 +
    bne %s8 %t0 @fail;
89 +
    li %t0 425;
90 +
    bne %s9 %t0 @fail;
91 +
    li %t0 442;
92 +
    bne %s10 %t0 @fail;
93 +
    li %t0 459;
94 +
    bne %s11 %t0 @fail;
95 +
    li %t0 476;
96 +
    bne %t3 %t0 @fail;
97 +
    li %t0 493;
98 +
    bne %t4 %t0 @fail;
99 +
    li %t0 510;
100 +
    bne %t5 %t0 @fail;
101 +
    li %t0 527;
102 +
    bne %t6 %t0 @fail;
103 +
    li %t0 1073807360;
104 +
    slli %t0 %t0 1;
105 +
    bne %s0 %t0 @fail;
106 +
    ld %t0 0(%s0);
107 +
    bne %sp %t0 @fail;
108 +
    ld %t0 64(%s0);
109 +
    li %t1 2;
110 +
    bne %t0 %t1 @fail;
111 +
    la %t0 @user;
112 +
    csrw mepc %t0;
113 +
    csrw mstatus %zero;
114 +
    li %sp 1073809400;
115 +
    slli %sp %sp 1;
116 +
    mret;
117 +
@user
118 +
    li %ra 17;
119 +
    li %gp 51;
120 +
    li %tp 68;
121 +
    li %t0 85;
122 +
    li %t1 102;
123 +
    li %t2 119;
124 +
    li %s1 153;
125 +
    li %a0 170;
126 +
    li %a1 187;
127 +
    li %a2 204;
128 +
    li %a3 221;
129 +
    li %a4 238;
130 +
    li %a5 255;
131 +
    li %a6 272;
132 +
    li %a7 289;
133 +
    li %s2 306;
134 +
    li %s3 323;
135 +
    li %s4 340;
136 +
    li %s5 357;
137 +
    li %s6 374;
138 +
    li %s7 391;
139 +
    li %s8 408;
140 +
    li %s9 425;
141 +
    li %s10 442;
142 +
    li %s11 459;
143 +
    li %t3 476;
144 +
    li %t4 493;
145 +
    li %t5 510;
146 +
    li %t6 527;
147 +
    ecall;
148 +
    addi %t0 %t0 -85;
149 +
    bnez %t0 @fail;
150 +
    li %t0 17;
151 +
    bne %ra %t0 @fail;
152 +
    li %t0 51;
153 +
    bne %gp %t0 @fail;
154 +
    li %t0 68;
155 +
    bne %tp %t0 @fail;
156 +
    li %t0 102;
157 +
    bne %t1 %t0 @fail;
158 +
    li %t0 119;
159 +
    bne %t2 %t0 @fail;
160 +
    li %t0 153;
161 +
    bne %s1 %t0 @fail;
162 +
    li %t0 170;
163 +
    bne %a0 %t0 @fail;
164 +
    li %t0 187;
165 +
    bne %a1 %t0 @fail;
166 +
    li %t0 204;
167 +
    bne %a2 %t0 @fail;
168 +
    li %t0 221;
169 +
    bne %a3 %t0 @fail;
170 +
    li %t0 238;
171 +
    bne %a4 %t0 @fail;
172 +
    li %t0 255;
173 +
    bne %a5 %t0 @fail;
174 +
    li %t0 272;
175 +
    bne %a6 %t0 @fail;
176 +
    li %t0 289;
177 +
    bne %a7 %t0 @fail;
178 +
    li %t0 306;
179 +
    bne %s2 %t0 @fail;
180 +
    li %t0 323;
181 +
    bne %s3 %t0 @fail;
182 +
    li %t0 340;
183 +
    bne %s4 %t0 @fail;
184 +
    li %t0 357;
185 +
    bne %s5 %t0 @fail;
186 +
    li %t0 374;
187 +
    bne %s6 %t0 @fail;
188 +
    li %t0 391;
189 +
    bne %s7 %t0 @fail;
190 +
    li %t0 408;
191 +
    bne %s8 %t0 @fail;
192 +
    li %t0 425;
193 +
    bne %s9 %t0 @fail;
194 +
    li %t0 442;
195 +
    bne %s10 %t0 @fail;
196 +
    li %t0 459;
197 +
    bne %s11 %t0 @fail;
198 +
    li %t0 476;
199 +
    bne %t3 %t0 @fail;
200 +
    li %t0 493;
201 +
    bne %t4 %t0 @fail;
202 +
    li %t0 510;
203 +
    bne %t5 %t0 @fail;
204 +
    li %t0 527;
205 +
    bne %t6 %t0 @fail;
206 +
    li %t0 1073807360;
207 +
    slli %t0 %t0 1;
208 +
    bne %s0 %t0 @fail;
209 +
    li %t0 1073809400;
210 +
    slli %t0 %t0 1;
211 +
    bne %sp %t0 @fail;
212 +
    li %t0 85;
213 +
@illegalInstruction
214 +
    csrr %t0 mhartid;
215 +
    addi %t0 %t0 -85;
216 +
    bnez %t0 @fail;
217 +
    li %a7 1234;
218 +
    ecall;
219 +
    j @fail;
220 +
@success
221 +
    li %t0 0x10001000;
222 +
    li %t1 0x5555;
223 +
    sw %t1 0(%t0);
224 +
@fail
225 +
    li %t0 0x10001000;
226 +
    li %t1 0x13333;
227 +
    sw %t1 0(%t0);
228 +
@park
229 +
    wfi;
230 +
    j @park;
231 +
232 +
// Validate stack/base switching before enabling a nested timer interrupt.
233 +
@handler
234 +
    li %t0 0x12345678;
235 +
    bne %gp %t0 @fail;
236 +
    li %t0 1073807360;
237 +
    slli %t0 %t0 1;
238 +
    bne %tp %t0 @fail;
239 +
    bne %a1 %tp @fail;
240 +
    bne %a0 %sp @fail;
241 +
    ld %t0 272(%a0);
242 +
    li %t1 1;
243 +
    slli %t1 %t1 63;
244 +
    ori %t1 %t1 7;
245 +
    beq %t0 %t1 @timer;
246 +
    li %t1 8;
247 +
    beq %t0 %t1 @userCall;
248 +
    li %t1 2;
249 +
    beq %t0 %t1 @illegal;
250 +
    li %t1 11;
251 +
    bne %t0 %t1 @fail;
252 +
    ld %t0 64(%tp);
253 +
    bnez %t0 @fail;
254 +
    li %t0 1;
255 +
    sd %t0 64(%tp);
256 +
    addi %sp %sp -16;
257 +
    sd %ra 0(%sp);
258 +
    sd %a0 8(%sp);
259 +
    li %t0 0x02004000;
260 +
    sd %zero 0(%t0);
261 +
    li %t0 128;
262 +
    csrw mie %t0;
263 +
    li %s10 0;
264 +
    csrr %t0 mstatus;
265 +
    ori %t0 %t0 8;
266 +
    csrw mstatus %t0;
267 +
@awaitTimer
268 +
    addi %s10 %s10 1;
269 +
    ld %t0 64(%tp);
270 +
    li %t1 2;
271 +
    bne %t0 %t1 @awaitTimer;
272 +
    li %t0 1;
273 +
    bne %s10 %t0 @fail;
274 +
    csrr %t0 mstatus;
275 +
    andi %t0 %t0 8;
276 +
    beqz %t0 @fail;
277 +
    csrr %t0 mstatus;
278 +
    andi %t0 %t0 -9;
279 +
    csrw mstatus %t0;
280 +
    csrw mie %zero;
281 +
    ld %ra 0(%sp);
282 +
    ld %a0 8(%sp);
283 +
    addi %sp %sp 16;
284 +
    j @advance;
285 +
@timer
286 +
    ld %t0 256(%a0);
287 +
    la %t1 @awaitTimer;
288 +
    bne %t0 %t1 @fail;
289 +
    ld %t0 264(%a0);
290 +
    li %t1 0x1800;
291 +
    and %t0 %t0 %t1;
292 +
    bne %t0 %t1 @fail;
293 +
    ld %t0 16(%a0);
294 +
    addi %t1 %a0 288;
295 +
    bne %t0 %t1 @fail;
296 +
    li %t0 0x02004000;
297 +
    li %t1 -1;
298 +
    sd %t1 0(%t0);
299 +
    li %t0 2;
300 +
    sd %t0 64(%tp);
301 +
    ret;
302 +
@illegal
303 +
    ld %t0 256(%a0);
304 +
    la %t1 @illegalInstruction;
305 +
    bne %t0 %t1 @fail;
306 +
    ld %t0 280(%a0);
307 +
    li %t1 2023756153;
308 +
    slli %t1 %t1 1;
309 +
    addi %t1 %t1 1;
310 +
    bne %t0 %t1 @fail;
311 +
    li %t0 1;
312 +
    sd %t0 72(%tp);
313 +
    j @advance;
314 +
@userCall
315 +
    ld %t0 136(%a0);
316 +
    li %t1 1234;
317 +
    bne %t0 %t1 @checkUserFrame;
318 +
    ld %t0 72(%tp);
319 +
    li %t1 1;
320 +
    bne %t0 %t1 @fail;
321 +
    j @success;
322 +
@checkUserFrame
323 +
    ld %t0 264(%a0);
324 +
    li %t1 0x1800;
325 +
    and %t0 %t0 %t1;
326 +
    bnez %t0 @fail;
327 +
    ld %t0 0(%tp);
328 +
    addi %t0 %t0 -288;
329 +
    bne %a0 %t0 @fail;
330 +
@advance
331 +
    ld %t0 256(%a0);
332 +
    addi %t0 %t0 4;
333 +
    sd %t0 256(%a0);
334 +
    ret;
test/trap/reservations.ras added +100 -0
1 +
//! Reservation isolation at trap entry, frame resume, and user context switches.
2 +
.text;
3 +
    li %s0 0x40010000;
4 +
    slli %s0 %s0 1;
5 +
    sd %sp 0(%s0);
6 +
    li %t0 4096;
7 +
    sub %t0 %sp %t0;
8 +
    sd %t0 8(%s0);
9 +
    sd %zero 16(%s0);
10 +
    la %t0 @handler;
11 +
    sd %t0 24(%s0);
12 +
    mv %a0 %s0;
13 +
    call @kernel::trap::install;
14 +
    addi %s1 %s0 80;
15 +
16 +
// Enter the vector with a live reservation and the machine trap CSR state.
17 +
    la %t0 @resumed;
18 +
    csrw mepc %t0;
19 +
    li %t0 11;
20 +
    csrw mcause %t0;
21 +
    li %t0 0x1800;
22 +
    csrw mstatus %t0;
23 +
    lr.d %t0 (%s1);
24 +
    j @kernel::trap::vector;
25 +
@resumed
26 +
    sc.d %t0 %zero (%s1);
27 +
    beqz %t0 @fail;
28 +
    ld %t0 0(%s1);
29 +
    bnez %t0 @fail;
30 +
    li %t0 2;
31 +
    sd %t0 88(%s0);
32 +
    la %t0 @first;
33 +
    csrw mepc %t0;
34 +
    csrw mstatus %zero;
35 +
    mret;
36 +
37 +
// The first user context retains its loaded value across a peer's execution.
38 +
@first
39 +
    lr.d %a2 (%s1);
40 +
    ecall;
41 +
@firstResume
42 +
    sc.d %a3 %a2 (%s1);
43 +
    beqz %a3 @fail;
44 +
    ld %t0 0(%s1);
45 +
    li %t1 1;
46 +
    bne %t0 %t1 @fail;
47 +
    ecall;
48 +
    j @fail;
49 +
50 +
// The peer changes shared memory and reserves the same location before exit.
51 +
@peer
52 +
    li %t0 1;
53 +
    sd %t0 0(%s1);
54 +
    lr.d %t0 (%s1);
55 +
    ecall;
56 +
    j @fail;
57 +
58 +
// Inject a live return reservation after trap entry has completed.
59 +
@handler
60 +
    ld %t0 88(%s0);
61 +
    beqz %t0 @entryCheck;
62 +
    ld %t1 272(%a0);
63 +
    li %t2 8;
64 +
    bne %t1 %t2 @fail;
65 +
    li %t1 2;
66 +
    beq %t0 %t1 @switchPeer;
67 +
    li %t1 3;
68 +
    beq %t0 %t1 @switchFirst;
69 +
    li %t1 4;
70 +
    bne %t0 %t1 @fail;
71 +
    j @success;
72 +
@entryCheck
73 +
    sc.d %t0 %zero (%s1);
74 +
    beqz %t0 @fail;
75 +
    li %t0 1;
76 +
    sd %t0 88(%s0);
77 +
    lr.d %t0 (%s1);
78 +
    ret;
79 +
@switchPeer
80 +
    li %t0 3;
81 +
    sd %t0 88(%s0);
82 +
    la %t0 @peer;
83 +
    sd %t0 256(%a0);
84 +
    ret;
85 +
@switchFirst
86 +
    li %t0 4;
87 +
    sd %t0 88(%s0);
88 +
    la %t0 @firstResume;
89 +
    sd %t0 256(%a0);
90 +
    lr.d %t0 (%s1);
91 +
    ret;
92 +
@success
93 +
    li %t0 0x10001000;
94 +
    li %t1 0x5555;
95 +
    sw %t1 0(%t0);
96 +
@fail
97 +
    li %t0 0x10001000;
98 +
    li %t1 0x13333;
99 +
    sw %t1 0(%t0);
100 +
    ebreak;
test/trap/run added +13 -0
1 +
#!/bin/sh
2 +
# Execute the production trap boundary with a deterministic nested timer.
3 +
set -eu
4 +
emulator=${RAD_EMULATOR:-emulator}
5 +
work=$(mktemp -d)
6 +
trap 'rm -rf "$work"' EXIT HUP INT TERM
7 +
for fixture in machine reservations; do
8 +
    cat "test/trap/$fixture.ras" kernel/kernel/trap.ras > "$work/trap.ras"
9 +
    "$emulator" -run bin/sync.build.rv64 -- "$work/trap.ras" "$work/trap.rv64"
10 +
    "$emulator" -machine -max-steps=1000000 -run "$work/trap.rv64"
11 +
done
12 +
printf 'trap contexts: machine/user registers, nested timer, illegal instruction, and stacks passed\n'
13 +
printf 'trap reservations: entry, resume, and user context isolation passed\n'