Skip to content

Commit

Permalink
monolithic: introduce crate memory_set to implement mmap and munmap
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jun 26, 2024
1 parent 3b4bc02 commit 4202056
Show file tree
Hide file tree
Showing 12 changed files with 656 additions and 43 deletions.
8 changes: 8 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 @@ -25,6 +25,7 @@ members = [
"crates/lazy_init",
"crates/linked_list",
"crates/memory_addr",
"crates/memory_set",
"crates/page_table",
"crates/page_table_entry",
"crates/percpu",
Expand Down
15 changes: 15 additions & 0 deletions crates/memory_set/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "memory_set"
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <[email protected]>"]
description = "Data structures and operations for managing memory mappings"
license = "GPL-3.0-or-later OR Apache-2.0"
homepage = "https://github.com/rcore-os/arceos"
repository = "https://github.com/rcore-os/arceos/tree/main/crates/memory_set"
documentation = "https://rcore-os.github.io/arceos/memory_set/index.html"
keywords = ["arceos", "virtual-memory", "memory-area", "mmap"]
categories = ["os", "memory-management", "no-std"]

[dependencies]
memory_addr = { path = "../memory_addr" }
151 changes: 151 additions & 0 deletions crates/memory_set/src/area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use core::fmt;
use core::marker::PhantomData;

use memory_addr::{VirtAddr, VirtAddrRange};

use crate::{MappingError, MappingResult};

/// Underlying memory mapping operations specific to a memory area.
///
/// The backend can be different for different memory areas. e.g., for fixed
/// mappings, the target physical address is known when it is added to the page
/// table. For lazy mappings, an empty mapping needs to be added to the page table
/// to trigger a page fault.
pub trait MappingBackend<F: Copy, P>: Clone {
/// Maps a memory region with the given flags.
fn map(&self, start: VirtAddr, size: usize, flags: F, page_table: &mut P) -> bool;
/// Unmaps a memory region.
fn unmap(&self, start: VirtAddr, size: usize, page_table: &mut P) -> bool;
}

/// A memory area represents a continuous range of virtual memory with the same
/// flags.
///
/// The target physical memory frames are determined by [`MappingBackend`] and
/// may not be contiguous.
pub struct MemoryArea<F: Copy, P, B: MappingBackend<F, P>> {
va_range: VirtAddrRange,
flags: F,
backend: B,
_phantom: PhantomData<(F, P)>,
}

impl<F: Copy, P, B: MappingBackend<F, P>> MemoryArea<F, P, B> {
/// Creates a new memory area.
pub const fn new(start: VirtAddr, size: usize, flags: F, backend: B) -> Self {
Self {
va_range: VirtAddrRange::from_start_size(start, size),
flags,
backend,
_phantom: PhantomData,
}
}

/// Returns the virtual address range.
pub const fn va_range(&self) -> VirtAddrRange {
self.va_range
}

/// Returns the memory flags, e.g., the permission bits.
pub const fn flags(&self) -> F {
self.flags
}

/// Returns the start address of the memory area.
pub const fn start(&self) -> VirtAddr {
self.va_range.start
}

/// Returns the end address of the memory area.
pub const fn end(&self) -> VirtAddr {
self.va_range.end
}

/// Returns the size of the memory area.
pub const fn size(&self) -> usize {
self.va_range.size()
}
}

impl<F: Copy, P, B: MappingBackend<F, P>> MemoryArea<F, P, B> {
/// Maps the whole memory area in the page table.
pub(crate) fn map_area(&self, page_table: &mut P) -> MappingResult {
self.backend
.map(self.start(), self.size(), self.flags, page_table)
.then_some(())
.ok_or(MappingError::BadState)
}

/// Unmaps the whole memory area in the page table.
pub(crate) fn unmap_area(&self, page_table: &mut P) -> MappingResult {
self.backend
.unmap(self.start(), self.size(), page_table)
.then_some(())
.ok_or(MappingError::BadState)
}

/// Shrinks the memory area at the left side.
///
/// The start address of the memory area is increased by `new_size`. The
/// shrunk part is unmapped.
pub(crate) fn shrink_left(&mut self, new_size: usize, page_table: &mut P) -> MappingResult {
let unmap_size = self.size() - new_size;
if !self.backend.unmap(self.start(), unmap_size, page_table) {
return Err(MappingError::BadState);
}
self.va_range.start += unmap_size;
Ok(())
}

/// Shrinks the memory area at the right side.
///
/// The end address of the memory area is decreased by `new_size`. The
/// shrunk part is unmapped.
pub(crate) fn shrink_right(&mut self, new_size: usize, page_table: &mut P) -> MappingResult {
let unmap_size = self.size() - new_size;
if !self
.backend
.unmap(self.start() + new_size, unmap_size, page_table)
{
return Err(MappingError::BadState);
}
self.va_range.end -= unmap_size;
Ok(())
}

/// Splits the memory area at the given position.
///
/// The original memory area is shrunk to the left part, and the right part
/// is returned.
///
/// Returns `None` if the given position is not in the memory area, or one
/// of the parts is empty after splitting.
pub(crate) fn split(&mut self, pos: VirtAddr) -> Option<Self> {
let start = self.start();
let end = self.end();
if start < pos && pos < end {
let new_area = Self::new(
pos,
end.as_usize() - pos.as_usize(),
self.flags,
self.backend.clone(),
);
self.va_range.end = pos;
Some(new_area)
} else {
None
}
}
}

impl<F, P, B: MappingBackend<F, P>> fmt::Debug for MemoryArea<F, P, B>
where
F: fmt::Debug + Copy,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MemoryArea")
.field("va_range", &self.va_range)
.field("flags", &self.flags)
.finish()
}
}
28 changes: 28 additions & 0 deletions crates/memory_set/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Data structures and operations for managing memory mappings.
#![cfg_attr(not(test), no_std)]

extern crate alloc;

mod area;
mod set;

#[cfg(test)]
mod tests;

pub use self::area::{MappingBackend, MemoryArea};
pub use self::set::MemorySet;

/// Error type for memory mapping operations.
#[derive(Debug, Eq, PartialEq)]
pub enum MappingError {
/// Invalid parameter (e.g., `addr`, `size`, `flags`, etc.)
InvalidParam,
/// The given range clashes with an existing.
AlreadyExists,
/// The backend page table is in a bad state.
BadState,
}

/// A [`Result`] type with [`MappingError`] as the error type.
pub type MappingResult<T = ()> = Result<T, MappingError>;
Loading

0 comments on commit 4202056

Please sign in to comment.