Skip to main content

os/mm/
frame_allocator.rs

1//! Implementation of [`FrameAllocator`] which
2//! controls all the frames in the operating system.
3
4use super::{PhysAddr, PhysPageNum};
5use crate::config::MEMORY_END;
6use crate::sync::UPSafeCell;
7use alloc::vec::Vec;
8use core::fmt::{self, Debug, Formatter};
9use lazy_static::*;
10
11/// manage a frame which has the same lifecycle as the tracker
12pub struct FrameTracker {
13    pub ppn: PhysPageNum,
14}
15
16impl FrameTracker {
17    pub fn new(ppn: PhysPageNum) -> Self {
18        // page cleaning
19        let bytes_array = ppn.get_bytes_array();
20        for i in bytes_array {
21            *i = 0;
22        }
23        Self { ppn }
24    }
25}
26
27impl Debug for FrameTracker {
28    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
29        f.write_fmt(format_args!("FrameTracker:PPN={:#x}", self.ppn.0))
30    }
31}
32
33impl Drop for FrameTracker {
34    fn drop(&mut self) {
35        frame_dealloc(self.ppn);
36    }
37}
38
39trait FrameAllocator {
40    fn new() -> Self;
41    fn alloc(&mut self) -> Option<PhysPageNum>;
42    fn dealloc(&mut self, ppn: PhysPageNum);
43}
44
45/// an implementation for frame allocator
46pub struct StackFrameAllocator {
47    current: usize,
48    end: usize,
49    recycled: Vec<usize>,
50}
51
52impl StackFrameAllocator {
53    pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) {
54        self.current = l.0;
55        self.end = r.0;
56    }
57}
58impl FrameAllocator for StackFrameAllocator {
59    fn new() -> Self {
60        Self {
61            current: 0,
62            end: 0,
63            recycled: Vec::new(),
64        }
65    }
66    fn alloc(&mut self) -> Option<PhysPageNum> {
67        if let Some(ppn) = self.recycled.pop() {
68            Some(ppn.into())
69        } else if self.current == self.end {
70            None
71        } else {
72            self.current += 1;
73            Some((self.current - 1).into())
74        }
75    }
76    fn dealloc(&mut self, ppn: PhysPageNum) {
77        let ppn = ppn.0;
78        // validity check
79        if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) {
80            panic!("Frame ppn={:#x} has not been allocated!", ppn);
81        }
82        // recycle
83        self.recycled.push(ppn);
84    }
85}
86
87type FrameAllocatorImpl = StackFrameAllocator;
88
89lazy_static! {
90    /// frame allocator instance through lazy_static!
91    pub static ref FRAME_ALLOCATOR: UPSafeCell<FrameAllocatorImpl> =
92        unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) };
93}
94
95/// initiate the frame allocator using `ekernel` and `MEMORY_END`
96pub fn init_frame_allocator() {
97    unsafe extern "C" {
98        safe fn ekernel();
99    }
100    FRAME_ALLOCATOR.exclusive_access().init(
101        PhysAddr::from(linker_symbol_addr!(ekernel)).ceil(),
102        PhysAddr::from(MEMORY_END).floor(),
103    );
104}
105
106/// allocate a frame
107pub fn frame_alloc() -> Option<FrameTracker> {
108    FRAME_ALLOCATOR
109        .exclusive_access()
110        .alloc()
111        .map(FrameTracker::new)
112}
113
114/// deallocate a frame
115fn frame_dealloc(ppn: PhysPageNum) {
116    FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
117}
118
119#[allow(unused)]
120/// a simple test for frame allocator
121pub fn frame_allocator_test() {
122    let mut v: Vec<FrameTracker> = Vec::new();
123    for i in 0..5 {
124        let frame = frame_alloc().unwrap();
125        println!("{:?}", frame);
126        v.push(frame);
127    }
128    v.clear();
129    for i in 0..5 {
130        let frame = frame_alloc().unwrap();
131        println!("{:?}", frame);
132        v.push(frame);
133    }
134    drop(v);
135    println!("frame_allocator_test passed!");
136}