Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::arch;
9use crate::mem::frame_alloc::FrameAllocator;
10use crate::mem::provider::{Provider, THE_ZERO_FRAME};
11use crate::mem::{
12 AddressRangeExt, PhysicalAddress,
13 frame_alloc::{
14 Frame,
15 frame_list::{Entry, FrameList},
16 },
17};
18use alloc::sync::Arc;
19use anyhow::ensure;
20use core::range::Range;
21use spin::RwLock;
22
23#[derive(Debug)]
24pub enum Vmo {
25 Wired,
26 Phys(PhysVmo),
27 Paged(RwLock<PagedVmo>),
28}
29
30impl Vmo {
31 pub fn new_wired() -> Self {
32 Self::Wired
33 }
34
35 pub fn new_phys(range: Range<PhysicalAddress>) -> Self {
36 Self::Phys(PhysVmo { range })
37 }
38
39 pub fn new_zeroed(frame_alloc: &'static FrameAllocator) -> Self {
40 Self::Paged(RwLock::new(PagedVmo {
41 frames: FrameList::new(),
42 provider: THE_ZERO_FRAME.clone(),
43 frame_alloc,
44 }))
45 }
46
47 pub fn is_valid_offset(&self, offset: usize) -> bool {
48 match self {
49 Vmo::Wired => unreachable!(),
50 Vmo::Phys(vmo) => vmo.is_valid_offset(offset),
51 Vmo::Paged(vmo) => vmo.read().is_valid_offset(offset),
52 }
53 }
54}
55
56#[derive(Debug)]
57pub struct PhysVmo {
58 range: Range<PhysicalAddress>,
59}
60
61impl PhysVmo {
62 pub fn is_valid_offset(&self, offset: usize) -> bool {
63 offset <= self.range.size()
64 }
65
66 pub fn lookup_contiguous(&self, range: Range<usize>) -> crate::Result<Range<PhysicalAddress>> {
67 ensure!(
68 range.start % arch::PAGE_SIZE == 0,
69 "range is not arch::PAGE_SIZE aligned"
70 );
71 let start = self.range.start.checked_add(range.start).unwrap();
72 let end = self.range.start.checked_add(range.end).unwrap();
73
74 ensure!(
75 self.range.start <= start && self.range.end >= end,
76 "requested range {range:?} is out of bounds for {:?}",
77 self.range
78 );
79
80 Ok(Range::from(start..end))
81 }
82}
83
84#[derive(Debug)]
85pub struct PagedVmo {
86 frames: FrameList,
87 provider: Arc<dyn Provider + Send + Sync>,
88 frame_alloc: &'static FrameAllocator,
89}
90
91impl PagedVmo {
92 pub fn is_valid_offset(&self, offset: usize) -> bool {
93 offset <= self.frames.size()
94 }
95
96 pub fn require_owned_frame(&mut self, at_offset: usize) -> crate::Result<&mut Frame> {
97 if let Some(old_frame) = self.frames.get(at_offset) {
98 // we already have a unique frame reference, a write page fault against it shouldn't happen
99 assert!(!old_frame.is_unique());
100
101 tracing::trace!("require_owned_frame for resident frame, allocating new...");
102
103 let mut new_frame = self.frame_alloc.alloc_one_zeroed()?;
104
105 // If `old_frame` is the zero frame we don't need to copy any data around, it's
106 // all zeroes anyway
107 if !Frame::ptr_eq(old_frame, THE_ZERO_FRAME.frame()) {
108 tracing::trace!("performing copy-on-write...");
109 let src = old_frame.as_slice();
110 let dst = Frame::get_mut(&mut new_frame)
111 .expect("newly allocated frame should be unique")
112 .as_mut_slice();
113
114 tracing::trace!(
115 "copying from {:?} to {:?}",
116 src.as_ptr_range(),
117 dst.as_ptr_range()
118 );
119 dst.copy_from_slice(src);
120 }
121
122 let new_frame = self.frames.insert(at_offset, new_frame.clone());
123 Ok(new_frame)
124 } else {
125 let new_frame = self.provider.get_frame(at_offset, true)?;
126 debug_assert!(new_frame.is_unique());
127 let new_frame = self.frames.insert(at_offset, new_frame);
128 Ok(new_frame)
129 }
130 }
131
132 pub fn require_read_frame(&mut self, at_offset: usize) -> crate::Result<&Frame> {
133 let frame = match self.frames.entry(at_offset) {
134 Entry::Occupied(entry) => entry.into_mut(),
135 Entry::Vacant(entry) => {
136 let new_frame = self.provider.get_frame(at_offset, false)?;
137 entry.insert(new_frame)
138 }
139 };
140
141 Ok(frame)
142 }
143
144 pub fn free_frames(&mut self, range: Range<usize>) {
145 let mut c = self.frames.cursor_mut(range.start);
146
147 while c.offset() < range.end {
148 // TODO use `Provider::free_frames` here
149 if let Some(frame) = c.remove() {
150 self.provider.free_frame(frame);
151 }
152
153 c.move_next();
154 }
155 }
156}