hal_core/
mem.rs

1use crate::Address;
2use core::cmp;
3pub mod page;
4use mycelium_util::fmt;
5
6/// A cross-platform representation of a memory region.
7#[derive(Clone, Eq, PartialEq)]
8pub struct Region<A = crate::PAddr> {
9    base: A,
10    // TODO(eliza): should regions be stored as (start -> end) or as
11    // (base + offset)?
12    size: usize,
13    kind: RegionKind,
14}
15
16#[derive(Copy, Clone, Eq, PartialEq)]
17#[repr(transparent)]
18pub struct RegionKind(KindInner);
19
20#[derive(Copy, Clone, Debug, Eq, PartialEq)]
21#[repr(u8)]
22enum KindInner {
23    /// For whatever reason, this memory region's kind is undetermined.
24    // TODO(eliza): do we need this?
25    Unknown = 0,
26    /// Free memory
27    Free,
28    /// Memory used by the kernel
29    Used,
30    /// Memory containing bootloader info that may be reclaimed by the kernel.
31    ///
32    /// The kernel is responsible for ensuring that any required information
33    /// from the bootloader is consumed before reclaiming this memory.
34    ///
35    /// This may include, for example, ACPI tables.
36    BootReclaimable,
37    /// Memory containing bootloader info that may **not** be reclaimed by the
38    /// kernel.
39    Boot,
40    /// Bad memory.
41    Bad,
42    /// Kernel memory
43    Kernel,
44    /// Kernel stack
45    KernelStack,
46    /// Memory used for a page table.
47    PageTable,
48}
49
50impl<A: Address> Region<A> {
51    pub fn new(base: A, size: usize, kind: RegionKind) -> Self {
52        Self { base, size, kind }
53    }
54
55    /// Returns the base address of the memory region
56    pub fn base_addr(&self) -> A {
57        self.base
58    }
59
60    /// Returns the size (in bytes) of the memory region.
61    pub fn size(&self) -> usize {
62        self.size
63    }
64
65    pub fn is_aligned(&self, align: impl Into<usize>) -> bool {
66        self.base.is_aligned(align)
67    }
68
69    /// Returns `true` if `self` contains the specified address.
70    pub fn contains(&self, addr: impl Into<A>) -> bool {
71        let addr = addr.into();
72        addr >= self.base && addr < self.end_addr()
73    }
74
75    /// Returns the end address (exclusive) of the memory region.
76    ///
77    /// This is the start address of the next memory region.
78    pub fn end_addr(&self) -> A {
79        self.base + self.size
80    }
81
82    pub fn kind(&self) -> RegionKind {
83        self.kind
84    }
85
86    pub fn split_front(&mut self, size: usize) -> Option<Self> {
87        assert!(size <= isize::MAX as usize);
88        if size > self.size {
89            return None;
90        }
91        let base = self.base;
92        tracing::trace!(size, self.size, "splitting down by");
93        self.base = self.base.offset(size as isize);
94        self.size -= size;
95        Some(Self {
96            base,
97            size,
98            kind: self.kind,
99        })
100    }
101
102    pub fn split_back(&mut self, size: usize) -> Option<Self> {
103        assert!(size <= isize::MAX as usize);
104        if size >= self.size {
105            return None;
106        }
107        let rem_size = self.size - size;
108        let base = self.base.offset(size as isize);
109        tracing::trace!(
110            size,
111            self.size,
112            ?self.base,
113            self.addr = fmt::ptr(&self),
114            rem_size,
115            ?base,
116            "split_back",
117        );
118        self.size = size;
119        tracing::trace!(?self);
120        Some(Self {
121            base,
122            size: rem_size,
123            kind: self.kind,
124        })
125    }
126
127    pub fn page_range<S: page::Size>(
128        &self,
129        size: S,
130    ) -> Result<page::PageRange<A, S>, page::NotAligned<S>> {
131        tracing::trace!(?self.base, self.size, self.end = ?self.end_addr(), "Region -> PageRange");
132        let start = page::Page::starting_at(self.base, size)?;
133        let end = page::Page::starting_at(self.end_addr(), size)?;
134        Ok(start.range_to(end))
135    }
136
137    pub fn from_page_range<S: page::Size>(range: page::PageRange<A, S>, kind: RegionKind) -> Self {
138        let base = range.start().base_addr();
139        Self {
140            base,
141            size: range.size(),
142            kind,
143        }
144    }
145
146    pub fn merge(&mut self, other: &mut Self) {
147        debug_assert_eq!(self.kind, other.kind);
148        // TOOD(eliza): assert that there's no in-between space.
149        self.base = cmp::min(self.base, other.base);
150        self.size += other.size;
151    }
152}
153
154impl<A> fmt::Debug for Region<A>
155where
156    A: fmt::Debug,
157{
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        let Self { base, size, kind } = self;
160        f.debug_struct("Region")
161            .field("base", base)
162            .field("size", size)
163            .field("kind", kind)
164            .finish()
165    }
166}
167
168impl RegionKind {
169    pub const UNKNOWN: Self = Self(KindInner::Unknown);
170
171    /// Free memory
172    pub const FREE: Self = Self(KindInner::Free);
173
174    /// Memory used by the kernel
175    pub const USED: Self = Self(KindInner::Used);
176
177    /// Memory containing bootloader info that may be reclaimed by the kernel.
178    ///
179    /// The kernel is responsible for ensuring that any required information
180    /// from the bootloader is consumed before reclaiming this memory.
181    ///
182    /// This may include, for example, ACPI tables.
183    pub const BOOT_RECLAIMABLE: Self = Self(KindInner::BootReclaimable);
184
185    /// Memory containing bootloader info that may **not** be reclaimed by the
186    /// kernel.
187    pub const BOOT: Self = Self(KindInner::Boot);
188
189    /// Bad memory
190    pub const BAD: Self = Self(KindInner::Bad);
191
192    /// Kernel memory
193    pub const KERNEL: Self = Self(KindInner::Kernel);
194
195    /// Kernel stack
196    pub const KERNEL_STACK: Self = Self(KindInner::KernelStack);
197
198    /// Memory used for a page table.
199    pub const PAGE_TABLE: Self = Self(KindInner::PageTable);
200}
201
202impl fmt::Debug for RegionKind {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        match *self {
205            RegionKind::FREE => f.pad("FREE"),
206            RegionKind::USED => f.pad("USED"),
207            RegionKind::BOOT => f.pad("BOOT"),
208            RegionKind::BOOT_RECLAIMABLE => f.pad("BOOT_RECLAIMABLE"),
209            RegionKind::BAD => f.pad("BAD T_T"),
210            RegionKind::KERNEL => f.pad("KERNEL"),
211            RegionKind::KERNEL_STACK => f.pad("KERNEL_STACK"),
212            RegionKind::PAGE_TABLE => f.pad("PAGE_TABLE"),
213            _ => f.pad("UNKNOWN ?_?"),
214        }
215    }
216}