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

feat: RISC-V PTE flags/size #15

Merged
merged 6 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
.devenv

# Vim swap files
*.swp
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ authors = ["Théo Abel <[email protected]>", "Nathan Dandrimont <nathan.dand
serde = { version = "1.0", features = ["derive"] }
toml = "0.8.12"
anyhow = "1.0"
bitfield = "0.15.0"
1 change: 1 addition & 0 deletions src/architecture/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub trait CPURegister {
pub trait PageTableEntry {
type Address: hash::Hash + Eq + Default;
type Flags: hash::Hash + Eq + Default;
type Size: hash::Hash + Eq + Default;

fn is_dirty(&self) -> bool;
fn is_accessed(&self) -> bool;
Expand Down
169 changes: 151 additions & 18 deletions src/architecture/riscv.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::generic::{CPURegister as CPURegisterTrait, PageTableEntry as PageTableEntryTrait};

use anyhow::Result;
use bitfield::bitfield;
use serde::{Deserialize, Serialize};

/// Represents a RISC-V CPU register associated with a value.
Expand All @@ -23,56 +24,188 @@ impl CPURegister {
}
}

/// Represents a RISC-V page table entry.
bitfield! {
/// Represents a RISC-V SV32 page table entry flags.
/// The flags are used to determine the permissions of a page, or some other attributes.
/// This is baed on the RISC-V Sv39 page table entry flags.
/// All fields are read-only.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct PTE32Flags(u32);

pub read, _: 1;
pub write, _: 2;
pub exec, _: 3;
pub supervisor, _: 4;
pub global, _: 5;
pub accessed, _: 6;
pub dirty, _: 7;
}

bitfield! {
/// Represents a RISC-V SV39 and SV48 page table entry flags.
/// The flags are used to determine the permissions of a page, or some other attributes.
/// This is baed on the RISC-V Sv39 page table entry flags.
/// All fields are read-only.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct PTE64Flags(u64);

pub read, _: 1;
pub write, _: 2;
pub exec, _: 3;
pub supervisor, _: 4;
pub global, _: 5;
pub accessed, _: 6;
pub dirty, _: 7;
}

/// Represents a RISC-V SV32 page table entry size.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
pub enum PTE32Size {
/// 4 bytes
#[default]
PTP32,
/// 4KB, 1024 * 4 bytes
PTE4KB,
/// 4MB, 1024 * 1024 * 4 bytes
PTE4MB,
}

/// Represents a RISC-V SV39 and SV48 page table entry size.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
pub enum PTE64Size {
/// 8 bytes
#[default]
PTP64,
/// 4KB, 512 * 8 bytes
PTE4KB,
/// 2MB, 256 * 8 bytes
PTE2MB,
/// 1GB, 128 * 8 bytes
PTE1GB,
/// 512GB, 64 * 8 bytes
/// This is only available in Sv48
PTE512GB,
}

/// Represents a RISC-V SV32 page table entry.
/// It holds the mapping between a virtual address of a page and the address of a physical frame.
/// There is also auxiliary information about the page such as a present bit, a dirty or modified bit,
/// address space or process ID information, amongst others.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct PageTableEntry {
pub address: u64,
pub flags: u64,
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct PageTableEntry<A, F, S> {
standard3 marked this conversation as resolved.
Show resolved Hide resolved
pub address: A,
pub flags: F,
pub size: S,
}

impl PageTableEntry {
pub fn new(address: u64, flags: u64) -> Self {
Self { address, flags }
impl PageTableEntry<u32, PTE32Flags, PTE32Size> {
pub fn new(address: u32, flags: u32, size: PTE32Size) -> Self {
let flags = PTE32Flags(flags);
Self {
address,
flags,
size,
}
}

pub fn is_supervisor(&self) -> bool {
todo!()
self.flags.supervisor()
}
}

impl PageTableEntryTrait for PageTableEntry {
impl PageTableEntryTrait for PageTableEntry<u32, PTE32Flags, PTE32Size> {
type Address = u32;
type Flags = PTE32Flags;
type Size = PTE32Size;

fn is_dirty(&self) -> bool {
self.flags.dirty()
}

fn is_accessed(&self) -> bool {
self.flags.accessed()
}

fn is_global(&self) -> bool {
self.flags.global()
}

fn is_readable(&self) -> bool {
self.flags.read()
}

fn is_writable(&self) -> bool {
self.flags.write()
}

fn is_executable(&self) -> bool {
self.flags.exec()
}
}

impl PageTableEntry<u64, PTE64Flags, PTE64Size> {
pub fn new(address: u64, flags: u64, size: PTE64Size) -> Self {
let flags = PTE64Flags(flags);
Self {
address,
flags,
size,
}
}

pub fn is_supervisor(&self) -> bool {
self.flags.supervisor()
}
}

impl PageTableEntryTrait for PageTableEntry<u64, PTE64Flags, PTE64Size> {
type Address = u64;
type Flags = u64;
type Flags = PTE64Flags;
type Size = PTE64Size;

// FIXME: Implement the following methods
standard3 marked this conversation as resolved.
Show resolved Hide resolved
fn is_dirty(&self) -> bool {
todo!()
self.flags.dirty()
}

fn is_accessed(&self) -> bool {
todo!()
self.flags.accessed()
}

fn is_global(&self) -> bool {
todo!()
self.flags.global()
}

fn is_readable(&self) -> bool {
todo!()
self.flags.read()
}

fn is_writable(&self) -> bool {
todo!()
self.flags.write()
}

fn is_executable(&self) -> bool {
todo!()
self.flags.exec()
}
}

/// Represents a RISC-V page table.
/// A page table is the data structure used by a virtual memory system in a computer operating system to store the mapping between virtual addresses and physical addresses.
pub struct PageTable<A, F, S> {
standard3 marked this conversation as resolved.
Show resolved Hide resolved
/// Physical address of the page table
pub address: A,
/// Size of the page table
pub size: A,
/// Entries in the page table
pub entries: Vec<PageTableEntry<A, F, S>>,
/// Number of levels in the page table
pub levels: u8,
}

/// Represents a RISC-V SV32 page table.
pub type PageTable32 = PageTable<u32, PTE32Flags, PTE32Size>;
/// Represents a RISC-V SV39 and SV48 page table.
pub type PageTable64 = PageTable<u64, PTE64Flags, PTE64Size>;

/// Enumerates RISC-V MMU modes.
/// The MMU modes are used to determine the number of bits used for virtual and physical addresses.
/// The modes are named after the number of bits used for the virtual address space.
Expand Down
Loading