1 #include "mmio.h"
2 #include "mmio-buddy.h"
3 #include <common/math.h>
4 
mmio_init()5 void mmio_init()
6 {
7     mmio_buddy_init();
8 }
9 
10 /**
11  * @brief 创建一块mmio区域,并将vma绑定到initial_mm
12  *
13  * @param size mmio区域的大小(字节)
14  * @param vm_flags 要把vma设置成的标志
15  * @param res_vaddr 返回值-分配得到的虚拟地址
16  * @param res_length 返回值-分配的虚拟地址空间长度
17  * @return int 错误码
18  */
mmio_create(uint32_t size,vm_flags_t vm_flags,uint64_t * res_vaddr,uint64_t * res_size)19 int mmio_create(uint32_t size, vm_flags_t vm_flags, uint64_t *res_vaddr, uint64_t *res_size)
20 {
21     int retval = 0;
22     // 申请的内存超过允许的最大大小
23     if (unlikely(size > PAGE_1G_SIZE || size == 0))
24         return -EPERM;
25 
26     // 计算要从buddy中申请地址空间大小(按照2的n次幂来对齐)
27     int size_exp = 31 - __clz(size);
28     if (size_exp < PAGE_4K_SHIFT)
29     {
30         size_exp = PAGE_4K_SHIFT;
31         size = PAGE_4K_SIZE;
32     }
33     else if (size & (~(1 << size_exp)))
34     {
35         ++size_exp;
36         size = 1 << size_exp;
37     }
38     // 申请内存
39     struct __mmio_buddy_addr_region *buddy_region = mmio_buddy_query_addr_region(size_exp);
40     if (buddy_region == NULL) // 没有空闲的mmio空间了
41         return -ENOMEM;
42 
43     *res_vaddr = buddy_region->vaddr;
44     *res_size = size;
45     // 释放region
46     __mmio_buddy_release_addr_region(buddy_region);
47 
48     // ====创建vma===
49     // 设置vma flags
50     vm_flags |= (VM_IO | VM_DONTCOPY);
51     uint64_t len_4k = size % PAGE_2M_SIZE;
52     uint64_t len_2m = size - len_4k;
53     // 先创建2M的vma,然后创建4k的
54     for (uint32_t i = 0; i < len_2m; i += PAGE_2M_SIZE)
55     {
56 
57         retval = mm_create_vma(&initial_mm, buddy_region->vaddr + i, PAGE_2M_SIZE, vm_flags, NULL, NULL);
58         if (unlikely(retval != 0))
59             goto failed;
60     }
61 
62     for (uint32_t i = len_2m; i < size; i += PAGE_4K_SIZE)
63     {
64         retval = mm_create_vma(&initial_mm, buddy_region->vaddr + i, PAGE_4K_SIZE, vm_flags, NULL, NULL);
65         if (unlikely(retval != 0))
66             goto failed;
67     }
68     return 0;
69 failed:;
70     kerror("failed to create mmio vma. pid=%d", current_pcb->pid);
71     // todo: 当失败时,将已创建的vma删除
72     return retval;
73 }
74 
75 /**
76  * @brief 取消mmio的映射并将地址空间归还到buddy中
77  *
78  * @param vaddr 起始的虚拟地址
79  * @param length 要归还的地址空间的长度
80  * @return int 错误码
81  */
mmio_release(uint64_t vaddr,uint64_t length)82 int mmio_release(uint64_t vaddr, uint64_t length)
83 {
84     int retval = 0;
85     // 先将这些区域都unmap了
86     mm_unmap(&initial_mm, vaddr, length, false);
87 
88     // 将这些区域加入buddy
89     for (uint64_t i = 0; i < length;)
90     {
91         struct vm_area_struct *vma = vma_find(&initial_mm, vaddr + i);
92         if (unlikely(vma == NULL))
93         {
94             kerror("mmio_release failed: vma not found. At address: %#018lx, pid=%ld", vaddr + i, current_pcb->pid);
95             return -EINVAL;
96         }
97 
98         if (unlikely(vma->vm_start != (vaddr + i)))
99         {
100             kerror("mmio_release failed: addr_start is not equal to current: %#018lx.", vaddr + i);
101             return -EINVAL;
102         }
103         // 往buddy中插入内存块
104         retval = __mmio_buddy_give_back(vma->vm_start, 31 - __clz(vma->vm_end - vma->vm_start));
105         i += vma->vm_end - vma->vm_start;
106 
107         // 释放vma结构体
108         vm_area_del(vma);
109         vm_area_free(vma);
110 
111         if (unlikely(retval != 0))
112             goto give_back_failed;
113     }
114     return 0;
115 give_back_failed:;
116     kerror("mmio_release give_back failed: ");
117     return retval;
118 }
119