Skip to content

Commit

Permalink
feat: RISC-V PTE flags/size (#15)
Browse files Browse the repository at this point in the history
Co-authored-by: Juillard Thibault <[email protected]>
  • Loading branch information
theo-abel and Juillard Thibault authored Jul 24, 2024
1 parent b25ff43 commit a11f4c0
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 25 deletions.
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"
49 changes: 46 additions & 3 deletions src/architecture/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,17 @@ pub trait CPURegister {
type Value: hash::Hash + Eq + Default;

fn is_valid(&self) -> Result<Self::Value>;
fn is_mmu_equivalent_to(&self, other: &Self) -> bool;
}

/// Represents a page table entry with an address and flags.
/// 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.
pub trait PageTableEntry {
pub trait PageTableEntryTrait {
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 All @@ -117,12 +119,36 @@ pub trait PageTableEntry {
/// It is a data structure used in a virtual memory system to manage the mapping between virtual addresses and physical addresses.
/// It is used to translate virtual addresses to physical addresses and to manage the memory permissions of the pages.
/// It is also used to store additional information about the pages, such as the status of the page, the address space or process ID, amongst others.
pub trait PageTable {
type Entries: hash::Hash + Eq + Default + PageTableEntry;
pub trait PageTableTrait {
type Entries: hash::Hash + Eq + Default + PageTableEntryTrait;

// fn apply_on_entries(function: FnMut(PageTableEntry) -> Vec<?> ) -> ? // FIXME: to be defined, but is it necessary?
}

/// Represents a generic 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, Default)]
pub struct PageTableEntry<A, F, S> {
pub address: A,
pub flags: F,
pub size: S,
}

/// Represents a generic 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> {
/// 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,
}

/// Enumerates types of supported machines.
/// This enum is used to specify the type of machine that is being parsed.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
Expand Down Expand Up @@ -176,4 +202,21 @@ impl Machine {
outfolder,
})
}

pub fn resolve_spaces(&mut self) -> Result<&mut Self> {
todo!()
}
}

pub trait MMU {
type Address: hash::Hash + Eq + Default;
type Flags: hash::Hash + Eq + Default;
type Size: hash::Hash + Eq + Default;

fn classify_entry<
T: PageTableEntryTrait<Address = Self::Address, Flags = Self::Flags, Size = Self::Size>,
>(
&self,
entry: T,
) -> Result<MemoryRegionType>;
}
165 changes: 143 additions & 22 deletions src/architecture/riscv.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::generic::{CPURegister as CPURegisterTrait, PageTableEntry as PageTableEntryTrait};
use super::generic::{
CPURegister as CPURegisterTrait, PageTable, PageTableEntry, PageTableEntryTrait,
};

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

/// Represents a RISC-V CPU register associated with a value.
Expand All @@ -15,6 +18,11 @@ impl CPURegisterTrait for CPURegister {
fn is_valid(&self) -> Result<u64> {
todo!()
}

#[allow(unused_variables)]
fn is_mmu_equivalent_to(&self, other: &Self) -> bool {
todo!()
}
}

impl CPURegister {
Expand All @@ -23,56 +31,164 @@ impl CPURegister {
}
}

/// Represents a RISC-V 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,
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,
}

impl PageTableEntry {
pub fn new(address: u64, flags: u64) -> Self {
Self { address, flags }
/// 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,
}

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
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 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 Expand Up @@ -109,3 +225,8 @@ impl MMU {
Self { mode }
}
}

/// Represents a MMU that uses a radix tree.
pub struct MMURadixTree32 {
pub nb_radix_levels: u8,
}

0 comments on commit a11f4c0

Please sign in to comment.