kernel_alloc/frame_alloc.rs
1//! # Minimal Bitmap-based Physical Memory Manager (PMM)
2//!
3//! This module provides a minimal, no-heap physical memory manager for 4K frames,
4//! using a bitmap to track free/used frames in a fixed region. It is suitable for
5//! early kernel use or as a foundation for a more advanced PMM.
6//!
7//! ## Features
8//! - Tracks allocation and freeing of 4K frames using a bitmap.
9//! - No heap required; all state is stored inline.
10//! - Can be extended to initialize from a memory map.
11//!
12//! ## Usage Example
13//! ```rust
14//! use kernel_alloc::frame_alloc::BitmapFrameAlloc;
15//! use kernel_vmem::FrameAlloc;
16//! let mut pmm = BitmapFrameAlloc::new();
17//! let frame = pmm.alloc_4k();
18//! if let Some(pa) = frame {
19//!     // Use the physical address...
20//!     pmm.free_4k(pa);
21//! }
22//! ```
23//!
24//! ## Safety
25//! - Only physical addresses within the managed region are tracked.
26//! - The user must ensure that reserved/used frames (e.g., kernel, bootloader) are marked as used before allocation.
27//! - No synchronization is provided; not thread-safe.
28
29use kernel_vmem::FrameAlloc;
30use kernel_vmem::addresses::{PhysicalAddress, PhysicalPage, Size4K};
31
32const PHYS_MEM_START: u64 = 0x0010_0000; // 1 MiB, example
33const PHYS_MEM_SIZE: u64 = 64 * 1024 * 1024; // 64 MiB, example
34const FRAME_SIZE: u64 = 4096;
35const NUM_FRAMES: usize = (PHYS_MEM_SIZE / FRAME_SIZE) as usize;
36
37/// Minimal bitmap-based PMM for 4K frames in a fixed region.
38///
39/// This type manages a fixed region of physical memory, tracking free/used 4K frames
40/// using a bitmap. It supports allocation and freeing, but does not require a heap.
41///
42/// # Example
43/// ```rust
44/// use kernel_alloc::frame_alloc::BitmapFrameAlloc;
45/// use kernel_vmem::FrameAlloc;
46/// let mut pmm = BitmapFrameAlloc::new();
47/// let frame = pmm.alloc_4k();
48/// if let Some(pa) = frame {
49///     // Use the physical address...
50///     pmm.free_4k(pa);
51/// }
52/// ```
53///
54/// # Safety
55/// - Only physical addresses within the managed region are tracked.
56/// - The user must ensure that reserved/used frames (e.g., kernel, bootloader) are marked as used before allocation.
57/// - No synchronization is provided; not thread-safe.
58pub struct BitmapFrameAlloc {
59    bitmap: [u64; NUM_FRAMES.div_ceil(64)],
60    base: u64,
61}
62
63impl Default for BitmapFrameAlloc {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69impl BitmapFrameAlloc {
70    #[must_use]
71    pub const fn new() -> Self {
72        Self {
73            bitmap: [0; NUM_FRAMES.div_ceil(64)],
74            base: PHYS_MEM_START,
75        }
76    }
77
78    /// Mark a frame as used (allocated).
79    pub const fn mark_used(&mut self, frame_idx: usize) {
80        let (word, bit) = (frame_idx / 64, frame_idx % 64);
81        self.bitmap[word] |= 1 << bit;
82    }
83
84    /// Mark a frame as free.
85    pub const fn mark_free(&mut self, frame_idx: usize) {
86        let (word, bit) = (frame_idx / 64, frame_idx % 64);
87        self.bitmap[word] &= !(1 << bit);
88    }
89
90    /// Returns true if the frame is allocated.
91    #[must_use]
92    pub const fn is_used(&self, frame_idx: usize) -> bool {
93        let (word, bit) = (frame_idx / 64, frame_idx % 64);
94        (self.bitmap[word] & (1 << bit)) != 0
95    }
96}
97
98impl FrameAlloc for BitmapFrameAlloc {
99    fn alloc_4k(&mut self) -> Option<PhysicalPage<Size4K>> {
100        for (i, word) in self.bitmap.iter_mut().enumerate() {
101            if *word != u64::MAX {
102                for bit in 0..64 {
103                    let idx = i * 64 + bit;
104                    if idx >= NUM_FRAMES {
105                        break;
106                    }
107                    if (*word & (1 << bit)) == 0 {
108                        *word |= 1 << bit;
109                        let pa = self.base + (idx as u64) * FRAME_SIZE;
110                        return Some(PhysicalPage::from_addr(PhysicalAddress::new(pa)));
111                    }
112                }
113            }
114        }
115        None
116    }
117
118    fn free_4k(&mut self, pa: PhysicalPage<Size4K>) {
119        let idx = ((pa.base().as_u64() - self.base) / FRAME_SIZE) as usize;
120        self.mark_free(idx);
121    }
122}