test the trampoline is inert (zeroed + NX) when dormant
Adds paging.isExecutable and arch.trampolinePage; the smp case now asserts the frame is zeroed and non-executable after bring-up. Teeth-checked against a no-op disarm.
This commit is contained in:
@@ -130,6 +130,16 @@ pub fn testFailNextWakes(n: u32) void {
|
|||||||
smp.testFailNextWakes(n);
|
smp.testFailNextWakes(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The reserved AP-trampoline frame (0 if none). For tests that check it's inert.
|
||||||
|
pub fn trampolinePage() u64 {
|
||||||
|
return smp.trampolinePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the page at `virt` is currently mapped executable (present, NX clear).
|
||||||
|
pub fn pageExecutable(virt: u64) bool {
|
||||||
|
return paging.isExecutable(virt);
|
||||||
|
}
|
||||||
|
|
||||||
/// Kernel tick rate: 1000 Hz (1 ms), the scheduler's time quantum.
|
/// Kernel tick rate: 1000 Hz (1 ms), the scheduler's time quantum.
|
||||||
pub const timer_hz = 1000;
|
pub const timer_hz = 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,21 @@ pub fn map(virt: u64, phys: u64, writable_page: bool) void {
|
|||||||
invalidate(virt);
|
invalidate(virt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `virt` is currently mapped **executable** — present with the NX bit
|
||||||
|
/// clear. Walks the 4-level tables (all danos mappings are 4 KiB, so no huge-page
|
||||||
|
/// case). Returns false if unmapped. Used for W^X checks in tests.
|
||||||
|
pub fn isExecutable(virt: u64) bool {
|
||||||
|
const pml4e = tableAt(kernel_pml4)[(virt >> 39) & 0x1FF];
|
||||||
|
if (pml4e & present == 0) return false;
|
||||||
|
const pdpte = tableAt(pml4e & addr_mask)[(virt >> 30) & 0x1FF];
|
||||||
|
if (pdpte & present == 0) return false;
|
||||||
|
const pde = tableAt(pdpte & addr_mask)[(virt >> 21) & 0x1FF];
|
||||||
|
if (pde & present == 0) return false;
|
||||||
|
const pte = tableAt(pde & addr_mask)[(virt >> 12) & 0x1FF];
|
||||||
|
if (pte & present == 0) return false;
|
||||||
|
return pte & no_execute == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Make an already-identity-mapped RAM page **executable** (clear its NX bit),
|
/// Make an already-identity-mapped RAM page **executable** (clear its NX bit),
|
||||||
/// leaving it present and writable. The blanket RAM mapping is NX for W^X, but the
|
/// leaving it present and writable. The blanket RAM mapping is NX for W^X, but the
|
||||||
/// application processors fetch the AP trampoline from a low RAM page under paging —
|
/// application processors fetch the AP trampoline from a low RAM page under paging —
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ pub fn setTrampolinePage(phys: u64) void {
|
|||||||
tramp_phys = phys;
|
tramp_phys = phys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The reserved trampoline frame (0 if SMP bring-up never ran). Exposed so a test
|
||||||
|
/// can verify it's inert — zeroed and non-executable — when dormant.
|
||||||
|
pub fn trampolinePage() u64 {
|
||||||
|
return tramp_phys;
|
||||||
|
}
|
||||||
|
|
||||||
/// Arm the trampoline for a wake: make its page executable (W^X exception for the
|
/// Arm the trampoline for a wake: make its page executable (W^X exception for the
|
||||||
/// duration of the climb) and copy the blob in.
|
/// duration of the climb) and copy the blob in.
|
||||||
fn arm() void {
|
fn arm() void {
|
||||||
|
|||||||
@@ -483,6 +483,20 @@ fn smpTest() void {
|
|||||||
}
|
}
|
||||||
log("DANOS-SMP: workers ran on {d} distinct core(s)\n", .{cores_seen});
|
log("DANOS-SMP: workers ran on {d} distinct core(s)\n", .{cores_seen});
|
||||||
check("tasks ran on multiple cores in parallel", cores_seen >= 2);
|
check("tasks ran on multiple cores in parallel", cores_seen >= 2);
|
||||||
|
|
||||||
|
// Bring-up is done, so the trampoline frame must be inert: zeroed (no stale code)
|
||||||
|
// and non-executable (W^X restored). It's armed only while a core is climbing.
|
||||||
|
const tramp = arch.trampolinePage();
|
||||||
|
check("trampoline frame reserved", tramp != 0);
|
||||||
|
if (tramp != 0) {
|
||||||
|
const bytes: [*]const u8 = @ptrFromInt(tramp);
|
||||||
|
var zeroed = true;
|
||||||
|
for (0..4096) |b| {
|
||||||
|
if (bytes[b] != 0) zeroed = false;
|
||||||
|
}
|
||||||
|
check("trampoline page zeroed when dormant", zeroed);
|
||||||
|
check("trampoline page non-executable when dormant", !arch.pageExecutable(tramp));
|
||||||
|
}
|
||||||
result();
|
result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user