mm_list.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #include <assert.h>
  2. #include <errno.h>
  3. #include <stdint.h>
  4. #include <kernel/mem/mm_list.hpp>
  5. #include <kernel/mem/paging.hpp>
  6. #include <kernel/mem/vm_area.hpp>
  7. using namespace kernel::mem;
  8. static inline void __invalidate_all_tlb() {
  9. asm volatile(
  10. "mov %%cr3, %%rax\n\t"
  11. "mov %%rax, %%cr3\n\t"
  12. :
  13. :
  14. : "rax", "memory");
  15. }
  16. static inline void __dealloc_page_table_all(paging::pfn_t pt, int depth, int from, int to) {
  17. using namespace paging;
  18. if (depth > 1) {
  19. for (int i = from; i < to; ++i) {
  20. auto pse = PSE{pt}[i];
  21. if (!(pse.attributes() & PA_P))
  22. continue;
  23. int pfn = pse.pfn();
  24. __dealloc_page_table_all(pfn, depth - 1, 0, 512);
  25. }
  26. }
  27. free_page(pt);
  28. }
  29. static inline void __dealloc_page_table(paging::pfn_t pt) {
  30. using namespace paging;
  31. auto start_idx = idx_p4(0);
  32. auto end_idx = idx_p4(KERNEL_SPACE_START);
  33. __dealloc_page_table_all(pt, 4, start_idx, end_idx);
  34. }
  35. mm_list::mm_list() : m_pt{paging::alloc_page_table()}, m_brk{m_areas.end()} {
  36. // copy only kernel space
  37. memcpy(physaddr<void>{m_pt + 0x800}, physaddr<void>{KERNEL_PML4 + 0x800}, 0x800);
  38. }
  39. mm_list::mm_list(const mm_list& other) : mm_list{} {
  40. m_areas = other.m_areas;
  41. using namespace paging;
  42. for (auto iter = m_areas.begin(); iter != m_areas.end(); ++iter) {
  43. auto& area = *iter;
  44. if (area.flags & MM_BREAK)
  45. m_brk = iter;
  46. auto this_iter = vaddr_range{m_pt, area.start, area.end};
  47. auto other_iter = vaddr_range{other.m_pt, area.start, area.end};
  48. while (this_iter) {
  49. auto this_pte = *this_iter, other_pte = *other_iter;
  50. auto attributes = other_pte.attributes();
  51. auto pfn = other_pte.pfn();
  52. attributes &= ~(PA_RW | PA_A | PA_D);
  53. attributes |= PA_COW;
  54. this_pte.set(attributes, pfn);
  55. increase_refcount(pfn_to_page(pfn));
  56. // TODO: create a function to set COW mappings
  57. attributes = other_pte.attributes();
  58. attributes &= ~PA_RW;
  59. attributes |= PA_COW;
  60. other_pte.set(attributes, pfn);
  61. ++this_iter, ++other_iter;
  62. }
  63. }
  64. __invalidate_all_tlb();
  65. }
  66. mm_list::~mm_list() {
  67. if (!m_pt)
  68. return;
  69. clear();
  70. __dealloc_page_table(m_pt);
  71. }
  72. bool mm_list::is_avail(uintptr_t start, std::size_t len) const noexcept {
  73. start &= ~0xfff;
  74. uintptr_t end = (start + len + 0xfff) & ~0xfff;
  75. len = end - start;
  76. if (end > USER_SPACE_MEMORY_TOP)
  77. return false;
  78. for (const auto& area : m_areas) {
  79. if (!area.is_avail(start, end))
  80. return false;
  81. }
  82. return true;
  83. }
  84. bool mm_list::is_avail(uintptr_t addr) const {
  85. if (addr >= USER_SPACE_MEMORY_TOP)
  86. return false;
  87. auto iter = m_areas.find(addr);
  88. return iter == m_areas.end();
  89. }
  90. uintptr_t mm_list::find_avail(uintptr_t hint, size_t len) const {
  91. auto addr = std::max(hint, MMAP_MIN_ADDR);
  92. while (!is_avail(addr, len)) {
  93. auto iter = m_areas.lower_bound(addr);
  94. if (iter == m_areas.end())
  95. return 0;
  96. addr = iter->end;
  97. }
  98. return addr;
  99. }
  100. void mm_list::switch_pd() const noexcept {
  101. asm volatile("mov %0, %%cr3" : : "r"(m_pt) : "memory");
  102. }
  103. int mm_list::register_brk(uintptr_t addr) {
  104. assert(m_brk == m_areas.end());
  105. if (!is_avail(addr))
  106. return -ENOMEM;
  107. bool inserted;
  108. std::tie(m_brk, inserted) = m_areas.emplace(addr, MM_ANONYMOUS | MM_WRITE | MM_BREAK);
  109. assert(inserted);
  110. return 0;
  111. }
  112. uintptr_t mm_list::set_brk(uintptr_t addr) {
  113. using namespace paging;
  114. assert(m_brk != m_areas.end());
  115. uintptr_t curbrk = m_brk->end;
  116. addr += 4096 - 1;
  117. addr &= ~0xfff;
  118. if (addr <= curbrk || !is_avail(curbrk, addr - curbrk))
  119. return curbrk;
  120. for (auto pte : vaddr_range{m_pt, curbrk, addr})
  121. pte.set(PA_ANONYMOUS_PAGE | PA_NXE, EMPTY_PAGE_PFN);
  122. m_brk->end = addr;
  123. return m_brk->end;
  124. }
  125. void mm_list::clear() {
  126. for (auto iter = m_areas.begin(); iter != m_areas.end(); ++iter)
  127. unmap(iter, false);
  128. __invalidate_all_tlb();
  129. m_areas.clear();
  130. m_brk = m_areas.end();
  131. }
  132. mm_list::iterator mm_list::split(iterator area, uintptr_t addr) {
  133. assert(!(addr & 0xfff));
  134. assert(addr > area->start && addr < area->end);
  135. std::size_t old_len = addr - area->start;
  136. std::size_t new_file_offset = 0;
  137. if (area->mapped_file)
  138. new_file_offset = area->file_offset + old_len;
  139. auto new_end = area->end;
  140. area->end = addr;
  141. auto [iter, inserted] =
  142. m_areas.emplace(addr, area->flags, new_end, d_get(area->mapped_file), new_file_offset);
  143. assert(inserted);
  144. return iter;
  145. }
  146. int mm_list::unmap(iterator area, bool should_invalidate_tlb) {
  147. using namespace paging;
  148. bool should_use_invlpg = area->end - area->start <= 0x4000;
  149. auto range = vaddr_range{m_pt, area->start, area->end};
  150. uintptr_t cur_addr = area->start;
  151. // TODO: write back dirty pages
  152. for (auto pte : range) {
  153. free_page(pte.pfn());
  154. pte.clear();
  155. if (should_invalidate_tlb && should_use_invlpg) {
  156. asm volatile("invlpg (%0)" : : "r"(cur_addr) : "memory");
  157. cur_addr += 0x1000;
  158. }
  159. }
  160. if (should_invalidate_tlb && !should_use_invlpg)
  161. __invalidate_all_tlb();
  162. return 0;
  163. }
  164. int mm_list::unmap(uintptr_t start, std::size_t length, bool should_invalidate_tlb) {
  165. // standard says that addr and len MUST be
  166. // page-aligned or the call is invalid
  167. if (start & 0xfff)
  168. return -EINVAL;
  169. uintptr_t end = (start + length + 0xfff) & ~0xfff;
  170. // check address validity
  171. if (end > KERNEL_SPACE_START)
  172. return -EINVAL;
  173. if (end > USER_SPACE_MEMORY_TOP)
  174. return -ENOMEM;
  175. auto iter = m_areas.lower_bound(start);
  176. auto iter_end = m_areas.upper_bound(end);
  177. // start <= iter <= end a.k.a. !(start > *iter) && !(*iter > end)
  178. while (iter != iter_end) {
  179. // start == iter:
  180. // start is between (iter->start, iter->end)
  181. //
  182. // strip out the area before start
  183. if (!(start < *iter) && start != iter->start)
  184. iter = split(iter, start);
  185. // iter.end <= end
  186. // it is safe to unmap the area directly
  187. if (*iter < end) {
  188. if (int ret = unmap(iter, should_invalidate_tlb); ret != 0)
  189. return ret;
  190. iter = m_areas.erase(iter);
  191. continue;
  192. }
  193. // end == iter:
  194. // end is between [iter->start, iter->end)
  195. //
  196. // if end == iter->start, no need to strip the area
  197. if (end == iter->start) {
  198. ++iter;
  199. continue;
  200. }
  201. (void)split(iter, end);
  202. if (int ret = unmap(iter, should_invalidate_tlb); ret != 0)
  203. return ret;
  204. iter = m_areas.erase(iter);
  205. // no need to check areas after this
  206. break;
  207. }
  208. return 0;
  209. }
  210. int mm_list::mmap(const map_args& args) {
  211. auto& vaddr = args.vaddr;
  212. auto& length = args.length;
  213. auto& file = args.file;
  214. auto& foff = args.file_offset;
  215. auto& flags = args.flags;
  216. assert((vaddr & 0xfff) == 0 && (foff & 0xfff) == 0);
  217. assert((length & 0xfff) == 0 && length != 0);
  218. if (!is_avail(vaddr, length))
  219. return -EEXIST;
  220. using namespace kernel::mem::paging;
  221. // PA_RW is set during page fault while PA_NXE is preserved
  222. // so we set PA_NXE now
  223. psattr_t attributes = PA_US;
  224. if (!(flags & MM_EXECUTE))
  225. attributes |= PA_NXE;
  226. if (flags & MM_MAPPED) {
  227. assert(file);
  228. auto [area, inserted] =
  229. m_areas.emplace(vaddr, flags & ~MM_INTERNAL_MASK, vaddr + length, d_get(file), foff);
  230. assert(inserted);
  231. attributes |= PA_MMAPPED_PAGE;
  232. for (auto pte : vaddr_range{m_pt, vaddr, vaddr + length})
  233. pte.set(attributes, EMPTY_PAGE_PFN);
  234. } else if (flags & MM_ANONYMOUS) {
  235. // private mapping of zero-filled pages
  236. // TODO: shared mapping
  237. auto [area, inserted] = m_areas.emplace(vaddr, (flags & ~MM_INTERNAL_MASK), vaddr + length);
  238. assert(inserted);
  239. attributes |= PA_ANONYMOUS_PAGE;
  240. for (auto pte : vaddr_range{m_pt, vaddr, vaddr + length})
  241. pte.set(attributes, EMPTY_PAGE_PFN);
  242. } else {
  243. return -EINVAL;
  244. }
  245. return 0;
  246. }