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::address::VirtualAddress;
10use anyhow::bail;
11use core::range::Range;
12use core::{cmp, mem};
13
14#[must_use]
15pub struct Flush {
16 asid: u16,
17 range: Option<Range<VirtualAddress>>,
18}
19
20impl Drop for Flush {
21 fn drop(&mut self) {
22 if self.range.is_some() {
23 tracing::error!("dropped Flush without calling ignore/flush");
24 }
25 }
26}
27
28impl Flush {
29 pub fn empty(asid: u16) -> Self {
30 Self { asid, range: None }
31 }
32
33 pub fn new(asid: u16, range: Range<VirtualAddress>) -> Self {
34 Self {
35 asid,
36 range: Some(range),
37 }
38 }
39
40 pub fn range(&self) -> Option<&Range<VirtualAddress>> {
41 self.range.as_ref()
42 }
43
44 /// Flush the range of virtual addresses from the TLB.
45 ///
46 /// # Errors
47 ///
48 /// Returns an error if the range could not be flushed due to an underlying hardware error.
49 pub fn flush(mut self) -> crate::Result<()> {
50 if let Some(range) = self.range.take() {
51 tracing::trace!(?range, asid = self.asid, "flushing range");
52 arch::invalidate_range(self.asid, range)?;
53 } else {
54 tracing::warn!("attempted to flush empty range, ignoring");
55 }
56
57 Ok(())
58 }
59
60 /// # Safety
61 ///
62 /// Not flushing after mutating the page translation tables will likely lead to unintended
63 /// consequences such as inconsistent views of the address space between different cpus.
64 ///
65 /// You should only call this if you know what you're doing.
66 pub unsafe fn ignore(self) {
67 mem::forget(self);
68 }
69
70 /// Extend the range to include the given range.
71 ///
72 /// # Errors
73 ///
74 /// Returns an error if the given ASID does not match the ASID of this `Flush`.
75 pub fn extend_range(&mut self, asid: u16, other: Range<VirtualAddress>) -> crate::Result<()> {
76 if self.asid == asid {
77 if let Some(this) = self.range.take() {
78 self.range = Some(Range {
79 start: cmp::min(this.start, other.start),
80 end: cmp::max(this.end, other.end),
81 });
82 } else {
83 self.range = Some(other);
84 }
85
86 Ok(())
87 } else {
88 bail!(
89 "Attempted to operate on mismatched address space. Expected {} but found {asid}.",
90 self.asid
91 );
92 }
93 }
94}