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