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)] map_not_aligned( mut phys: PhysAddr, mut size: usize, read_only: bool, ) -> Result<VirtAddr, SystemError>43 pub fn map_not_aligned( 44 mut phys: PhysAddr, 45 mut size: usize, 46 read_only: bool, 47 ) -> Result<VirtAddr, SystemError> { 48 // debug!("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)] map( phys: PhysAddr, size: usize, read_only: bool, ) -> Result<(VirtAddr, usize), SystemError>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 // debug!("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 // debug!("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 // debug!("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)] unmap(virt: VirtAddr) -> Result<(), SystemError>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对应的虚拟地址 idx_to_virt(idx: usize) -> VirtAddr194 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