xref: /DragonOS/kernel/src/mm/mmio_buddy.rs (revision 9fab312ea9921618629924ab15c28c2d255b21c6)
1 use crate::libs::spinlock::{SpinLock, SpinLockGuard};
2 use crate::mm::kernel_mapper::KernelMapper;
3 use crate::mm::page::{PAGE_1G_SHIFT, PAGE_4K_SHIFT};
4 use crate::process::ProcessManager;
5 use crate::{
6     include::bindings::bindings::PAGE_4K_SIZE,
7     kdebug,
8     mm::{MMArch, MemoryManagementArch},
9 };
10 use crate::{kerror, kinfo, kwarn};
11 use alloc::{collections::LinkedList, vec::Vec};
12 use core::mem;
13 use core::mem::MaybeUninit;
14 use core::sync::atomic::{AtomicBool, Ordering};
15 use system_error::SystemError;
16 
17 use super::page::PageFlags;
18 use super::{PhysAddr, VirtAddr};
19 
20 // 最大的伙伴块的幂
21 const MMIO_BUDDY_MAX_EXP: u32 = PAGE_1G_SHIFT as u32;
22 // 最小的伙伴块的幂
23 const MMIO_BUDDY_MIN_EXP: u32 = PAGE_4K_SHIFT as u32;
24 // 内存池数组的范围
25 const MMIO_BUDDY_REGION_COUNT: u32 = MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1;
26 
27 const PAGE_1G_SIZE: usize = 1 << 30;
28 
29 static mut __MMIO_POOL: Option<MmioBuddyMemPool> = None;
30 
31 pub fn mmio_pool() -> &'static MmioBuddyMemPool {
32     unsafe { __MMIO_POOL.as_ref().unwrap() }
33 }
34 
35 pub enum MmioResult {
36     SUCCESS,
37     EINVAL,
38     ENOFOUND,
39     WRONGEXP,
40     ISEMPTY,
41 }
42 
43 /// @brief buddy内存池
44 #[derive(Debug)]
45 pub struct MmioBuddyMemPool {
46     pool_start_addr: VirtAddr,
47     pool_size: usize,
48     free_regions: [SpinLock<MmioFreeRegionList>; MMIO_BUDDY_REGION_COUNT as usize],
49 }
50 
51 impl MmioBuddyMemPool {
52     #[inline(never)]
53     fn new() -> Self {
54         let mut free_regions: [MaybeUninit<SpinLock<MmioFreeRegionList>>;
55             MMIO_BUDDY_REGION_COUNT as usize] = unsafe { MaybeUninit::uninit().assume_init() };
56         for i in 0..MMIO_BUDDY_REGION_COUNT {
57             free_regions[i as usize] = MaybeUninit::new(SpinLock::new(MmioFreeRegionList::new()));
58         }
59         let free_regions = unsafe {
60             mem::transmute::<_, [SpinLock<MmioFreeRegionList>; MMIO_BUDDY_REGION_COUNT as usize]>(
61                 free_regions,
62             )
63         };
64 
65         let pool = MmioBuddyMemPool {
66             pool_start_addr: MMArch::MMIO_BASE,
67             pool_size: MMArch::MMIO_SIZE,
68             free_regions,
69         };
70 
71         assert!(pool.pool_start_addr.data() % PAGE_1G_SIZE == 0);
72         kdebug!("MMIO buddy pool init: created");
73 
74         let mut vaddr_base = MMArch::MMIO_BASE;
75         let mut remain_size = MMArch::MMIO_SIZE;
76         kdebug!(
77             "BASE: {:?}, TOP: {:?}, size: {:?}",
78             MMArch::MMIO_BASE,
79             MMArch::MMIO_TOP,
80             MMArch::MMIO_SIZE
81         );
82 
83         for shift in (PAGE_4K_SHIFT..=PAGE_1G_SHIFT).rev() {
84             if remain_size & (1 << shift) != 0 {
85                 let ok = pool.give_back_block(vaddr_base, shift as u32).is_ok();
86                 if ok {
87                     vaddr_base += 1 << shift;
88                     remain_size -= 1 << shift;
89                 } else {
90                     panic!("MMIO buddy pool init failed");
91                 }
92             }
93         }
94 
95         kdebug!("MMIO buddy pool init success");
96         return pool;
97     }
98 
99     /// @brief 创建新的地址区域结构体
100     ///
101     /// @param vaddr 虚拟地址
102     ///
103     /// @return 创建好的地址区域结构体
104     fn create_region(&self, vaddr: VirtAddr) -> MmioBuddyAddrRegion {
105         // kdebug!("create_region for vaddr: {vaddr:?}");
106 
107         let region: MmioBuddyAddrRegion = MmioBuddyAddrRegion::new(vaddr);
108 
109         // kdebug!("create_region for vaddr: {vaddr:?} OK!!!");
110         return region;
111     }
112 
113     /// @brief 将内存块归还给buddy
114     ///
115     /// @param vaddr 虚拟地址
116     ///
117     /// @param exp 内存空间的大小(2^exp)
118     ///
119     /// @param list_guard 【exp】对应的链表
120     ///
121     /// @return Ok(i32) 返回0
122     ///
123     /// @return Err(SystemError) 返回错误码
124     fn give_back_block(&self, vaddr: VirtAddr, exp: u32) -> Result<i32, SystemError> {
125         // 确保内存对齐,低位都要为0
126         if (vaddr.data() & ((1 << exp) - 1)) != 0 {
127             return Err(SystemError::EINVAL);
128         }
129         let region: MmioBuddyAddrRegion = self.create_region(vaddr);
130         // 加入buddy
131         let mut list_guard = self.free_regions[exp2index(exp)].lock();
132 
133         self.push_block(region, &mut list_guard);
134         return Ok(0);
135     }
136 
137     /// @brief 将给定大小为2^{exp}的内存块一分为二,并插入内存块大小为2^{exp-1}的链表中
138     ///
139     /// @param region 要被分割的地址区域结构体(保证其已经从链表中取出)
140     ///
141     /// @param exp 要被分割的地址区域的大小的幂
142     ///
143     /// @param list_guard 【exp-1】对应的链表
144     fn split_block(
145         &self,
146         region: MmioBuddyAddrRegion,
147         exp: u32,
148         low_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
149     ) {
150         let vaddr = self.calculate_block_vaddr(region.vaddr, exp - 1);
151         let new_region: MmioBuddyAddrRegion = self.create_region(vaddr);
152         self.push_block(region, low_list_guard);
153         self.push_block(new_region, low_list_guard);
154     }
155 
156     /// @brief 从buddy中申请一块指定大小的内存区域
157     ///
158     /// @param exp 要申请的内存块的大小的幂(2^exp)
159     ///
160     /// @param list_guard exp对应的链表
161     ///
162     /// @return Ok(MmioBuddyAddrRegion) 符合要求的内存区域。
163     ///
164     /// @return Err(MmioResult)
165     /// - 没有满足要求的内存块时,返回ENOFOUND
166     /// - 申请的内存块大小超过合法范围,返回WRONGEXP
167     /// - 调用函数出错时,返回出错函数对应错误码
168     fn query_addr_region(
169         &self,
170         exp: u32,
171         list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
172     ) -> Result<MmioBuddyAddrRegion, MmioResult> {
173         // 申请范围错误
174         if !(MMIO_BUDDY_MIN_EXP..=MMIO_BUDDY_MAX_EXP).contains(&exp) {
175             kdebug!("query_addr_region: exp wrong");
176             return Err(MmioResult::WRONGEXP);
177         }
178         // 没有恰好符合要求的内存块
179         // 注意:exp对应的链表list_guard已上锁【注意避免死锁问题】
180         if list_guard.num_free == 0 {
181             // 找到最小符合申请范围的内存块
182             // 将大的内存块依次分成小块内存,直到能够满足exp大小,即将exp+1分成两块exp
183             for e in exp + 1..MMIO_BUDDY_MAX_EXP + 1 {
184                 let pop_list: &mut SpinLockGuard<MmioFreeRegionList> =
185                     &mut self.free_regions[exp2index(e)].lock();
186                 if pop_list.num_free == 0 {
187                     continue;
188                 }
189 
190                 for e2 in (exp + 1..e + 1).rev() {
191                     if e2 == e {
192                         match self.pop_block(pop_list) {
193                             Ok(region) => {
194                                 if e2 != exp + 1 {
195                                     // 要将分裂后的内存块插入到更小的链表中
196                                     let low_list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
197                                         &mut self.free_regions[exp2index(e2 - 1)].lock();
198                                     self.split_block(region, e2, low_list_guard);
199                                 } else {
200                                     // 由于exp对应的链表list_guard已经被锁住了 不能再加锁
201                                     // 所以直接将list_guard传入
202                                     self.split_block(region, e2, list_guard);
203                                 }
204                             }
205                             Err(err) => {
206                                 kdebug!("buddy_pop_region get wrong");
207                                 return Err(err);
208                             }
209                         }
210                     } else {
211                         match self.pop_block(&mut self.free_regions[exp2index(e2)].lock()) {
212                             Ok(region) => {
213                                 if e2 != exp + 1 {
214                                     // 要将分裂后的内存块插入到更小的链表中
215                                     let low_list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
216                                         &mut self.free_regions[exp2index(e2 - 1)].lock();
217                                     self.split_block(region, e2, low_list_guard);
218                                 } else {
219                                     // 由于exp对应的链表list_guard已经被锁住了 不能再加锁
220                                     // 所以直接将list_guard传入
221                                     self.split_block(region, e2, list_guard);
222                                 }
223                             }
224                             Err(err) => {
225                                 kdebug!("buddy_pop_region get wrong");
226                                 return Err(err);
227                             }
228                         }
229                     }
230                 }
231                 break;
232             }
233             // 判断是否获得了exp大小的内存块
234             if list_guard.num_free > 0 {
235                 match self.pop_block(list_guard) {
236                     Ok(ret) => return Ok(ret),
237                     Err(err) => return Err(err),
238                 }
239             }
240             // 拆分大内存块无法获得exp大小内存块
241             // 尝试用小内存块合成
242             // 即将两块exp合成一块exp+1
243 
244             // TODO:修改下一个循环的冗余代码,请不要删除此处的注释
245             // let merge = |high_list_guard: &mut SpinLockGuard<MmioFreeRegionList>, exp: u32| {
246             //     if let Err(err) = self.merge_all_exp(
247             //         exp,
248             //         &mut self.free_regions[exp2index(exp) as usize].lock(),
249             //         high_list_guard,
250             //     ) {
251             //         return err;
252             //     } else {
253             //         return MmioResult::SUCCESS;
254             //     }
255             // };
256             for e in MMIO_BUDDY_MIN_EXP..exp {
257                 if e != exp - 1 {
258                     match self.merge_all_exp(
259                         exp,
260                         &mut self.free_regions[exp2index(exp)].lock(),
261                         &mut self.free_regions[exp2index(exp + 1)].lock(),
262                     ) {
263                         Ok(_) => continue,
264                         Err(err) => {
265                             kdebug!("merge_all_exp get wrong");
266                             return Err(err);
267                         }
268                     }
269                 } else {
270                     match self.merge_all_exp(
271                         exp,
272                         &mut self.free_regions[exp2index(exp)].lock(),
273                         list_guard,
274                     ) {
275                         Ok(_) => continue,
276                         Err(err) => {
277                             kdebug!("merge_all_exp get wrong");
278                             return Err(err);
279                         }
280                     }
281                 }
282             }
283 
284             //判断是否获得了exp大小的内存块
285             if list_guard.num_free > 0 {
286                 match self.pop_block(list_guard) {
287                     Ok(ret) => return Ok(ret),
288                     Err(err) => return Err(err),
289                 }
290             }
291             return Err(MmioResult::ENOFOUND);
292         } else {
293             match self.pop_block(list_guard) {
294                 Ok(ret) => return Ok(ret),
295                 Err(err) => return Err(err),
296             }
297         }
298     }
299 
300     /// @brief 对query_addr_region进行封装
301     ///
302     /// @param exp 内存区域的大小(2^exp)
303     ///
304     /// @return Ok(MmioBuddyAddrRegion)符合要求的内存块信息结构体。
305     /// @return Err(MmioResult) 没有满足要求的内存块时,返回__query_addr_region的错误码。
306     fn mmio_buddy_query_addr_region(&self, exp: u32) -> Result<MmioBuddyAddrRegion, MmioResult> {
307         let mut list_guard: SpinLockGuard<MmioFreeRegionList> =
308             self.free_regions[exp2index(exp)].lock();
309         match self.query_addr_region(exp, &mut list_guard) {
310             Ok(ret) => return Ok(ret),
311             Err(err) => {
312                 kdebug!("mmio_buddy_query_addr_region failed");
313                 return Err(err);
314             }
315         }
316     }
317     /// @brief 往指定的地址空间链表中添加一个地址区域
318     ///
319     /// @param region 要被添加的地址结构体
320     ///
321     /// @param list_guard 目标链表
322     fn push_block(
323         &self,
324         region: MmioBuddyAddrRegion,
325         list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
326     ) {
327         list_guard.list.push_back(region);
328         list_guard.num_free += 1;
329     }
330 
331     /// @brief 根据地址和内存块大小,计算伙伴块虚拟内存的地址
332     #[inline(always)]
333     fn calculate_block_vaddr(&self, vaddr: VirtAddr, exp: u32) -> VirtAddr {
334         return VirtAddr::new(vaddr.data() ^ (1 << exp as usize));
335     }
336 
337     /// @brief 寻找并弹出指定内存块的伙伴块
338     ///
339     /// @param region 对应内存块的信息
340     ///
341     /// @param exp 内存块大小
342     ///
343     /// @param list_guard 【exp】对应的链表
344     ///
345     /// @return Ok(Box<MmioBuddyAddrRegion) 返回伙伴块的引用
346     /// @return Err(MmioResult)
347     /// - 当链表为空,返回ISEMPTY
348     /// - 没有找到伙伴块,返回ENOFOUND
349     fn pop_buddy_block(
350         &self,
351         vaddr: VirtAddr,
352         exp: u32,
353         list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
354     ) -> Result<MmioBuddyAddrRegion, MmioResult> {
355         if list_guard.list.is_empty() {
356             return Err(MmioResult::ISEMPTY);
357         } else {
358             //计算伙伴块的地址
359             let buddy_vaddr = self.calculate_block_vaddr(vaddr, exp);
360 
361             // element 只会有一个元素
362             let mut element: Vec<MmioBuddyAddrRegion> = list_guard
363                 .list
364                 .extract_if(|x| x.vaddr == buddy_vaddr)
365                 .collect();
366             if element.len() == 1 {
367                 list_guard.num_free -= 1;
368                 return Ok(element.pop().unwrap());
369             }
370 
371             //没有找到对应的伙伴块
372             return Err(MmioResult::ENOFOUND);
373         }
374     }
375 
376     /// @brief 从指定空闲链表中取出内存区域
377     ///
378     /// @param list_guard 【exp】对应的链表
379     ///
380     /// @return Ok(MmioBuddyAddrRegion) 内存块信息结构体的引用。
381     ///
382     /// @return Err(MmioResult) 当链表为空,无法删除时,返回ISEMPTY
383     fn pop_block(
384         &self,
385         list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
386     ) -> Result<MmioBuddyAddrRegion, MmioResult> {
387         if !list_guard.list.is_empty() {
388             list_guard.num_free -= 1;
389             return Ok(list_guard.list.pop_back().unwrap());
390         }
391         return Err(MmioResult::ISEMPTY);
392     }
393 
394     /// @brief 合并所有2^{exp}大小的内存块
395     ///
396     /// @param exp 内存块大小的幂(2^exp)
397     ///
398     /// @param list_guard exp对应的链表
399     ///
400     /// @param high_list_guard exp+1对应的链表
401     ///
402     /// @return Ok(MmioResult) 合并成功返回SUCCESS
403     /// @return Err(MmioResult)
404     /// - 内存块过少,无法合并,返回EINVAL
405     /// - pop_buddy_block调用出错,返回其错误码
406     /// - merge_blocks调用出错,返回其错误码
407     fn merge_all_exp(
408         &self,
409         exp: u32,
410         list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
411         high_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
412     ) -> Result<MmioResult, MmioResult> {
413         // 至少要两个内存块才能合并
414         if list_guard.num_free <= 1 {
415             return Err(MmioResult::EINVAL);
416         }
417         loop {
418             if list_guard.num_free <= 1 {
419                 break;
420             }
421             // 获取内存块
422             let vaddr: VirtAddr = list_guard.list.back().unwrap().vaddr;
423             // 获取伙伴内存块
424             match self.pop_buddy_block(vaddr, exp, list_guard) {
425                 Err(err) => {
426                     return Err(err);
427                 }
428                 Ok(buddy_region) => {
429                     let region: MmioBuddyAddrRegion = list_guard.list.pop_back().unwrap();
430                     let copy_region = region.clone();
431                     // 在两块内存都被取出之后才进行合并
432                     match self.merge_blocks(region, buddy_region, exp, high_list_guard) {
433                         Err(err) => {
434                             // 如果合并失败了要将取出来的元素放回去
435                             self.push_block(copy_region, list_guard);
436                             kdebug!("merge_all_exp: merge_blocks failed");
437                             return Err(err);
438                         }
439                         Ok(_) => continue,
440                     }
441                 }
442             }
443         }
444         return Ok(MmioResult::SUCCESS);
445     }
446 
447     /// @brief 合并两个【已经从链表中取出】的内存块
448     ///
449     /// @param region_1 第一个内存块
450     ///
451     /// @param region_2 第二个内存
452     ///
453     /// @return Ok(MmioResult) 成功返回SUCCESS
454     ///
455     /// @return Err(MmioResult) 两个内存块不是伙伴块,返回EINVAL
456     fn merge_blocks(
457         &self,
458         region_1: MmioBuddyAddrRegion,
459         region_2: MmioBuddyAddrRegion,
460         exp: u32,
461         high_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
462     ) -> Result<MmioResult, MmioResult> {
463         // 判断是否为伙伴块
464         if region_1.vaddr != self.calculate_block_vaddr(region_2.vaddr, exp) {
465             return Err(MmioResult::EINVAL);
466         }
467         // 将大的块放进下一级链表
468         self.push_block(region_1, high_list_guard);
469         return Ok(MmioResult::SUCCESS);
470     }
471 
472     /// @brief 创建一块mmio区域,并将vma绑定到initial_mm
473     ///
474     /// @param size mmio区域的大小(字节)
475     ///
476     /// @param vm_flags 要把vma设置成的标志
477     ///
478     /// @param res_vaddr 返回值-分配得到的虚拟地址
479     ///
480     /// @param res_length 返回值-分配的虚拟地址空间长度
481     ///
482     /// @return Ok(i32) 成功返回0
483     ///
484     /// @return Err(SystemError) 失败返回错误码
485     pub fn create_mmio(&self, size: usize) -> Result<MMIOSpaceGuard, SystemError> {
486         if size > PAGE_1G_SIZE || size == 0 {
487             return Err(SystemError::EPERM);
488         }
489         // 计算前导0
490         #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
491         let mut size_exp: u32 = 63 - size.leading_zeros();
492         // kdebug!("create_mmio: size_exp: {}", size_exp);
493         // 记录最终申请的空间大小
494         let mut new_size = size;
495         // 对齐要申请的空间大小
496         // 如果要申请的空间大小小于4k,则分配4k
497         if size_exp < PAGE_4K_SHIFT as u32 {
498             new_size = PAGE_4K_SIZE as usize;
499             size_exp = PAGE_4K_SHIFT as u32;
500         } else if (new_size & (!(1 << size_exp))) != 0 {
501             // 向左对齐空间大小
502             size_exp += 1;
503             new_size = 1 << size_exp;
504         }
505         match self.mmio_buddy_query_addr_region(size_exp) {
506             Ok(region) => {
507                 let space_guard =
508                     unsafe { MMIOSpaceGuard::from_raw(region.vaddr, new_size, false) };
509                 return Ok(space_guard);
510             }
511             Err(_) => {
512                 kerror!(
513                     "failed to create mmio. pid = {:?}",
514                     ProcessManager::current_pcb().pid()
515                 );
516                 return Err(SystemError::ENOMEM);
517             }
518         }
519     }
520 
521     /// @brief 取消mmio的映射并将地址空间归还到buddy中
522     ///
523     /// @param vaddr 起始的虚拟地址
524     ///
525     /// @param length 要归还的地址空间的长度
526     ///
527     /// @return Ok(i32) 成功返回0
528     ///
529     /// @return Err(SystemError) 失败返回错误码
530     pub fn release_mmio(&self, vaddr: VirtAddr, length: usize) -> Result<i32, SystemError> {
531         assert!(vaddr.check_aligned(MMArch::PAGE_SIZE));
532         assert!(length & (MMArch::PAGE_SIZE - 1) == 0);
533         if vaddr < self.pool_start_addr
534             || vaddr.data() >= self.pool_start_addr.data() + self.pool_size
535         {
536             return Err(SystemError::EINVAL);
537         }
538         // todo: 重构MMIO管理机制,创建类似全局的manager之类的,管理MMIO的空间?
539 
540         // 暂时认为传入的vaddr都是正确的
541         let page_count = length / MMArch::PAGE_SIZE;
542         // 取消映射
543         let mut bindings = KernelMapper::lock();
544         let mut kernel_mapper = bindings.as_mut();
545         if kernel_mapper.is_none() {
546             kwarn!("release_mmio: kernel_mapper is read only");
547             return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
548         }
549 
550         for i in 0..page_count {
551             unsafe {
552                 let x: Option<(
553                     PhysAddr,
554                     PageFlags<MMArch>,
555                     crate::mm::page::PageFlush<MMArch>,
556                 )> = kernel_mapper
557                     .as_mut()
558                     .unwrap()
559                     .unmap_phys(vaddr + i * MMArch::PAGE_SIZE, false);
560                 if let Some((_, _, flush)) = x {
561                     flush.flush();
562                 }
563             };
564         }
565 
566         // 归还到buddy
567         mmio_pool()
568             .give_back_block(vaddr, length.trailing_zeros())
569             .unwrap_or_else(|err| {
570                 panic!("MMIO release failed: self: {self:?}, err msg: {:?}", err);
571             });
572 
573         return Ok(0);
574     }
575 }
576 
577 /// @brief mmio伙伴系统内部的地址区域结构体
578 #[derive(Debug, Clone)]
579 struct MmioBuddyAddrRegion {
580     vaddr: VirtAddr,
581 }
582 impl MmioBuddyAddrRegion {
583     pub fn new(vaddr: VirtAddr) -> Self {
584         return MmioBuddyAddrRegion { vaddr };
585     }
586 
587     #[allow(dead_code)]
588     pub fn vaddr(&self) -> VirtAddr {
589         return self.vaddr;
590     }
591 }
592 
593 /// @brief 空闲页数组结构体
594 #[derive(Debug, Default)]
595 pub struct MmioFreeRegionList {
596     /// 存储mmio_buddy的地址链表
597     list: LinkedList<MmioBuddyAddrRegion>,
598     /// 空闲块的数量
599     num_free: i64,
600 }
601 impl MmioFreeRegionList {
602     #[allow(dead_code)]
603     fn new() -> Self {
604         return MmioFreeRegionList {
605             ..Default::default()
606         };
607     }
608 }
609 
610 /// @brief 将内存对象大小的幂转换成内存池中的数组的下标
611 ///
612 /// @param exp内存大小
613 ///
614 /// @return 内存池数组下标
615 #[inline(always)]
616 fn exp2index(exp: u32) -> usize {
617     return (exp - 12) as usize;
618 }
619 
620 #[derive(Debug)]
621 pub struct MMIOSpaceGuard {
622     vaddr: VirtAddr,
623     size: usize,
624     mapped: AtomicBool,
625 }
626 
627 impl MMIOSpaceGuard {
628     pub unsafe fn from_raw(vaddr: VirtAddr, size: usize, mapped: bool) -> Self {
629         // check size
630         assert!(
631             size & (MMArch::PAGE_SIZE - 1) == 0,
632             "MMIO space size must be page aligned"
633         );
634         assert!(size.is_power_of_two(), "MMIO space size must be power of 2");
635         assert!(
636             vaddr.check_aligned(size),
637             "MMIO space vaddr must be aligned with size"
638         );
639         assert!(
640             vaddr.data() >= MMArch::MMIO_BASE.data()
641                 && vaddr.data() + size <= MMArch::MMIO_TOP.data(),
642             "MMIO space must be in MMIO region"
643         );
644 
645         // 人工创建的MMIO空间,认为已经映射
646         MMIOSpaceGuard {
647             vaddr,
648             size,
649             mapped: AtomicBool::new(mapped),
650         }
651     }
652 
653     pub fn vaddr(&self) -> VirtAddr {
654         self.vaddr
655     }
656 
657     pub fn size(&self) -> usize {
658         self.size
659     }
660 
661     /// 将物理地址填写到虚拟地址空间中
662     ///
663     /// ## Safety
664     ///
665     /// 传入的物理地址【一定要是设备的物理地址】。
666     /// 如果物理地址是从内存分配器中分配的,那么会造成内存泄露。因为mmio_release的时候,只取消映射,不会释放内存。
667     pub unsafe fn map_phys(&self, paddr: PhysAddr, length: usize) -> Result<(), SystemError> {
668         if length > self.size {
669             return Err(SystemError::EINVAL);
670         }
671 
672         let check = self
673             .mapped
674             .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
675         if check.is_err() {
676             return Err(SystemError::EINVAL);
677         }
678 
679         let flags = PageFlags::mmio_flags();
680         let mut kernel_mapper = KernelMapper::lock();
681         let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true);
682         return r;
683     }
684 
685     /// 泄露一个MMIO space guard,不会释放映射的空间
686     pub unsafe fn leak(self) {
687         core::mem::forget(self);
688     }
689 }
690 
691 impl Drop for MMIOSpaceGuard {
692     fn drop(&mut self) {
693         let _ = mmio_pool()
694             .release_mmio(self.vaddr, self.size)
695             .unwrap_or_else(|err| {
696                 panic!("MMIO release failed: self: {self:?}, err msg: {:?}", err);
697             });
698     }
699 }
700 
701 pub fn mmio_init() {
702     kdebug!("Initializing MMIO buddy memory pool...");
703     // 初始化mmio内存池
704     unsafe {
705         __MMIO_POOL = Some(MmioBuddyMemPool::new());
706     }
707 
708     kinfo!("MMIO buddy memory pool init done");
709 }
710