《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

647 lines
21 KiB

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