mmio.c 3.4 KB

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