Skip to content

Commit 75f7dda

Browse files
authored
Make most basic memory type functions const (theseus-os#973)
* The following functions in `memory_structs` are now `const`: * `is_canonical_virtual_address()` * `is_canonical_physical_address()` * `PageRange::from_virtual_address()` * `FrameRange::from_physical_address()` * `PageRange::contains_address()` * `FrameRange::contains_address()` * Fix docs and implementation of `PageRange::address_at_offset()` and `FrameRange::address_at_offset()`
1 parent 234ca0c commit 75f7dda

File tree

3 files changed

+72
-44
lines changed

3 files changed

+72
-44
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kernel/memory_structs/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ version = "0.1.0"
66
edition = "2021"
77

88
[dependencies]
9-
bit_field = "0.7.0"
109
zerocopy = "0.5.0"
1110
derive_more = "0.99.0"
1211
paste = "1.0.5"

kernel/memory_structs/src/lib.rs

Lines changed: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,23 @@ macro_rules! implement_address {
121121

122122
#[cfg(target_arch = "x86_64")]
123123
mod canonical_address {
124-
use bit_field::BitField;
124+
const CANONICAL_VIRT_ADDR_MASK: usize = 0x0000_7FFF_FFFF_FFFF;
125+
const CANONICAL_PHYS_ADDR_MASK: usize = 0x000F_FFFF_FFFF_FFFF;
125126

127+
/// Returns whether the given virtual address value is canonical.
128+
///
129+
/// On x86_64, virtual addresses must have their 16 most-significant bits
130+
/// be sign-extended from bit 47.
126131
#[inline]
127-
pub fn is_canonical_virtual_address(virt_addr: usize) -> bool {
128-
matches!(virt_addr.get_bits(47..64), 0 | 0b1_1111_1111_1111_1111)
132+
pub const fn is_canonical_virtual_address(virt_addr: usize) -> bool {
133+
let upper17 = virt_addr & !CANONICAL_VIRT_ADDR_MASK;
134+
upper17 == 0 || upper17 == !CANONICAL_VIRT_ADDR_MASK
129135
}
130136

137+
/// Returns a canonicalized instance of the given virtual address value.
138+
///
139+
/// On x86_64, virtual addresses must have their 16 most-significant bits
140+
/// be sign-extended from bit 47.
131141
#[inline]
132142
pub const fn canonicalize_virtual_address(virt_addr: usize) -> usize {
133143
// match virt_addr.get_bit(47) {
@@ -139,57 +149,72 @@ mod canonical_address {
139149
((virt_addr << 16) as isize >> 16) as usize
140150
}
141151

152+
/// Returns whether the given phyiscal address value is canonical.
153+
///
154+
/// On x86_64, physical addresses are 52 bits long,
155+
/// so their 12 most-significant bits must be cleared.
142156
#[inline]
143-
pub fn is_canonical_physical_address(phys_addr: usize) -> bool {
144-
matches!(phys_addr.get_bits(52..64), 0)
157+
pub const fn is_canonical_physical_address(phys_addr: usize) -> bool {
158+
phys_addr & !CANONICAL_PHYS_ADDR_MASK == 0
145159
}
146160

161+
/// Returns a canonicalized instance of the given phyiscal address value.
162+
///
163+
/// On x86_64, physical addresses are 52 bits long,
164+
/// so their 12 most-significant bits must be cleared.
147165
#[inline]
148166
pub const fn canonicalize_physical_address(phys_addr: usize) -> usize {
149-
phys_addr & 0x000F_FFFF_FFFF_FFFF
167+
phys_addr & CANONICAL_PHYS_ADDR_MASK
150168
}
151169
}
152170

153171
#[cfg(target_arch = "aarch64")]
154172
mod canonical_address {
155-
use bit_field::BitField;
156-
157-
/// On aarch64, VAs are composed of an ASID
158-
/// which is 8 or 16 bits long depending
159-
/// on MMU config. In Theseus, we use 8-bits
160-
/// and the next 8 bits are unused.
161-
/// Our ASID is zero, so a "canonical" VA has
162-
/// the 16 most significant bits cleared.
173+
const CANONICAL_VIRT_ADDR_MASK: usize = 0x0000_FFFF_FFFF_FFFF;
174+
const CANONICAL_PHYS_ADDR_MASK: usize = 0x0000_FFFF_FFFF_FFFF;
175+
176+
/// Returns whether the given virtual address value is canonical.
177+
///
178+
/// On aarch64, virtual addresses contain an address space ID (ASID),
179+
/// which is 8 or 16 bits long, depending on MMU config.
180+
///
181+
/// In Theseus, we use 8-bit ASIDs, with the next 8 bits are unused.
182+
/// Theseus's ASID is zero, so a canonical virtual address has its
183+
/// 16 most-significant bits cleared (set to zero).
163184
#[inline]
164-
pub fn is_canonical_virtual_address(virt_addr: usize) -> bool {
165-
matches!(virt_addr.get_bits(48..64), 0)
185+
pub const fn is_canonical_virtual_address(virt_addr: usize) -> bool {
186+
virt_addr & !CANONICAL_VIRT_ADDR_MASK == 0
166187
}
167188

168-
/// On aarch64, VAs are composed of an ASID
169-
/// which is 8 or 16 bits long depending
170-
/// on MMU config. In Theseus, we use 8-bits
171-
/// and the next 8 bits are unused.
172-
/// Our ASID is zero, so a "canonical" VA has
173-
/// the 16 most significant bits cleared.
189+
/// Returns a canonicalized instance of the given virtual address value.
190+
///
191+
/// On aarch64, virtual addresses contain an address space ID (ASID),
192+
/// which is 8 or 16 bits long, depending on MMU config.
193+
///
194+
/// In Theseus, we use 8-bit ASIDs, with the next 8 bits are unused.
195+
/// Theseus's ASID is zero, so a virtual address is canonicalized
196+
/// by clearing (setting to zero) its 16 most-significant bits.
174197
#[inline]
175198
pub const fn canonicalize_virtual_address(virt_addr: usize) -> usize {
176-
virt_addr & 0x0000_FFFF_FFFF_FFFF
199+
virt_addr & CANONICAL_VIRT_ADDR_MASK
177200
}
178201

179-
/// On aarch64, we configure the MMU to use 48-bit
180-
/// physical addresses; "canonical" physical addresses
181-
/// have the 16 most significant bits cleared.
202+
/// Returns whether the given physical address value is canonical.
203+
///
204+
/// On aarch64, Theseus configures the MMU to use 48-bit physical addresses.
205+
/// Thus, a canonical physical address has its 16 most-significant bits cleared.
182206
#[inline]
183-
pub fn is_canonical_physical_address(phys_addr: usize) -> bool {
184-
matches!(phys_addr.get_bits(48..64), 0)
207+
pub const fn is_canonical_physical_address(phys_addr: usize) -> bool {
208+
phys_addr & !CANONICAL_PHYS_ADDR_MASK == 0
185209
}
186210

187-
/// On aarch64, we configure the MMU to use 48-bit
188-
/// physical addresses; "canonical" physical addresses
189-
/// have the 16 most significant bits cleared.
211+
/// Returns a canonicalized instance of the given physical address value.
212+
///
213+
/// On aarch64, Theseus configures the MMU to use 48-bit physical addresses.
214+
/// Thus, a physical address is canonicalized by clearing its 16 most-significant bits.
190215
#[inline]
191216
pub const fn canonicalize_physical_address(phys_addr: usize) -> usize {
192-
phys_addr & 0x0000_FFFF_FFFF_FFFF
217+
phys_addr & CANONICAL_PHYS_ADDR_MASK
193218
}
194219
}
195220

@@ -355,13 +380,15 @@ macro_rules! implement_page_frame_range {
355380

356381
#[doc = "A convenience method for creating a new `" $TypeName "` that spans \
357382
all [`" $chunk "`]s from the given [`" $address "`] to an end bound based on the given size."]
358-
pub fn [<from_ $short _addr>](starting_addr: $address, size_in_bytes: usize) -> $TypeName {
383+
pub const fn [<from_ $short _addr>](starting_addr: $address, size_in_bytes: usize) -> $TypeName {
359384
if size_in_bytes == 0 {
360385
$TypeName::empty()
361386
} else {
362387
let start = $chunk::containing_address(starting_addr);
363388
// The end bound is inclusive, hence the -1. Parentheses are needed to avoid overflow.
364-
let end = $chunk::containing_address(starting_addr + (size_in_bytes - 1));
389+
let end = $chunk::containing_address(
390+
$address::new_canonical(starting_addr.value() + (size_in_bytes - 1))
391+
);
365392
$TypeName::new(start, end)
366393
}
367394
}
@@ -385,16 +412,18 @@ macro_rules! implement_page_frame_range {
385412
}
386413

387414
#[doc = "Returns `true` if this `" $TypeName "` contains the given [`" $address "`]."]
388-
pub fn contains_address(&self, addr: $address) -> bool {
389-
self.0.contains(&$chunk::containing_address(addr))
415+
pub const fn contains_address(&self, addr: $address) -> bool {
416+
let c = $chunk::containing_address(addr);
417+
self.0.start().number <= c.number
418+
&& c.number <= self.0.end().number
390419
}
391420

392421
#[doc = "Returns the offset of the given [`" $address "`] within this `" $TypeName "`, \
393422
i.e., `addr - self.start_address()`.\n\n \
394423
If the given `addr` is not covered by this range of [`" $chunk "`]s, this returns `None`.\n\n \
395424
# Examples\n \
396425
If the range covers addresses `0x2000` to `0x4000`, then `offset_of_address(0x3500)` would return `Some(0x1500)`."]
397-
pub fn offset_of_address(&self, addr: $address) -> Option<usize> {
426+
pub const fn offset_of_address(&self, addr: $address) -> Option<usize> {
398427
if self.contains_address(addr) {
399428
Some(addr.value() - self.start_address().value())
400429
} else {
@@ -403,13 +432,14 @@ macro_rules! implement_page_frame_range {
403432
}
404433

405434
#[doc = "Returns the [`" $address "`] at the given `offset` into this `" $TypeName "`within this `" $TypeName "`, \
406-
i.e., `addr - self.start_address()`.\n\n \
435+
i.e., `self.start_address() + offset`.\n\n \
407436
If the given `offset` is not within this range of [`" $chunk "`]s, this returns `None`.\n\n \
408437
# Examples\n \
409-
If the range covers addresses `0x2000` to `0x4000`, then `address_at_offset(0x1500)` would return `Some(0x3500)`."]
410-
pub fn address_at_offset(&self, offset: usize) -> Option<$address> {
411-
if offset <= self.size_in_bytes() {
412-
Some(self.start_address() + offset)
438+
If the range covers addresses `0x2000` through `0x3FFF`, then `address_at_offset(0x1500)` would return `Some(0x3500)`, \
439+
and `address_at_offset(0x2000)` would return `None`."]
440+
pub const fn address_at_offset(&self, offset: usize) -> Option<$address> {
441+
if offset < self.size_in_bytes() {
442+
Some($address::new_canonical(self.start_address().value() + offset))
413443
}
414444
else {
415445
None

0 commit comments

Comments
 (0)