Skip to main content

strat9_kernel/memory/
zone.rs

1// Memory zone management for buddy allocator
2
3use x86_64::PhysAddr;
4
5/// Memory zone types
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[repr(u8)]
8pub enum ZoneType {
9    /// DMA zone: 0-16MB (for legacy ISA DMA)
10    DMA = 0,
11    /// Normal zone: 16MB-896MB (for most allocations)
12    Normal = 1,
13    /// HighMem zone: > 896MB (for high memory)
14    HighMem = 2,
15}
16
17impl ZoneType {
18    /// Number of zones supported in Phase 1
19    pub const COUNT: usize = 3;
20}
21
22/// Maximum buddy order (0-11 for 4KB to 8MB blocks)
23pub const MAX_ORDER: usize = 11;
24
25/// Bitmap used by buddy coalescing logic.
26///
27/// The storage is provided externally (stolen from early boot free pages)
28/// and addressed through HHDM.
29#[derive(Debug, Clone, Copy)]
30pub struct BuddyBitmap {
31    pub data: *mut u8,
32    pub num_bits: usize,
33}
34
35impl BuddyBitmap {
36    /// Empty bitmap for const initialization.
37    pub const fn empty() -> Self {
38        Self {
39            data: core::ptr::null_mut(),
40            num_bits: 0,
41        }
42    }
43
44    /// Returns whether empty.
45    #[inline]
46    pub fn is_empty(&self) -> bool {
47        self.data.is_null() || self.num_bits == 0
48    }
49
50    /// Toggle a bit and return its new value.
51    #[inline]
52    pub fn toggle(&self, idx: usize) -> bool {
53        debug_assert!(idx < self.num_bits);
54        let byte_idx = idx >> 3;
55        let mask = 1u8 << (idx & 7);
56        unsafe {
57            let byte = self.data.add(byte_idx);
58            let new_val = *byte ^ mask;
59            *byte = new_val;
60            (new_val & mask) != 0
61        }
62    }
63
64    /// Performs the test operation.
65    #[inline]
66    pub fn test(&self, idx: usize) -> bool {
67        if idx >= self.num_bits || self.is_empty() {
68            return false;
69        }
70        let byte_idx = idx >> 3;
71        let mask = 1u8 << (idx & 7);
72        unsafe { (*self.data.add(byte_idx) & mask) != 0 }
73    }
74
75    /// Performs the set operation.
76    #[inline]
77    pub fn set(&self, idx: usize) {
78        debug_assert!(idx < self.num_bits);
79        let byte_idx = idx >> 3;
80        let mask = 1u8 << (idx & 7);
81        unsafe {
82            *self.data.add(byte_idx) |= mask;
83        }
84    }
85
86    /// Performs the clear operation.
87    #[inline]
88    pub fn clear(&self, idx: usize) {
89        debug_assert!(idx < self.num_bits);
90        let byte_idx = idx >> 3;
91        let mask = 1u8 << (idx & 7);
92        unsafe {
93            *self.data.add(byte_idx) &= !mask;
94        }
95    }
96}
97
98/// Memory zone with buddy allocator free lists
99pub struct Zone {
100    /// Zone type
101    pub zone_type: ZoneType,
102
103    /// Base physical address of this zone
104    pub base: PhysAddr,
105
106    /// Total number of managed pages in this zone.
107    pub page_count: usize,
108
109    /// Total address span covered by this zone metadata, in pages.
110    ///
111    /// Unlike `page_count`, this includes holes and is used to size bitmaps.
112    pub span_pages: usize,
113
114    /// Number of allocated pages
115    pub allocated: usize,
116
117    /// Free lists for each order (0-11), intrusive list head as physical addr.
118    /// 0 means empty.
119    pub free_lists: [u64; MAX_ORDER + 1],
120
121    /// Per-order buddy pair bitmaps (Linux-style parity map).
122    pub buddy_bitmaps: [BuddyBitmap; MAX_ORDER + 1],
123
124    /// Optional debug bitmap: 1 bit per page = allocated.
125    #[cfg(debug_assertions)]
126    pub alloc_bitmap: BuddyBitmap,
127}
128
129impl Zone {
130    /// Create a new empty zone
131    pub const fn new(zone_type: ZoneType) -> Self {
132        Zone {
133            zone_type,
134            base: PhysAddr::new(0),
135            page_count: 0,
136            span_pages: 0,
137            allocated: 0,
138            free_lists: [0; MAX_ORDER + 1],
139            buddy_bitmaps: [BuddyBitmap::empty(); MAX_ORDER + 1],
140            #[cfg(debug_assertions)]
141            alloc_bitmap: BuddyBitmap::empty(),
142        }
143    }
144
145    /// Check if an address is within this zone
146    pub fn contains_address(&self, addr: PhysAddr) -> bool {
147        let zone_start = self.base.as_u64();
148        let zone_end = zone_start + (self.span_pages as u64 * 4096);
149        let addr_val = addr.as_u64();
150        addr_val >= zone_start && addr_val < zone_end
151    }
152
153    /// Get number of available (free) pages
154    pub fn available_pages(&self) -> usize {
155        self.page_count.saturating_sub(self.allocated)
156    }
157}
158
159// SAFETY: access is protected by the allocator lock.
160unsafe impl Send for BuddyBitmap {}