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 core::alloc::{Layout, LayoutError};
10use core::fmt;
11use core::range::Range;
12
13macro_rules! address_impl {
14 ($addr:ident) => {
15 impl $addr {
16 pub const MAX: Self = Self(usize::MAX);
17 pub const MIN: Self = Self(0);
18 pub const BITS: u32 = usize::BITS;
19
20 #[must_use]
21 #[inline]
22 pub const fn checked_add(self, rhs: usize) -> Option<Self> {
23 if let Some(out) = self.0.checked_add(rhs) {
24 Some(Self(out))
25 } else {
26 None
27 }
28 }
29
30 #[must_use]
31 #[inline]
32 pub const fn checked_add_signed(self, rhs: isize) -> Option<Self> {
33 if let Some(out) = self.0.checked_add_signed(rhs) {
34 Some(Self(out))
35 } else {
36 None
37 }
38 }
39
40 #[must_use]
41 #[inline]
42 pub const fn checked_sub(self, rhs: usize) -> Option<Self> {
43 if let Some(out) = self.0.checked_sub(rhs) {
44 Some(Self(out))
45 } else {
46 None
47 }
48 }
49 #[must_use]
50 #[inline]
51 pub const fn checked_div(self, rhs: usize) -> Option<Self> {
52 if let Some(out) = self.0.checked_div(rhs) {
53 Some(Self(out))
54 } else {
55 None
56 }
57 }
58 #[must_use]
59 #[inline]
60 pub const fn checked_mul(self, rhs: usize) -> Option<Self> {
61 if let Some(out) = self.0.checked_mul(rhs) {
62 Some(Self(out))
63 } else {
64 None
65 }
66 }
67 #[must_use]
68 #[inline]
69 pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
70 if let Some(out) = self.0.checked_shl(rhs) {
71 Some(Self(out))
72 } else {
73 None
74 }
75 }
76 #[must_use]
77 #[inline]
78 pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
79 if let Some(out) = self.0.checked_shr(rhs) {
80 Some(Self(out))
81 } else {
82 None
83 }
84 }
85 // #[must_use]
86 // #[inline]
87 // pub const fn saturating_add(self, rhs: usize) -> Self {
88 // Self(self.0.saturating_add(rhs))
89 // }
90 // #[must_use]
91 // #[inline]
92 // pub const fn saturating_add_signed(self, rhs: isize) -> Self {
93 // Self(self.0.saturating_add_signed(rhs))
94 // }
95 // #[must_use]
96 // #[inline]
97 // pub const fn saturating_div(self, rhs: usize) -> Self {
98 // Self(self.0.saturating_div(rhs))
99 // }
100 // #[must_use]
101 // #[inline]
102 // pub const fn saturating_sub(self, rhs: usize) -> Self {
103 // Self(self.0.saturating_sub(rhs))
104 // }
105 // #[must_use]
106 // #[inline]
107 // pub const fn saturating_mul(self, rhs: usize) -> Self {
108 // Self(self.0.saturating_mul(rhs))
109 // }
110 #[must_use]
111 #[inline]
112 pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
113 let (a, b) = self.0.overflowing_shl(rhs);
114 (Self(a), b)
115 }
116 #[must_use]
117 #[inline]
118 pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
119 let (a, b) = self.0.overflowing_shr(rhs);
120 (Self(a), b)
121 }
122
123 #[must_use]
124 #[inline]
125 pub const fn checked_sub_addr(self, rhs: Self) -> Option<usize> {
126 self.0.checked_sub(rhs.0)
127 }
128
129 // #[must_use]
130 // #[inline]
131 // pub const fn saturating_sub_addr(self, rhs: Self) -> usize {
132 // self.0.saturating_sub(rhs.0)
133 // }
134
135 #[must_use]
136 #[inline]
137 pub const fn is_aligned_to(&self, align: usize) -> bool {
138 assert!(
139 align.is_power_of_two(),
140 "is_aligned_to: align is not a power-of-two"
141 );
142
143 self.0 & (align - 1) == 0
144 }
145
146 #[must_use]
147 #[inline]
148 pub const fn checked_align_up(self, align: usize) -> Option<Self> {
149 if !align.is_power_of_two() {
150 panic!("checked_align_up: align is not a power-of-two");
151 }
152
153 // SAFETY: `align` has been checked to be a power of 2 above
154 let align_minus_one = unsafe { align.unchecked_sub(1) };
155
156 // addr.wrapping_add(align_minus_one) & 0usize.wrapping_sub(align)
157 if let Some(addr_plus_align) = self.0.checked_add(align_minus_one) {
158 let aligned = Self(addr_plus_align & 0usize.wrapping_sub(align));
159 debug_assert!(aligned.is_aligned_to(align));
160 debug_assert!(aligned.0 >= self.0);
161 Some(aligned)
162 } else {
163 None
164 }
165 }
166
167 // #[must_use]
168 // #[inline]
169 // pub const fn wrapping_align_up(self, align: usize) -> Self {
170 // if !align.is_power_of_two() {
171 // panic!("checked_align_up: align is not a power-of-two");
172 // }
173 //
174 // // SAFETY: `align` has been checked to be a power of 2 above
175 // let align_minus_one = unsafe { align.unchecked_sub(1) };
176 //
177 // // addr.wrapping_add(align_minus_one) & 0usize.wrapping_sub(align)
178 // let out = addr.wrapping_add(align_minus_one) & 0usize.wrapping_sub(align);
179 // debug_assert!(out.is_aligned_to(align));
180 // out
181 // }
182
183 #[inline]
184 pub const fn alignment(&self) -> usize {
185 self.0 & (!self.0 + 1)
186 }
187
188 #[must_use]
189 #[inline]
190 pub const fn align_down(self, align: usize) -> Self {
191 if !align.is_power_of_two() {
192 panic!("checked_align_up: align is not a power-of-two");
193 }
194
195 let aligned = Self(self.0 & 0usize.wrapping_sub(align));
196 debug_assert!(aligned.is_aligned_to(align));
197 debug_assert!(aligned.0 <= self.0);
198 aligned
199 }
200
201 #[inline]
202 pub const fn as_ptr(self) -> *const u8 {
203 self.0 as *const u8
204 }
205 #[inline]
206 pub const fn as_mut_ptr(self) -> *mut u8 {
207 self.0 as *mut u8
208 }
209 #[inline]
210 pub const fn get(self) -> usize {
211 self.0
212 }
213 }
214
215 impl fmt::Display for $addr {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.write_fmt(format_args!("{:#018x}", self.0)) // 18 digits to account for the leading 0x
218 }
219 }
220
221 impl fmt::Debug for $addr {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 f.debug_tuple(stringify!($addr))
224 .field(&format_args!("{:#018x}", self.0)) // 18 digits to account for the leading 0x
225 .finish()
226 }
227 }
228
229 impl core::iter::Step for $addr {
230 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
231 core::iter::Step::steps_between(&start.0, &end.0)
232 }
233
234 fn forward_checked(start: Self, count: usize) -> Option<Self> {
235 core::iter::Step::forward_checked(start.0, count).map(Self)
236 }
237
238 fn forward(start: Self, count: usize) -> Self {
239 Self(core::iter::Step::forward(start.0, count))
240 }
241
242 unsafe fn forward_unchecked(start: Self, count: usize) -> Self {
243 // Safety: checked by the caller
244 Self(unsafe { core::iter::Step::forward_unchecked(start.0, count) })
245 }
246
247 fn backward_checked(start: Self, count: usize) -> Option<Self> {
248 core::iter::Step::backward_checked(start.0, count).map(Self)
249 }
250
251 fn backward(start: Self, count: usize) -> Self {
252 Self(core::iter::Step::backward(start.0, count))
253 }
254
255 unsafe fn backward_unchecked(start: Self, count: usize) -> Self {
256 // Safety: checked by the caller
257 Self(unsafe { core::iter::Step::backward_unchecked(start.0, count) })
258 }
259 }
260 };
261}
262
263macro_rules! address_range_impl {
264 () => {
265 fn size(&self) -> usize {
266 debug_assert!(self.start <= self.end);
267 let is = self.end.checked_sub_addr(self.start).unwrap_or_default();
268 let should = if self.is_empty() {
269 0
270 } else {
271 self.end.get() - self.start.get()
272 };
273 debug_assert_eq!(is, should);
274 is
275 }
276 fn checked_add(self, offset: usize) -> Option<Self> {
277 Some(Range::from(
278 self.start.checked_add(offset)?..self.end.checked_add(offset)?,
279 ))
280 }
281 fn as_ptr_range(&self) -> Range<*const u8> {
282 Range::from(self.start.as_ptr()..self.end.as_ptr())
283 }
284 fn as_mut_ptr_range(&self) -> Range<*mut u8> {
285 Range::from(self.start.as_mut_ptr()..self.end.as_mut_ptr())
286 }
287 fn checked_align_in(self, align: usize) -> Option<Self>
288 where
289 Self: Sized,
290 {
291 let res = Range::from(self.start.checked_align_up(align)?..self.end.align_down(align));
292 Some(res)
293 }
294 fn checked_align_out(self, align: usize) -> Option<Self>
295 where
296 Self: Sized,
297 {
298 let res = Range::from(self.start.align_down(align)..self.end.checked_align_up(align)?);
299 // aligning outwards can only increase the size
300 debug_assert!(res.start.0 <= res.end.0);
301 Some(res)
302 }
303 // fn saturating_align_in(self, align: usize) -> Self {
304 // self.start.saturating_align_up(align)..self.end.saturating_align_down(align)
305 // }
306 // fn saturating_align_out(self, align: usize) -> Self {
307 // self.start.saturating_align_down(align)..self.end.saturating_align_up(align)
308 // }
309
310 // TODO test
311 fn alignment(&self) -> usize {
312 self.start.alignment()
313 }
314 fn into_layout(self) -> core::result::Result<Layout, LayoutError> {
315 Layout::from_size_align(self.size(), self.alignment())
316 }
317 fn is_overlapping(&self, other: &Self) -> bool {
318 (self.start < other.end) & (other.start < self.end)
319 }
320 fn difference(&self, other: Self) -> (Option<Self>, Option<Self>) {
321 debug_assert!(self.is_overlapping(&other));
322 let a = Range::from(self.start..other.start);
323 let b = Range::from(other.end..self.end);
324 ((!a.is_empty()).then_some(a), (!b.is_empty()).then_some(b))
325 }
326 fn clamp(&self, range: Self) -> Self {
327 Range::from(self.start.max(range.start)..self.end.min(range.end))
328 }
329 };
330}
331
332#[repr(transparent)]
333#[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
334pub struct VirtualAddress(usize);
335address_impl!(VirtualAddress);
336
337impl VirtualAddress {
338 pub const ZERO: Self = Self(0);
339
340 #[must_use]
341 pub const fn new(n: usize) -> Option<Self> {
342 let this = Self(n);
343 if this.is_canonical() {
344 Some(this)
345 } else {
346 None
347 }
348 }
349
350 pub const fn is_canonical(self) -> bool {
351 (self.0 & arch::CANONICAL_ADDRESS_MASK).wrapping_sub(1) >= arch::CANONICAL_ADDRESS_MASK - 1
352 }
353
354 #[must_use]
355 pub fn from_phys(phys: PhysicalAddress) -> Option<VirtualAddress> {
356 arch::KERNEL_ASPACE_RANGE.start.checked_add(phys.0)
357 }
358
359 #[inline]
360 pub const fn is_user_accessible(self) -> bool {
361 // This address refers to userspace if it is in the lower half of the
362 // canonical addresses. IOW - if all of the bits in the canonical address
363 // mask are zero.
364 (self.0 & arch::CANONICAL_ADDRESS_MASK) == 0
365 }
366}
367
368#[repr(transparent)]
369#[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
370pub struct PhysicalAddress(usize);
371address_impl!(PhysicalAddress);
372
373impl PhysicalAddress {
374 #[must_use]
375 pub const fn new(n: usize) -> Self {
376 Self(n)
377 }
378}
379
380pub trait AddressRangeExt {
381 fn size(&self) -> usize;
382 #[must_use]
383 fn checked_add(self, offset: usize) -> Option<Self>
384 where
385 Self: Sized;
386 #[must_use]
387 fn as_ptr_range(&self) -> Range<*const u8>;
388 #[must_use]
389 fn as_mut_ptr_range(&self) -> Range<*mut u8>;
390 #[must_use]
391 fn checked_align_in(self, align: usize) -> Option<Self>
392 where
393 Self: Sized;
394 #[must_use]
395 fn checked_align_out(self, align: usize) -> Option<Self>
396 where
397 Self: Sized;
398 // #[must_use]
399 // fn saturating_align_in(self, align: usize) -> Self;
400 // #[must_use]
401 // fn saturating_align_out(self, align: usize) -> Self;
402 fn alignment(&self) -> usize;
403 fn into_layout(self) -> core::result::Result<Layout, LayoutError>;
404 fn is_user_accessible(&self) -> bool;
405 fn is_overlapping(&self, other: &Self) -> bool;
406 fn difference(&self, other: Self) -> (Option<Self>, Option<Self>)
407 where
408 Self: Sized;
409 fn clamp(&self, range: Self) -> Self;
410}
411
412impl AddressRangeExt for Range<PhysicalAddress> {
413 address_range_impl!();
414 fn is_user_accessible(&self) -> bool {
415 unimplemented!("PhysicalAddress is never user accessible")
416 }
417}
418
419impl AddressRangeExt for Range<VirtualAddress> {
420 address_range_impl!();
421
422 fn is_user_accessible(&self) -> bool {
423 if self.is_empty() {
424 return false;
425 }
426 let Some(end_minus_one) = self.end.checked_sub(1) else {
427 return false;
428 };
429
430 self.start.is_user_accessible() && end_minus_one.is_user_accessible()
431 }
432}
433
434static_assertions::const_assert!(VirtualAddress(0xffffffc000000000).is_aligned_to(4096));
435static_assertions::const_assert_eq!(
436 VirtualAddress(0xffffffc0000156e8).align_down(4096).0,
437 0xffffffc000015000
438);
439static_assertions::const_assert_eq!(
440 VirtualAddress(0xffffffc000000000)
441 .checked_align_up(4096)
442 .unwrap()
443 .0,
444 0xffffffc000000000
445);
446static_assertions::const_assert_eq!(
447 VirtualAddress(0xffffffc0000156e8)
448 .checked_align_up(4096)
449 .unwrap()
450 .0,
451 0xffffffc000016000
452);