mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add ktest cases for vmspace
This commit is contained in:
parent
52e0776591
commit
6c0827b681
@ -8,7 +8,10 @@ use ostd_pod::Pod;
|
|||||||
use crate::{
|
use crate::{
|
||||||
mm::{
|
mm::{
|
||||||
io::{VmIo, VmReader, VmWriter},
|
io::{VmIo, VmReader, VmWriter},
|
||||||
FallibleVmRead, FallibleVmWrite, FrameAllocOptions,
|
tlb::TlbFlushOp,
|
||||||
|
vm_space::{get_activated_vm_space, VmItem, VmSpaceClearError},
|
||||||
|
CachePolicy, FallibleVmRead, FallibleVmWrite, FrameAllocOptions, PageFlags, PageProperty,
|
||||||
|
UFrame, VmSpace,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
Error,
|
Error,
|
||||||
@ -489,3 +492,439 @@ mod io {
|
|||||||
assert_eq!(read_buffer, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
|
assert_eq!(read_buffer, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod vmspace {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Helper function to create a dummy `UFrame`.
|
||||||
|
fn create_dummy_frame() -> UFrame {
|
||||||
|
let frame = FrameAllocOptions::new().alloc_frame().unwrap();
|
||||||
|
let uframe: UFrame = frame.into();
|
||||||
|
uframe
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `VmSpace` and verifies its initial state.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_creation() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x0..0x1000;
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(),
|
||||||
|
Some(VmItem::NotMapped { va: 0, len: 0x1000 })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps and unmaps a single page using `CursorMut`.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_map_unmap() {
|
||||||
|
let vmspace = VmSpace::default();
|
||||||
|
let range = 0x1000..0x2000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
// Initially, the page should not be mapped.
|
||||||
|
assert_eq!(
|
||||||
|
cursor_mut.query().unwrap(),
|
||||||
|
VmItem::NotMapped {
|
||||||
|
va: range.start,
|
||||||
|
len: range.start + 0x1000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Maps a frame.
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries the mapping.
|
||||||
|
{
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(cursor.virt_addr(), range.start);
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::Mapped {
|
||||||
|
va: range.start,
|
||||||
|
frame,
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
// Unmaps the frame.
|
||||||
|
cursor_mut.unmap(range.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries to ensure it's unmapped.
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::NotMapped {
|
||||||
|
va: range.start,
|
||||||
|
len: range.start + 0x1000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps a page twice and unmaps twice using `CursorMut`.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_map_twice() {
|
||||||
|
let vmspace = VmSpace::default();
|
||||||
|
let range = 0x1000..0x2000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::Mapped {
|
||||||
|
va: range.start,
|
||||||
|
frame: frame.clone(),
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::Mapped {
|
||||||
|
va: range.start,
|
||||||
|
frame,
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.unmap(range.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::NotMapped {
|
||||||
|
va: range.start,
|
||||||
|
len: range.start + 0x1000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmaps twice using `CursorMut`.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_unmap_twice() {
|
||||||
|
let vmspace = VmSpace::default();
|
||||||
|
let range = 0x1000..0x2000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.unmap(range.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.unmap(range.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.query().unwrap(),
|
||||||
|
VmItem::NotMapped {
|
||||||
|
va: range.start,
|
||||||
|
len: range.start + 0x1000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the `VmSpace`.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_clear() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x2000..0x3000;
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
cursor_mut.map(frame, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the VmSpace.
|
||||||
|
assert!(vmspace.clear().is_ok());
|
||||||
|
|
||||||
|
// Verifies that the mapping is cleared.
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(),
|
||||||
|
Some(VmItem::NotMapped {
|
||||||
|
va: range.start,
|
||||||
|
len: range.start + 0x1000
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that `VmSpace::clear` returns an error when cursors are active.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_clear_with_alive_cursors() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x3000..0x4000;
|
||||||
|
let _cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
|
||||||
|
// Attempts to clear the VmSpace while a cursor is active.
|
||||||
|
let result = vmspace.clear();
|
||||||
|
assert!(matches!(result, Err(VmSpaceClearError::CursorsAlive)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Activates and deactivates the `VmSpace` in single-CPU scenarios.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_activate() {
|
||||||
|
let vmspace = Arc::new(VmSpace::new());
|
||||||
|
|
||||||
|
// Activates the VmSpace.
|
||||||
|
vmspace.activate();
|
||||||
|
assert_eq!(get_activated_vm_space().unwrap(), Arc::as_ptr(&vmspace));
|
||||||
|
|
||||||
|
// Deactivates the VmSpace.
|
||||||
|
let vmspace2 = Arc::new(VmSpace::new());
|
||||||
|
vmspace2.activate();
|
||||||
|
assert_eq!(get_activated_vm_space().unwrap(), Arc::as_ptr(&vmspace2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests the `flusher` method of `CursorMut`.
|
||||||
|
#[ktest]
|
||||||
|
fn cursor_mut_flusher() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x4000..0x5000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Verifies that the mapping exists.
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(),
|
||||||
|
Some(VmItem::Mapped {
|
||||||
|
va: 0x4000,
|
||||||
|
frame: frame.clone(),
|
||||||
|
prop: PageProperty::new(PageFlags::R, CachePolicy::Writeback),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Flushes the TLB using a mutable cursor.
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.flusher().issue_tlb_flush(TlbFlushOp::All);
|
||||||
|
cursor_mut.flusher().dispatch_tlb_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Verifies that the mapping still exists.
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(),
|
||||||
|
Some(VmItem::Mapped {
|
||||||
|
va: 0x4000,
|
||||||
|
frame,
|
||||||
|
prop: PageProperty::new(PageFlags::R, CachePolicy::Writeback),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies the `VmReader` and `VmWriter` interfaces.
|
||||||
|
#[ktest]
|
||||||
|
fn vmspace_reader_writer() {
|
||||||
|
let vmspace = Arc::new(VmSpace::new());
|
||||||
|
let range = 0x4000..0x5000;
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
cursor_mut.map(frame, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mocks the current page table paddr to match the VmSpace's root paddr.
|
||||||
|
// Fails if the VmSpace is not the current task's user space.
|
||||||
|
|
||||||
|
// Attempts to create a reader.
|
||||||
|
let reader_result = vmspace.reader(0x4000, 0x1000);
|
||||||
|
// Expects failure in a test environment.
|
||||||
|
assert!(reader_result.is_err());
|
||||||
|
|
||||||
|
// Attempts to create a writer.
|
||||||
|
let writer_result = vmspace.writer(0x4000, 0x1000);
|
||||||
|
assert!(writer_result.is_err());
|
||||||
|
|
||||||
|
// Activates the VmSpace.
|
||||||
|
vmspace.activate();
|
||||||
|
|
||||||
|
// Attempts to create a reader.
|
||||||
|
let reader_result = vmspace.reader(0x4000, 0x1000);
|
||||||
|
assert!(reader_result.is_ok());
|
||||||
|
// Attempts to create a writer.
|
||||||
|
let writer_result = vmspace.writer(0x4000, 0x1000);
|
||||||
|
assert!(writer_result.is_ok());
|
||||||
|
|
||||||
|
// Attempts to create a reader with an out-of-range address.
|
||||||
|
let reader_result = vmspace.reader(0x4000, usize::MAX);
|
||||||
|
assert!(reader_result.is_err());
|
||||||
|
// Attempts to create a writer with an out-of-range address.
|
||||||
|
let writer_result = vmspace.writer(0x4000, usize::MAX);
|
||||||
|
assert!(writer_result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates overlapping cursors and verifies handling.
|
||||||
|
#[ktest]
|
||||||
|
fn overlapping_cursors() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range1 = 0x5000..0x6000;
|
||||||
|
let range2 = 0x5800..0x6800; // Overlaps with range1.
|
||||||
|
|
||||||
|
// Creates the first cursor.
|
||||||
|
let _cursor1 = vmspace
|
||||||
|
.cursor(&range1)
|
||||||
|
.expect("Failed to create first cursor");
|
||||||
|
|
||||||
|
// Attempts to create the second overlapping cursor.
|
||||||
|
let cursor2_result = vmspace.cursor(&range2);
|
||||||
|
assert!(cursor2_result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over the `Cursor` using the `Iterator` trait.
|
||||||
|
#[ktest]
|
||||||
|
fn cursor_iterator() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x6000..0x7000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
let prop = PageProperty::new(PageFlags::R, CachePolicy::Writeback);
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert!(cursor.jump(range.start).is_ok());
|
||||||
|
let item = cursor.next();
|
||||||
|
assert_eq!(
|
||||||
|
item,
|
||||||
|
Some(VmItem::Mapped {
|
||||||
|
va: 0x6000,
|
||||||
|
frame,
|
||||||
|
prop: PageProperty::new(PageFlags::R, CachePolicy::Writeback),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Confirms no additional items.
|
||||||
|
assert!(cursor.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Protects a range of pages.
|
||||||
|
#[ktest]
|
||||||
|
fn protect_next() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0x7000..0x8000;
|
||||||
|
let frame = create_dummy_frame();
|
||||||
|
{
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
||||||
|
cursor_mut.map(frame.clone(), prop);
|
||||||
|
cursor_mut.jump(range.start).expect("Failed to jump cursor");
|
||||||
|
let protected_range = cursor_mut.protect_next(0x1000, |prop| {
|
||||||
|
prop.flags = PageFlags::R;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(protected_range, Some(0x7000..0x8000));
|
||||||
|
}
|
||||||
|
// Confirms that the property was updated.
|
||||||
|
let mut cursor = vmspace.cursor(&range).expect("Failed to create cursor");
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(),
|
||||||
|
Some(VmItem::Mapped {
|
||||||
|
va: 0x7000,
|
||||||
|
frame,
|
||||||
|
prop: PageProperty::new(PageFlags::R, CachePolicy::Writeback),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to map unaligned lengths and expects a panic.
|
||||||
|
#[ktest]
|
||||||
|
#[should_panic(expected = "assertion failed: len % super::PAGE_SIZE == 0")]
|
||||||
|
fn unaligned_unmap_panics() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0xA000..0xB000;
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.unmap(0x800); // Not page-aligned.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to protect a partial page and expects a panic.
|
||||||
|
#[ktest]
|
||||||
|
#[should_panic]
|
||||||
|
fn protect_out_range_page() {
|
||||||
|
let vmspace = VmSpace::new();
|
||||||
|
let range = 0xB000..0xC000;
|
||||||
|
let mut cursor_mut = vmspace
|
||||||
|
.cursor_mut(&range)
|
||||||
|
.expect("Failed to create mutable cursor");
|
||||||
|
cursor_mut.protect_next(0x2000, |_| {}); // Not page-aligned.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -462,6 +462,17 @@ cpu_local_cell! {
|
|||||||
static ACTIVATED_VM_SPACE: *const VmSpace = core::ptr::null();
|
static ACTIVATED_VM_SPACE: *const VmSpace = core::ptr::null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(ktest)]
|
||||||
|
pub(crate) fn get_activated_vm_space() -> Option<*const VmSpace> {
|
||||||
|
let ptr = ACTIVATED_VM_SPACE.load();
|
||||||
|
if ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// SAFETY: The pointer is only set to a valid `Arc` pointer.
|
||||||
|
Some(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of a query over the VM space.
|
/// The result of a query over the VM space.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VmItem {
|
pub enum VmItem {
|
||||||
@ -483,6 +494,30 @@ pub enum VmItem {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for VmItem {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
// The `len` varies, so we only compare `va`.
|
||||||
|
(VmItem::NotMapped { va: va1, len: _ }, VmItem::NotMapped { va: va2, len: _ }) => {
|
||||||
|
va1 == va2
|
||||||
|
}
|
||||||
|
(
|
||||||
|
VmItem::Mapped {
|
||||||
|
va: va1,
|
||||||
|
frame: frame1,
|
||||||
|
prop: prop1,
|
||||||
|
},
|
||||||
|
VmItem::Mapped {
|
||||||
|
va: va2,
|
||||||
|
frame: frame2,
|
||||||
|
prop: prop2,
|
||||||
|
},
|
||||||
|
) => va1 == va2 && frame1.start_paddr() == frame2.start_paddr() && prop1 == prop2,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<PageTableItem> for VmItem {
|
impl TryFrom<PageTableItem> for VmItem {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user