Next Generation WASM Microkernel Operating System
at trap_handler 156 lines 4.8 kB view raw
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}