mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-26 19:03:27 +00:00
add VM capabilities
This commit is contained in:
55
src/kxos-std/src/rights.rs
Normal file
55
src/kxos-std/src/rights.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use typeflags::typeflags;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Value-based access rights.
|
||||||
|
///
|
||||||
|
/// These access rights are provided to cover a wide range of use cases.
|
||||||
|
/// The access rights' semantics and how they would restrict the behaviors
|
||||||
|
/// of a capability are decided by the capability's designer.
|
||||||
|
/// Here, we give some sensible semantics for each access right.
|
||||||
|
pub struct Rights: u32 {
|
||||||
|
/// Allows duplicating a capability.
|
||||||
|
const DUP: u32 = 1 << 0;
|
||||||
|
/// Allows reading data from a data source (files, VM objects, etc.) or
|
||||||
|
/// creating readable memory mappings.
|
||||||
|
const READ: u32 = 1 << 1;
|
||||||
|
/// Allows writing data to a data sink (files, VM objects, etc.) or
|
||||||
|
/// creating writable memory mappings.
|
||||||
|
const WRITE: u32 = 1 << 2;
|
||||||
|
/// Allows creating executable memory mappings.
|
||||||
|
const EXEC: u32 = 1 << 3;
|
||||||
|
/// Allows sending notifications or signals.
|
||||||
|
const SIGNAL: u32 = 1 << 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeflags! {
|
||||||
|
/// Type-based access rights.
|
||||||
|
///
|
||||||
|
/// Similar to value-based access rights (`Rights`), but represented in
|
||||||
|
/// types.
|
||||||
|
pub trait TRights: u32 {
|
||||||
|
/// Allows duplicating a capability.
|
||||||
|
struct Dup: u32 = Rights::DUP;
|
||||||
|
/// Allows reading data from a data source (files, VM objects, etc.) or
|
||||||
|
/// creating readable memory mappings.
|
||||||
|
struct Read: u32 = Rights::READ;
|
||||||
|
/// Allows writing data to a data sink (files, VM objects, etc.) or
|
||||||
|
/// creating writable memory mappings.
|
||||||
|
struct Write: u32 = Rights::WRITE;
|
||||||
|
/// Allows creating executable memory mappings.
|
||||||
|
struct Exec: u32 = Rights::EXEC;
|
||||||
|
/// Allows sending notifications or signals.
|
||||||
|
struct Signal: u32 = Rights::SIGNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The full set of access rights.
|
||||||
|
pub type Full = TRights![
|
||||||
|
Dup,
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
Exec,
|
||||||
|
Signal,
|
||||||
|
];
|
18
src/kxos-std/src/vm/mod.rs
Normal file
18
src/kxos-std/src/vm/mod.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//! Virtual memory (VM).
|
||||||
|
//!
|
||||||
|
//! There are two primary VM abstractions:
|
||||||
|
//! * Virtual Memory Address Regions (VMARs) a type of capability that manages
|
||||||
|
/// user address spaces.
|
||||||
|
//! * Virtual Memory Objects (VMOs) are are a type of capability that
|
||||||
|
//! represents a set of memory pages.
|
||||||
|
//!
|
||||||
|
//! The concepts of VMARs and VMOs are originally introduced by
|
||||||
|
//! [Zircon](https://fuchsia.dev/fuchsia-src/reference/kernel_objects/vm_object).
|
||||||
|
//! As capabilities, the two abstractions are aligned with our goal
|
||||||
|
//! of everything-is-a-capability, although their specifications and
|
||||||
|
//! implementations in C/C++ cannot apply directly to KxOS.
|
||||||
|
//! In KxOS, VMARs and VMOs, as well as other capabilities, are implemented
|
||||||
|
//! as zero-cost capabilities.
|
||||||
|
|
||||||
|
mod vmar;
|
||||||
|
mod vmo;
|
130
src/kxos-std/src/vm/vmar/dyn_cap.rs
Normal file
130
src/kxos-std/src/vm/vmar/dyn_cap.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
impl Vmar<Rights> {
|
||||||
|
/// Creates a root VMAR.
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let inner = Arc::new(Vmar_::new());
|
||||||
|
let rights = Rights::all();
|
||||||
|
let new_self = Self(inner, rights);
|
||||||
|
Ok(new_self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps the given VMO into the VMAR through a set of VMAR mapping options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::prelude::*;
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, Vmar, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let vmar = Vmar::new().unwrap();
|
||||||
|
/// let vmo = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let target_vaddr = 0x1234000;
|
||||||
|
/// let real_vaddr = vmar
|
||||||
|
/// // Map the VMO to create a read-only mapping
|
||||||
|
/// .new_map(vmo, VmPerms::READ)
|
||||||
|
/// // Provide an optional offset for the mapping inside the VMAR
|
||||||
|
/// .offset(target_vaddr)
|
||||||
|
/// .build()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(real_vaddr == target_vaddr);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmarMapOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the following access rights:
|
||||||
|
/// 1. The VMAR contains the rights corresponding to the memory permissions of
|
||||||
|
/// the mapping. For example, if `perms` contains `VmPerm::WRITE`,
|
||||||
|
/// then the VMAR must have the Write right.
|
||||||
|
/// 2. Similarly, the VMO contains the rights corresponding to the memory
|
||||||
|
/// permissions of the mapping.
|
||||||
|
///
|
||||||
|
/// Memory permissions may be changed through the `protect` method,
|
||||||
|
/// which ensures that any updated memory permissions do not go beyond
|
||||||
|
/// the access rights of the underlying VMOs.
|
||||||
|
pub fn new_map(&self, vmo: Vmo, perms: VmPerms) -> VmarMapOptions<'_, Rights> {
|
||||||
|
let dup_self = self.dup()?;
|
||||||
|
VmarMapOptions::new(dup_self, vmo, perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new child VMAR through a set of VMAR child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = Vmar::new().unwrap();
|
||||||
|
/// let child_size = 10 * PAGE_SIZE;
|
||||||
|
/// let child = parent.new_child(child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmarChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMAR child will be of the same capability class and
|
||||||
|
/// access rights as the parent.
|
||||||
|
pub fn new_child(&self, size: usize) -> VmarChildOptions<'a, Rights> {
|
||||||
|
let dup_self = self.dup()?;
|
||||||
|
VmarChildOptions::new(dup_self, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the permissions of the memory mappings in the specified range.
|
||||||
|
///
|
||||||
|
/// The range's start and end addresses must be page-aligned.
|
||||||
|
/// Also, the range must be completely mapped.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The VMAR must have the rights corresponding to the specified memory
|
||||||
|
/// permissions.
|
||||||
|
///
|
||||||
|
/// The mappings overlapped with the specified range must be backed by
|
||||||
|
/// VMOs whose rights contain the rights corresponding to the specified
|
||||||
|
/// memory permissions.
|
||||||
|
pub fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
|
self.check_rights(perms.into())?;
|
||||||
|
self.0.protect(perms, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy a VMAR, including all its mappings and children VMARs.
|
||||||
|
///
|
||||||
|
/// After being destroyed, the VMAR becomes useless and returns errors
|
||||||
|
/// for most of its methods.
|
||||||
|
pub fn destroy_all(&self) -> Result<()> {
|
||||||
|
self.0.destroy_all()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy all mappings and children VMARs that fall within the specified
|
||||||
|
/// range in bytes.
|
||||||
|
///
|
||||||
|
/// The range's start and end addresses must be page-aligned.
|
||||||
|
///
|
||||||
|
/// Mappings may fall partially within the range; only the overlapped
|
||||||
|
/// portions of the mappings are unmapped.
|
||||||
|
/// As for children VMARs, they must be fully within the range.
|
||||||
|
/// All children VMARs that fall within the range get their `destroy` methods
|
||||||
|
/// called.
|
||||||
|
pub fn destroy(&self, range: &Range<usize>) -> Result<()> {
|
||||||
|
self.0.destroy(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the access rights.
|
||||||
|
pub fn rights(&self) -> Rights {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmIo for Vmar<Rights> {
|
||||||
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
self.check_rights!(Rights::READ)?;
|
||||||
|
self.0.read(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
self.check_rights!(Rights::WRITE)?;
|
||||||
|
self.0.write(offset, buf)
|
||||||
|
}
|
||||||
|
}
|
110
src/kxos-std/src/vm/vmar/mod.rs
Normal file
110
src/kxos-std/src/vm/vmar/mod.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//! Virtual Memory Address Regions (VMARs).
|
||||||
|
|
||||||
|
mod static_cap;
|
||||||
|
mod dyn_cap;
|
||||||
|
mod options;
|
||||||
|
|
||||||
|
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
|
||||||
|
/// user address spaces.
|
||||||
|
///
|
||||||
|
/// # Capabilities
|
||||||
|
///
|
||||||
|
/// As a capability, each VMAR is associated with a set of access rights,
|
||||||
|
/// whose semantics are explained below.
|
||||||
|
///
|
||||||
|
/// The semantics of each access rights for VMARs are described below:
|
||||||
|
/// * The Dup right allows duplicating a VMAR and creating children out of
|
||||||
|
/// a VMAR.
|
||||||
|
/// * The Read, Write, Exec rights allow creating memory mappings with
|
||||||
|
/// readable, writable, and executable access permissions, respectively.
|
||||||
|
/// * The Read and Write rights allow the VMAR to be read from and written to
|
||||||
|
/// directly.
|
||||||
|
///
|
||||||
|
/// VMARs are implemented with two flavors of capabilities:
|
||||||
|
/// the dynamic one (`Vmar<Rights>`) and the static one (`Vmar<R: TRights>).
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// `Vmar` provides high-level APIs for address space management by wrapping
|
||||||
|
/// around its low-level counterpart `kx_frame::vm::VmFrames`.
|
||||||
|
/// Compared with `VmFrames`,
|
||||||
|
/// `Vmar` is easier to use (by offering more powerful APIs) and
|
||||||
|
/// harder to misuse (thanks to its nature of being capability).
|
||||||
|
///
|
||||||
|
pub struct Vmar<R = Rights>(Arc<Vmar_>, R);
|
||||||
|
|
||||||
|
// TODO: how page faults can be delivered to and handled by the current VMAR.
|
||||||
|
|
||||||
|
struct Vmar_ {
|
||||||
|
inner: Mutex<Inner>,
|
||||||
|
// The offset relative to the root VMAR
|
||||||
|
base: Vaddr,
|
||||||
|
parent: Option<Arc<Vmar_>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
is_destroyed: bool,
|
||||||
|
vm_space: VmSpace,
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vmar_ {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy_all(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Vmar<R> {
|
||||||
|
/// The base address, i.e., the offset relative to the root VMAR.
|
||||||
|
///
|
||||||
|
/// The base address of a root VMAR is zero.
|
||||||
|
pub fn base(&self) -> Vaddr {
|
||||||
|
self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_rights(&self, rights: Rights) -> Result<()> {
|
||||||
|
if self.rights.contains(rights) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(EACCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// The memory access permissions of memory mappings.
|
||||||
|
pub struct VmPerms: u32 {
|
||||||
|
/// Readable.
|
||||||
|
const READ: u32 = 1 << 0;
|
||||||
|
/// Writable.
|
||||||
|
const WRITE: u32 = 1 << 1;
|
||||||
|
/// Executable.
|
||||||
|
const EXEC: u32 = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rights> for VmPerms {
|
||||||
|
fn from(perms: VmPerms) -> Rights {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
202
src/kxos-std/src/vm/vmar/options.rs
Normal file
202
src/kxos-std/src/vm/vmar/options.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
//! Options for allocating child VMARs and creating mappings.
|
||||||
|
|
||||||
|
/// Options for allocating a child VMAR, which must not overlap with any
|
||||||
|
/// existing mappings or child VMARs.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A child VMAR created from a parent VMAR of _dynamic_ capability is also a
|
||||||
|
/// _dynamic_ capability.
|
||||||
|
/// ```
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, Vmar};
|
||||||
|
///
|
||||||
|
/// let parent_vmar = Vmar::new();
|
||||||
|
/// let child_size = 10 * PAGE_SIZE;
|
||||||
|
/// let child_vmar = parent_vmar
|
||||||
|
/// .new_child(child_size)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(child_vmar.rights() == parent_vmo.rights());
|
||||||
|
/// assert!(child_vmar.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// A child VMO created from a parent VMO of _static_ capability is also a
|
||||||
|
/// _static_ capability.
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::prelude::*;
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, Vmar};
|
||||||
|
///
|
||||||
|
/// let parent_vmar: Vmar<Full> = Vmar::new();
|
||||||
|
/// let child_size = 10 * PAGE_SIZE;
|
||||||
|
/// let child_vmar = parent_vmar
|
||||||
|
/// .new_child(child_size)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(child_vmar.rights() == parent_vmo.rights());
|
||||||
|
/// assert!(child_vmar.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
pub struct VmarChildOptions<R> {
|
||||||
|
parent: Vmar<R>,
|
||||||
|
size: usize,
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmarChildOptions<R> {
|
||||||
|
/// Creates a default set of options with the specified size of the VMAR
|
||||||
|
/// (in bytes).
|
||||||
|
///
|
||||||
|
/// The size of the VMAR will be rounded up to align with the page size.
|
||||||
|
pub fn new(parent: Vmar<R>, size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
size,
|
||||||
|
offset: 0,
|
||||||
|
align: PAGE_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the alignment of the child VMAR.
|
||||||
|
///
|
||||||
|
/// By default, the alignment is the page size.
|
||||||
|
///
|
||||||
|
/// The alignment must be a power of two and a multiple of the page size.
|
||||||
|
pub fn align(mut self, align: usize) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the offset of the child VMAR.
|
||||||
|
///
|
||||||
|
/// If not set, the system will choose an offset automatically.
|
||||||
|
///
|
||||||
|
/// The offset must satisfy the alignment requirement.
|
||||||
|
/// Also, the child VMAR's range `[offset, offset + size)` must be within
|
||||||
|
/// the VMAR.
|
||||||
|
///
|
||||||
|
/// If not specified,
|
||||||
|
///
|
||||||
|
/// The offset must be page-aligned.
|
||||||
|
pub fn offset(mut self, offset: usize) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates the child VMAR according to the specified options.
|
||||||
|
///
|
||||||
|
/// The new child VMAR
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The child VMAR is initially assigned all the parent's access rights.
|
||||||
|
pub fn alloc(mut self) -> Result<Vmar<R>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for creating a new mapping. The mapping is not allowed to overlap
|
||||||
|
/// with any child VMARs. And unless specified otherwise, it is not allowed
|
||||||
|
/// to overlap with any existing mapping, either.
|
||||||
|
pub struct VmarMapOptions<R> {
|
||||||
|
parent: Vmar<R>,
|
||||||
|
vmo: Vmo,
|
||||||
|
perms: VmPerms,
|
||||||
|
vmo_offset: usize,
|
||||||
|
size: usize,
|
||||||
|
offset: Option<usize>,
|
||||||
|
align: usize,
|
||||||
|
can_overwrite: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmarMapOptions<'a, R> {
|
||||||
|
/// Creates a default set of options with the VMO and the memory access
|
||||||
|
/// permissions.
|
||||||
|
///
|
||||||
|
/// The VMO must have access rights that correspond to the memory
|
||||||
|
/// access permissions. For example, if `perms` contains `VmPerm::Write`,
|
||||||
|
/// then `vmo.rights()` should contain `Rights::WRITE`.
|
||||||
|
pub fn new(parent: Vmar<R>, vmo: Vmo, perms: VmPerms) -> Self {
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
vmo,
|
||||||
|
perms,
|
||||||
|
vmo_offset: 0,
|
||||||
|
size: vmo.size(),
|
||||||
|
offset: None,
|
||||||
|
align: PAGE_SIZE,
|
||||||
|
can_overwrite: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the offset of the first memory page in the VMO that is to be
|
||||||
|
/// mapped into the VMAR.
|
||||||
|
///
|
||||||
|
/// The offset must be page-aligned and within the VMO.
|
||||||
|
///
|
||||||
|
/// The default value is zero.
|
||||||
|
pub fn vmo_offset(mut self, offset: usize) -> Self {
|
||||||
|
self.vmo_offset = offset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the size of the mapping.
|
||||||
|
///
|
||||||
|
/// The size of a mapping may not be equal to that of the VMO.
|
||||||
|
/// For example, it is ok to create a mapping whose size is larger than
|
||||||
|
/// that of the VMO, although one cannot read from or write to the
|
||||||
|
/// part of the mapping that is not backed by the VMO.
|
||||||
|
/// So you may wonder: what is the point of supporting such _oversized_
|
||||||
|
/// mappings? The reason is two-fold.
|
||||||
|
/// 1. VMOs are resizable. So even if a mapping is backed by a VMO whose
|
||||||
|
/// size is equal to that of the mapping initially, we cannot prevent
|
||||||
|
/// the VMO from shrinking.
|
||||||
|
/// 2. Mappings are not allowed to overlap by default. As a result,
|
||||||
|
/// oversized mappings can serve as a placeholder to prevent future
|
||||||
|
/// mappings from occupying some particular address ranges accidentally.
|
||||||
|
///
|
||||||
|
/// The default value is the size of the VMO.
|
||||||
|
pub fn size(mut self, size: usize) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mapping's alignment.
|
||||||
|
///
|
||||||
|
/// The default value is the page size.
|
||||||
|
///
|
||||||
|
/// The provided alignment must be a power of two and a multiple of the
|
||||||
|
/// page size.
|
||||||
|
pub fn align(mut self, align: usize) -> Self {
|
||||||
|
self.align = align;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mapping's offset inside the VMAR.
|
||||||
|
///
|
||||||
|
/// The offset must satisfy the alignment requirement.
|
||||||
|
/// Also, the mapping's range `[offset, offset + size)` must be within
|
||||||
|
/// the VMAR.
|
||||||
|
///
|
||||||
|
/// If not set, the system will choose an offset automatically.
|
||||||
|
pub fn offset(mut self, offset: usize) -> Self {
|
||||||
|
self.offset = offset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether the mapping can overwrite existing mappings.
|
||||||
|
///
|
||||||
|
/// The default value is false.
|
||||||
|
///
|
||||||
|
/// If this option is set to true, then the `offset` option must be
|
||||||
|
/// set.
|
||||||
|
pub fn can_overwrite(mut self, can_overwrite: bool) -> Self {
|
||||||
|
self.can_overwrite = can_overwrite;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the mapping.
|
||||||
|
///
|
||||||
|
/// All options will be checked at this point.
|
||||||
|
///
|
||||||
|
/// On success, the virtual address of the new mapping is returned.
|
||||||
|
pub fn build(mut self) -> Result<Vaddr> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
135
src/kxos-std/src/vm/vmar/static_cap.rs
Normal file
135
src/kxos-std/src/vm/vmar/static_cap.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
impl<R: TRights> Vmar<R> {
|
||||||
|
/// Creates a root VMAR.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// A root VMAR is initially given full access rights.
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let inner = Arc::new(Vmar_::new());
|
||||||
|
let rights = R::new();
|
||||||
|
let new_self = Self(inner, rights);
|
||||||
|
Ok(new_self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps the given VMO into the VMAR through a set of VMAR mapping options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::prelude::*;
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, Vmar, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let vmar = Vmar::<Full>::new().unwrap();
|
||||||
|
/// let vmo = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let target_vaddr = 0x1234000;
|
||||||
|
/// let real_vaddr = vmar
|
||||||
|
/// // Map the VMO to create a read-only mapping
|
||||||
|
/// .new_map(vmo, VmPerms::READ)
|
||||||
|
/// // Provide an optional offset for the mapping inside the VMAR
|
||||||
|
/// .offset(target_vaddr)
|
||||||
|
/// .build()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(real_vaddr == target_vaddr);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmarMapOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the following access rights:
|
||||||
|
/// 1. The VMAR contains the rights corresponding to the memory permissions of
|
||||||
|
/// the mapping. For example, if `perms` contains `VmPerm::WRITE`,
|
||||||
|
/// then the VMAR must have the Write right.
|
||||||
|
/// 2. Similarly, the VMO contains the rights corresponding to the memory
|
||||||
|
/// permissions of the mapping.
|
||||||
|
///
|
||||||
|
/// Memory permissions may be changed through the `protect` method,
|
||||||
|
/// which ensures that any updated memory permissions do not go beyond
|
||||||
|
/// the access rights of the underlying VMOs.
|
||||||
|
pub fn new_map(&self, vmo: Vmo, perms: VmPerms) -> VmarMapOptions<'_, Rights> {
|
||||||
|
let dup_self = self.dup();
|
||||||
|
VmarMapOptions::new(dup_self, vmo_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new child VMAR through a set of VMAR child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = Vmar::new().unwrap();
|
||||||
|
/// let child_size = 10 * PAGE_SIZE;
|
||||||
|
/// let child = parent.new_child(child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmarChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMAR child will be of the same capability class and
|
||||||
|
/// access rights as the parent.
|
||||||
|
#[require(R > Dup)]
|
||||||
|
pub fn new_child(&self, size: usize) -> VmarChildOptions<'a, R> {
|
||||||
|
let dup_self = self.dup();
|
||||||
|
VmarChildOptions::new(dup_self, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the permissions of the memory mappings in the specified range.
|
||||||
|
///
|
||||||
|
/// The range's start and end addresses must be page-aligned.
|
||||||
|
/// Also, the range must be completely mapped.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The VMAR must have the rights corresponding to the specified memory
|
||||||
|
/// permissions.
|
||||||
|
///
|
||||||
|
/// The mappings overlapped with the specified range must be backed by
|
||||||
|
/// VMOs whose rights contain the rights corresponding to the specified
|
||||||
|
/// memory permissions.
|
||||||
|
pub fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
|
self.check_rights(perms.into())?;
|
||||||
|
self.0.protect(perms, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy a VMAR, including all its mappings and children VMARs.
|
||||||
|
///
|
||||||
|
/// After being destroyed, the VMAR becomes useless and returns errors
|
||||||
|
/// for most of its methods.
|
||||||
|
pub fn destroy_all(&self) -> Result<()> {
|
||||||
|
self.0.destroy_all()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy all mappings and children VMARs that fall within the specified
|
||||||
|
/// range in bytes.
|
||||||
|
///
|
||||||
|
/// The range's start and end addresses must be page-aligned.
|
||||||
|
///
|
||||||
|
/// Mappings may fall partially within the range; only the overlapped
|
||||||
|
/// portions of the mappings are unmapped.
|
||||||
|
/// As for children VMARs, they must be fully within the range.
|
||||||
|
/// All children VMARs that fall within the range get their `destroy` methods
|
||||||
|
/// called.
|
||||||
|
pub fn destroy(&self, range: &Range<usize>) -> Result<()> {
|
||||||
|
self.0.destroy(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the access rights.
|
||||||
|
pub const fn rights(&self) -> Rights {
|
||||||
|
R::BITS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmIo for Vmar<Rights> {
|
||||||
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
self.check_rights!(Rights::READ)?;
|
||||||
|
self.0.read(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
self.check_rights!(Rights::WRITE)?;
|
||||||
|
self.0.write(offset, buf)
|
||||||
|
}
|
||||||
|
}
|
140
src/kxos-std/src/vm/vmo/dyn_cap.rs
Normal file
140
src/kxos-std/src/vm/vmo/dyn_cap.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
impl Vmo<Rights> {
|
||||||
|
/// Creates a new slice VMO through a set of VMO child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let child_size = parent.size();
|
||||||
|
/// let child = parent.new_slice_child(0..child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmoChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMO child will be of the same capability flavor as the parent;
|
||||||
|
/// so are the access rights.
|
||||||
|
pub fn new_slice_child(&self, range: Range<usize>) -> VmoChildOptions<'_, Rights, VmoSliceChild> {
|
||||||
|
let dup_self = self.dup()?;
|
||||||
|
VmoChildOptions::new_slice(dup_self, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new COW VMO through a set of VMO child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let child_size = 2 * parent.size();
|
||||||
|
/// let child = parent.new_cow_child(0..child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmoChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMO child will be of the same capability flavor as the parent.
|
||||||
|
/// The child will be given the access rights of the parent
|
||||||
|
/// plus the Write right.
|
||||||
|
pub fn new_cow_child(&self, range: Range<usize>) -> VmoChildOptions<'_, Rights, VmoCowChild> {
|
||||||
|
let dup_self = self.dup()?;
|
||||||
|
VmoChildOptions::new_cow(dup_self, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commits the pages specified in the range (in bytes).
|
||||||
|
///
|
||||||
|
/// The range must be within the size of the VMO.
|
||||||
|
///
|
||||||
|
/// The start and end addresses will be rounded down and up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.commit(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decommits the pages specified in the range (in bytes).
|
||||||
|
///
|
||||||
|
/// The range must be within the size of the VMO.
|
||||||
|
///
|
||||||
|
/// The start and end addresses will be rounded down and up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.decommit(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resizes the VMO by giving a new size.
|
||||||
|
///
|
||||||
|
/// The VMO must be resizable.
|
||||||
|
///
|
||||||
|
/// The new size will be rounded up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
pub fn resize(&self, new_size: usize) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.resize(new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the specified range by writing zeros.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
pub fn clear(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.clear(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Duplicates the capability.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Dup right.
|
||||||
|
pub fn dup(&self) -> Result<Self> {
|
||||||
|
self.check_rights(Rights::DUP)?;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restricts the access rights given the mask.
|
||||||
|
pub fn restrict(mut self, mask: Rights) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a static capability.
|
||||||
|
pub fn to_static<R1: TRights>(self) -> Result<Vmo<R1>> {
|
||||||
|
self.check_rights(R1::BITS)?;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the access rights.
|
||||||
|
pub fn rights(&self) -> Rights {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmIo for Vmo<Rights> {
|
||||||
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
self.check_rights(Rights::READ)?;
|
||||||
|
self.0.read(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.write(offset, buf)
|
||||||
|
}
|
||||||
|
}
|
170
src/kxos-std/src/vm/vmo/mod.rs
Normal file
170
src/kxos-std/src/vm/vmo/mod.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
//! Virtual Memory Objects (VMOs).
|
||||||
|
|
||||||
|
use kx_frame::vm::VmIo;
|
||||||
|
|
||||||
|
use crate::rights::{Rights, TRights};
|
||||||
|
|
||||||
|
mod static_cap;
|
||||||
|
mod dyn_cap;
|
||||||
|
mod options;
|
||||||
|
mod pager;
|
||||||
|
|
||||||
|
pub use options::{VmoOptions, VmoChildOptions};
|
||||||
|
pub use pager::Pager;
|
||||||
|
|
||||||
|
/// Virtual Memory Objects (VMOs) are a type of capability that represents a
|
||||||
|
/// range of memory pages.
|
||||||
|
///
|
||||||
|
/// # Features
|
||||||
|
///
|
||||||
|
/// * **I/O interface.** A VMO provides read and write methods to access the
|
||||||
|
/// memory pages that it contain.
|
||||||
|
/// * **On-demand paging.** The memory pages of a VMO (except for _contiguous_
|
||||||
|
/// VMOs) are allocated lazily when the page is first accessed.
|
||||||
|
/// * **Tree structure.** Given a VMO, one can create a child VMO from it.
|
||||||
|
/// The child VMO can only access a subset of the parent's memory,
|
||||||
|
/// which is a good thing for the perspective of access control.
|
||||||
|
/// * **Copy-on-write (COW).** A child VMO may be created with COW semantics,
|
||||||
|
/// which prevents any writes on the child from affecting the parent
|
||||||
|
/// by duplicating memory pages only upon the first writes.
|
||||||
|
/// * **Access control.** As capabilities, VMOs restrict the
|
||||||
|
/// accessible range of memory and the allowed I/O operations.
|
||||||
|
/// * **Device driver support.** If specified upon creation, VMOs will be
|
||||||
|
/// backed by physically contiguous memory pages starting at a target address.
|
||||||
|
/// * **File system support.** By default, a VMO's memory pages are initially
|
||||||
|
/// all zeros. But if a VMO is attached to a pager (`Pager`) upon creation,
|
||||||
|
/// then its memory pages will be populated by the pager.
|
||||||
|
/// With this pager mechanism, file systems can easily implement page caches
|
||||||
|
/// with VMOs by attaching the VMOs to pagers backed by inodes.
|
||||||
|
///
|
||||||
|
/// # Capabilities
|
||||||
|
///
|
||||||
|
/// As a capability, each VMO is associated with a set of access rights,
|
||||||
|
/// whose semantics are explained below.
|
||||||
|
///
|
||||||
|
/// * The Dup right allows duplicating a VMO and creating children out of
|
||||||
|
/// a VMO.
|
||||||
|
/// * The Read, Write, Exec rights allow creating memory mappings with
|
||||||
|
/// readable, writable, and executable access permissions, respectively.
|
||||||
|
/// * The Read and Write rights allow the VMO to be read from and written to
|
||||||
|
/// directly.
|
||||||
|
/// * The Write right allows resizing a resizable VMO.
|
||||||
|
///
|
||||||
|
/// VMOs are implemented with two flavors of capabilities:
|
||||||
|
/// the dynamic one (`Vmo<Rights>`) and the static one (`Vmo<R: TRights>).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// For creating root VMOs, see `VmoOptions`.`
|
||||||
|
///
|
||||||
|
/// For creating child VMOs, see `VmoChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// `Vmo` provides high-level APIs for address space management by wrapping
|
||||||
|
/// around its low-level counterpart `kx_frame::vm::VmFrames`.
|
||||||
|
/// Compared with `VmFrames`,
|
||||||
|
/// `Vmo` is easier to use (by offering more powerful APIs) and
|
||||||
|
/// harder to misuse (thanks to its nature of being capability).
|
||||||
|
///
|
||||||
|
pub struct Vmo<R>(Arc<Vmo_>, R);
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// VMO flags.
|
||||||
|
pub struct VmoFlags: u32 {
|
||||||
|
/// Set this flag if a VMO is resizable.
|
||||||
|
const RESIZABLE: u32 = 1 << 0;
|
||||||
|
/// Set this flags if a VMO is backed by physically contiguous memory
|
||||||
|
/// pages.
|
||||||
|
///
|
||||||
|
/// To ensure the memory pages to be contiguous, these pages
|
||||||
|
/// are allocated upon the creation of the VMO, rather than on demands.
|
||||||
|
const CONTIGUOUS: u32 = 1 << 1;
|
||||||
|
/// Set this flag if a VMO is backed by memory pages that supports
|
||||||
|
/// Direct Memory Access (DMA) by devices.
|
||||||
|
const DMA: u32 = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vmo_ {
|
||||||
|
flags: VmoFlags,
|
||||||
|
inner: Mutex<VmoInner>,
|
||||||
|
parent: Option<Arc<Vmo_>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VmoInner {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vmo_ {
|
||||||
|
pub fn commit_page(&self, offset: usize) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decommit_page(&self, offset: usize) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.0.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&self, new_size: usize) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paddr(&self) -> Option<Paddr> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(&self) -> VmoFlags {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<R> Vmo<R> {
|
||||||
|
/// Returns the size (in bytes) of a VMO.
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.0.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the starting physical address of a VMO, if it is contiguous.
|
||||||
|
/// Otherwise, returns none.
|
||||||
|
pub fn paddr(&self) -> Option<Paddr> {
|
||||||
|
self.0.paddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the flags of a VMO.
|
||||||
|
pub fn flags(&self) -> VmoFlags {
|
||||||
|
self.0.flags()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_rights(&self, rights: Rights) -> Result<()> {
|
||||||
|
if self.rights().contains(rights) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::AccessDenied)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
303
src/kxos-std/src/vm/vmo/options.rs
Normal file
303
src/kxos-std/src/vm/vmo/options.rs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
//! Options for allocating root and child VMOs.
|
||||||
|
|
||||||
|
/// Options for allocating a root VMO.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Creating a VMO as a _dynamic_ capability with full access rights:
|
||||||
|
/// ```
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let vmo = VmoOptions::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Creating a VMO as a _static_ capability with all access rights:
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::prelude::*;
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let vmo = VmoOptions::<Full>::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Creating a resizable VMO backed by 10 memory pages that may not be
|
||||||
|
/// physically contiguous:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, VmoOptions, VmoFlags};
|
||||||
|
///
|
||||||
|
/// let vmo = VmoOptions::new(10 * PAGE_SIZE)
|
||||||
|
/// .flags(VmoFlags::RESIZABLE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// ```
|
||||||
|
pub struct VmoOptions<R = Rights> {
|
||||||
|
size: usize,
|
||||||
|
paddr: Option<Paddr>,
|
||||||
|
flags: VmoFlags,
|
||||||
|
supplier: Option<Arc<dyn FrameSupplier>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmoOptions<R> {
|
||||||
|
/// Creates a default set of options with the specified size of the VMO
|
||||||
|
/// (in bytes).
|
||||||
|
///
|
||||||
|
/// The size of the VMO will be rounded up to align with the page size.
|
||||||
|
pub fn new(size: usize) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the starting physical address of the VMO.
|
||||||
|
///
|
||||||
|
/// By default, this option is not set.
|
||||||
|
///
|
||||||
|
/// If this option is set, then the underlying pages of VMO must be contiguous.
|
||||||
|
/// So `VmoFlags::IS_CONTIGUOUS` will be set automatically.
|
||||||
|
pub fn paddr(mut self, paddr: Paddr) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the VMO flags.
|
||||||
|
///
|
||||||
|
/// The default value is `VmoFlags::empty()`.
|
||||||
|
///
|
||||||
|
/// For more information about the flags, see `VmoFlags`.
|
||||||
|
pub fn flags(mut self, flags: VmoFlags) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pager of the VMO.
|
||||||
|
pub fn pager(mut self, pager: Arc<dyn Pager>) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmoOptions<Rights> {
|
||||||
|
/// Allocates the VMO according to the specified options.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The VMO is initially assigned full access rights.
|
||||||
|
pub fn alloc(mut self) -> Result<Vmo<R>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TRights> VmoOptions<R> {
|
||||||
|
/// Allocates the VMO according to the specified options.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The VMO is initially assigned the access rights represented
|
||||||
|
/// by `R: TRights`.
|
||||||
|
pub fn alloc(mut self) -> Result<Vmo<R>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for allocating a child VMO out of a parent VMO.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A child VMO created from a parent VMO of _dynamic_ capability is also a
|
||||||
|
/// _dynamic_ capability.
|
||||||
|
/// ```
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// let child_vmo = parent_vmo.new_slice_child(0..PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(parent_vmo.rights() == child_vmo.rights());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// A child VMO created from a parent VMO of _static_ capability is also a
|
||||||
|
/// _static_ capability.
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::prelude::*;
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, VmoOptions, VmoChildOptions};
|
||||||
|
///
|
||||||
|
/// let parent_vmo: Vmo<Full> = VmoOptions::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// let child_vmo: Vmo<Full> = parent_vmo.new_slice_child(0..PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(parent_vmo.rights() == child_vmo.rights());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Normally, a child VMO is initially given the same set of access rights
|
||||||
|
/// as its parent (as shown above). But there is one exception:
|
||||||
|
/// if the child VMO is created as a COW child, then it is granted the Write
|
||||||
|
/// right regardless of whether the parent is writable or not.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions, VmoChildOptions};
|
||||||
|
///
|
||||||
|
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap()
|
||||||
|
/// .restrict(Rights::DUP | Rights::READ);
|
||||||
|
/// let child_vmo = parent_vmo.new_cow_child(0..PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(child_vmo.rights().contains(Rights::WRITE));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The above rule for COW VMO children also applies to static capabilities.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kxos_std::vm::{PAGE_SIZE, VmoOptions, VmoChildOptions};
|
||||||
|
///
|
||||||
|
/// let parent_vmo = VmoOptions::<TRights![Read, Dup]>::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// let child_vmo = parent_vmo.new_cow_child(0..PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(child_vmo.rights().contains(Rights::WRITE));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// One can set VMO flags for a child VMO. Currently, the only flag that is
|
||||||
|
/// valid when creating VMO children is `VmoFlags::RESIZABLE`.
|
||||||
|
/// Note that a slice VMO child and its parent cannot not be resizable.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
|
||||||
|
///
|
||||||
|
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// let child_vmo = parent_vmo.new_cow_child(0..PAGE_SIZE)
|
||||||
|
/// // Make the child resizable!
|
||||||
|
/// .flags(VmoFlags::RESIZABLE)
|
||||||
|
/// .alloc()
|
||||||
|
/// .unwrap();
|
||||||
|
/// assert!(parent_vmo.rights() == child_vmo.rights());
|
||||||
|
/// ```
|
||||||
|
pub struct VmoChildOptions<R, C> {
|
||||||
|
parent: Vmo<R>,
|
||||||
|
range: Range<usize>,
|
||||||
|
flags: VmoFlags,
|
||||||
|
// Specifies whether the child is a slice or a COW
|
||||||
|
child_marker: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmoChildOptions<R, VmoSliceChild> {
|
||||||
|
/// Creates a default set of options for creating a slice VMO child.
|
||||||
|
///
|
||||||
|
/// A slice child of a VMO, which has direct access to a range of memory
|
||||||
|
/// pages in the parent VMO. In other words, any updates of the parent will
|
||||||
|
/// reflect on the child, and vice versa.
|
||||||
|
///
|
||||||
|
/// The range of a child must be within that of the parent.
|
||||||
|
#[require(R > Dup)]
|
||||||
|
pub fn new_slice(parent: Vmo<R>, range: Range<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags: parent.flags & Self::PARENT_FLAGS_MASK,
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmoChildOptions<R, VmoCowChild> {
|
||||||
|
/// Creates a default set of options for creating a copy-on-write (COW)
|
||||||
|
/// VMO child.
|
||||||
|
///
|
||||||
|
/// A COW VMO child behaves as if all its
|
||||||
|
/// memory pages are copied from the parent VMO upon creation, although
|
||||||
|
/// the copying is done lazily when the parent's memory pages are updated.
|
||||||
|
///
|
||||||
|
/// The range of a child may go beyond that of the parent.
|
||||||
|
/// Any pages that are beyond the parent's range are initially all zeros.
|
||||||
|
pub fn new_cow(parent: Vmo<R>, range: Range<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags: parent.flags & Self::PARENT_FLAGS_MASK,
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, C> VmoChildOptions<R, C> {
|
||||||
|
/// Flags that a VMO child inherits from its parent.
|
||||||
|
pub const PARENT_FLAGS_MASK: VmoFlags = VmoFlags::CONTIGUOUS.bits |
|
||||||
|
VmoFlags::DMA.bits;
|
||||||
|
/// Flags that a VMO child may differ from its parent.
|
||||||
|
pub const CHILD_FLAGS_MASK: VmoFlags = VmoFlags::RESIZABLE.bits;
|
||||||
|
|
||||||
|
/// Sets the VMO flags.
|
||||||
|
///
|
||||||
|
/// Only the flags among `Self::CHILD_FLAGS_MASK` may be set through this
|
||||||
|
/// method.
|
||||||
|
///
|
||||||
|
/// To set `VmoFlags::RESIZABLE`, the child must be COW.
|
||||||
|
///
|
||||||
|
/// The default value is `VmoFlags::empty()`.
|
||||||
|
pub fn flags(mut self, flags: VmoFlags) -> Self {
|
||||||
|
self.flags = flags & Self::CHILD_FLAGS_MASK;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C> VmoChildOptions<'a, Rights, C> {
|
||||||
|
/// Allocates the child VMO.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The child VMO is initially assigned all the parent's access rights.
|
||||||
|
pub fn alloc(mut self) -> Result<Vmo<Rights>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: TRights> VmoChildOptions<'a, R, VmoSliceChild> {
|
||||||
|
/// Allocates the child VMO.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The child VMO is initially assigned all the parent's access rights.
|
||||||
|
pub fn alloc(mut self) -> Result<Vmo<R>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: TRights> VmoChildOptions<'a, R, VmoCowChild> {
|
||||||
|
/// Allocates the child VMO.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The child VMO is initially assigned all the parent's access rights
|
||||||
|
/// plus the Write right.
|
||||||
|
pub fn alloc<R1>(mut self) -> Result<Vmo<R1>>
|
||||||
|
where
|
||||||
|
// TODO: R1 must contain the Write right. To do so at the type level,
|
||||||
|
// we need to implement a type-level operator
|
||||||
|
// (say, `TRightsExtend(L, F)`)
|
||||||
|
// that may extend a list (`L`) of type-level flags with an extra flag `F`.
|
||||||
|
R1: R // TRightsExtend<R, Write>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type to specify the "type" of a child, which is either a slice or a COW.
|
||||||
|
pub trait VmoChildType {}
|
||||||
|
|
||||||
|
/// A type to mark a child is slice.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct VmoSliceChild;
|
||||||
|
impl VmoChildType for VmoSliceChild {};
|
||||||
|
|
||||||
|
/// A type to mark a child is COW.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct VmoCowChild;
|
||||||
|
impl VmoChildType for VmoCowChild {};
|
55
src/kxos-std/src/vm/vmo/pager.rs
Normal file
55
src/kxos-std/src/vm/vmo/pager.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/// Pagers provide frame to a VMO.
|
||||||
|
///
|
||||||
|
/// A `Pager` object can be attached to a VMO. Whenever the
|
||||||
|
/// VMO needs more frames (i.e., on commits), it will turn to the pager,
|
||||||
|
/// which should then provide frames whose data have been initialized properly.
|
||||||
|
/// Any time a frame is updated through the VMO, the VMO will
|
||||||
|
/// notify the attached pager that the frame has been updated.
|
||||||
|
/// Finally, when a frame is no longer needed (i.e., on decommits),
|
||||||
|
/// the frame pager will also be notified.
|
||||||
|
pub trait Pager {
|
||||||
|
/// Ask the pager to provide a frame at a specified offset (in bytes).
|
||||||
|
///
|
||||||
|
/// After a page of a VMO is committed, the VMO shall not call this method
|
||||||
|
/// again until the page is decommitted. But a robust implementation of
|
||||||
|
/// `Pager` should not rely on this behavior for its correctness;
|
||||||
|
/// instead, it should returns the _same_ frame.
|
||||||
|
///
|
||||||
|
/// If a VMO page has been previously committed and decommited,
|
||||||
|
/// and is to be committed again, then the pager is free to return
|
||||||
|
/// whatever frame that may or may not be the same as the last time.
|
||||||
|
///
|
||||||
|
/// It is up to the pager to decide the range of valid offsets.
|
||||||
|
///
|
||||||
|
/// The offset will be rounded down to page boundary.
|
||||||
|
fn commit_page(&self, offset: usize) -> Result<VmFrame>;
|
||||||
|
|
||||||
|
/// Notify the pager that the frame at a specified offset (in bytes)
|
||||||
|
/// has been updated.
|
||||||
|
///
|
||||||
|
/// Being aware of the updates allow the pager (e.g., an inode) to
|
||||||
|
/// know which pages are dirty and only write back the _dirty_ pages back
|
||||||
|
/// to disk.
|
||||||
|
///
|
||||||
|
/// The VMO will not call this method for an uncommitted page.
|
||||||
|
/// But a robust implementation of `Pager` should not make
|
||||||
|
/// such an assumption for its correctness; instead, it should simply ignore the
|
||||||
|
/// call or return an error.
|
||||||
|
///
|
||||||
|
/// The offset will be rounded down to page boundary.
|
||||||
|
fn update_page(&self, offset: usize) -> Result<()>;
|
||||||
|
|
||||||
|
/// Notify the pager that the frame at the specified offset (in bytes)
|
||||||
|
/// has been decommitted.
|
||||||
|
///
|
||||||
|
/// Knowing that a frame is no longer needed, the pager (e.g., an inode)
|
||||||
|
/// can free the frame after writing back its data to the disk.
|
||||||
|
///
|
||||||
|
/// The VMO will not call this method for an uncommitted page.
|
||||||
|
/// But a robust implementation of `Pager` should not make
|
||||||
|
/// such an assumption for its correctness; instead, it should simply ignore the
|
||||||
|
/// call or return an error.
|
||||||
|
///
|
||||||
|
/// The offset will be rounded down to page boundary.
|
||||||
|
fn decommit_page(&self, offset: usize) -> Result<()>;
|
||||||
|
}
|
147
src/kxos-std/src/vm/vmo/static_cap.rs
Normal file
147
src/kxos-std/src/vm/vmo/static_cap.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
impl<R: TRights> Vmo<R> {
|
||||||
|
/// Creates a new slice VMO through a set of VMO child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let child_size = parent.size();
|
||||||
|
/// let child = parent.new_slice_child(0..child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmoChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMO child will be of the same capability flavor as the parent;
|
||||||
|
/// so are the access rights.
|
||||||
|
#[require(R > Dup)]
|
||||||
|
pub fn new_slice_child(&self, range: Range<usize>) -> VmoChildOptions<'_, R, VmoSliceChild> {
|
||||||
|
let dup_self = self.dup();
|
||||||
|
VmoChildOptions::new_slice(dup_self, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new COW VMO through a set of VMO child options.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let parent = VmoOptions::new(PAGE_SIZE).alloc().unwrap();
|
||||||
|
/// let child_size = 2 * parent.size();
|
||||||
|
/// let child = parent.new_cow_child(0..child_size).alloc().unwrap();
|
||||||
|
/// assert!(child.size() == child_size);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For more details on the available options, see `VmoChildOptions`.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// This method requires the Dup right.
|
||||||
|
///
|
||||||
|
/// The new VMO child will be of the same capability flavor as the parent.
|
||||||
|
/// The child will be given the access rights of the parent
|
||||||
|
/// plus the Write right.
|
||||||
|
#[require(R > Dup)]
|
||||||
|
pub fn new_cow_child(&self, range: Range<usize>) -> VmoChildOptions<'_, R, VmoCowChild> {
|
||||||
|
let dup_self = self.dup();
|
||||||
|
VmoChildOptions::new_cow(dup_self, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit the pages specified in the range (in bytes).
|
||||||
|
///
|
||||||
|
/// The range must be within the size of the VMO.
|
||||||
|
///
|
||||||
|
/// The start and end addresses will be rounded down and up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
#[require(R > Write)]
|
||||||
|
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.0.commit(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decommit the pages specified in the range (in bytes).
|
||||||
|
///
|
||||||
|
/// The range must be within the size of the VMO.
|
||||||
|
///
|
||||||
|
/// The start and end addresses will be rounded down and up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
#[require(R > Write)]
|
||||||
|
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.0.decommit(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resize the VMO by giving a new size.
|
||||||
|
///
|
||||||
|
/// The VMO must be resizable.
|
||||||
|
///
|
||||||
|
/// The new size will be rounded up to page boundaries.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
#[require(R > Write)]
|
||||||
|
pub fn resize(&self, new_size: usize) -> Result<()> {
|
||||||
|
self.0.resize(new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the specified range by writing zeros.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Write right.
|
||||||
|
#[require(R > Write)]
|
||||||
|
pub fn clear(&self, range: Range<usize>) -> Result<()> {
|
||||||
|
self.0.clear(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the VMO in bytes.
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.0.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Duplicate the capability.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The method requires the Dup right.
|
||||||
|
#[require(R > Dup)]
|
||||||
|
pub fn dup(&self) -> Result<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strict the access rights.
|
||||||
|
#[require(R > R1)]
|
||||||
|
pub fn restrict<R1>(mut self) -> Vmo<R1> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a dynamic capability.
|
||||||
|
pub fn to_dyn(self) -> Vmo {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the access rights.
|
||||||
|
pub const fn rights(&self) -> Rights {
|
||||||
|
R::BITS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TRights> VmIo for Vmo<R> {
|
||||||
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
self.check_rights(Rights::READ)?;
|
||||||
|
self.0.read_bytes(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.write_bytes(offset, buf)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user