summaryrefslogtreecommitdiff
path: root/kernel/src/allocators
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/allocators')
-rw-r--r--kernel/src/allocators/buddy/bitvec.rs50
-rw-r--r--kernel/src/allocators/buddy/mod.rs52
-rw-r--r--kernel/src/allocators/buddy/stripe.rs148
-rw-r--r--kernel/src/allocators/buddy/tree.rs112
-rw-r--r--kernel/src/allocators/mod.rs24
-rw-r--r--kernel/src/allocators/physical_memory_free_list.rs322
6 files changed, 0 insertions, 708 deletions
diff --git a/kernel/src/allocators/buddy/bitvec.rs b/kernel/src/allocators/buddy/bitvec.rs
deleted file mode 100644
index c7f415a..0000000
--- a/kernel/src/allocators/buddy/bitvec.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use core::mem::transmute;
-
-use contracts::requires;
-
-/// A fixed-length vector of bits.
-pub struct BitVec([usize]);
-
-impl BitVec {
- fn from_mut(words: &mut [usize]) -> &mut BitVec {
- // SAFETY: The types have a newtype relationship.
- unsafe { transmute(words) }
- }
-
- fn from_ref(words: &[usize]) -> &BitVec {
- // SAFETY: The types have a newtype relationship.
- unsafe { transmute(words) }
- }
-
- /// Retrieves the value of a bit from the BitVec.
- #[requires(i < self.len())]
- pub fn get(&self, i: usize) -> bool {
- let word_index = i / usize::BITS as usize;
- let subword_index = i % usize::BITS as usize;
- let one_hot = 1 << subword_index;
- (self.0[word_index] & one_hot) != 0
- }
-
- /// Returns whether the BitVec is empty.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// Returns the number of bits in the BitVec.
- pub fn len(&self) -> usize {
- self.0.len() * usize::BITS as usize
- }
-
- /// Sets the value of a bit in the BitVec.
- #[requires(i < self.len())]
- pub fn set(&mut self, i: usize, value: bool) {
- let word_index = i / usize::BITS as usize;
- let subword_index = i % usize::BITS as usize;
- let one_hot = 1 << subword_index;
- if value {
- self.0[word_index] |= one_hot;
- } else {
- self.0[word_index] &= !one_hot;
- }
- }
-}
diff --git a/kernel/src/allocators/buddy/mod.rs b/kernel/src/allocators/buddy/mod.rs
deleted file mode 100644
index 08b30a7..0000000
--- a/kernel/src/allocators/buddy/mod.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-//! A buddy allocator, used to allocate pages.
-//!
-//! The allocator can be split into three abstractions: stripes, trees, and the allocator.
-//!
-//! TODO: See if there's standard terminology for these.
-//!
-//! ## Stripes
-//!
-//! The buddy allocator works in terms of size classes, which are power-of-two sized, starting at a
-//! single page and going up from there. Each stripe corresponds to a single size class and a
-//! particular region of memory.
-//!
-//! A stripe contains a circular doubly-linked free-list for subregions of that size class, and a
-//! bitset marking whether a particular region has been allocated or not. Being a circular
-//! doubly-linked list makes it cheap to remove an element whose address we know, as well as cheap
-//! to push and pop elements.
-//!
-//! It's efficient to go from the address of a subregion to the index of its corresponding bit, so
-//! when we hand out a subregion from the free-list or put one back, it's cheap to read or write
-//! its bit.
-//!
-//! ## Trees
-//!
-//! A tree is logically a collection of stripes, one per size class. To pack the structures more
-//! efficiently, they are stored interleaved with each other, and the tree manages this.
-//!
-//! The important logic at the level of trees happens when we allocate a subregion from a size
-//! class whose stripe's free-list is empty, and when we free subregions.
-//!
-//! When a stripe's free-list is empty, the tree instead allocates a subregion of a larger size
-//! from the next stripe. It can then store the unused portion in the current size class.
-//!
-//! The really important bit is the ability to merge subregions when they're freed. When we free a
-//! subregion of a certain size class, we can check whether its neighbor (its buddy) is unallocated
-//! as well. If so, we can remove it from the free-list by its address. We can combine the two
-//! subregions into one of the next larger size class, and then return that subregion to the next
-//! stripe.
-//!
-//! ## The buddy allocator
-//!
-//! Finally, the overall buddy allocator needs to be able to handle multiple memory regions. To
-//! facilitate this, the trees are stored in an array, which forms the overall allocator.
-
-mod bitvec;
-mod stripe;
-mod tree;
-
-/// The index of the largest size class; i.e., one less than the number of size classes.
-const MAX_ORDER: usize = 18;
-
-// The max order comes out to a largest size class of 1GiB pages.
-static_assertions::const_assert_eq!(4096 << MAX_ORDER, 1024 * 1024 * 1024);
diff --git a/kernel/src/allocators/buddy/stripe.rs b/kernel/src/allocators/buddy/stripe.rs
deleted file mode 100644
index 9ec5985..0000000
--- a/kernel/src/allocators/buddy/stripe.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use crate::allocators::{buddy::bitvec::BitVec, PAGE_SIZE_BITS};
-use core::ptr::NonNull;
-
-/// A single size class for a single region of the allocator. See the comment on the
-/// `crate::allocators::buddy` module for more information.
-pub struct Stripe<'buddy> {
- /// The base address of the tree this stripe is a part of.
- pub base: *const (),
-
- /// The order of the stripe. Order `n` means the subregions are `2ⁿ` pages in size.
- pub order: usize,
-
- /// The sentinel node of the free-list for this node. As an invariant of the type, there are no
- /// live references to any node in this list.
- pub free_list: NonNull<FreeListNode>,
-
- /// The bitset used to track whether a given subregion is allocated or not. A `true` bit
- /// corresponds to an allocated subregion.
- pub bitset: &'buddy mut BitVec,
-
- /// The offset from the start of the bitset to the region used by this stripe.
- pub bitset_offset: usize,
-}
-
-impl<'buddy> Stripe<'buddy> {
- /// Returns the buddy of the given pointer.
- ///
- /// ## Safety
- ///
- /// - The pointer must actually be part of this region.
- unsafe fn buddy_of(&self, ptr: NonNull<FreeListNode>) -> NonNull<FreeListNode> {
- let index = self.buddy_of_index(self.index_of(ptr));
- let addr = self.base as usize + (index << (PAGE_SIZE_BITS + self.order));
- NonNull::new_unchecked(addr as *mut FreeListNode)
- }
-
- /// Returns the buddy of the given index.
- fn buddy_of_index(&self, index: usize) -> usize {
- index ^ (1 << (PAGE_SIZE_BITS + self.order))
- }
-
- /// Returns the index the given pointer should have in the BitVec.
- fn index_of(&self, ptr: NonNull<FreeListNode>) -> usize {
- (ptr.as_ptr() as usize - self.base as usize) >> (PAGE_SIZE_BITS + self.order)
- }
-
- /// Pops a subregion from the free-list.
- pub fn pop(&mut self) -> Option<NonNull<FreeListNode>> {
- // SAFETY: The `free_list` is guaranteed to be valid by the invariants of the buddy
- // allocator. Retrieving the next pointer doesn't, on its own, break aliasing rules.
- let next = unsafe { self.free_list.read().next };
-
- // If the sentinel and the next pointer refer to the same spot, the free-list was empty, so
- // we can't pop from it.
- if self.free_list == next {
- return None;
- }
-
- // Otherwise, remove the node from the free-list.
- unsafe {
- FreeListNode::remove(next);
- }
-
- // Finally, mark the node as allocated in the bitvec.
- let index = self.index_of(next);
- assert!(self.bitset.get(self.bitset_offset + index));
- self.bitset.set(self.bitset_offset + index, true);
-
- Some(next)
- }
-
- /// Pushes a subregion back into the free-list.
- ///
- /// # Safety
- ///
- /// - There must be no live references to `subregion`.
- /// - `subregion` must not be a member of any list.
- pub unsafe fn push(&mut self, subregion: NonNull<FreeListNode>) {
- // Insert the subregion as the first element of the free-list.
- //
- // SAFETY: The free-list is guaranteed to be valid by the invariants of the buddy
- // allocator.
- unsafe {
- FreeListNode::insert(self.free_list, subregion);
- }
-
- // Mark the node as unallocated in the bitvec.
- let index = self.index_of(subregion);
- assert!(self.bitset.get(self.bitset_offset + index));
- self.bitset.set(self.bitset_offset + index, false);
- }
-
- /// Pushes a subregion into the free-list for the first time.
- ///
- /// # Safety
- ///
- /// - There must be no live references to `subregion`.
- /// - `subregion` must not be a member of any list.
- pub unsafe fn push_initial(&mut self, subregion: NonNull<FreeListNode>) {
- // Insert the subregion as the first element of the free-list.
- //
- // SAFETY: The free-list is guaranteed to be valid by the invariants of the buddy
- // allocator.
- unsafe {
- FreeListNode::insert(self.free_list, subregion);
- }
-
- // Mark the node as unallocated in the bitvec.
- let index = self.index_of(subregion);
- assert!(self.bitset.get(self.bitset_offset + index));
- self.bitset.set(self.bitset_offset + index, false);
- }
-}
-
-pub struct FreeListNode {
- next: NonNull<FreeListNode>,
- prev: NonNull<FreeListNode>,
-}
-
-impl FreeListNode {
- /// Inserts `new` after `prev`, initializing it. `prev` may be the sentinel.
- ///
- /// # Safety
- ///
- /// - There must be no live references to any node in the list that includes `prev`, including
- /// the sentinel.
- /// - There must be no live references to `new`.
- /// - `new` must not be a member of any list.
- pub unsafe fn insert(prev: NonNull<FreeListNode>, new: NonNull<FreeListNode>) {
- let next = prev.read().next;
- (*prev.as_ptr()).next = new;
- (*next.as_ptr()).prev = new;
- new.write(FreeListNode { next, prev });
- }
-
- /// Removes this node from the free list it is a part of.
- ///
- /// # Safety
- ///
- /// - The pointer must point to a node that is part of a free list.
- /// - There must be no live references to any node in the list, including the sentinel.
- /// - The pointer must not point to the sentinel node.
- pub unsafe fn remove(ptr: NonNull<FreeListNode>) {
- let FreeListNode { next, prev } = ptr.read();
- (*next.as_ptr()).prev = prev;
- (*prev.as_ptr()).next = next;
- }
-}
diff --git a/kernel/src/allocators/buddy/tree.rs b/kernel/src/allocators/buddy/tree.rs
deleted file mode 100644
index 3953f39..0000000
--- a/kernel/src/allocators/buddy/tree.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use crate::allocators::{
- buddy::{
- bitvec::BitVec,
- stripe::{FreeListNode, Stripe},
- MAX_ORDER,
- },
- PAGE_SIZE_BITS,
-};
-use contracts::requires;
-use core::ptr::NonNull;
-
-/// A single region of the allocator. See the comment on the `crate::allocators::buddy` module for
-/// more information.
-pub struct Tree<'buddy> {
- /// The base address of the tree.
- pub base: *const (),
-
- /// The log2 of the number of pages in the region represented by the tree.
- pub log2_page_count: usize,
-
- /// The array of sentinel nodes of the free-list for this node. As an invariant of the type,
- /// there are no live references to any node in any list in this array, and there are
- /// MAX_ORDER + 1 nodes.
- pub free_lists: NonNull<FreeListNode>,
-
- /// The bitset used to track whether subregion are allocated or not in this tree.
- pub bitset: &'buddy mut BitVec,
-}
-
-impl<'buddy> Tree<'buddy> {
- /// Tries to allocate a subregion with the given order, possibly splitting a larger subregion
- /// in order to so so.
- #[requires(order <= MAX_ORDER)]
- pub fn alloc(&mut self, order: usize) -> Option<NonNull<FreeListNode>> {
- if let Some(ptr) = self.stripe(order).pop() {
- Some(ptr)
- } else if order == MAX_ORDER {
- None
- } else {
- // Get a larger region.
- let ptr = self.alloc(order + 1)?;
-
- // Get a pointer to the higher half.
- //
- // SAFETY: This has to be in-bounds, it's part of the same allocation!
- let higher_half = unsafe { ptr.byte_add(1 << (PAGE_SIZE_BITS + order)) };
-
- // Put the higher half in the current buddy's stripe.
- //
- // SAFETY: The higher half is from this region, not in the higher stripe, and of the
- // right size.
- unsafe {
- self.stripe(order).push(higher_half);
- }
-
- // Return the pointer.
- Some(ptr)
- }
- }
-
- /// Returns the stripe with the given order.
- #[requires(order <= MAX_ORDER)]
- fn stripe<'stripe>(&'stripe mut self, order: usize) -> Stripe<'stripe> {
- // TODO: There should be some smart bitwise-math version of this...
- let mut bitset_offset = 0;
- for i in 0..order {
- bitset_offset += (1 << self.log2_page_count) >> i;
- }
-
- Stripe {
- base: self.base,
- order,
- // SAFETY: order is constrained to be in-bounds.
- free_list: unsafe { self.free_lists.add(order) },
- bitset: self.bitset,
- bitset_offset,
- }
- }
-}
-
-/// Evil bitwise version of the reasonable loop to compute the `bitset_offset` of a stripe.
-#[requires(log2_page_count < usize::BITS as usize)]
-#[requires(order < usize::BITS as usize)]
-fn compute_bitset_offset(log2_page_count: usize, order: usize) -> usize {
- let ones = |i: usize| !(usize::MAX << i);
-
- if order > log2_page_count + 1 {
- ones(log2_page_count + 1)
- } else {
- ones(order).wrapping_shl((log2_page_count + 1 - order) as u32)
- }
-}
-
-#[test]
-fn compute_bitset_offset_test() {
- fn compute_bitset_offset_loop(log2_page_count: usize, order: usize) -> usize {
- let mut bitset_offset = 0;
- for i in 0..order {
- bitset_offset += (1 << log2_page_count) >> i;
- }
- bitset_offset
- }
-
- for log2_page_count in 0..64 {
- for order in 0..64 {
- assert_eq!(
- compute_bitset_offset(log2_page_count, order),
- compute_bitset_offset_loop(log2_page_count, order),
- );
- }
- }
-}
diff --git a/kernel/src/allocators/mod.rs b/kernel/src/allocators/mod.rs
deleted file mode 100644
index 49f29e2..0000000
--- a/kernel/src/allocators/mod.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use core::{ffi::c_void, ops::Range, ptr::addr_of};
-
-pub mod buddy;
-pub mod physical_memory_free_list;
-
-/// The number of bits in the offset in a page.
-pub const PAGE_SIZE_BITS: usize = 12;
-
-/// The size of a page, in bytes.
-pub const PAGE_SIZE: usize = 1 << PAGE_SIZE_BITS;
-
-/// Returns the physical address range the kernel is in.
-pub fn kernel_boundaries() -> Range<usize> {
- extern "C" {
- static kernel_start: c_void;
- static kernel_end: c_void;
- }
-
- // SAFETY: We only use these as addresses, we never dereference them.
- let (kernel_start_addr, kernel_end_addr) =
- unsafe { (addr_of!(kernel_start), addr_of!(kernel_end)) };
-
- kernel_start_addr as usize..kernel_end_addr as usize
-}
diff --git a/kernel/src/allocators/physical_memory_free_list.rs b/kernel/src/allocators/physical_memory_free_list.rs
deleted file mode 100644
index 0d2f1b4..0000000
--- a/kernel/src/allocators/physical_memory_free_list.rs
+++ /dev/null
@@ -1,322 +0,0 @@
-//! A free-list allocator that runs in physical memory. This should only be used to bootstrap the
-//! buddy allocator for physical memory.
-
-use crate::{
- allocators::{kernel_boundaries, PAGE_SIZE, PAGE_SIZE_BITS},
- device_tree::FlattenedDeviceTree,
- prelude::*,
- util::ranges_overlap,
-};
-use contracts::requires;
-use core::{
- fmt, iter,
- ops::Range,
- ptr::{self, NonNull},
-};
-
-/// A free-list allocator that runs in physical memory. This should only be used to bootstrap the
-/// buddy allocator for physical memory.
-pub struct FreeList<'dt> {
- /// The DeviceTree the machine was booted with.
- device_tree: &'dt FlattenedDeviceTree<'dt>,
-
- /// The physical address of the head of the free-list allocator.
- ///
- /// The free-list is sorted by size, smallest to largest.
- head: Option<FreeListAddr>,
-
- /// The number of regions in the free-list.
- len: usize,
-}
-
-impl<'dt> FreeList<'dt> {
- /// Creates a new, empty free-list that checks addresses against the given DeviceTree.
- pub fn new(device_tree: &'dt FlattenedDeviceTree<'dt>) -> FreeList {
- let out = FreeList {
- device_tree,
- head: None,
- len: 0,
- };
-
- // Log the memory reservations.
- let reservation_count = out.iter_reserved().count();
- debug!(
- "found {} memory reservations{}",
- reservation_count,
- if reservation_count == 0 { "" } else { ":" }
- );
- for reservation in out.iter_reserved() {
- debug!(
- "{:p}..{:p} ({} byte{})",
- reservation.start as *const u8,
- reservation.end as *const u8,
- reservation.len(),
- if reservation.len() == 1 { "" } else { "s" }
- )
- }
-
- out
- }
-
- /// Adds a physical address range to the free-list allocator.
- ///
- /// ## Safety
- ///
- /// - This allocator must have been created with the DeviceTree that gave us this memory.
- /// - The region must be a valid physical memory range.
- /// - Paging must not be enabled for the entire lifetime of this allocator.
- pub unsafe fn add_range(&mut self, mut addrs: Range<usize>) {
- // Clamp the range, so that we can't somehow end up with the zero address or with
- // higher-half addresses.
- addrs.start = addrs.start.max(1);
- addrs.end = addrs.end.min(isize::MAX as usize);
-
- // Trim the range to be page-aligned.
- addrs.start = (addrs.start + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1);
- addrs.end &= !(PAGE_SIZE - 1);
-
- // Add each unreserved subrange to the allocator's list.
- loop {
- assert_eq!(addrs.start & (PAGE_SIZE - 1), 0);
- assert_eq!(addrs.end & (PAGE_SIZE - 1), 0);
-
- // Walk forwards until the first page is unreserved.
- loop {
- // Ensure the range is non-empty.
- if addrs.end <= addrs.start {
- return;
- }
-
- // If the start of the range is reserved, walk forwards a page.
- let first_page = addrs.start..addrs.start + PAGE_SIZE;
- if self
- .iter_reserved()
- .any(|range| ranges_overlap(&range, &first_page))
- {
- addrs.start = first_page.end;
- } else {
- break;
- }
- }
-
- // Find the first reservation that overlaps.
- if let Some(reservation) = self
- .iter_reserved()
- .filter(|range| ranges_overlap(range, &addrs))
- .min_by_key(|range| range.start)
- {
- // Get the range that's before the reservation.
- let mut unreserved = addrs.start..reservation.start;
-
- // Trim the range to be page-aligned. We don't need to trim the start, because
- // addrs.start is already aligned (by our loop invariant).
- unreserved.end &= !(PAGE_SIZE - 1);
-
- // Add the range up to the start of that overlap.
- self.add_range_unchecked(unreserved);
-
- // Adjust the remaining range to be after the overlap.
- addrs.start = reservation.end;
-
- // Trim the range to be page-aligned. We don't need to trim the end, because it's
- // unchanged.
- addrs.start = (addrs.start + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1);
- } else {
- // If no range overlaps, the entire remaining range is valid.
- self.add_range_unchecked(addrs);
- return;
- }
- }
- }
-
- /// Adds a physical address range to the free-list allocator, without checking for reservations
- /// inside it.
- ///
- /// ## Safety
- ///
- /// - This allocator must have been created with the DeviceTree that gave us this memory.
- /// - The region must be a valid physical memory range.
- /// - The region must be page-aligned.
- /// - The region must not overlap with any reservations.
- /// - Paging must not be enabled for the entire lifetime of this allocator.
- pub unsafe fn add_range_unchecked(&mut self, addrs: Range<usize>) {
- // Initialize the list node.
- let mut new_node = FreeListAddr::new(addrs);
-
- // Walk forwards through the list until the node we're going to put ourselves before has a
- // size greater or equal to ours.
- let mut spot_to_store = &mut self.head;
- while let Some(existing_node) = spot_to_store {
- if existing_node.pages_after() < new_node.pages_after() {
- spot_to_store = spot_to_store.as_mut().unwrap().next_mut();
- } else {
- break;
- }
- }
-
- *new_node.next_mut() = spot_to_store.take();
- *spot_to_store = Some(new_node);
- self.len += 1;
- }
-
- /// Returns an iterator that removes ranges from the free-list. Ranges are returned sorted from
- /// smallest to largest.
- pub fn drain<'iter: 'dt>(&'iter mut self) -> impl 'dt + Iterator<Item = Range<usize>> {
- iter::from_fn(move || self.pop())
- }
-
- /// Returns whether the free-list is empty.
- pub fn is_empty(&self) -> bool {
- self.len == 0
- }
-
- /// Returns an iterator over the reserved ranges of addresses.
- ///
- /// Addresses may be reserved by:
- ///
- /// - Overlapping with the kernel, including the early-boot kernel stack.
- /// - Overlapping with the DeviceTree.
- /// - Overlapping with any memory reservations in the DeviceTree.
- fn iter_reserved(&self) -> impl '_ + Iterator<Item = Range<usize>> {
- // Get the boundaries of the kernel.
- let kernel = kernel_boundaries();
-
- // As a sanity check, make sure this function is inside those boundaries...
- assert!(kernel.contains(&(FreeList::iter_reserved as usize)));
-
- // Check if the address is either in the kernel or somewhere in the DeviceTree.
- iter::once(kernel).chain(self.device_tree.iter_reserved())
- }
-
- /// Returns the number of regions in the free-list.
- pub fn len(&self) -> usize {
- self.len
- }
-
- /// Pops the smallest range from the free-list, returning it.
- pub fn pop(&mut self) -> Option<Range<usize>> {
- match self.head.take() {
- Some(head) => {
- let (addrs, next) = head.take();
- self.head = next;
- Some(addrs)
- }
- None => None,
- }
- }
-}
-
-impl<'dt> fmt::Debug for FreeList<'dt> {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- struct FreeListDebug<'a>(&'a Option<FreeListAddr>);
-
- impl<'a> fmt::Debug for FreeListDebug<'a> {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let mut addr = self.0;
- fmt.debug_list()
- .entries(iter::from_fn(|| match addr {
- Some(head) => {
- addr = head.next();
- Some(head)
- }
- None => None,
- }))
- .finish()
- }
- }
-
- fmt.debug_struct("FreeList")
- .field("device_tree", &self.device_tree)
- .field("addrs", &FreeListDebug(&self.head))
- .finish()
- }
-}
-
-/// An address in the free-list.
-///
-/// # Invariants
-///
-/// - The pointer must point to physical memory.
-/// - The FreeListHeader must be initialized.
-/// - The region being pointed to must actually be free.
-/// - The region being pointed to must be unreserved.
-struct FreeListAddr(NonNull<FreeListHeader>);
-
-impl FreeListAddr {
- /// Initializes the page with the given address range, with no next address.
- ///
- /// # Safety
- ///
- /// - All of the invariants in the struct-level documentation, except the header being
- /// initialized (this function will initialize it).
- #[requires(addrs.start != 0)]
- #[requires(addrs.start & (PAGE_SIZE - 1) == 0)]
- #[requires(addrs.end & (PAGE_SIZE - 1) == 0)]
- #[requires(addrs.start < addrs.end)]
- unsafe fn new(addrs: Range<usize>) -> FreeListAddr {
- let addr = addrs.start;
- *(addr as *mut FreeListHeader) = FreeListHeader {
- next: None,
- pages_after: ((addrs.end - addrs.start) >> PAGE_SIZE_BITS) - 1,
- };
-
- FreeListAddr(NonNull::new_unchecked(addr as *mut FreeListHeader))
- }
-
- /// Returns the `next` field of the header.
- fn next(&self) -> &Option<FreeListAddr> {
- // SAFETY: The invariants of FreeListAddr::new are sufficient to make this valid.
- unsafe { &self.0.as_ref().next }
- }
-
- /// Returns a mutable reference to the `next` field of the header.
- fn next_mut(&mut self) -> &mut Option<FreeListAddr> {
- // SAFETY: The invariants of FreeListAddr::new are sufficient to make this valid.
- unsafe { &mut self.0.as_mut().next }
- }
-
- /// Returns the number of pages *after* the first one in this region.
- fn pages_after(&self) -> usize {
- // SAFETY: The invariants of FreeListAddr::new are sufficient to make this valid.
- unsafe { self.0.as_ref().pages_after }
- }
-
- /// Returns the range of addresses in the region and the address of the next region.
- fn take(self) -> (Range<usize>, Option<FreeListAddr>) {
- let start = self.0.as_ptr() as usize;
- let len_pages = 1 + self.pages_after();
- let len_bytes = len_pages << PAGE_SIZE_BITS;
- let addrs = start..start + len_bytes;
-
- // SAFETY: The invariants of FreeListAddr::new are sufficient to make the read valid.
- // Because we drop the address, we know we won't accidentally have any ownership issues
- // with passing ownership up.
- let header = unsafe { ptr::read(self.0.as_ptr()) };
-
- (addrs, header.next)
- }
-}
-
-impl fmt::Debug for FreeListAddr {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let addr = self.0.as_ptr() as *const u8;
- let len_pages = 1 + self.pages_after();
- let len_bytes = len_pages << PAGE_SIZE_BITS;
- write!(
- fmt,
- "{:p}..{:p} ({} page{})",
- addr,
- addr.wrapping_add(len_bytes),
- len_pages,
- if len_pages == 1 { "" } else { "s" }
- )
- }
-}
-
-struct FreeListHeader {
- /// The physical address of the next free-list header.
- next: Option<FreeListAddr>,
-
- /// The number of pages after this one.
- pages_after: usize,
-}