Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PMP emulation #21

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
config.toml
/.vscode
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"payloads/ecall",
"payloads/mscratch",
"payloads/csr_id",
"payloads/pmp",

# Crates
"crates/mirage_abi",
Expand Down
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ test:
cargo run --package runner -- --payload mscratch
cargo run --package runner -- --payload csr_ops
cargo run --package runner -- --payload csr_id
cargo run --package runner -- --payload pmp

# Checking formatting...
cargo fmt --all -- --check
Expand Down
7 changes: 7 additions & 0 deletions payloads/pmp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "pmp"
version = "0.1.0"
edition = "2021"

[dependencies]
mirage_abi = { path = "../../crates/mirage_abi" }
152 changes: 152 additions & 0 deletions payloads/pmp/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#![no_std]
#![no_main]

use core::arch::{asm, global_asm};
use core::panic::PanicInfo;
use core::usize;

use mirage_abi::{failure, success};

global_asm!(
r#"
.text
.align 4
.global _start
_start:
j {entry}
"#,
entry = sym entry,
);

extern "C" fn entry() -> ! {
// For now we expose 0 PMPs to the payload, because QEMU supports only 16 PMPs.
// So we ensure that indeed no PMPs are exposed.
test_0_pmp();

success();
}

fn test_0_pmp() {
let addr: usize = 0x00000fffffffff42;
let cfg: usize = 0b00000111;
let mut res: usize;

unsafe {
asm!(
"csrw pmpcfg0, {0}",
"csrr {1}, pmpcfg0",
in(reg) cfg,
out(reg) res,
);
}

// PMPs are WARL, so when no PMPs are implemented we must read 0
assert_eq!(res, 0);

unsafe {
asm!(
"csrw pmpaddr0, {0}",
"csrr {1}, pmpaddr0",
in(reg) addr,
out(reg) res,
);
}

// PMPs are WARL, so when no PMPs are implemented we must read 0
assert_eq!(res, 0);
}

// Not yet tested because QEMU exposes only 16 PMPs, se Mirage exposes 0 to the payload.
#[allow(unused)]
fn test_16_pmp() {
let secret_addr: usize = 0x0000000000000042;
let secret_cfg: usize = 0b00000111;
let mut res: usize;

// Test normal write to config
unsafe {
asm!(
"li {0}, 0b00000111",
"csrw pmpcfg0, {0}",
"csrr {1}, pmpcfg0",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, secret_cfg);

// Test invalid write to config
unsafe {
asm!(
"li {0}, 0b01100111",
"csrw pmpcfg0, {0}",
"csrr {1}, pmpcfg0",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, secret_cfg);

// Test out of range write to config : for 16 pmp
unsafe {
asm!(
"li {0}, 0b01100111",
"csrw pmpcfg4, {0}",
"csrr {1}, pmpcfg4",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, 0);

// Test normal write to address
unsafe {
asm!(
"li {0}, 0x42",
"csrw pmpaddr0, {0}",
"csrr {1}, pmpaddr0",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, secret_addr);

// Test invalid write to address
unsafe {
asm!(
"li {0}, 0xf000000000000042",
"csrw pmpaddr0, {0}",
"csrr {1}, pmpaddr0",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, secret_addr);

// Test out of range write to address : for 16 pmp
unsafe {
asm!(
"li {0}, 0x42",
"csrw pmpaddr17, {0}",
"csrr {1}, pmpaddr17",
in(reg) secret_cfg,
out(reg) res,
);
}

read_test(res, 0);
}

fn read_test(out_csr: usize, expected: usize) {
assert_eq!(out_csr, expected);
}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
failure();
}
24 changes: 24 additions & 0 deletions src/arch/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,35 @@ pub enum Csr {
Marchid,
/// Machine Implementation ID
Mimpid,
/// PMP config
Pmpcfg(usize),
/// PMP addr
Pmpaddr(usize),
/// An unknown CSR
Unknown,
}

impl Csr {
pub const PMP_CFG_LOCK_MASK: usize = (0b1 << 7) << 0
| (0b1 << 7) << 8
| (0b1 << 7) << 16
| (0b1 << 7) << 24
| (0b1 << 7) << 32
| (0b1 << 7) << 40
| (0b1 << 7) << 48
| (0b1 << 7) << 56;

pub const PMP_CFG_LEGAL_MASK: usize = !((0b11 << 5) << 0
| (0b11 << 5) << 8
| (0b11 << 5) << 16
| (0b11 << 5) << 24
| (0b11 << 5) << 32
| (0b11 << 5) << 40
| (0b11 << 5) << 48
| (0b11 << 5) << 56);

pub const PMP_ADDR_LEGAL_MASK: usize = !(0b1111111111 << 54);

pub fn is_unknown(self) -> bool {
match self {
Csr::Unknown => true,
Expand Down
2 changes: 2 additions & 0 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ fn decode_csr(csr: usize) -> Csr {
0xF11 => Csr::Mvendorid,
0xF12 => Csr::Marchid,
0xF13 => Csr::Mimpid,
0x3A0..=0x3AF => Csr::Pmpcfg(csr - 0x3A0),
0x3B0..=0x3EF => Csr::Pmpaddr(csr - 0x3AF),
_ => {
log::info!("Unknown CSR: 0x{:x}", csr);
Csr::Unknown
Expand Down
3 changes: 3 additions & 0 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ pub trait Platform {

/// Return the initial payload stack address.
fn payload_stack_address() -> usize;

/// Return the number of PMPs of the platform.
fn get_nb_pmp() -> usize;
}

pub fn init() {
Expand Down
4 changes: 4 additions & 0 deletions src/platform/virt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ impl Platform for VirtPlatform {
fn payload_stack_address() -> usize {
return PAYLOAD_STACK;
}

fn get_nb_pmp() -> usize {
16
}
}

fn exit_qemu(success: bool) -> ! {
Expand Down
69 changes: 67 additions & 2 deletions src/virt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Firmware Virtualisation

use crate::arch::{Arch, Architecture, Csr, Register};
use crate::platform::{Plat, Platform};

/// The context of a virtual firmware.
#[derive(Debug)]
Expand All @@ -10,8 +11,10 @@ pub struct VirtContext {
host_stack: usize,
/// Basic registers
regs: [usize; 32],
/// Virtual Constrol and Status Registers
/// Virtual Control and Status Registers
csr: VirtCsr,
/// Number of virtual PMPs
nbr_pmps: usize,
/// Hart ID
hart_id: usize,
/// Number of exists to Mirage
Expand All @@ -26,12 +29,18 @@ impl VirtContext {
csr: Default::default(),
nb_exits: 0,
hart_id,
nbr_pmps: match Plat::get_nb_pmp() {
0 => 0,
16 => 0,
64 => 16,
_ => 0,
},
}
}
}

/// Control and Status Registers (CSR) for a virtual firmware.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct VirtCsr {
misa: usize,
mie: usize,
Expand All @@ -41,6 +50,25 @@ pub struct VirtCsr {
mvendorid: usize,
marchid: usize,
mimpid: usize,
pmp_cfg: [usize; 16],
pmp_addr: [usize; 64],
}

impl Default for VirtCsr {
fn default() -> VirtCsr {
VirtCsr {
misa: 0,
mie: 0,
mip: 0,
mtvec: 0,
mscratch: 0,
mvendorid: 0,
marchid: 0,
mimpid: 0,
pmp_cfg: [0; 16],
pmp_addr: [0; 64],
}
}
}

// ———————————————————————— Register Setters/Getters ———————————————————————— //
Expand Down Expand Up @@ -79,6 +107,24 @@ impl RegisterContext<Csr> for VirtContext {
Csr::Mvendorid => self.csr.mvendorid,
Csr::Marchid => self.csr.marchid,
Csr::Mimpid => self.csr.mimpid,
Csr::Pmpcfg(pmp_cfg_idx) => {
if pmp_cfg_idx % 2 == 1 {
// Illegal because we are in a RISCV64 setting
panic!("Illegal PMP_CFG {:?}", register)
}
if pmp_cfg_idx >= self.nbr_pmps / 8 {
// This PMP is not emulated
return 0;
}
self.csr.pmp_cfg[pmp_cfg_idx]
}
Csr::Pmpaddr(pmp_addr_idx) => {
if pmp_addr_idx >= self.nbr_pmps {
// This PMP is not emulated
return 0;
}
self.csr.pmp_addr[pmp_addr_idx]
}
Csr::Unknown => panic!("Tried to access unknown CSR: {:?}", register),
}
}
Expand Down Expand Up @@ -111,6 +157,25 @@ impl RegisterContext<Csr> for VirtContext {
Csr::Mvendorid => (), // Read-only
Csr::Marchid => (), // Read-only
Csr::Mimpid => (), // Read-only
Csr::Pmpcfg(pmp_cfg_idx) => {
if Csr::PMP_CFG_LOCK_MASK & value != 0 {
panic!("PMP lock bits are not yet supported")
} else if pmp_cfg_idx % 2 == 1 {
// Illegal because we are in a RISCV64 setting
panic!("Illegal PMP_CFG {:?}", register)
} else if pmp_cfg_idx >= self.nbr_pmps / 8 {
// This PMP is not emulated, ignore changes
return;
}
self.csr.pmp_cfg[pmp_cfg_idx] = Csr::PMP_CFG_LEGAL_MASK & value;
}
Csr::Pmpaddr(pmp_addr_idx) => {
if pmp_addr_idx >= self.nbr_pmps {
// This PMP is not emulated, ignore
return;
}
self.csr.pmp_addr[pmp_addr_idx] = Csr::PMP_ADDR_LEGAL_MASK & value;
}
Csr::Unknown => panic!("Tried to access unknown CSR: {:?}", register),
}
}
Expand Down
Loading