《操作系统》的实验代码。
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.

684 lines
22 KiB

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