kernel_api/memory/
allocator.rs

1//! Provides physical memory allocation APIs
2
3#![stable(feature = "kernel_core_api", since = "1.0.0")]
4
5use core::num::NonZero;
6use core::ops::Range;
7use auto_impl::auto_impl;
8
9use super::{Frame, AllocError, PAGE_SIZE};
10
11/// The error returned when an allocation requesting zeroed out memory was unsuccessful
12#[unstable(feature = "kernel_allocation_zeroing", issue = "2")]
13#[derive(Debug, Eq, PartialEq)]
14pub enum ZeroAllocError {
15    /// No allocation could be made
16    AllocError,
17    /// An allocation could be made, but the result is not zeroed
18    Uninit(Frame),
19}
20
21#[unstable(feature = "kernel_allocation_zeroing", issue = "2")]
22#[doc(hidden)]
23impl From<AllocError> for ZeroAllocError {
24    fn from(_: AllocError) -> Self { Self::AllocError }
25}
26
27/// Configuration for creating a new allocator
28#[unstable(feature = "kernel_allocation_new", issue = "5")]
29pub struct Config<'a> {
30    /// The lowest and highest frames that can be allocated
31    /// 
32    /// These should be equal to the minimum and maximum frames in (`regions`)[Self::regions]
33    pub allocation_range: Range<Frame>,
34    
35    /// The regions of memory that are valid to allocate from
36    pub regions: &'a mut dyn Iterator<Item = Range<Frame>>
37}
38
39#[unstable(feature = "kernel_allocation_new", issue = "5")]
40#[non_exhaustive]
41pub struct AllocationMeta {
42    pub region: Range<Frame>
43}
44
45impl AllocationMeta {
46    #[unstable(feature = "kernel_allocation_new", issue = "5")]
47    pub fn new(region: Range<Frame>) -> Self {
48        Self { region }
49    }
50}
51
52#[unstable(feature = "kernel_physical_allocator_non_contiguous", issue = "none")]
53pub struct AllocateNonContiguousRet(Range<Frame>);
54
55#[unstable(feature = "kernel_physical_allocator_non_contiguous", issue = "none")]
56impl IntoIterator for AllocateNonContiguousRet {
57    type Item = Frame;
58    type IntoIter = Range<Frame>;
59
60    fn into_iter(self) -> Self::IntoIter { self.0 }
61}
62
63/// An allocator that managed physical memory
64///
65/// In future, this may be replaced by a more general resource allocator. In that case, this trait will be deprecated
66/// and implementors will be expected to move to the more general trait.
67#[auto_impl(&, Box, Arc)]
68#[stable(feature = "kernel_core_api", since = "1.0.0")]
69pub unsafe trait PhysicalAllocator: Send + Sync {
70    // (Bitmap, Buddy, Watermark, ...)
71
72    // UNRESOLVED: how should errors work into this - does it error early and somehow figure out ahead of time if there's enough free space for the entire allocate, or does the allocation itself happen lazily and so an alloc error can happen on each iteration. If the latter, what happens if you call next() after getting an alloc error?
73    // Allocates `frame_count` frames of physical memory, not necessarily contiguously.
74    // Returns an iterator over the allocated frames.
75    // It is undecided for now whether allocation should be done upfront or lazily as the iterator is polled.
76    //
77    // Implementations should only implement this when there is a fast path compared to [`allocate_contiguous`](Self::allocate_contiguous).
78    // Otherwise the default implementation will use [`allocate_contiguous`](Self::allocate_contiguous).
79    /*fn allocate(&self, frame_count: usize) -> impl Iterator<Item = Result<Frame, AllocError>> where Self: Sized {
80        gen move {
81            let base = self.allocate_contiguous(frame_count);
82            if let Ok(base) = base {
83                for i in 0..frame_count { yield Ok(base + i); }
84            } else { yield Err(AllocError); }
85        }
86    }*/
87
88    #[unstable(feature = "kernel_physical_allocator_non_contiguous", issue = "none")]
89    fn allocate(&self, frame_count: usize) -> Result<AllocateNonContiguousRet, AllocError> {
90        let base = self.allocate_contiguous(frame_count)?;
91        Ok(AllocateNonContiguousRet(base..(base + frame_count)))
92    }
93
94    /// Allocates a contiguous range of physical memory
95    #[stable(feature = "kernel_core_api", since = "1.0.0")]
96    fn allocate_contiguous(&self, frame_count: usize) -> Result<Frame, AllocError>;
97
98    /// Allocates a single [`Frame`]
99    #[stable(feature = "kernel_core_api", since = "1.0.0")]
100    fn allocate_one(&self) -> Result<Frame, AllocError> {
101        let frame = self.allocate(1)?;
102        Ok(frame.into_iter().next().expect("`allocate(1)` must return one frame"))
103    }
104
105    /// Tries to allocate a contiguous region of `frame_count` frames from a prezeroed buffer
106    #[unstable(feature = "kernel_allocation_zeroing", issue = "2")]
107    fn try_allocate_zeroed(&self, frame_count: usize) -> Result<Frame, ZeroAllocError> { Err(ZeroAllocError::Uninit(self.allocate_contiguous(frame_count)?)) }
108
109    /// Allocates a contiguous region of `frame_count` frames, manually zeroing them if there were no prezeroed frames
110    #[unstable(feature = "kernel_allocation_zeroing", issue = "2")]
111    fn allocate_zeroed(&self, frame_count: usize) -> Result<Frame, AllocError> {
112        #[cold]
113        fn do_zero(frame: Frame, frame_count: usize) {
114            let page = frame.to_page();
115
116            unsafe {
117                core::ptr::write_bytes(
118                    page.as_ptr(),
119                    0,
120                    frame_count * PAGE_SIZE
121                );
122            }
123        }
124
125        let frames = self.try_allocate_zeroed(frame_count);
126        match frames {
127            Ok(frame) => Ok(frame),
128            Err(ZeroAllocError::AllocError) => Err(AllocError),
129            Err(ZeroAllocError::Uninit(frame)) => {
130                do_zero(frame, frame_count);
131                Ok(frame)
132            }
133        }
134    }
135
136    /// # Safety
137    /// Must be deallocated with the same allocator that made the allocation
138    #[stable(feature = "kernel_core_api", since = "1.0.0")]
139    unsafe fn deallocate_contiguous(&self, base: Frame, frame_count: NonZero<usize>);
140
141    #[unstable(feature = "kernel_allocation_new", issue = "5")]
142    fn push(&mut self, _allocation: AllocationMeta) { unimplemented!("experimental") }
143
144    #[unstable(feature = "kernel_physical_allocator_location", issue = "none")]
145    fn allocate_at(&self, frame_count: usize, location: SpecificLocation) -> Result<Frame, AllocError>;
146}
147
148#[unstable(feature = "kernel_allocation_new", issue = "5")]
149pub unsafe trait SizedBackingAllocator: PhysicalAllocator + Sized {
150    #[unstable(feature = "kernel_allocation_new", issue = "5")]
151    fn new(config: Config) -> &'static mut dyn PhysicalAllocator;
152}
153
154#[unstable(feature = "kernel_physical_allocator_location", issue = "none")]
155pub enum SpecificLocation {
156    /// The mapping must be aligned to a specific number of [`Frame`]s
157    Aligned(NonZero<u32>),
158    /// The mapping will fail if it cannot be allocated at this exact location
159    At(Frame),
160    /// The mapping must be below this location, aligned to `with_alignment` number of [`Frame`]s
161    Below { location: Frame, with_alignment: NonZero<u32> }
162}
163
164#[unstable(feature = "kernel_physical_allocator_location", issue = "none")]
165pub enum Location {
166    Any,
167    Specific(SpecificLocation)
168}
169
170#[unstable(feature = "kernel_physical_allocator_location", issue = "none")]
171pub enum AlignError {
172    OomError,
173    Unaligned(AllocateNonContiguousRet)
174}