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