Next Generation WASM Microkernel Operating System
at trap_handler 205 lines 6.7 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::wasm::TrapKind; 9use crate::wasm::vm::mmap_vec::MmapVec; 10use crate::wasm::vm::provenance::VmPtr; 11use crate::wasm::vm::{VMFuncRef, VMTableDefinition}; 12use anyhow::anyhow; 13use core::ptr; 14use core::ptr::NonNull; 15use core::range::Range; 16 17#[derive(Debug, Clone, Copy)] 18pub enum TableElement { 19 /// A `funcref`. 20 FuncRef(Option<NonNull<VMFuncRef>>), 21} 22 23#[derive(Copy, Clone, PartialEq, Eq, Debug)] 24pub enum TableElementType { 25 Func, 26 GcRef, 27} 28 29#[derive(Debug)] 30pub struct Table { 31 /// The underlying allocation backing this memory 32 elements: MmapVec<TableElement>, 33 /// The current size of the table. 34 size: usize, 35 /// The optional maximum accessible size, in elements, for this table. 36 maximum: Option<usize>, 37} 38// Safety: The store synchronization protocol ensures this type will only ever be access in a thread-safe way 39unsafe impl Send for Table {} 40// Safety: The store synchronization protocol ensures this type will only ever be access in a thread-safe way 41unsafe impl Sync for Table {} 42 43impl Table { 44 pub(super) fn from_parts(elements: MmapVec<TableElement>, maximum: Option<usize>) -> Self { 45 Self { 46 size: elements.len(), 47 elements, 48 maximum, 49 } 50 } 51 52 pub fn size(&self) -> usize { 53 self.elements.len() 54 } 55 56 pub fn init_func( 57 &mut self, 58 dst: u64, 59 items: impl ExactSizeIterator<Item = Option<NonNull<VMFuncRef>>>, 60 ) -> Result<(), TrapKind> { 61 let dst = usize::try_from(dst).map_err(|_| TrapKind::TableOutOfBounds)?; 62 let elements = self 63 .elements 64 .slice_mut() 65 .get_mut(dst..) 66 .and_then(|s| s.get_mut(..items.len())) 67 .ok_or(TrapKind::TableOutOfBounds)?; 68 69 for (item, slot) in items.zip(elements) { 70 *slot = TableElement::FuncRef(item); 71 } 72 73 Ok(()) 74 } 75 76 pub fn fill(&mut self, dst: u64, val: TableElement, len: u64) -> Result<(), TrapKind> { 77 let start = usize::try_from(dst).map_err(|_| TrapKind::TableOutOfBounds)?; 78 let len = usize::try_from(len).map_err(|_| TrapKind::TableOutOfBounds)?; 79 let end = start.checked_add(len).ok_or(TrapKind::TableOutOfBounds)?; 80 81 if end > self.size() { 82 return Err(TrapKind::TableOutOfBounds); 83 } 84 85 self.elements.slice_mut()[start..end].fill(val); 86 87 Ok(()) 88 } 89 90 pub fn get(&self, index: u64) -> Option<TableElement> { 91 let index = usize::try_from(index).ok()?; 92 self.elements.get(index).copied() 93 } 94 95 pub fn set(&mut self, index: u64, elem: TableElement) -> crate::Result<()> { 96 let index: usize = index.try_into()?; 97 let slot = self 98 .elements 99 .slice_mut() 100 .get_mut(index) 101 .ok_or(anyhow!("table element index out of bounds"))?; 102 *slot = elem; 103 104 Ok(()) 105 } 106 107 pub fn grow(&mut self, delta: u64, init: TableElement) -> Result<Option<usize>, TrapKind> { 108 let old_size = self.size(); 109 110 // Don't try to resize the table if its size isn't changing, just return 111 // success. 112 if delta == 0 { 113 return Ok(Some(old_size)); 114 } 115 116 let delta = usize::try_from(delta).map_err(|_| TrapKind::TableOutOfBounds)?; 117 let new_size = old_size 118 .checked_add(delta) 119 .ok_or(TrapKind::TableOutOfBounds)?; 120 121 // The WebAssembly spec requires failing a `table.grow` request if 122 // it exceeds the declared limits of the table. We may have set lower 123 // limits in the instance allocator as well. 124 if let Some(max) = self.maximum { 125 if new_size > max { 126 return Ok(None); 127 } 128 } 129 130 // we only support static tables that have all their memory reserved (not allocated) upfront 131 // this means resizing is as simple as just updating the size field 132 self.size = new_size; 133 134 self.fill( 135 u64::try_from(old_size).unwrap(), 136 init, 137 u64::try_from(delta).unwrap(), 138 ) 139 .expect("table should not be out of bounds"); 140 141 Ok(Some(old_size)) 142 } 143 144 pub fn copy( 145 dst_table: *mut Self, 146 src_table: *mut Self, 147 dst_index: u64, 148 src_index: u64, 149 len: u64, 150 ) -> Result<(), TrapKind> { 151 // Safety: the table pointers are valid 152 unsafe { 153 let src_index = usize::try_from(src_index).map_err(|_| TrapKind::TableOutOfBounds)?; 154 let dst_index = usize::try_from(dst_index).map_err(|_| TrapKind::TableOutOfBounds)?; 155 let len = usize::try_from(len).map_err(|_| TrapKind::TableOutOfBounds)?; 156 157 if src_index 158 .checked_add(len) 159 .is_none_or(|n| n > (*src_table).size()) 160 || dst_index 161 .checked_add(len) 162 .is_none_or(|m| m > (*dst_table).size()) 163 { 164 return Err(TrapKind::TableOutOfBounds); 165 } 166 167 let src_range = Range::from(src_index..src_index + len); 168 let dst_range = Range::from(dst_index..dst_index + len); 169 170 if ptr::eq(dst_table, src_table) { 171 (*dst_table).copy_elements_within(dst_range, src_range); 172 } else { 173 Self::copy_elements(&mut *dst_table, &*src_table, dst_range, src_range); 174 } 175 176 Ok(()) 177 } 178 } 179 180 fn copy_elements_within(&mut self, dst_range: Range<usize>, src_range: Range<usize>) { 181 self.elements 182 .slice_mut() 183 .copy_within(src_range, dst_range.start); 184 } 185 186 fn copy_elements( 187 dst_table: &mut Self, 188 src_table: &Self, 189 dst_range: Range<usize>, 190 src_range: Range<usize>, 191 ) { 192 // This can only be used when copying between different tables 193 debug_assert!(!ptr::eq(dst_table, src_table)); 194 195 dst_table.elements.slice_mut()[dst_range] 196 .copy_from_slice(&src_table.elements.slice()[src_range]); 197 } 198 199 pub fn as_vmtable_definition(&self) -> VMTableDefinition { 200 VMTableDefinition { 201 base: VmPtr::from(NonNull::new(self.elements.as_ptr().cast_mut().cast()).unwrap()), 202 current_elements: self.elements.len().into(), 203 } 204 } 205}