xref: /DragonOS/kernel/src/mm/early_ioremap.rs (revision 3959e94df38073fdb80b199777015f95611ba05f)
1 use system_error::SystemError;
2 
3 use crate::{
4     arch::MMArch,
5     libs::{
6         align::{page_align_down, page_align_up},
7         spinlock::SpinLock,
8     },
9     mm::no_init::{pseudo_map_phys, pseudo_map_phys_ro, pseudo_unmap_phys},
10 };
11 
12 use super::{allocator::page_frame::PageFrameCount, MemoryManagementArch, PhysAddr, VirtAddr};
13 
14 static SLOTS: SpinLock<[Slot; EarlyIoRemap::SLOT_CNT]> =
15     SpinLock::new([Slot::DEFAULT; EarlyIoRemap::SLOT_CNT]);
16 
17 /// 早期IO映射机制
18 ///
19 /// 该机制在内存管理初始化之前,提供IO重映射的功能。
20 ///
21 /// ## 注意
22 ///
23 /// 该机制使用固定大小的slot来记录所有的映射,
24 /// 而这些映射空间是有限的,由MMArch::FIXMAP_SIZE指定
25 pub struct EarlyIoRemap;
26 
27 impl EarlyIoRemap {
28     const SLOT_CNT: usize = MMArch::FIXMAP_SIZE / MMArch::PAGE_SIZE;
29 
30     /// 把物理内存映射到虚拟内存中(物理地址不要求对齐
31     ///
32     /// ## 参数
33     ///
34     /// - phys: 物理内存地址(不需要对齐)
35     /// - size: 映射的内存大小
36     /// - read_only: 映射区与是否只读
37     ///
38     /// ## 返回值
39     ///
40     /// - 成功: (phys对应的虚拟内存地址)
41     /// - Err(SystemError::ENOMEM): 可用的slot不足
42     #[allow(dead_code)]
43     pub fn map_not_aligned(
44         mut phys: PhysAddr,
45         mut size: usize,
46         read_only: bool,
47     ) -> Result<VirtAddr, SystemError> {
48         // kdebug!("map not aligned phys:{phys:?}, size:{size:?}, read_only:{read_only:?}");
49 
50         let offset = phys.data() - page_align_down(phys.data());
51         size += offset;
52         phys -= offset;
53 
54         let (map_vaddr, _) = Self::map(phys, size, read_only)?;
55         return Ok(map_vaddr + offset);
56     }
57 
58     /// 把物理内存映射到虚拟内存中
59     ///
60     /// ## 说明
61     ///
62     /// 虚拟内存由early io remap机制自动分配。
63     ///
64     /// ## 参数
65     ///
66     /// - phys: 物理内存地址(需要按页对齐)
67     /// - size: 映射的内存大小
68     /// - read_only: 映射区与是否只读
69     ///
70     /// ## 返回值
71     ///
72     /// - 成功: (虚拟内存地址, 映射的内存大小)
73     /// - Err(SystemError::ENOMEM): 可用的slot不足
74     /// - Err(SystemError::EINVAL): 传入的物理地址没有对齐
75     #[allow(dead_code)]
76     pub fn map(
77         phys: PhysAddr,
78         size: usize,
79         read_only: bool,
80     ) -> Result<(VirtAddr, usize), SystemError> {
81         if !phys.check_aligned(MMArch::PAGE_SIZE) {
82             return Err(SystemError::EINVAL);
83         }
84 
85         // kdebug!("Early io remap:{phys:?}, size:{size}");
86 
87         let mut slot_guard = SLOTS.lock();
88 
89         let slot_count = PageFrameCount::from_bytes(page_align_up(size))
90             .unwrap()
91             .data();
92         // 寻找连续的slot
93         let mut start_slot = None;
94         for i in 0..(Self::SLOT_CNT - slot_count + 1) {
95             let mut is_continuous = true;
96 
97             for j in 0..slot_count {
98                 let slot_idx = i + j;
99                 if slot_guard[slot_idx].start_idx.is_some() {
100                     is_continuous = false;
101                     break;
102                 }
103             }
104 
105             if is_continuous {
106                 start_slot = Some(i);
107                 break;
108             }
109         }
110 
111         let start_slot = start_slot.ok_or(SystemError::ENOMEM)?;
112         let vaddr = Self::idx_to_virt(start_slot);
113 
114         // kdebug!("start_slot:{start_slot}, vaddr: {vaddr:?}, slot_count: {slot_count:?}");
115         let page_count = PageFrameCount::new(slot_count);
116         // 执行映射
117         if read_only {
118             unsafe { pseudo_map_phys_ro(vaddr, phys, page_count) }
119         } else {
120             unsafe { pseudo_map_phys(vaddr, phys, page_count) }
121         }
122 
123         // kdebug!("map ok");
124 
125         // 更新slot信息
126         let map_size = slot_count * MMArch::PAGE_SIZE;
127         for i in 0..slot_count {
128             let slot_idx = start_slot + i;
129             slot_guard[slot_idx].start_idx = Some(start_slot as u32);
130 
131             if i == 0 {
132                 slot_guard[slot_idx].size = map_size as u32;
133                 slot_guard[slot_idx].phys = phys;
134             }
135         }
136 
137         return Ok((vaddr, map_size));
138     }
139 
140     /// 取消映射
141     ///
142     /// ## 参数
143     ///
144     /// - virt: 映射范围内的任意虚拟地址
145     ///
146     /// ## 返回值
147     ///
148     /// - Ok: 成功
149     /// - Err(SystemError::EINVAL): 传入的虚拟地址不在early io remap范围内,
150     ///     或者虚拟地址未映射
151     #[allow(dead_code)]
152     pub fn unmap(virt: VirtAddr) -> Result<(), SystemError> {
153         if virt < MMArch::FIXMAP_START_VADDR || virt >= MMArch::FIXMAP_END_VADDR {
154             return Err(SystemError::EINVAL);
155         }
156 
157         let mut slot_guard = SLOTS.lock();
158         let mut idx = None;
159 
160         // 寻找虚拟地址对应的区域的第一个slot
161         for slot_idx in 0..Self::SLOT_CNT {
162             let slot = &mut slot_guard[slot_idx];
163             if let Some(start_idx) = slot.start_idx {
164                 if start_idx == slot_idx as u32 {
165                     let vaddr_start = Self::idx_to_virt(start_idx as usize);
166                     let vaddr_end = vaddr_start + slot.size as usize;
167                     if vaddr_start <= virt && virt < vaddr_end {
168                         // 找到区域了
169                         idx = Some(slot_idx);
170                         break;
171                     }
172                 }
173             }
174         }
175 
176         let idx = idx.ok_or(SystemError::EINVAL)?;
177 
178         let vaddr = Self::idx_to_virt(idx);
179         let count = PageFrameCount::from_bytes(slot_guard[idx].size as usize).unwrap();
180 
181         // 取消映射
182         unsafe { pseudo_unmap_phys(vaddr, count) };
183 
184         for i in 0..count.data() {
185             let slot_idx = idx + i;
186             let slot = &mut slot_guard[slot_idx];
187             *slot = Slot::DEFAULT;
188         }
189 
190         return Ok(());
191     }
192 
193     /// 把slot下标转换为这个slot对应的虚拟地址
194     fn idx_to_virt(idx: usize) -> VirtAddr {
195         MMArch::FIXMAP_START_VADDR + idx * MMArch::PAGE_SIZE
196     }
197 }
198 
199 #[derive(Debug, Clone, Copy)]
200 struct Slot {
201     /// 连续映射的起始槽位号
202     start_idx: Option<u32>,
203     /// 连续映射的区域大小(仅在起始槽位中设置)
204     size: u32,
205     /// 映射的起始物理地址
206     phys: PhysAddr,
207 }
208 
209 impl Slot {
210     const DEFAULT: Self = Self {
211         start_idx: None,
212         size: 0,
213         phys: PhysAddr::new(0),
214     };
215 }
216