use crate::common::PhysAddr;
use crate::config::BackendKind;
use crate::config::Config;
use crate::isa::csr;
use crate::isa::privileged::PrivilegeMode;
use crate::tests::support::builder::instruction::InstructionBuilder;
use crate::tests::support::harness::TestContext;
const RAM_BASE: u64 = 0x8000_0000;
const RAM_SIZE: usize = 0x80_0000;
const ROOT_PPN: u64 = 0x8_0100;
const CODE_L1_PPN: u64 = 0x8_0101;
const DATA_L1_PPN: u64 = 0x8_0102;
const DATA_L0_PPN: u64 = 0x8_0103;
const CODE: u64 = 0x8020_0000;
const HANDLER: u64 = CODE + 0x100;
const DATA_VA: u64 = 0x4000_0000;
const FIRST_PA: u64 = 0x8030_0000;
const ADJACENT_PA: u64 = FIRST_PA + 0x1000;
const FAR_PA: u64 = 0x8032_0000;
const A0: u32 = 10;
const A1: u32 = 11;
const A2: u32 = 12;
const A3: u32 = 13;
const MCAUSE: u32 = 0x342;
const MTVAL: u32 = 0x343;
const LOAD_ADDRESS_MISALIGNED: u64 = 4;
const LOAD_PAGE_FAULT: u64 = 13;
const PTE_V: u64 = 1;
const PTE_LEAF_RWX_AD: u64 = 0b1100_1111;
const CROSSING_VA: u64 = DATA_VA + 0xFFC;
fn write_pte(ctx: &mut TestContext, table_ppn: u64, index: u64, pte: u64) {
ctx.sim.probe_mem_store(PhysAddr::new((table_ppn << 12) | (index * 8)), pte, 8);
}
fn store_program(ctx: &mut TestContext, base: u64, program: &[u32]) {
for (i, word) in program.iter().enumerate() {
ctx.sim.probe_mem_store(PhysAddr::new(base + (i as u64) * 4), u64::from(*word), 4);
}
}
fn program() -> Vec<u32> {
let i = InstructionBuilder::new;
vec![
i().lui(A0, ((DATA_VA + 0x1000) >> 12) as i32).build(),
i().addi(A0, A0, -4).build(),
i().ld(A1, A0, 0).build(),
i().jal(0, 0).build(),
]
}
fn handler() -> Vec<u32> {
let i = InstructionBuilder::new;
vec![i().csrrs(A2, MCAUSE, 0).build(), i().csrrs(A3, MTVAL, 0).build(), i().jal(0, 0).build()]
}
struct Outcome {
loaded: u64,
mcause: u64,
mtval: u64,
}
fn run(backend: BackendKind, second_pa: Option<u64>) -> Outcome {
let mut config = Config::default();
config.pipeline.backend = backend;
config.pipeline.width = 4;
config.system.console = crate::config::Console::Quiet;
let mut ctx = TestContext::new_with_config(&config).with_memory(RAM_SIZE, RAM_BASE);
store_program(&mut ctx, CODE, &program());
store_program(&mut ctx, HANDLER, &handler());
for (offset, byte) in (0u64..).zip(0x11u64..=0x18) {
let pa = if offset < 4 {
FIRST_PA + 0xFFC + offset
} else {
second_pa.unwrap_or(0) + offset - 4
};
if pa != 0 {
ctx.sim.probe_mem_store(PhysAddr::new(pa), byte, 1);
}
}
write_pte(&mut ctx, ROOT_PPN, (CODE >> 30) & 0x1ff, (CODE_L1_PPN << 10) | PTE_V);
write_pte(&mut ctx, CODE_L1_PPN, (CODE >> 21) & 0x1ff, ((CODE >> 12) << 10) | PTE_LEAF_RWX_AD);
write_pte(&mut ctx, ROOT_PPN, (DATA_VA >> 30) & 0x1ff, (DATA_L1_PPN << 10) | PTE_V);
write_pte(&mut ctx, DATA_L1_PPN, (DATA_VA >> 21) & 0x1ff, (DATA_L0_PPN << 10) | PTE_V);
write_pte(
&mut ctx,
DATA_L0_PPN,
(DATA_VA >> 12) & 0x1ff,
((FIRST_PA >> 12) << 10) | PTE_LEAF_RWX_AD,
);
if let Some(pa) = second_pa {
write_pte(
&mut ctx,
DATA_L0_PPN,
((DATA_VA + 0x1000) >> 12) & 0x1ff,
((pa >> 12) << 10) | PTE_LEAF_RWX_AD,
);
}
{
let hart = &mut ctx.sim.state.harts[0];
hart.csrs.satp = (csr::SATP_MODE_SV39 << 60) | ROOT_PPN;
hart.csrs.mtvec = HANDLER;
hart.privilege = PrivilegeMode::Supervisor;
hart.pmp.set_addr(0, u64::MAX >> 10);
hart.pmp.set_cfg(0, 0b0000_1111);
hart.pc = CODE;
}
ctx.sim.state.direct_mode = false;
ctx.sim.sync_arch_regs();
ctx.run(3_000);
Outcome {
loaded: ctx.get_reg(A1 as usize),
mcause: ctx.get_reg(A2 as usize),
mtval: ctx.get_reg(A3 as usize),
}
}
fn check(backend: BackendKind) {
let adjacent = run(backend, Some(ADJACENT_PA));
assert_eq!(adjacent.mcause, 0, "{backend:?}: adjacent pages need no trap");
assert_eq!(adjacent.loaded, 0x1817_1615_1413_1211, "{backend:?}: bytes from both pages");
let far = run(backend, Some(FAR_PA));
assert_eq!(far.mcause, LOAD_ADDRESS_MISALIGNED, "{backend:?}: split pages trap as misaligned");
assert_eq!(far.mtval, CROSSING_VA, "{backend:?}: tval is the access address");
let unmapped = run(backend, None);
assert_eq!(
unmapped.mcause, LOAD_PAGE_FAULT,
"{backend:?}: the second page's fault is reported"
);
assert_eq!(unmapped.mtval, DATA_VA + 0x1000, "{backend:?}: tval names the faulting page");
}
#[test]
fn a_page_crossing_load_translates_both_pages_inorder() {
check(BackendKind::InOrder);
}
#[test]
fn a_page_crossing_load_translates_both_pages_o3() {
check(BackendKind::OutOfOrder);
}