Coverage Report

Created: 2026-07-25 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MigTD/deps/td-shim/td-paging/src/frame.rs
Line
Count
Source
1
// Copyright (c) 2021 Intel Corporation
2
// Copyright (c) 2022 Alibaba Cloud
3
//
4
// SPDX-License-Identifier: BSD-2-Clause-Patent
5
6
use alloc::vec;
7
use alloc::vec::Vec;
8
use core::ops::Range;
9
use log::{info, trace};
10
use spin::Mutex;
11
use x86_64::{
12
    structures::paging::{FrameAllocator, PhysFrame, Size4KiB},
13
    PhysAddr,
14
};
15
16
use super::consts::PAGE_SIZE;
17
18
/// Global page table page allocator.
19
pub static FRAME_ALLOCATOR: Mutex<BMFrameAllocator> = Mutex::new(BMFrameAllocator::empty());
20
21
struct FrameAlloc {
22
    //  A bit of `1` means available.
23
    bitmap: Vec<u128>,
24
}
25
26
impl FrameAlloc {
27
0
    fn new(num_frames: usize) -> Self {
28
0
        Self {
29
0
            bitmap: vec![0u128; (num_frames + 127) / 128],
30
0
        }
31
0
    }
32
33
0
    fn alloc(&mut self) -> Option<usize> {
34
0
        for (idx, map) in self.bitmap.iter_mut().enumerate() {
35
0
            let pos = map.trailing_zeros();
36
0
            if pos < 128 {
37
0
                *map &= !(1 << pos);
38
0
                return Some(idx * 128 + pos as usize);
39
0
            }
40
        }
41
42
0
        None
43
0
    }
44
45
0
    fn free(&mut self, range: Range<usize>) {
46
0
        for idx in range {
47
0
            if idx >= self.bitmap.len() * 128 {
48
0
                panic!("invalid page frame index {} for FrameAlloc::free()!", idx);
49
0
            }
50
0
            if self.bitmap[idx / 128] & (1 << (idx % 128)) != 0 {
51
0
                panic!(
52
0
                    "try to free unallocated page frame index {} for FrameAlloc::free()!",
53
                    idx
54
                );
55
0
            }
56
0
            self.bitmap[idx / 128] |= 1 << (idx % 128);
57
        }
58
0
    }
59
60
0
    fn reserve(&mut self, idx: usize) {
61
0
        if idx >= self.bitmap.len() * 128 {
62
0
            panic!("invalid page frame index {} for FrameAlloc::free()!", idx);
63
0
        }
64
0
        if self.bitmap[idx / 128] & (1 << (idx % 128)) == 0 {
65
0
            panic!(
66
0
                "try to reserve unavailable page frame index {} for FrameAlloc::free()!",
67
                idx
68
            );
69
0
        }
70
0
        self.bitmap[idx / 128] &= !(1 << (idx % 128));
71
0
    }
72
}
73
74
pub struct BMFrameAllocator {
75
    base: usize,
76
    size: usize,
77
    inner: FrameAlloc,
78
}
79
80
#[allow(dead_code)]
81
impl BMFrameAllocator {
82
    const fn empty() -> Self {
83
        Self {
84
            base: 0,
85
            size: 0,
86
            inner: FrameAlloc { bitmap: Vec::new() },
87
        }
88
    }
89
90
    // Caller needs to ensure:
91
    // - base is page aligned
92
    // - size is page aligned
93
    // - base + size doesn't wrap around
94
0
    fn new(base: usize, size: usize) -> Self {
95
0
        let page_count = size / PAGE_SIZE;
96
0
        let mut inner = FrameAlloc::new(page_count);
97
98
0
        inner.free(0..page_count);
99
100
0
        Self { base, size, inner }
101
0
    }
102
103
0
    fn alloc(&mut self) -> Option<usize> {
104
0
        let ret = self.inner.alloc().map(|idx| idx * PAGE_SIZE + self.base);
105
0
        trace!("Allocate frame: {:x?}\n", ret);
106
0
        ret
107
0
    }
108
109
0
    pub(crate) fn reserve(&mut self, addr: u64) {
110
0
        if addr > usize::MAX as u64
111
0
            || (addr as usize) < self.base
112
0
            || (addr as usize) >= self.base + self.size
113
        {
114
0
            panic!(
115
0
                "Invalid address 0x{:x} to BMFrameAllocator::reserve()",
116
                addr
117
            );
118
0
        }
119
0
        self.inner.reserve((addr as usize - self.base) / PAGE_SIZE);
120
0
    }
121
}
122
123
unsafe impl FrameAllocator<Size4KiB> for BMFrameAllocator {
124
0
    fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
125
0
        let addr = self.alloc()?;
126
0
        Some(PhysFrame::containing_address(
127
0
            PhysAddr::try_new(addr as u64).ok()?,
128
        ))
129
0
    }
130
}
131
132
/// Initialize the physical frame allocator.
133
0
pub(super) fn init(base: u64, size: usize) {
134
0
    let mut allocator = FRAME_ALLOCATOR.lock();
135
0
    if allocator.base == 0 && allocator.size == 0 {
136
0
        *allocator = BMFrameAllocator::new(base as usize, size);
137
        // The first frame should've already been allocated to level 4 PT
138
        // Safe since the PAGE_TABLE_SIZE can be ensured
139
0
        allocator.alloc().unwrap();
140
0
        info!(
141
0
            "Frame allocator init done: {:#x?}\n",
142
0
            base..base + size as u64
143
        );
144
0
    }
145
0
}
146
147
#[cfg(test)]
148
mod tests {
149
    use super::*;
150
151
    const PAGE_TABLE_BASE: u64 = 0x800000;
152
    const PAGE_TABLE_SIZE: usize = 0x800000;
153
    const NUM_PAGE_TABLE_FRAME: usize = PAGE_TABLE_SIZE / PAGE_SIZE;
154
155
    #[test]
156
    fn test_frame_alloc() {
157
        let mut allocator = FrameAlloc::new(NUM_PAGE_TABLE_FRAME);
158
159
        assert_eq!(allocator.bitmap[0] & 0x1, 0);
160
        assert_eq!(allocator.bitmap[0] & 0x2, 0);
161
        assert_eq!(allocator.bitmap[0] & 0x4, 0);
162
163
        allocator.free(0..3);
164
        assert_eq!(allocator.bitmap[0] & 0x1, 0x1);
165
        assert_eq!(allocator.bitmap[0] & 0x2, 0x2);
166
        assert_eq!(allocator.bitmap[0] & 0x4, 0x4);
167
168
        allocator.reserve(0);
169
        assert_eq!(allocator.bitmap[0] & 0x1, 0x0);
170
        assert_eq!(allocator.bitmap[0] & 0x2, 0x2);
171
        assert_eq!(allocator.bitmap[0] & 0x4, 0x4);
172
173
        assert_eq!(allocator.alloc().unwrap(), 1);
174
        assert_eq!(allocator.bitmap[0] & 0x1, 0x0);
175
        assert_eq!(allocator.bitmap[0] & 0x2, 0x0);
176
        assert_eq!(allocator.bitmap[0] & 0x4, 0x4);
177
178
        allocator.free(1..2);
179
        assert_eq!(allocator.bitmap[0] & 0x1, 0x0);
180
        assert_eq!(allocator.bitmap[0] & 0x2, 0x2);
181
        assert_eq!(allocator.bitmap[0] & 0x4, 0x4);
182
183
        assert_eq!(allocator.alloc().unwrap(), 1);
184
        assert_eq!(allocator.alloc().unwrap(), 2);
185
        assert!(allocator.alloc().is_none());
186
        assert!(allocator.alloc().is_none());
187
    }
188
189
    #[test]
190
    #[should_panic]
191
    fn test_frame_free_invalid_index() {
192
        let mut allocator = FrameAlloc::new(NUM_PAGE_TABLE_FRAME);
193
194
        allocator.free(0..NUM_PAGE_TABLE_FRAME + 1);
195
    }
196
197
    #[test]
198
    #[should_panic]
199
    fn test_frame_free_invalid_state() {
200
        let mut allocator = FrameAlloc::new(NUM_PAGE_TABLE_FRAME);
201
202
        allocator.free(0..3);
203
        allocator.free(0..3);
204
    }
205
206
    #[test]
207
    #[should_panic]
208
    fn test_frame_reserve_invalid_index() {
209
        let mut allocator = FrameAlloc::new(NUM_PAGE_TABLE_FRAME);
210
211
        allocator.reserve(NUM_PAGE_TABLE_FRAME);
212
    }
213
214
    #[test]
215
    #[should_panic]
216
    fn test_frame_reserve_invalid_state() {
217
        let mut allocator = FrameAlloc::new(NUM_PAGE_TABLE_FRAME);
218
219
        allocator.free(0..3);
220
        allocator.reserve(1);
221
        allocator.reserve(1);
222
    }
223
224
    #[test]
225
    fn test_empty_bm_allocator() {
226
        let mut allocator = BMFrameAllocator::empty();
227
        assert!(allocator.alloc().is_none());
228
        assert!(allocator.alloc().is_none());
229
    }
230
231
    #[test]
232
    fn test_bm_allocator() {
233
        init(PAGE_TABLE_BASE, PAGE_TABLE_SIZE);
234
        let mut allocator = FRAME_ALLOCATOR.lock();
235
236
        // First page has been allocated by init(), try second page
237
        allocator.reserve(PAGE_TABLE_BASE + PAGE_SIZE as u64);
238
        allocator.reserve(PAGE_TABLE_BASE + PAGE_TABLE_SIZE as u64 - 1);
239
        assert_eq!(
240
            allocator.allocate_frame().unwrap().start_address().as_u64(),
241
            PAGE_TABLE_BASE + 2 * PAGE_SIZE as u64
242
        );
243
    }
244
}