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