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