mm_list.cc 7.6 KB

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