Next Generation WASM Microkernel Operating System
at trap_handler 304 lines 11 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::address::{PhysicalAddress, VirtualAddress}; 10use crate::mem::frame_alloc::FRAME_ALLOC; 11use alloc::slice; 12use cordyceps::Linked; 13use cordyceps::list; 14use core::marker::PhantomData; 15use core::mem::offset_of; 16use core::ops::Deref; 17use core::ptr::NonNull; 18use core::sync::atomic; 19use core::sync::atomic::{AtomicUsize, Ordering}; 20use core::{fmt, ptr}; 21use static_assertions::assert_impl_all; 22 23/// Soft limit on the amount of references that may be made to a `Frame`. 24const MAX_REFCOUNT: usize = isize::MAX as usize; 25 26/// A thread-safe reference-counted pointer to a frame of physical memory. 27/// 28/// This type is similar to [`alloc::sync::Arc`][1] and provides shared ownership of a [`FrameInfo`] instance 29/// and by extension its associated physical memory. Just like [`Arc`][1] invoking [`clone`][Frame::clone] will 30/// produce a new `Frame` instance, which points to the same [`FrameInfo`] instance as the source `Frame`, while 31/// increasing its reference count. When the last `Frame` is dropped instance is dropped, the underlying 32/// [`FrameInfo`] is marked as *free* and returned to the frame allocator freelists. 33/// 34/// [1]: [alloc::sync::Arc] 35pub struct Frame { 36 ptr: NonNull<FrameInfo>, 37 phantom: PhantomData<FrameInfo>, 38} 39 40pub struct FrameInfo { 41 /// Links to other frames in a freelist either a global `Arena` or cpu-local page cache. 42 links: list::Links<FrameInfo>, 43 /// Number of references to this frame, zero indicates a free frame. 44 refcount: AtomicUsize, 45 /// The physical address of the frame. 46 addr: PhysicalAddress, 47} 48assert_impl_all!(FrameInfo: Send, Sync); 49 50// === Frame === 51 52// Safety: assert_impl_all! above ensures that `FrameInfo` is `Send` 53unsafe impl Send for Frame {} 54 55// Safety: assert_impl_all! above ensures that `FrameInfo` is `Sync` 56unsafe impl Sync for Frame {} 57 58impl Clone for Frame { 59 /// Makes a clone of the `Frame`. 60 /// 61 /// This creates reference to the same `FrameInfo`, increasing the reference count by one. 62 fn clone(&self) -> Self { 63 // Increase the reference count by one. Using relaxed ordering, as knowledge of the 64 // original reference prevents other threads from erroneously deleting 65 // the object. 66 // 67 // Again, restating what the `Arc` implementation quotes from the 68 // [Boost documentation][1]: 69 // 70 // > Increasing the reference counter can always be done with memory_order_relaxed: New 71 // > references to an object can only be formed from an existing 72 // > reference, and passing an existing reference from one thread to 73 // > another must already provide any required synchronization. 74 // 75 // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) 76 let old_size = self.info().refcount.fetch_add(1, Ordering::Relaxed); 77 debug_assert_ne!(old_size, 0); 78 79 // Just like with `Arc` we want to prevent excessive refcounts in the case that we are leaking 80 // `Frame`s somewhere (which we really shouldn't but just in case). Overflowing the refcount 81 // would *really* bad as it would treat the frame as free and potentially cause a use-after-free 82 // scenario. Realistically this branch should never be taken. 83 // 84 // Also worth noting: Just like `Arc`, the refcount could still overflow when in between 85 // the load above and this check some other cpu increased the refcount from `isize::MAX` to 86 // `usize::MAX` but that seems unlikely. The other option, doing the comparison and update in 87 // one conditional atomic operation produces much worse code, so if its good enough for the 88 // standard library, it is good enough for us. 89 assert!(old_size <= MAX_REFCOUNT, "Frame refcount overflow"); 90 91 // Safety: self was valid so it's info ptr is as well 92 unsafe { Self::from_info(self.ptr) } 93 } 94} 95impl Drop for Frame { 96 /// Drops the `Frame`. 97 /// 98 /// This will decrement the reference count. If the reference count reaches zero 99 /// then this frame will be marked as free and returned to the frame allocator. 100 fn drop(&mut self) { 101 if self.info().refcount.fetch_sub(1, Ordering::Release) != 1 { 102 return; 103 } 104 105 // Ensure uses of `FrameInfo` happen before freeing it. 106 // Because it is marked `Release`, the decreasing of the reference count synchronizes 107 // with this `Acquire` fence. This means that use of `FrameInfo` happens before decreasing 108 // the reference count, which happens before this fence, which happens before freeing `FrameInfo`. 109 // 110 // This section of the [Boost documentation][1] as quoted in Rusts `Arc` implementation and 111 // may explain further: 112 // 113 // > It is important to enforce any possible access to the object in one 114 // > thread (through an existing reference) to *happen before* deleting 115 // > the object in a different thread. This is achieved by a "release" 116 // > operation after dropping a reference (any access to the object 117 // > through this reference must obviously happened before), and an 118 // > "acquire" operation before deleting the object. 119 // 120 // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) 121 atomic::fence(Ordering::Acquire); 122 123 self.drop_slow(); 124 } 125} 126 127impl Frame { 128 #[inline] 129 #[must_use] 130 pub fn ptr_eq(this: &Self, other: &Self) -> bool { 131 ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr()) 132 } 133 134 /// Asserts the `Frame` is in a valid state. 135 pub fn assert_valid(&self) { 136 let refcount = self.info().refcount.load(Ordering::Acquire); 137 assert!(refcount > 0); 138 assert!(refcount < MAX_REFCOUNT); 139 self.info().assert_valid(); 140 } 141 142 #[inline] 143 fn info(&self) -> &FrameInfo { 144 // Safety: Through `Clone` and `Drop` we're guaranteed that the FrameInfo remains valid as long as 145 // this Frame is alive, we also know that `FrameInfo` is `Sync` and therefore - analogous to `Arc` - 146 // handing out an immutable reference is fine. 147 // Because it is an immutable reference, safe code can also not move out of `FrameInner`. 148 unsafe { self.ptr.as_ref() } 149 } 150 151 pub(crate) unsafe fn from_free_info(info: NonNull<FrameInfo>) -> Frame { 152 // Safety: caller has to ensure ptr is valid 153 unsafe { 154 let prev_refcount = info.as_ref().refcount.swap(1, Ordering::Acquire); 155 debug_assert_eq!( 156 prev_refcount, 0, 157 "attempted to create Frame from non-free FrameInfo" 158 ); 159 160 Self::from_info(info) 161 } 162 } 163 164 #[inline] 165 unsafe fn from_info(info: NonNull<FrameInfo>) -> Self { 166 Self { 167 ptr: info, 168 phantom: PhantomData, 169 } 170 } 171 172 pub fn get_mut(this: &mut Frame) -> Option<&mut FrameInfo> { 173 if this.is_unique() { 174 // Safety: This unsafety is ok because we're guaranteed that the pointer 175 // returned is the *only* pointer that will ever be returned to T. Our 176 // reference count is guaranteed to be 1 at this point, and we required 177 // the Arc itself to be `mut`, so we're returning the only possible 178 // reference to the inner data. 179 unsafe { Some(Frame::get_mut_unchecked(this)) } 180 } else { 181 None 182 } 183 } 184 185 pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut FrameInfo { 186 // Safety: construction ensures the base ptr is valid 187 unsafe { this.ptr.as_mut() } 188 } 189 190 pub fn is_unique(&self) -> bool { 191 self.refcount() == 1 192 } 193 194 #[inline(never)] 195 fn drop_slow(&mut self) { 196 // TODO if we ever add more fields to FrameInfo we should reset them here 197 198 let alloc = FRAME_ALLOC 199 .get() 200 .expect("cannot access FRAME_ALLOC before it is initialized"); 201 let mut cpu_local_cache = alloc.cpu_local_cache.get().unwrap().borrow_mut(); 202 cpu_local_cache.free_list.push_back(self.ptr); 203 } 204} 205 206impl Deref for Frame { 207 type Target = FrameInfo; 208 209 #[inline] 210 fn deref(&self) -> &FrameInfo { 211 self.info() 212 } 213} 214 215impl fmt::Debug for Frame { 216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 217 // Safety: construction ensures the base ptr is valid 218 fmt::Debug::fmt(unsafe { self.ptr.as_ref() }, f) 219 } 220} 221 222impl fmt::Pointer for Frame { 223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 224 fmt::Pointer::fmt(&self.ptr, f) 225 } 226} 227 228// === FrameInfo === 229 230impl FrameInfo { 231 /// Private constructor for use in `frame_alloc/arena.rs` 232 pub(crate) fn new(addr: PhysicalAddress) -> Self { 233 Self { 234 links: list::Links::default(), 235 addr, 236 refcount: AtomicUsize::new(0), 237 } 238 } 239 240 /// The physical address of this frame. 241 pub fn addr(&self) -> PhysicalAddress { 242 self.addr 243 } 244 245 /// Gets the number of references to this frame. 246 #[inline] 247 #[must_use] 248 pub fn refcount(&self) -> usize { 249 self.refcount.load(Ordering::Relaxed) 250 } 251 252 /// Returns a slice of the corresponding physical memory 253 #[inline] 254 pub fn as_slice(&self) -> &[u8] { 255 let base = VirtualAddress::from_phys(self.addr).unwrap().as_ptr(); 256 // Safety: construction ensures the base ptr is valid 257 unsafe { slice::from_raw_parts(base, arch::PAGE_SIZE) } 258 } 259 260 /// Returns a mutable slice of the corresponding physical memory 261 #[inline] 262 pub fn as_mut_slice(&mut self) -> &mut [u8] { 263 let base = VirtualAddress::from_phys(self.addr).unwrap().as_mut_ptr(); 264 // Safety: construction ensures the base ptr is valid 265 unsafe { slice::from_raw_parts_mut(base, arch::PAGE_SIZE) } 266 } 267 268 #[inline] 269 pub fn assert_valid(&self) { 270 // TODO add asserts here as we add more fields 271 } 272} 273 274/// Implement the cordyceps [Linked] trait so that [FrameInfo] can be used 275/// with coryceps linked [List]. 276// Safety: unsafe trait 277unsafe impl Linked<list::Links<FrameInfo>> for FrameInfo { 278 type Handle = NonNull<FrameInfo>; 279 280 fn into_ptr(r: Self::Handle) -> NonNull<Self> { 281 r 282 } 283 284 unsafe fn from_ptr(ptr: NonNull<Self>) -> Self::Handle { 285 ptr 286 } 287 288 unsafe fn links(ptr: NonNull<Self>) -> NonNull<list::Links<Self>> { 289 ptr.map_addr(|addr| { 290 let offset = offset_of!(Self, links); 291 addr.checked_add(offset).unwrap() 292 }) 293 .cast() 294 } 295} 296 297impl fmt::Debug for FrameInfo { 298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 299 f.debug_struct("FrameInfo") 300 .field("refcount", &self.refcount) 301 .field("addr", &self.addr) 302 .finish_non_exhaustive() 303 } 304}