Files
asterinas/services/libs/jinux-std/src/vm/vmar/options.rs
2023-09-28 11:53:53 +08:00

215 lines
6.5 KiB
Rust

//! Options for allocating child VMARs.
use jinux_frame::config::PAGE_SIZE;
use jinux_frame::{Error, Result};
use super::Vmar;
/// 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 jinux_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 VMAR created from a parent VMAR of _static_ capability is also a
/// _static_ capability.
/// ```
/// use jinux_std::prelude::*;
/// use jinux_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: Option<usize>,
align: Option<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: None,
align: None,
}
}
/// 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 {
self.align = Some(align);
self
}
/// 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 {
self.offset = Some(offset);
self
}
/// 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(self) -> Result<Vmar<R>> {
// check align
let align = if let Some(align) = self.align {
debug_assert!(align % PAGE_SIZE == 0);
debug_assert!(align.is_power_of_two());
if align % PAGE_SIZE != 0 || !align.is_power_of_two() {
return Err(Error::InvalidArgs);
}
align
} else {
PAGE_SIZE
};
// check size
if self.size % align != 0 {
return Err(Error::InvalidArgs);
}
// check offset
let root_vmar_offset = if let Some(offset) = self.offset {
if offset % PAGE_SIZE != 0 {
return Err(Error::InvalidArgs);
}
let root_vmar_offset = offset + self.parent.base();
if root_vmar_offset % align != 0 {
return Err(Error::InvalidArgs);
}
Some(root_vmar_offset)
} else {
None
};
let child_vmar_ = self
.parent
.0
.alloc_child_vmar(root_vmar_offset, self.size, align)?;
let child_vmar = Vmar(child_vmar_, self.parent.1);
Ok(child_vmar)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::vm::page_fault_handler::PageFaultHandler;
use crate::vm::perms::VmPerms;
use crate::vm::vmo::VmoRightsOp;
use crate::vm::{vmar::ROOT_VMAR_HIGHEST_ADDR, vmo::VmoOptions};
use jinux_frame::vm::VmIo;
use jinux_rights::Full;
#[test]
fn root_vmar() {
let vmar = Vmar::<Full>::new_root();
assert!(vmar.size() == ROOT_VMAR_HIGHEST_ADDR);
}
#[test]
fn child_vmar() {
let root_vmar = Vmar::<Full>::new_root();
let root_vmar_dup = root_vmar.dup().unwrap();
let child_vmar = VmarChildOptions::new(root_vmar_dup, 10 * PAGE_SIZE)
.alloc()
.unwrap();
assert!(child_vmar.size() == 10 * PAGE_SIZE);
let root_vmar_dup = root_vmar.dup().unwrap();
let second_child = VmarChildOptions::new(root_vmar_dup, 9 * PAGE_SIZE)
.alloc()
.unwrap();
let root_vmar_dup = root_vmar.dup().unwrap();
assert!(VmarChildOptions::new(root_vmar_dup, 9 * PAGE_SIZE)
.offset(11 * PAGE_SIZE)
.alloc()
.is_err());
}
#[test]
fn map_vmo() {
let root_vmar = Vmar::<Full>::new_root();
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap().to_dyn();
let perms = VmPerms::READ | VmPerms::WRITE;
let map_offset = 0x1000_0000;
let vmo_dup = vmo.dup().unwrap();
root_vmar
.new_map(vmo_dup, perms)
.unwrap()
.offset(map_offset)
.build()
.unwrap();
root_vmar.write_val(map_offset, &100u8).unwrap();
assert!(root_vmar.read_val::<u8>(map_offset).unwrap() == 100);
let another_map_offset = 0x1100_0000;
let vmo_dup = vmo.dup().unwrap();
root_vmar
.new_map(vmo_dup, perms)
.unwrap()
.offset(another_map_offset)
.build()
.unwrap();
assert!(root_vmar.read_val::<u8>(another_map_offset).unwrap() == 100);
}
#[test]
fn handle_page_fault() {
const OFFSET: usize = 0x1000_0000;
let root_vmar = Vmar::<Full>::new_root();
// the page is not mapped by a vmo
assert!(root_vmar.handle_page_fault(OFFSET, true, true).is_err());
// the page is mapped READ
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap().to_dyn();
let perms = VmPerms::READ;
let vmo_dup = vmo.dup().unwrap();
root_vmar
.new_map(vmo_dup, perms)
.unwrap()
.offset(OFFSET)
.build()
.unwrap();
root_vmar.handle_page_fault(OFFSET, true, false).unwrap();
}
}