Skip to content

Commit

Permalink
[test] Create test_helpers crate to group all clint test code
Browse files Browse the repository at this point in the history
The new crate can be extended in the future to hold more test helpers,
such as macros to generate proper interrupt handlers. For now it is
limited to clint functionalities and assumes the QEMU virt platform (it
is not meant to be a general-purpose library).
  • Loading branch information
CharlyCst committed Oct 30, 2024
1 parent dce18b7 commit 9c541dc
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 129 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ members = [
# Payload
"payload/hello_world",
"payload/test_protect_payload_payload",

# Crates
"crates/abi",
"crates/core",
"crates/config_helpers",
"crates/config_select",
"crates/test_helpers",

# Tooling
"runner",
Expand Down
6 changes: 6 additions & 0 deletions crates/test_helpers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "test_helpers"
version = "0.1.0"
edition = "2021"

[dependencies]
42 changes: 42 additions & 0 deletions crates/test_helpers/src/clint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Client utilities
//!
//! The functions exposed in this modules assumes the CLINT is located at the same addresses as on
//! the QEMU virt platform, which we use for out tests.

/// Clint base, assuming a QEMU virt-like layout.
const CLINT_BASE: usize = 0x2000000;
const MTIME_OFFSET: usize = 0xBFF8;
const MTIMECMP_OFFSET: usize = 0x4000;

/// Get the current mtime value
pub fn read_mtime() -> usize {
let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize;
unsafe { mtime_ptr.read_volatile() }
}

/// Set mtimecmp deadline in the future
pub fn set_mtimecmp_deadline(delta: usize, hart: usize) {
let current_mtime = read_mtime();
let future_time = current_mtime.saturating_add(delta);

let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET + 8 * hart) as *mut usize;
unsafe {
mtimecmp_ptr.write_volatile(future_time);
}
}

/// Send an MSI to the given hart.
pub fn send_msi(hart: usize) {
let msip_ptr = (CLINT_BASE + 4 * hart) as *mut u32;
unsafe {
msip_ptr.write_volatile(1);
}
}

/// Clear MSI for the given hart.
pub fn clear_msi(hart: usize) {
let msip_ptr = (CLINT_BASE + 4 * hart) as *mut u32;
unsafe {
msip_ptr.write_volatile(0);
}
}
8 changes: 8 additions & 0 deletions crates/test_helpers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Test helpers
//!
//! Utility functions, macros, and contants for the Miralis integration tests (firmware and
//! payloads).

#![no_std]

pub mod clint;
1 change: 1 addition & 0 deletions firmware/clint_interrupt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ path = "main.rs"

[dependencies]
miralis_abi = { path = "../../crates/abi" }
test_helpers = { path = "../../crates/test_helpers" }
log = { workspace = true }
27 changes: 3 additions & 24 deletions firmware/clint_interrupt/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use core::usize;

use miralis_abi::{setup_binary, success};
use test_helpers::clint::{self, set_mtimecmp_deadline};

setup_binary!(main);

Expand All @@ -16,7 +17,7 @@ setup_binary!(main);
/// 4. If the virtual interrupt is not cleared, the firmware will trap on each execution cycle.
fn main() -> ! {
// Setup a timer deadline
set_mtimecmp_future_value(0);
clint::set_mtimecmp_deadline(0, 0);

// Configure trap handler and enable interrupts
unsafe {
Expand Down Expand Up @@ -44,31 +45,9 @@ unsafe fn enable_interrupts() {

// ———————————————————————————— Timer Interrupt ————————————————————————————— //

// Clint
const CLINT_BASE: usize = 0x2000000;
const MTIME_OFFSET: usize = 0xBFF8;
const MTIMECMP_OFFSET: usize = 0x4000;

static INTERRUPT_COUNTER: AtomicUsize = AtomicUsize::new(0);
const REPETITIONS: usize = 10;

// Get the current mtime value
fn get_current_mtime() -> usize {
let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize;
unsafe { mtime_ptr.read_volatile() }
}

// Set mtimecmp value in the future
fn set_mtimecmp_future_value(value: usize) {
let current_mtime = get_current_mtime();
let future_time = current_mtime.saturating_add(value);

let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize;
unsafe {
mtimecmp_ptr.write_volatile(future_time);
}
}

/// This function should be called from the raw trap handler
extern "C" fn trap_handler() {
let mip: usize;
Expand Down Expand Up @@ -120,7 +99,7 @@ fn handle_timer_interrupt() {
panic!("Expected another timer interrupt, but got nothing");
} else {
// We got all the timers we expected!
set_mtimecmp_future_value(usize::MAX);
set_mtimecmp_deadline(usize::MAX, 0);
log::trace!("Now timer should clear, re-enabling interrupts");
unsafe { enable_interrupts() };

Expand Down
1 change: 1 addition & 0 deletions firmware/clint_interrupt_multihart/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ path = "main.rs"

[dependencies]
miralis_abi = { path = "../../crates/abi" }
test_helpers = { path = "../../crates/test_helpers" }
log = { workspace = true }
36 changes: 10 additions & 26 deletions firmware/clint_interrupt_multihart/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use core::arch::{asm, global_asm};
use core::usize;

use miralis_abi::{failure, setup_binary, success};
use miralis_abi::{setup_binary, success};
use test_helpers::clint;

setup_binary!(main);

Expand Down Expand Up @@ -42,32 +43,19 @@ fn main() -> ! {
msie = in(reg) 0x8,
);
}
success()
panic!("Expected an MSI interrupt");
}
1 => {
set_msip(0, 1);
clint::send_msi(0);
loop {
unsafe { asm!("nop") };
core::hint::spin_loop();
}
}
_ => failure(),
_ => panic!("Invalid hart ID"),
}
}

// ———————————————————————————— Timer Interrupt ————————————————————————————— //

const CLINT_BASE: usize = 0x2000000;
const MSIP_WIDTH: usize = 0x4;

// Set msip bit pending for other hart
fn set_msip(hart: usize, value: u32) {
log::debug!("Set interrupt for hart {:}", hart);

let msip_ptr = (CLINT_BASE + MSIP_WIDTH * hart) as *mut u32;
unsafe {
msip_ptr.write_volatile(value);
}
}
// —————————————————————————————— Trap Handler —————————————————————————————— //

/// This function should be called from the raw trap handler
extern "C" fn trap_handler() {
Expand All @@ -80,17 +68,13 @@ extern "C" fn trap_handler() {
}

if mcause == 0x8000000000000003 {
set_msip(0, 0);
unsafe {
asm!("mret",);
}
clint::clear_msi(0);
success();
} else {
failure();
panic!("Unexpected mcause: 0x{:x}", mcause);
}
}

// —————————————————————————————— Trap Handler —————————————————————————————— //

global_asm!(
r#"
.text
Expand Down
1 change: 1 addition & 0 deletions firmware/clint_interrupt_priority/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ path = "main.rs"

[dependencies]
miralis_abi = { path = "../../crates/abi" }
test_helpers = { path = "../../crates/test_helpers" }
log = { workspace = true }
42 changes: 5 additions & 37 deletions firmware/clint_interrupt_priority/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::arch::{asm, global_asm};
use core::usize;

use miralis_abi::{failure, setup_binary, success};
use test_helpers::clint;

setup_binary!(main);

Expand All @@ -14,8 +15,8 @@ setup_binary!(main);
/// memory-mapped registers.
fn main() -> ! {
// Set up two interrupts simultaniously
set_msip(0, 1);
set_mtimecmp_future_value(0);
clint::send_msi(0);
clint::set_mtimecmp_deadline(0, 0);

// MTIP is not guaranteed to be reflected instantly, so wait for a bit
for _ in 1..10000 {
Expand Down Expand Up @@ -59,39 +60,6 @@ fn main() -> ! {

// ———————————————————————————— Timer Interrupt ————————————————————————————— //

const CLINT_BASE: usize = 0x2000000;
const MTIME_OFFSET: usize = 0xBFF8;
const MTIMECMP_OFFSET: usize = 0x4000;
const MSIP_WIDTH: usize = 0x4;

// Get the current mtime value
fn get_current_mtime() -> usize {
let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize;
unsafe { mtime_ptr.read_volatile() }
}

// Set mtimecmp value in the future
fn set_mtimecmp_future_value(value: usize) {
log::trace!("Updated timer");
let current_mtime = get_current_mtime();
let future_time = current_mtime.saturating_add(value);

let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize;
unsafe {
mtimecmp_ptr.write_volatile(future_time);
}
}

// Set msip bit pending for other hart
fn set_msip(hart: usize, value: u32) {
log::trace!("Set interrupt for hart {:}", hart);

let msip_ptr = (CLINT_BASE + MSIP_WIDTH * hart) as *mut u32;
unsafe {
msip_ptr.write_volatile(value);
}
}

/// This function should be called from the raw trap handler
extern "C" fn trap_handler() {
let mip: usize;
Expand All @@ -116,10 +84,10 @@ extern "C" fn trap_handler() {

if mcause == 0x8000000000000003 {
log::trace!("Cleared MSIP");
set_msip(0, 0);
clint::clear_msi(0);
} else if mcause == 0x8000000000000007 {
log::trace!("Cleared MTIP");
set_mtimecmp_future_value(usize::MAX);
clint::set_mtimecmp_deadline(usize::MAX, 0);
} else {
log::warn!("Received non-interrupt trap, {:x}", mcause);
failure();
Expand Down
1 change: 1 addition & 0 deletions firmware/interrupt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ path = "main.rs"

[dependencies]
miralis_abi = { path = "../../crates/abi" }
test_helpers = { path = "../../crates/test_helpers" }
log = { workspace = true }
28 changes: 2 additions & 26 deletions firmware/interrupt/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use core::arch::{asm, global_asm};

use miralis_abi::{failure, setup_binary, success};
use test_helpers::clint;

setup_binary!(main);

Expand Down Expand Up @@ -83,31 +84,6 @@ fn test_sie_by_mie() {

// ———————————————————————————— Timer Interrupt ————————————————————————————— //

const CLINT_BASE: usize = 0x2000000;
const MTIME_OFFSET: usize = 0xBFF8;
const MTIMECMP_OFFSET: usize = 0x4000;

// Get the current mtime value
fn get_current_mtime() -> usize {
let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize;
unsafe { mtime_ptr.read_volatile() }
}

// Set mtimecmp value in the future
fn set_mtimecmp_future_value() {
let current_mtime = get_current_mtime();
let future_time = current_mtime + 10000;

let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize; // TODO: add support for different harts
unsafe {
mtimecmp_ptr.write_volatile(future_time);
}

// Read back the value to verify
let read_back = unsafe { mtimecmp_ptr.read_volatile() };
assert_eq!(read_back, future_time, "mtimecmp set correctly");
}

#[allow(unreachable_code)]
fn test_timer_interrupts() -> ! {
// Configure trap handler and enable interrupts
Expand All @@ -123,7 +99,7 @@ fn test_timer_interrupts() -> ! {
}

// Setup a timer deadline
set_mtimecmp_future_value();
clint::set_mtimecmp_deadline(10_000, 0);

// Wait for an interrupt
loop {
Expand Down
Loading

0 comments on commit 9c541dc

Please sign in to comment.