xref: /DragonOS/kernel/src/mm/no_init.rs (revision c75ef4e2126c180bf04c08635ffa5a278619c035)
1 //! 该文件用于系统启动早期,内存管理器初始化之前,提供一些简单的内存映射功能
2 //!
3 //! 映射关系为:
4 //!
5 //! 虚拟地址 0-100M与虚拟地址 0x8000_0000_0000 - 0x8000_0640_0000 之间具有重映射关系。
6 //! 也就是说,他们的第二级页表在最顶级页表中,占用了第0和第256个页表项。
7 //!
8 //! 对于x86:
9 //! 这里假设在内核引导文件中,已经填写了前100M的页表,其中,前50M是真实映射到内存的,后面的仅仅创建了页表,表项全部为0。
10 
11 use bitmap::{traits::BitMapOps, StaticBitmap};
12 
13 use crate::{
14     libs::spinlock::SpinLock,
15     mm::{MMArch, MemoryManagementArch, PhysAddr},
16 };
17 
18 use core::marker::PhantomData;
19 
20 use super::{
21     allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage},
22     page::PageFlags,
23     PageTableKind, VirtAddr,
24 };
25 
26 /// 用于存储重映射页表的位图和页面
27 static EARLY_IOREMAP_PAGES: SpinLock<EarlyIoRemapPages> = SpinLock::new(EarlyIoRemapPages::new());
28 
29 /// 早期重映射使用的页表
30 #[repr(C)]
31 #[repr(align(4096))]
32 #[derive(Clone, Copy)]
33 struct EarlyRemapPage {
34     data: [u64; MMArch::PAGE_SIZE],
35 }
36 
37 impl EarlyRemapPage {
38     /// 清空数据
39     fn zero(&mut self) {
40         self.data.fill(0);
41     }
42 }
43 
44 #[repr(C)]
45 struct EarlyIoRemapPages {
46     pages: [EarlyRemapPage; Self::EARLY_REMAP_PAGES_NUM],
47     bmp: StaticBitmap<{ Self::EARLY_REMAP_PAGES_NUM }>,
48 }
49 
50 impl EarlyIoRemapPages {
51     /// 预留的用于在内存管理初始化之前,映射内存所使用的页表数量
52     pub const EARLY_REMAP_PAGES_NUM: usize = 256;
53     pub const fn new() -> Self {
54         Self {
55             pages: [EarlyRemapPage {
56                 data: [0; MMArch::PAGE_SIZE],
57             }; Self::EARLY_REMAP_PAGES_NUM],
58             bmp: StaticBitmap::new(),
59         }
60     }
61 
62     /// 分配一个页面
63     ///
64     /// 如果成功,返回虚拟地址
65     ///
66     /// 如果失败,返回None
67     pub fn allocate_page(&mut self) -> Option<VirtAddr> {
68         if let Some(index) = self.bmp.first_false_index() {
69             self.bmp.set(index, true);
70             // 清空数据
71             self.pages[index].zero();
72 
73             let p = &self.pages[index] as *const EarlyRemapPage as usize;
74             let vaddr = VirtAddr::new(p);
75             assert!(vaddr.check_aligned(MMArch::PAGE_SIZE));
76             return Some(vaddr);
77         } else {
78             return None;
79         }
80     }
81 
82     pub fn free_page(&mut self, addr: VirtAddr) {
83         // 判断地址是否合法
84         let start_vaddr = &self.pages[0] as *const EarlyRemapPage as usize;
85         let offset = addr.data() - start_vaddr;
86         let index = offset / MMArch::PAGE_SIZE;
87         if index < Self::EARLY_REMAP_PAGES_NUM {
88             assert_eq!(self.bmp.get(index).unwrap(), true);
89             self.bmp.set(index, false);
90         }
91     }
92 }
93 
94 /// 伪分配器
95 struct PseudoAllocator<MMA> {
96     phantom: PhantomData<MMA>,
97 }
98 
99 impl<MMA: MemoryManagementArch> PseudoAllocator<MMA> {
100     pub const fn new() -> Self {
101         Self {
102             phantom: PhantomData,
103         }
104     }
105 }
106 
107 /// 为NoInitAllocator实现FrameAllocator
108 impl<MMA: MemoryManagementArch> FrameAllocator for PseudoAllocator<MMA> {
109     unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> {
110         assert!(count.data() == 1);
111         let vaddr = EARLY_IOREMAP_PAGES.lock_irqsave().allocate_page()?;
112         let paddr = MMA::virt_2_phys(vaddr)?;
113         return Some((paddr, count));
114     }
115 
116     unsafe fn free(&mut self, address: PhysAddr, count: PageFrameCount) {
117         assert_eq!(count.data(), 1);
118         assert!(address.check_aligned(MMA::PAGE_SIZE));
119         let vaddr = MMA::phys_2_virt(address);
120         if let Some(vaddr) = vaddr {
121             EARLY_IOREMAP_PAGES.lock_irqsave().free_page(vaddr);
122         }
123     }
124     /// @brief: 获取内存区域页帧的使用情况
125     /// @param  self
126     /// @return 页帧的使用情况
127     unsafe fn usage(&self) -> PageFrameUsage {
128         // 暂时不支持
129         panic!("NoInitAllocator can't get page frame usage");
130     }
131 }
132 
133 /// Use pseudo mapper to map physical memory to virtual memory.
134 ///
135 /// ## Safety
136 ///
137 /// 调用该函数时,必须保证内存管理器尚未初始化。否则将导致未定义的行为
138 ///
139 /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准)
140 pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) {
141     assert!(vaddr.check_aligned(MMArch::PAGE_SIZE));
142     assert!(paddr.check_aligned(MMArch::PAGE_SIZE));
143 
144     let mut pseudo_allocator = PseudoAllocator::<MMArch>::new();
145 
146     let mut mapper = crate::mm::page::PageMapper::<MMArch, _>::new(
147         PageTableKind::Kernel,
148         MMArch::table(PageTableKind::Kernel),
149         &mut pseudo_allocator,
150     );
151 
152     let flags: PageFlags<MMArch> = PageFlags::new().set_write(true).set_execute(true);
153 
154     for i in 0..count.data() {
155         let vaddr = vaddr + i * MMArch::PAGE_SIZE;
156         let paddr = paddr + i * MMArch::PAGE_SIZE;
157         let flusher = mapper.map_phys(vaddr, paddr, flags).unwrap();
158         flusher.ignore();
159     }
160 
161     mapper.make_current();
162 }
163