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

riscv: define satp CSR with macro helpers #258

Merged
merged 3 commits into from
Jan 25, 2025
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 riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Use CSR helper macros to define `mstatush` register
- Use CSR helper macros to define `mtvec` register
- Use CSR helper macros to define `mtvendorid` register
- Use CSR helper macros to define `satp` register

## [v0.12.1] - 2024-10-20

Expand Down
16 changes: 8 additions & 8 deletions riscv/src/register/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,8 @@ macro_rules! csr_field_enum {
#[macro_export]
macro_rules! read_write_csr {
($(#[$doc:meta])+
$ty:ident: $csr:tt,
mask: $mask:tt$(,)?
$ty:ident: $csr:expr,
mask: $mask:expr$(,)?
) => {
$crate::csr!($(#[$doc])+ $ty, $mask);

Expand All @@ -617,17 +617,17 @@ macro_rules! read_write_csr {
#[macro_export]
macro_rules! read_only_csr {
($(#[$doc:meta])+
$ty:ident: $csr:tt,
mask: $mask:tt$(,)?
$ty:ident: $csr:expr,
mask: $mask:expr$(,)?
) => {
$crate::csr! { $(#[$doc])+ $ty, $mask }

$crate::read_csr_as!($ty, $csr);
};

($(#[$doc:meta])+
$ty:ident: $csr:tt,
mask: $mask:tt,
$ty:ident: $csr:expr,
mask: $mask:expr,
sentinel: $sentinel:tt$(,)?,
) => {
$crate::csr! { $(#[$doc])+ $ty, $mask }
Expand All @@ -642,8 +642,8 @@ macro_rules! read_only_csr {
#[macro_export]
macro_rules! write_only_csr {
($(#[$doc:meta])+
$ty:ident: $csr:literal,
mask: $mask:literal$(,)?
$ty:ident: $csr:expr,
mask: $mask:expr$(,)?
) => {
$crate::csr! { $(#[$doc])+ $ty, $mask }

Expand Down
246 changes: 131 additions & 115 deletions riscv/src/register/satp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,144 +2,86 @@

use crate::result::{Error, Result};

/// satp register
#[derive(Clone, Copy, Debug)]
pub struct Satp {
bits: usize,
read_write_csr! {
/// `satp` register
Satp: 0x180,
mask: usize::MAX,
}

impl Satp {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}

/// Current address-translation scheme
///
/// **WARNING**: panics if the field has an invalid variant.
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn mode(&self) -> Mode {
self.try_mode().unwrap()
}

/// Attempts to get the current address-translation scheme.
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn try_mode(&self) -> Result<Mode> {
((self.bits >> 31) as u8).try_into()
}

/// Current address-translation scheme
///
/// **WARNING**: panics if the field has an invalid variant.
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn mode(&self) -> Mode {
self.try_mode().unwrap()
}

/// Attempts to get the current address-translation scheme.
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn try_mode(&self) -> Result<Mode> {
((self.bits >> 60) as u8).try_into()
}

/// Address space identifier
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn asid(&self) -> usize {
(self.bits >> 22) & 0x1FF // bits 22-30
#[cfg(target_pointer_width = "32")]
csr_field_enum! {
/// 32-bit satp mode
Mode {
default: Bare,
/// No translation or protection
Bare = 0,
/// Page-based 32-bit virtual addressing
Sv32 = 1,
}
}

/// Address space identifier
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn asid(&self) -> usize {
(self.bits >> 44) & 0xFFFF // bits 44-59
#[cfg(target_pointer_width = "64")]
csr_field_enum! {
/// 64-bit satp mode
Mode {
default: Bare,
/// No translation or protection
Bare = 0,
/// Page-based 39-bit virtual addressing
Sv39 = 8,
/// Page-based 48-bit virtual addressing
Sv48 = 9,
/// Page-based 57-bit virtual addressing
Sv57 = 10,
/// Page-based 64-bit virtual addressing
Sv64 = 11,
}
}

#[cfg(target_pointer_width = "32")]
read_write_csr_field! {
Satp,
/// Physical page number
#[inline]
#[cfg(target_pointer_width = "32")]
pub fn ppn(&self) -> usize {
self.bits & 0x3F_FFFF // bits 0-21
}
ppn: [0:21],
}

#[cfg(target_pointer_width = "64")]
read_write_csr_field! {
Satp,
/// Physical page number
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn ppn(&self) -> usize {
self.bits & 0xFFF_FFFF_FFFF // bits 0-43
}
ppn: [0:43],
}

/// 32-bit satp mode
#[cfg(target_pointer_width = "32")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
/// No translation or protection
Bare = 0,
/// Page-based 32-bit virtual addressing
Sv32 = 1,
read_write_csr_field! {
Satp,
/// Address space identifier
asid: [22:30],
}

/// 64-bit satp mode
#[cfg(target_pointer_width = "64")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
/// No translation or protection
Bare = 0,
/// Page-based 39-bit virtual addressing
Sv39 = 8,
/// Page-based 48-bit virtual addressing
Sv48 = 9,
/// Page-based 57-bit virtual addressing
Sv57 = 10,
/// Page-based 64-bit virtual addressing
Sv64 = 11,
read_write_csr_field! {
Satp,
/// Address space identifier
asid: [44:59],
}

#[cfg(target_pointer_width = "32")]
impl TryFrom<u8> for Mode {
type Error = Error;

fn try_from(val: u8) -> Result<Self> {
match val {
0 => Ok(Mode::Bare),
1 => Ok(Mode::Sv32),
_ => Err(Error::InvalidFieldVariant {
field: "mode",
value: val as usize,
}),
}
}
read_write_csr_field! {
Satp,
/// Current address-translation scheme.
mode,
Mode: [31:31],
}

#[cfg(target_pointer_width = "64")]
impl TryFrom<u8> for Mode {
type Error = Error;

fn try_from(val: u8) -> Result<Self> {
match val {
0 => Ok(Mode::Bare),
8 => Ok(Mode::Sv39),
9 => Ok(Mode::Sv48),
10 => Ok(Mode::Sv57),
11 => Ok(Mode::Sv64),
_ => Err(Error::InvalidFieldVariant {
field: "mode",
value: val as usize,
}),
}
}
read_write_csr_field! {
Satp,
/// Current address-translation scheme.
mode,
Mode: [60:63],
}

read_csr_as!(Satp, 0x180);
write_csr_as_usize!(0x180);

/// Sets the register to corresponding page table mode, physical page number and address space id.
///
/// **WARNING**: panics on:
Expand Down Expand Up @@ -207,3 +149,77 @@ pub unsafe fn try_set(mode: Mode, asid: usize, ppn: usize) -> Result<()> {
_try_write(bits)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[cfg(target_pointer_width = "32")]
const ASID_START: usize = 22;
#[cfg(target_pointer_width = "64")]
const ASID_START: usize = 44;
#[cfg(target_pointer_width = "32")]
const MODE_START: usize = 31;
#[cfg(target_pointer_width = "64")]
const MODE_START: usize = 60;

#[cfg(target_pointer_width = "32")]
const MODES: [Mode; 2] = [Mode::Bare, Mode::Sv32];
#[cfg(target_pointer_width = "64")]
const MODES: [Mode; 5] = [Mode::Bare, Mode::Sv39, Mode::Sv48, Mode::Sv57, Mode::Sv64];

#[test]
fn test_satp() {
let new_mode = Mode::new();

(1..=usize::BITS)
.map(|r| ((1u128 << r) - 1) as usize)
.for_each(|raw| {
let mut satp = Satp::from_bits(raw);

let exp_ppn = raw & ((1usize << ASID_START) - 1);
let exp_asid = (raw & ((1usize << MODE_START) - 1)) >> ASID_START;

assert_eq!(satp.ppn(), exp_ppn);

satp.set_ppn(0);
assert_eq!(satp.ppn(), 0);

satp.set_ppn(exp_ppn);
assert_eq!(satp.ppn(), exp_ppn);

assert_eq!(satp.asid(), exp_asid);

satp.set_asid(0);
assert_eq!(satp.asid(), 0);

satp.set_asid(exp_asid);
assert_eq!(satp.asid(), exp_asid);

match Mode::from_usize(raw >> 60) {
Ok(exp_mode) => {
assert_eq!(satp.try_mode(), Ok(exp_mode));
assert_eq!(satp.mode(), exp_mode);

satp.set_mode(new_mode);

assert_eq!(satp.try_mode(), Ok(new_mode));
assert_eq!(satp.mode(), new_mode);

satp.set_mode(exp_mode);

assert_eq!(satp.try_mode(), Ok(exp_mode));
assert_eq!(satp.mode(), exp_mode);
}
Err(exp_err) => {
assert_eq!(satp.try_mode(), Err(exp_err));
}
}
});

let mut satp = Satp::from_bits(0);
MODES
.into_iter()
.for_each(|mode| test_csr_field!(satp, mode: mode));
}
}
Loading