MemoryManager.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #include "MemoryManager.h"
  2. #include <AK/Assertions.h>
  3. #include <AK/kstdio.h>
  4. #include <AK/kmalloc.h>
  5. #include "i386.h"
  6. #include "StdLib.h"
  7. #include "Process.h"
  8. //#define MM_DEBUG
  9. #define SCRUB_DEALLOCATED_PAGE_TABLES
  10. static MemoryManager* s_the;
  11. MemoryManager& MM
  12. {
  13. return *s_the;
  14. }
  15. MemoryManager::MemoryManager()
  16. {
  17. m_kernel_page_directory = (PageDirectory*)0x4000;
  18. m_pageTableZero = (dword*)0x6000;
  19. m_pageTableOne = (dword*)0x7000;
  20. m_next_laddr.set(0xd0000000);
  21. initializePaging();
  22. }
  23. MemoryManager::~MemoryManager()
  24. {
  25. }
  26. void MemoryManager::populate_page_directory(PageDirectory& page_directory)
  27. {
  28. memset(&page_directory, 0, sizeof(PageDirectory));
  29. page_directory.entries[0] = m_kernel_page_directory->entries[0];
  30. page_directory.entries[1] = m_kernel_page_directory->entries[1];
  31. }
  32. void MemoryManager::release_page_directory(PageDirectory& page_directory)
  33. {
  34. ASSERT_INTERRUPTS_DISABLED();
  35. #ifdef MM_DEBUG
  36. dbgprintf("MM: release_page_directory for PD K%x\n", &page_directory);
  37. #endif
  38. for (size_t i = 0; i < 1024; ++i) {
  39. auto& page_table = page_directory.physical_pages[i];
  40. if (!page_table.is_null()) {
  41. #ifdef MM_DEBUG
  42. dbgprintf("MM: deallocating user page table P%x\n", page_table->paddr().get());
  43. #endif
  44. deallocate_page_table(page_directory, i);
  45. }
  46. }
  47. #ifdef SCRUB_DEALLOCATED_PAGE_TABLES
  48. memset(&page_directory, 0xc9, sizeof(PageDirectory));
  49. #endif
  50. }
  51. void MemoryManager::initializePaging()
  52. {
  53. static_assert(sizeof(MemoryManager::PageDirectoryEntry) == 4);
  54. static_assert(sizeof(MemoryManager::PageTableEntry) == 4);
  55. memset(m_pageTableZero, 0, PAGE_SIZE);
  56. memset(m_pageTableOne, 0, PAGE_SIZE);
  57. memset(m_kernel_page_directory, 0, sizeof(PageDirectory));
  58. #ifdef MM_DEBUG
  59. kprintf("MM: Kernel page directory @ %p\n", m_kernel_page_directory);
  60. #endif
  61. // Make null dereferences crash.
  62. protectMap(LinearAddress(0), PAGE_SIZE);
  63. // The bottom 4 MB are identity mapped & supervisor only. Every process shares these mappings.
  64. create_identity_mapping(LinearAddress(PAGE_SIZE), 4 * MB);
  65. // The physical pages 4 MB through 8 MB are available for allocation.
  66. for (size_t i = (4 * MB) + PAGE_SIZE; i < (8 * MB); i += PAGE_SIZE)
  67. m_free_physical_pages.append(adopt(*new PhysicalPage(PhysicalAddress(i))));
  68. asm volatile("movl %%eax, %%cr3"::"a"(m_kernel_page_directory));
  69. asm volatile(
  70. "movl %cr0, %eax\n"
  71. "orl $0x80000001, %eax\n"
  72. "movl %eax, %cr0\n"
  73. );
  74. }
  75. RetainPtr<PhysicalPage> MemoryManager::allocate_page_table(PageDirectory& page_directory, unsigned index)
  76. {
  77. auto& page_directory_physical_ptr = page_directory.physical_pages[index];
  78. ASSERT(!page_directory_physical_ptr);
  79. auto ppages = allocate_physical_pages(1);
  80. ASSERT(ppages.size() == 1);
  81. dword address = ppages[0]->paddr().get();
  82. create_identity_mapping(LinearAddress(address), PAGE_SIZE);
  83. memset((void*)address, 0, PAGE_SIZE);
  84. page_directory.physical_pages[index] = move(ppages[0]);
  85. return page_directory.physical_pages[index];
  86. }
  87. void MemoryManager::deallocate_page_table(PageDirectory& page_directory, unsigned index)
  88. {
  89. auto& physical_page = page_directory.physical_pages[index];
  90. ASSERT(physical_page);
  91. //FIXME: This line is buggy and effectful somehow :(
  92. //ASSERT(!m_free_physical_pages.contains_slow(physical_page));
  93. for (size_t i = 0; i < MM.m_free_physical_pages.size(); ++i) {
  94. ASSERT(MM.m_free_physical_pages[i].ptr() != physical_page.ptr());
  95. }
  96. remove_identity_mapping(LinearAddress(physical_page->paddr().get()), PAGE_SIZE);
  97. page_directory.physical_pages[index] = nullptr;
  98. }
  99. void MemoryManager::remove_identity_mapping(LinearAddress laddr, size_t size)
  100. {
  101. InterruptDisabler disabler;
  102. // FIXME: ASSERT(laddr is 4KB aligned);
  103. for (dword offset = 0; offset < size; offset += PAGE_SIZE) {
  104. auto pte_address = laddr.offset(offset);
  105. auto pte = ensurePTE(m_kernel_page_directory, pte_address);
  106. pte.setPhysicalPageBase(0);
  107. pte.setUserAllowed(false);
  108. pte.setPresent(true);
  109. pte.setWritable(true);
  110. flushTLB(pte_address);
  111. }
  112. }
  113. auto MemoryManager::ensurePTE(PageDirectory* page_directory, LinearAddress laddr) -> PageTableEntry
  114. {
  115. ASSERT_INTERRUPTS_DISABLED();
  116. dword page_directory_index = (laddr.get() >> 22) & 0x3ff;
  117. dword page_table_index = (laddr.get() >> 12) & 0x3ff;
  118. PageDirectoryEntry pde = PageDirectoryEntry(&page_directory->entries[page_directory_index]);
  119. if (!pde.isPresent()) {
  120. #ifdef MM_DEBUG
  121. dbgprintf("MM: PDE %u not present, allocating\n", page_directory_index);
  122. #endif
  123. if (page_directory_index == 0) {
  124. ASSERT(page_directory == m_kernel_page_directory);
  125. pde.setPageTableBase((dword)m_pageTableZero);
  126. pde.setUserAllowed(false);
  127. pde.setPresent(true);
  128. pde.setWritable(true);
  129. } else if (page_directory_index == 1) {
  130. ASSERT(page_directory == m_kernel_page_directory);
  131. pde.setPageTableBase((dword)m_pageTableOne);
  132. pde.setUserAllowed(false);
  133. pde.setPresent(true);
  134. pde.setWritable(true);
  135. } else {
  136. auto page_table = allocate_page_table(*page_directory, page_directory_index);
  137. #ifdef MM_DEBUG
  138. dbgprintf("MM: PD K%x (%s) allocated page table #%u (for L%x) at P%x\n",
  139. page_directory,
  140. page_directory == m_kernel_page_directory ? "Kernel" : "User",
  141. page_directory_index,
  142. laddr.get(),
  143. page_table->paddr().get());
  144. #endif
  145. pde.setPageTableBase(page_table->paddr().get());
  146. pde.setUserAllowed(true);
  147. pde.setPresent(true);
  148. pde.setWritable(true);
  149. page_directory->physical_pages[page_directory_index] = move(page_table);
  150. }
  151. }
  152. return PageTableEntry(&pde.pageTableBase()[page_table_index]);
  153. }
  154. void MemoryManager::protectMap(LinearAddress linearAddress, size_t length)
  155. {
  156. InterruptDisabler disabler;
  157. // FIXME: ASSERT(linearAddress is 4KB aligned);
  158. for (dword offset = 0; offset < length; offset += PAGE_SIZE) {
  159. auto pteAddress = linearAddress.offset(offset);
  160. auto pte = ensurePTE(m_kernel_page_directory, pteAddress);
  161. pte.setPhysicalPageBase(pteAddress.get());
  162. pte.setUserAllowed(false);
  163. pte.setPresent(false);
  164. pte.setWritable(false);
  165. flushTLB(pteAddress);
  166. }
  167. }
  168. void MemoryManager::create_identity_mapping(LinearAddress laddr, size_t size)
  169. {
  170. InterruptDisabler disabler;
  171. // FIXME: ASSERT(laddr is 4KB aligned);
  172. for (dword offset = 0; offset < size; offset += PAGE_SIZE) {
  173. auto pteAddress = laddr.offset(offset);
  174. auto pte = ensurePTE(m_kernel_page_directory, pteAddress);
  175. pte.setPhysicalPageBase(pteAddress.get());
  176. pte.setUserAllowed(false);
  177. pte.setPresent(true);
  178. pte.setWritable(true);
  179. flushTLB(pteAddress);
  180. }
  181. }
  182. void MemoryManager::initialize()
  183. {
  184. s_the = new MemoryManager;
  185. }
  186. PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault)
  187. {
  188. ASSERT_INTERRUPTS_DISABLED();
  189. kprintf("MM: handle_page_fault(%w) at L%x\n", fault.code(), fault.laddr().get());
  190. if (fault.is_not_present()) {
  191. kprintf(" >> NP fault!\n");
  192. } else if (fault.is_protection_violation()) {
  193. kprintf(" >> PV fault!\n");
  194. }
  195. return PageFaultResponse::ShouldCrash;
  196. }
  197. Vector<RetainPtr<PhysicalPage>> MemoryManager::allocate_physical_pages(size_t count)
  198. {
  199. InterruptDisabler disabler;
  200. if (count > m_free_physical_pages.size())
  201. return { };
  202. Vector<RetainPtr<PhysicalPage>> pages;
  203. pages.ensureCapacity(count);
  204. for (size_t i = 0; i < count; ++i) {
  205. pages.append(m_free_physical_pages.takeLast());
  206. #ifdef MM_DEBUG
  207. dbgprintf("MM: allocate_physical_pages vending P%x\n", pages.last()->paddr().get());
  208. #endif
  209. }
  210. return pages;
  211. }
  212. void MemoryManager::enter_kernel_paging_scope()
  213. {
  214. InterruptDisabler disabler;
  215. current->m_tss.cr3 = (dword)m_kernel_page_directory;
  216. asm volatile("movl %%eax, %%cr3"::"a"(m_kernel_page_directory):"memory");
  217. }
  218. void MemoryManager::enter_process_paging_scope(Process& process)
  219. {
  220. InterruptDisabler disabler;
  221. current->m_tss.cr3 = (dword)process.m_page_directory;
  222. asm volatile("movl %%eax, %%cr3"::"a"(process.m_page_directory):"memory");
  223. }
  224. void MemoryManager::flushEntireTLB()
  225. {
  226. asm volatile(
  227. "mov %cr3, %eax\n"
  228. "mov %eax, %cr3\n"
  229. );
  230. }
  231. void MemoryManager::flushTLB(LinearAddress laddr)
  232. {
  233. asm volatile("invlpg %0": :"m" (*(char*)laddr.get()) : "memory");
  234. }
  235. void MemoryManager::map_region_at_address(PageDirectory* page_directory, Region& region, LinearAddress laddr, bool user_allowed)
  236. {
  237. InterruptDisabler disabler;
  238. for (size_t i = 0; i < region.physical_pages.size(); ++i) {
  239. auto page_laddr = laddr.offset(i * PAGE_SIZE);
  240. auto pte = ensurePTE(page_directory, page_laddr);
  241. auto& physical_page = region.physical_pages[i];
  242. if (physical_page) {
  243. pte.setPhysicalPageBase(physical_page->paddr().get());
  244. pte.setPresent(true); // FIXME: Maybe we should use the is_readable flag here?
  245. } else {
  246. pte.setPhysicalPageBase(0);
  247. pte.setPresent(false);
  248. }
  249. pte.setWritable(region.is_writable);
  250. pte.setUserAllowed(user_allowed);
  251. flushTLB(page_laddr);
  252. #ifdef MM_DEBUG
  253. dbgprintf("MM: >> map_region_at_address (PD=%x) '%s' L%x => P%x (@%p)\n", page_directory, region.name.characters(), page_laddr, physical_page ? physical_page->paddr().get() : 0, physical_page.ptr());
  254. #endif
  255. }
  256. }
  257. void MemoryManager::unmap_range(PageDirectory* page_directory, LinearAddress laddr, size_t size)
  258. {
  259. ASSERT((size % PAGE_SIZE) == 0);
  260. InterruptDisabler disabler;
  261. size_t numPages = size / PAGE_SIZE;
  262. for (size_t i = 0; i < numPages; ++i) {
  263. auto page_laddr = laddr.offset(i * PAGE_SIZE);
  264. auto pte = ensurePTE(page_directory, page_laddr);
  265. pte.setPhysicalPageBase(0);
  266. pte.setPresent(false);
  267. pte.setWritable(false);
  268. pte.setUserAllowed(false);
  269. flushTLB(page_laddr);
  270. #ifdef MM_DEBUG
  271. dbgprintf("MM: << unmap_range L%x =/> 0\n", page_laddr);
  272. #endif
  273. }
  274. }
  275. LinearAddress MemoryManager::allocate_linear_address_range(size_t size)
  276. {
  277. ASSERT((size % PAGE_SIZE) == 0);
  278. // FIXME: Recycle ranges!
  279. auto laddr = m_next_laddr;
  280. m_next_laddr.set(m_next_laddr.get() + size);
  281. return laddr;
  282. }
  283. byte* MemoryManager::create_kernel_alias_for_region(Region& region)
  284. {
  285. InterruptDisabler disabler;
  286. #ifdef MM_DEBUG
  287. dbgprintf("MM: create_kernel_alias_for_region region=%p (L%x size=%u)\n", &region, region.linearAddress.get(), region.size);
  288. #endif
  289. auto laddr = allocate_linear_address_range(region.size);
  290. map_region_at_address(m_kernel_page_directory, region, laddr, false);
  291. #ifdef MM_DEBUG
  292. dbgprintf("MM: Created alias L%x for L%x\n", laddr.get(), region.linearAddress.get());
  293. #endif
  294. return laddr.asPtr();
  295. }
  296. void MemoryManager::remove_kernel_alias_for_region(Region& region, byte* addr)
  297. {
  298. #ifdef MM_DEBUG
  299. dbgprintf("remove_kernel_alias_for_region region=%p, addr=L%x\n", &region, addr);
  300. #endif
  301. unmap_range(m_kernel_page_directory, LinearAddress((dword)addr), region.size);
  302. }
  303. bool MemoryManager::unmapRegion(Process& process, Region& region)
  304. {
  305. InterruptDisabler disabler;
  306. for (size_t i = 0; i < region.physical_pages.size(); ++i) {
  307. auto laddr = region.linearAddress.offset(i * PAGE_SIZE);
  308. auto pte = ensurePTE(process.m_page_directory, laddr);
  309. pte.setPhysicalPageBase(0);
  310. pte.setPresent(false);
  311. pte.setWritable(false);
  312. pte.setUserAllowed(false);
  313. flushTLB(laddr);
  314. #ifdef MM_DEBUG
  315. auto& physical_page = region.physical_pages[i];
  316. dbgprintf("MM: >> Unmapped L%x => P%x <<\n", laddr, physical_page ? physical_page->paddr().get() : 0);
  317. #endif
  318. }
  319. return true;
  320. }
  321. bool MemoryManager::mapRegion(Process& process, Region& region)
  322. {
  323. map_region_at_address(process.m_page_directory, region, region.linearAddress, true);
  324. return true;
  325. }
  326. bool MemoryManager::validate_user_read(const Process& process, LinearAddress laddr) const
  327. {
  328. dword pageDirectoryIndex = (laddr.get() >> 22) & 0x3ff;
  329. dword pageTableIndex = (laddr.get() >> 12) & 0x3ff;
  330. auto pde = PageDirectoryEntry(&process.m_page_directory->entries[pageDirectoryIndex]);
  331. if (!pde.isPresent())
  332. return false;
  333. auto pte = PageTableEntry(&pde.pageTableBase()[pageTableIndex]);
  334. if (!pte.isPresent())
  335. return false;
  336. if (!pte.isUserAllowed())
  337. return false;
  338. return true;
  339. }
  340. bool MemoryManager::validate_user_write(const Process& process, LinearAddress laddr) const
  341. {
  342. dword pageDirectoryIndex = (laddr.get() >> 22) & 0x3ff;
  343. dword pageTableIndex = (laddr.get() >> 12) & 0x3ff;
  344. auto pde = PageDirectoryEntry(&process.m_page_directory->entries[pageDirectoryIndex]);
  345. if (!pde.isPresent())
  346. return false;
  347. auto pte = PageTableEntry(&pde.pageTableBase()[pageTableIndex]);
  348. if (!pte.isPresent())
  349. return false;
  350. if (!pte.isUserAllowed())
  351. return false;
  352. if (!pte.isWritable())
  353. return false;
  354. return true;
  355. }
  356. RetainPtr<Region> Region::clone()
  357. {
  358. InterruptDisabler disabler;
  359. KernelPagingScope pagingScope;
  360. if (is_readable && !is_writable) {
  361. // Create a new region backed by the same physical pages.
  362. return adopt(*new Region(linearAddress, size, physical_pages, String(name), is_readable, is_writable));
  363. }
  364. // FIXME: Implement COW regions.
  365. auto clone_physical_pages = MM.allocate_physical_pages(physical_pages.size());
  366. auto clone_region = adopt(*new Region(linearAddress, size, move(clone_physical_pages), String(name), is_readable, is_writable));
  367. // FIXME: It would be cool to make the src_alias a read-only mapping.
  368. byte* src_alias = MM.create_kernel_alias_for_region(*this);
  369. byte* dest_alias = MM.create_kernel_alias_for_region(*clone_region);
  370. memcpy(dest_alias, src_alias, size);
  371. MM.remove_kernel_alias_for_region(*clone_region, dest_alias);
  372. MM.remove_kernel_alias_for_region(*this, src_alias);
  373. return clone_region;
  374. }
  375. Region::Region(LinearAddress a, size_t s, Vector<RetainPtr<PhysicalPage>> pp, String&& n, bool r, bool w)
  376. : linearAddress(a)
  377. , size(s)
  378. , physical_pages(move(pp))
  379. , name(move(n))
  380. , is_readable(r)
  381. , is_writable(w)
  382. {
  383. }
  384. Region::~Region()
  385. {
  386. }
  387. void PhysicalPage::return_to_freelist()
  388. {
  389. InterruptDisabler disabler;
  390. m_retain_count = 1;
  391. MM.m_free_physical_pages.append(adopt(*this));
  392. #ifdef MM_DEBUG
  393. dbgprintf("MM: P%x released to freelist\n", m_paddr.get());
  394. #endif
  395. }