《操作系统》的实验代码。
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

665 satır
22 KiB

12 yıl önce
12 yıl önce
12 yıl önce
12 yıl önce
12 yıl önce
12 yıl önce
12 yıl önce
  1. #include <defs.h>
  2. #include <x86.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <mmu.h>
  6. #include <memlayout.h>
  7. #include <pmm.h>
  8. #include <default_pmm.h>
  9. #include <sync.h>
  10. #include <error.h>
  11. #include <swap.h>
  12. #include <vmm.h>
  13. #include <kmalloc.h>
  14. /* *
  15. * Task State Segment:
  16. *
  17. * The TSS may reside anywhere in memory. A special segment register called
  18. * the Task Register (TR) holds a segment selector that points a valid TSS
  19. * segment descriptor which resides in the GDT. Therefore, to use a TSS
  20. * the following must be done in function gdt_init:
  21. * - create a TSS descriptor entry in GDT
  22. * - add enough information to the TSS in memory as needed
  23. * - load the TR register with a segment selector for that segment
  24. *
  25. * There are several fileds in TSS for specifying the new stack pointer when a
  26. * privilege level change happens. But only the fields SS0 and ESP0 are useful
  27. * in our os kernel.
  28. *
  29. * The field SS0 contains the stack segment selector for CPL = 0, and the ESP0
  30. * contains the new ESP value for CPL = 0. When an interrupt happens in protected
  31. * mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value
  32. * into SS and ESP respectively.
  33. * */
  34. static struct taskstate ts = {0};
  35. // virtual address of physicall page array
  36. struct Page *pages;
  37. // amount of physical memory (in pages)
  38. size_t npage = 0;
  39. // virtual address of boot-time page directory
  40. pde_t *boot_pgdir = NULL;
  41. // physical address of boot-time page directory
  42. uintptr_t boot_cr3;
  43. // physical memory management
  44. const struct pmm_manager *pmm_manager;
  45. /* *
  46. * The page directory entry corresponding to the virtual address range
  47. * [VPT, VPT + PTSIZE) points to the page directory itself. Thus, the page
  48. * directory is treated as a page table as well as a page directory.
  49. *
  50. * One result of treating the page directory as a page table is that all PTEs
  51. * can be accessed though a "virtual page table" at virtual address VPT. And the
  52. * PTE for number n is stored in vpt[n].
  53. *
  54. * A second consequence is that the contents of the current page directory will
  55. * always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which
  56. * vpd is set bellow.
  57. * */
  58. pte_t * const vpt = (pte_t *)VPT;
  59. pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 0);
  60. /* *
  61. * Global Descriptor Table:
  62. *
  63. * The kernel and user segments are identical (except for the DPL). To load
  64. * the %ss register, the CPL must equal the DPL. Thus, we must duplicate the
  65. * segments for the user and the kernel. Defined as follows:
  66. * - 0x0 : unused (always faults -- for trapping NULL far pointers)
  67. * - 0x8 : kernel code segment
  68. * - 0x10: kernel data segment
  69. * - 0x18: user code segment
  70. * - 0x20: user data segment
  71. * - 0x28: defined for tss, initialized in gdt_init
  72. * */
  73. static struct segdesc gdt[] = {
  74. SEG_NULL,
  75. [SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
  76. [SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
  77. [SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
  78. [SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
  79. [SEG_TSS] = SEG_NULL,
  80. };
  81. static struct pseudodesc gdt_pd = {
  82. sizeof(gdt) - 1, (uintptr_t)gdt
  83. };
  84. static void check_alloc_page(void);
  85. static void check_pgdir(void);
  86. static void check_boot_pgdir(void);
  87. /* *
  88. * lgdt - load the global descriptor table register and reset the
  89. * data/code segement registers for kernel.
  90. * */
  91. static inline void
  92. lgdt(struct pseudodesc *pd) {
  93. asm volatile ("lgdt (%0)" :: "r" (pd));
  94. asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
  95. asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
  96. asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
  97. asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
  98. asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
  99. // reload cs
  100. asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
  101. }
  102. /* *
  103. * load_esp0 - change the ESP0 in default task state segment,
  104. * so that we can use different kernel stack when we trap frame
  105. * user to kernel.
  106. * */
  107. void
  108. load_esp0(uintptr_t esp0) {
  109. ts.ts_esp0 = esp0;
  110. }
  111. /* gdt_init - initialize the default GDT and TSS */
  112. static void
  113. gdt_init(void) {
  114. // set boot kernel stack and default SS0
  115. load_esp0((uintptr_t)bootstacktop);
  116. ts.ts_ss0 = KERNEL_DS;
  117. // initialize the TSS filed of the gdt
  118. gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL);
  119. // reload all segment registers
  120. lgdt(&gdt_pd);
  121. // load the TSS
  122. ltr(GD_TSS);
  123. }
  124. //init_pmm_manager - initialize a pmm_manager instance
  125. static void
  126. init_pmm_manager(void) {
  127. pmm_manager = &default_pmm_manager;
  128. cprintf("memory management: %s\n", pmm_manager->name);
  129. pmm_manager->init();
  130. }
  131. //init_memmap - call pmm->init_memmap to build Page struct for free memory
  132. static void
  133. init_memmap(struct Page *base, size_t n) {
  134. pmm_manager->init_memmap(base, n);
  135. }
  136. //alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
  137. struct Page *
  138. alloc_pages(size_t n) {
  139. struct Page *page=NULL;
  140. bool intr_flag;
  141. while (1)
  142. {
  143. local_intr_save(intr_flag);
  144. {
  145. page = pmm_manager->alloc_pages(n);
  146. }
  147. local_intr_restore(intr_flag);
  148. if (page != NULL || n > 1 || swap_init_ok == 0) break;
  149. extern struct mm_struct *check_mm_struct;
  150. //cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
  151. swap_out(check_mm_struct, n, 0);
  152. }
  153. //cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
  154. return page;
  155. }
  156. //free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory
  157. void
  158. free_pages(struct Page *base, size_t n) {
  159. bool intr_flag;
  160. local_intr_save(intr_flag);
  161. {
  162. pmm_manager->free_pages(base, n);
  163. }
  164. local_intr_restore(intr_flag);
  165. }
  166. //nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE)
  167. //of current free memory
  168. size_t
  169. nr_free_pages(void) {
  170. size_t ret;
  171. bool intr_flag;
  172. local_intr_save(intr_flag);
  173. {
  174. ret = pmm_manager->nr_free_pages();
  175. }
  176. local_intr_restore(intr_flag);
  177. return ret;
  178. }
  179. /* pmm_init - initialize the physical memory management */
  180. static void
  181. page_init(void) {
  182. struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE);
  183. uint64_t maxpa = 0;
  184. cprintf("e820map:\n");
  185. int i;
  186. for (i = 0; i < memmap->nr_map; i ++) {
  187. uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
  188. cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n",
  189. memmap->map[i].size, begin, end - 1, memmap->map[i].type);
  190. if (memmap->map[i].type == E820_ARM) {
  191. if (maxpa < end && begin < KMEMSIZE) {
  192. maxpa = end;
  193. }
  194. }
  195. }
  196. if (maxpa > KMEMSIZE) {
  197. maxpa = KMEMSIZE;
  198. }
  199. extern char end[];
  200. npage = maxpa / PGSIZE;
  201. pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);
  202. for (i = 0; i < npage; i ++) {
  203. SetPageReserved(pages + i);
  204. }
  205. uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);
  206. for (i = 0; i < memmap->nr_map; i ++) {
  207. uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
  208. if (memmap->map[i].type == E820_ARM) {
  209. if (begin < freemem) {
  210. begin = freemem;
  211. }
  212. if (end > KMEMSIZE) {
  213. end = KMEMSIZE;
  214. }
  215. if (begin < end) {
  216. begin = ROUNDUP(begin, PGSIZE);
  217. end = ROUNDDOWN(end, PGSIZE);
  218. if (begin < end) {
  219. init_memmap(pa2page(begin), (end - begin) / PGSIZE);
  220. }
  221. }
  222. }
  223. }
  224. }
  225. static void
  226. enable_paging(void) {
  227. lcr3(boot_cr3);
  228. // turn on paging
  229. uint32_t cr0 = rcr0();
  230. cr0 |= CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP;
  231. cr0 &= ~(CR0_TS | CR0_EM);
  232. lcr0(cr0);
  233. }
  234. //boot_map_segment - setup&enable the paging mechanism
  235. // parameters
  236. // la: linear address of this memory need to map (after x86 segment map)
  237. // size: memory size
  238. // pa: physical address of this memory
  239. // perm: permission of this memory
  240. static void
  241. boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) {
  242. assert(PGOFF(la) == PGOFF(pa));
  243. size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE;
  244. la = ROUNDDOWN(la, PGSIZE);
  245. pa = ROUNDDOWN(pa, PGSIZE);
  246. for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) {
  247. pte_t *ptep = get_pte(pgdir, la, 1);
  248. assert(ptep != NULL);
  249. *ptep = pa | PTE_P | perm;
  250. }
  251. }
  252. //boot_alloc_page - allocate one page using pmm->alloc_pages(1)
  253. // return value: the kernel virtual address of this allocated page
  254. //note: this function is used to get the memory for PDT(Page Directory Table)&PT(Page Table)
  255. static void *
  256. boot_alloc_page(void) {
  257. struct Page *p = alloc_page();
  258. if (p == NULL) {
  259. panic("boot_alloc_page failed.\n");
  260. }
  261. return page2kva(p);
  262. }
  263. //pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism
  264. // - check the correctness of pmm & paging mechanism, print PDT&PT
  265. void
  266. pmm_init(void) {
  267. //We need to alloc/free the physical memory (granularity is 4KB or other size).
  268. //So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h
  269. //First we should init a physical memory manager(pmm) based on the framework.
  270. //Then pmm can alloc/free the physical memory.
  271. //Now the first_fit/best_fit/worst_fit/buddy_system pmm are available.
  272. init_pmm_manager();
  273. // detect physical memory space, reserve already used memory,
  274. // then use pmm->init_memmap to create free page list
  275. page_init();
  276. //use pmm->check to verify the correctness of the alloc/free function in a pmm
  277. check_alloc_page();
  278. // create boot_pgdir, an initial page directory(Page Directory Table, PDT)
  279. boot_pgdir = boot_alloc_page();
  280. memset(boot_pgdir, 0, PGSIZE);
  281. boot_cr3 = PADDR(boot_pgdir);
  282. check_pgdir();
  283. static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0);
  284. // recursively insert boot_pgdir in itself
  285. // to form a virtual page table at virtual address VPT
  286. boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W;
  287. // map all physical memory to linear memory with base linear addr KERNBASE
  288. //linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE
  289. //But shouldn't use this map until enable_paging() & gdt_init() finished.
  290. boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W);
  291. //temporary map:
  292. //virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M
  293. boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)];
  294. enable_paging();
  295. //reload gdt(third time,the last time) to map all physical memory
  296. //virtual_addr 0~4G=liear_addr 0~4G
  297. //then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS
  298. gdt_init();
  299. //disable the map of virtual_addr 0~4M
  300. boot_pgdir[0] = 0;
  301. //now the basic virtual memory map(see memalyout.h) is established.
  302. //check the correctness of the basic virtual memory map.
  303. check_boot_pgdir();
  304. print_pgdir();
  305. kmalloc_init();
  306. }
  307. //get_pte - get pte and return the kernel virtual address of this pte for la
  308. // - if the PT contians this pte didn't exist, alloc a page for PT
  309. // parameter:
  310. // pgdir: the kernel virtual base address of PDT
  311. // la: the linear address need to map
  312. // create: a logical value to decide if alloc a page for PT
  313. // return vaule: the kernel virtual address of this pte
  314. pte_t *
  315. get_pte(pde_t *pgdir, uintptr_t la, bool create) {
  316. /* LAB2 EXERCISE 2: YOUR CODE
  317. *
  318. * If you need to visit a physical address, please use KADDR()
  319. * please read pmm.h for useful macros
  320. *
  321. * Maybe you want help comment, BELOW comments can help you finish the code
  322. *
  323. * Some Useful MACROs and DEFINEs, you can use them in below implementation.
  324. * MACROs or Functions:
  325. * PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la.
  326. * KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address.
  327. * set_page_ref(page,1) : means the page be referenced by one time
  328. * page2pa(page): get the physical address of memory which this (struct Page *) page manages
  329. * struct Page * alloc_page() : allocation a page
  330. * memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s
  331. * to the specified value c.
  332. * DEFINEs:
  333. * PTE_P 0x001 // page table/directory entry flags bit : Present
  334. * PTE_W 0x002 // page table/directory entry flags bit : Writeable
  335. * PTE_U 0x004 // page table/directory entry flags bit : User can access
  336. */
  337. #if 0
  338. pde_t *pdep = NULL; // (1) find page directory entry
  339. if (0) { // (2) check if entry is not present
  340. // (3) check if creating is needed, then alloc page for page table
  341. // CAUTION: this page is used for page table, not for common data page
  342. // (4) set page reference
  343. uintptr_t pa = 0; // (5) get linear address of page
  344. // (6) clear page content using memset
  345. // (7) set page directory entry's permission
  346. }
  347. return NULL; // (8) return page table entry
  348. #endif
  349. }
  350. //get_page - get related Page struct for linear address la using PDT pgdir
  351. struct Page *
  352. get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) {
  353. pte_t *ptep = get_pte(pgdir, la, 0);
  354. if (ptep_store != NULL) {
  355. *ptep_store = ptep;
  356. }
  357. if (ptep != NULL && *ptep & PTE_P) {
  358. return pte2page(*ptep);
  359. }
  360. return NULL;
  361. }
  362. //page_remove_pte - free an Page sturct which is related linear address la
  363. // - and clean(invalidate) pte which is related linear address la
  364. //note: PT is changed, so the TLB need to be invalidate
  365. static inline void
  366. page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
  367. /* LAB2 EXERCISE 3: YOUR CODE
  368. *
  369. * Please check if ptep is valid, and tlb must be manually updated if mapping is updated
  370. *
  371. * Maybe you want help comment, BELOW comments can help you finish the code
  372. *
  373. * Some Useful MACROs and DEFINEs, you can use them in below implementation.
  374. * MACROs or Functions:
  375. * struct Page *page pte2page(*ptep): get the according page from the value of a ptep
  376. * free_page : free a page
  377. * page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free.
  378. * tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being
  379. * edited are the ones currently in use by the processor.
  380. * DEFINEs:
  381. * PTE_P 0x001 // page table/directory entry flags bit : Present
  382. */
  383. #if 0
  384. if (0) { //(1) check if this page table entry is present
  385. struct Page *page = NULL; //(2) find corresponding page to pte
  386. //(3) decrease page reference
  387. //(4) and free this page when page reference reachs 0
  388. //(5) clear second page table entry
  389. //(6) flush tlb
  390. }
  391. #endif
  392. }
  393. //page_remove - free an Page which is related linear address la and has an validated pte
  394. void
  395. page_remove(pde_t *pgdir, uintptr_t la) {
  396. pte_t *ptep = get_pte(pgdir, la, 0);
  397. if (ptep != NULL) {
  398. page_remove_pte(pgdir, la, ptep);
  399. }
  400. }
  401. //page_insert - build the map of phy addr of an Page with the linear addr la
  402. // paramemters:
  403. // pgdir: the kernel virtual base address of PDT
  404. // page: the Page which need to map
  405. // la: the linear address need to map
  406. // perm: the permission of this Page which is setted in related pte
  407. // return value: always 0
  408. //note: PT is changed, so the TLB need to be invalidate
  409. int
  410. page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) {
  411. pte_t *ptep = get_pte(pgdir, la, 1);
  412. if (ptep == NULL) {
  413. return -E_NO_MEM;
  414. }
  415. page_ref_inc(page);
  416. if (*ptep & PTE_P) {
  417. struct Page *p = pte2page(*ptep);
  418. if (p == page) {
  419. page_ref_dec(page);
  420. }
  421. else {
  422. page_remove_pte(pgdir, la, ptep);
  423. }
  424. }
  425. *ptep = page2pa(page) | PTE_P | perm;
  426. tlb_invalidate(pgdir, la);
  427. return 0;
  428. }
  429. // invalidate a TLB entry, but only if the page tables being
  430. // edited are the ones currently in use by the processor.
  431. void
  432. tlb_invalidate(pde_t *pgdir, uintptr_t la) {
  433. if (rcr3() == PADDR(pgdir)) {
  434. invlpg((void *)la);
  435. }
  436. }
  437. // pgdir_alloc_page - call alloc_page & page_insert functions to
  438. // - allocate a page size memory & setup an addr map
  439. // - pa<->la with linear address la and the PDT pgdir
  440. struct Page *
  441. pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
  442. struct Page *page = alloc_page();
  443. if (page != NULL) {
  444. if (page_insert(pgdir, page, la, perm) != 0) {
  445. free_page(page);
  446. return NULL;
  447. }
  448. if (swap_init_ok){
  449. swap_map_swappable(check_mm_struct, la, page, 0);
  450. page->pra_vaddr=la;
  451. assert(page_ref(page) == 1);
  452. //cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
  453. }
  454. }
  455. return page;
  456. }
  457. static void
  458. check_alloc_page(void) {
  459. pmm_manager->check();
  460. cprintf("check_alloc_page() succeeded!\n");
  461. }
  462. static void
  463. check_pgdir(void) {
  464. assert(npage <= KMEMSIZE / PGSIZE);
  465. assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0);
  466. assert(get_page(boot_pgdir, 0x0, NULL) == NULL);
  467. struct Page *p1, *p2;
  468. p1 = alloc_page();
  469. assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0);
  470. pte_t *ptep;
  471. assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL);
  472. assert(pte2page(*ptep) == p1);
  473. assert(page_ref(p1) == 1);
  474. ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1];
  475. assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep);
  476. p2 = alloc_page();
  477. assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0);
  478. assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
  479. assert(*ptep & PTE_U);
  480. assert(*ptep & PTE_W);
  481. assert(boot_pgdir[0] & PTE_U);
  482. assert(page_ref(p2) == 1);
  483. assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0);
  484. assert(page_ref(p1) == 2);
  485. assert(page_ref(p2) == 0);
  486. assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
  487. assert(pte2page(*ptep) == p1);
  488. assert((*ptep & PTE_U) == 0);
  489. page_remove(boot_pgdir, 0x0);
  490. assert(page_ref(p1) == 1);
  491. assert(page_ref(p2) == 0);
  492. page_remove(boot_pgdir, PGSIZE);
  493. assert(page_ref(p1) == 0);
  494. assert(page_ref(p2) == 0);
  495. assert(page_ref(pde2page(boot_pgdir[0])) == 1);
  496. free_page(pde2page(boot_pgdir[0]));
  497. boot_pgdir[0] = 0;
  498. cprintf("check_pgdir() succeeded!\n");
  499. }
  500. static void
  501. check_boot_pgdir(void) {
  502. pte_t *ptep;
  503. int i;
  504. for (i = 0; i < npage; i += PGSIZE) {
  505. assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL);
  506. assert(PTE_ADDR(*ptep) == i);
  507. }
  508. assert(PDE_ADDR(boot_pgdir[PDX(VPT)]) == PADDR(boot_pgdir));
  509. assert(boot_pgdir[0] == 0);
  510. struct Page *p;
  511. p = alloc_page();
  512. assert(page_insert(boot_pgdir, p, 0x100, PTE_W) == 0);
  513. assert(page_ref(p) == 1);
  514. assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W) == 0);
  515. assert(page_ref(p) == 2);
  516. const char *str = "ucore: Hello world!!";
  517. strcpy((void *)0x100, str);
  518. assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0);
  519. *(char *)(page2kva(p) + 0x100) = '\0';
  520. assert(strlen((const char *)0x100) == 0);
  521. free_page(p);
  522. free_page(pde2page(boot_pgdir[0]));
  523. boot_pgdir[0] = 0;
  524. cprintf("check_boot_pgdir() succeeded!\n");
  525. }
  526. //perm2str - use string 'u,r,w,-' to present the permission
  527. static const char *
  528. perm2str(int perm) {
  529. static char str[4];
  530. str[0] = (perm & PTE_U) ? 'u' : '-';
  531. str[1] = 'r';
  532. str[2] = (perm & PTE_W) ? 'w' : '-';
  533. str[3] = '\0';
  534. return str;
  535. }
  536. //get_pgtable_items - In [left, right] range of PDT or PT, find a continuous linear addr space
  537. // - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT
  538. // - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT
  539. // paramemters:
  540. // left: no use ???
  541. // right: the high side of table's range
  542. // start: the low side of table's range
  543. // table: the beginning addr of table
  544. // left_store: the pointer of the high side of table's next range
  545. // right_store: the pointer of the low side of table's next range
  546. // return value: 0 - not a invalid item range, perm - a valid item range with perm permission
  547. static int
  548. get_pgtable_items(size_t left, size_t right, size_t start, uintptr_t *table, size_t *left_store, size_t *right_store) {
  549. if (start >= right) {
  550. return 0;
  551. }
  552. while (start < right && !(table[start] & PTE_P)) {
  553. start ++;
  554. }
  555. if (start < right) {
  556. if (left_store != NULL) {
  557. *left_store = start;
  558. }
  559. int perm = (table[start ++] & PTE_USER);
  560. while (start < right && (table[start] & PTE_USER) == perm) {
  561. start ++;
  562. }
  563. if (right_store != NULL) {
  564. *right_store = start;
  565. }
  566. return perm;
  567. }
  568. return 0;
  569. }
  570. //print_pgdir - print the PDT&PT
  571. void
  572. print_pgdir(void) {
  573. cprintf("-------------------- BEGIN --------------------\n");
  574. size_t left, right = 0, perm;
  575. while ((perm = get_pgtable_items(0, NPDEENTRY, right, vpd, &left, &right)) != 0) {
  576. cprintf("PDE(%03x) %08x-%08x %08x %s\n", right - left,
  577. left * PTSIZE, right * PTSIZE, (right - left) * PTSIZE, perm2str(perm));
  578. size_t l, r = left * NPTEENTRY;
  579. while ((perm = get_pgtable_items(left * NPTEENTRY, right * NPTEENTRY, r, vpt, &l, &r)) != 0) {
  580. cprintf(" |-- PTE(%05x) %08x-%08x %08x %s\n", r - l,
  581. l * PGSIZE, r * PGSIZE, (r - l) * PGSIZE, perm2str(perm));
  582. }
  583. }
  584. cprintf("--------------------- END ---------------------\n");
  585. }