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

433 lines
14 KiB

10 years ago
  1. #include <vmm.h>
  2. #include <sync.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <error.h>
  7. #include <pmm.h>
  8. #include <x86.h>
  9. #include <swap.h>
  10. #include <kmalloc.h>
  11. /*
  12. vmm design include two parts: mm_struct (mm) & vma_struct (vma)
  13. mm is the memory manager for the set of continuous virtual memory
  14. area which have the same PDT. vma is a continuous virtual memory area.
  15. There a linear link list for vma & a redblack link list for vma in mm.
  16. ---------------
  17. mm related functions:
  18. golbal functions
  19. struct mm_struct * mm_create(void)
  20. void mm_destroy(struct mm_struct *mm)
  21. int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr)
  22. --------------
  23. vma related functions:
  24. global functions
  25. struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...)
  26. void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma)
  27. struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr)
  28. local functions
  29. inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next)
  30. ---------------
  31. check correctness functions
  32. void check_vmm(void);
  33. void check_vma_struct(void);
  34. void check_pgfault(void);
  35. */
  36. static void check_vmm(void);
  37. static void check_vma_struct(void);
  38. static void check_pgfault(void);
  39. // mm_create - alloc a mm_struct & initialize it.
  40. struct mm_struct *
  41. mm_create(void) {
  42. struct mm_struct *mm = kmalloc(sizeof(struct mm_struct));
  43. if (mm != NULL) {
  44. list_init(&(mm->mmap_list));
  45. mm->mmap_cache = NULL;
  46. mm->pgdir = NULL;
  47. mm->map_count = 0;
  48. if (swap_init_ok) swap_init_mm(mm);
  49. else mm->sm_priv = NULL;
  50. }
  51. return mm;
  52. }
  53. // vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end)
  54. struct vma_struct *
  55. vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) {
  56. struct vma_struct *vma = kmalloc(sizeof(struct vma_struct));
  57. if (vma != NULL) {
  58. vma->vm_start = vm_start;
  59. vma->vm_end = vm_end;
  60. vma->vm_flags = vm_flags;
  61. }
  62. return vma;
  63. }
  64. // find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end)
  65. struct vma_struct *
  66. find_vma(struct mm_struct *mm, uintptr_t addr) {
  67. struct vma_struct *vma = NULL;
  68. if (mm != NULL) {
  69. vma = mm->mmap_cache;
  70. if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) {
  71. bool found = 0;
  72. list_entry_t *list = &(mm->mmap_list), *le = list;
  73. while ((le = list_next(le)) != list) {
  74. vma = le2vma(le, list_link);
  75. if (vma->vm_start<=addr && addr < vma->vm_end) {
  76. found = 1;
  77. break;
  78. }
  79. }
  80. if (!found) {
  81. vma = NULL;
  82. }
  83. }
  84. if (vma != NULL) {
  85. mm->mmap_cache = vma;
  86. }
  87. }
  88. return vma;
  89. }
  90. // check_vma_overlap - check if vma1 overlaps vma2 ?
  91. static inline void
  92. check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) {
  93. assert(prev->vm_start < prev->vm_end);
  94. assert(prev->vm_end <= next->vm_start);
  95. assert(next->vm_start < next->vm_end);
  96. }
  97. // insert_vma_struct -insert vma in mm's list link
  98. void
  99. insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) {
  100. assert(vma->vm_start < vma->vm_end);
  101. list_entry_t *list = &(mm->mmap_list);
  102. list_entry_t *le_prev = list, *le_next;
  103. list_entry_t *le = list;
  104. while ((le = list_next(le)) != list) {
  105. struct vma_struct *mmap_prev = le2vma(le, list_link);
  106. if (mmap_prev->vm_start > vma->vm_start) {
  107. break;
  108. }
  109. le_prev = le;
  110. }
  111. le_next = list_next(le_prev);
  112. /* check overlap */
  113. if (le_prev != list) {
  114. check_vma_overlap(le2vma(le_prev, list_link), vma);
  115. }
  116. if (le_next != list) {
  117. check_vma_overlap(vma, le2vma(le_next, list_link));
  118. }
  119. vma->vm_mm = mm;
  120. list_add_after(le_prev, &(vma->list_link));
  121. mm->map_count ++;
  122. }
  123. // mm_destroy - free mm and mm internal fields
  124. void
  125. mm_destroy(struct mm_struct *mm) {
  126. list_entry_t *list = &(mm->mmap_list), *le;
  127. while ((le = list_next(list)) != list) {
  128. list_del(le);
  129. kfree(le2vma(le, list_link)); //kfree vma
  130. }
  131. kfree(mm); //kfree mm
  132. mm=NULL;
  133. }
  134. // vmm_init - initialize virtual memory management
  135. // - now just call check_vmm to check correctness of vmm
  136. void
  137. vmm_init(void) {
  138. check_vmm();
  139. }
  140. // check_vmm - check correctness of vmm
  141. static void
  142. check_vmm(void) {
  143. size_t nr_free_pages_store = nr_free_pages();
  144. check_vma_struct();
  145. check_pgfault();
  146. // assert(nr_free_pages_store == nr_free_pages());
  147. cprintf("check_vmm() succeeded.\n");
  148. }
  149. static void
  150. check_vma_struct(void) {
  151. size_t nr_free_pages_store = nr_free_pages();
  152. struct mm_struct *mm = mm_create();
  153. assert(mm != NULL);
  154. int step1 = 10, step2 = step1 * 10;
  155. int i;
  156. for (i = step1; i >= 1; i --) {
  157. struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
  158. assert(vma != NULL);
  159. insert_vma_struct(mm, vma);
  160. }
  161. for (i = step1 + 1; i <= step2; i ++) {
  162. struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
  163. assert(vma != NULL);
  164. insert_vma_struct(mm, vma);
  165. }
  166. list_entry_t *le = list_next(&(mm->mmap_list));
  167. for (i = 1; i <= step2; i ++) {
  168. assert(le != &(mm->mmap_list));
  169. struct vma_struct *mmap = le2vma(le, list_link);
  170. assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2);
  171. le = list_next(le);
  172. }
  173. for (i = 5; i <= 5 * step2; i +=5) {
  174. struct vma_struct *vma1 = find_vma(mm, i);
  175. assert(vma1 != NULL);
  176. struct vma_struct *vma2 = find_vma(mm, i+1);
  177. assert(vma2 != NULL);
  178. struct vma_struct *vma3 = find_vma(mm, i+2);
  179. assert(vma3 == NULL);
  180. struct vma_struct *vma4 = find_vma(mm, i+3);
  181. assert(vma4 == NULL);
  182. struct vma_struct *vma5 = find_vma(mm, i+4);
  183. assert(vma5 == NULL);
  184. assert(vma1->vm_start == i && vma1->vm_end == i + 2);
  185. assert(vma2->vm_start == i && vma2->vm_end == i + 2);
  186. }
  187. for (i =4; i>=0; i--) {
  188. struct vma_struct *vma_below_5= find_vma(mm,i);
  189. if (vma_below_5 != NULL ) {
  190. cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end);
  191. }
  192. assert(vma_below_5 == NULL);
  193. }
  194. mm_destroy(mm);
  195. // assert(nr_free_pages_store == nr_free_pages());
  196. cprintf("check_vma_struct() succeeded!\n");
  197. }
  198. struct mm_struct *check_mm_struct;
  199. // check_pgfault - check correctness of pgfault handler
  200. static void
  201. check_pgfault(void) {
  202. size_t nr_free_pages_store = nr_free_pages();
  203. check_mm_struct = mm_create();
  204. assert(check_mm_struct != NULL);
  205. struct mm_struct *mm = check_mm_struct;
  206. pde_t *pgdir = mm->pgdir = boot_pgdir;
  207. assert(pgdir[0] == 0);
  208. struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE);
  209. assert(vma != NULL);
  210. insert_vma_struct(mm, vma);
  211. uintptr_t addr = 0x100;
  212. assert(find_vma(mm, addr) == vma);
  213. int i, sum = 0;
  214. for (i = 0; i < 100; i ++) {
  215. *(char *)(addr + i) = i;
  216. sum += i;
  217. }
  218. for (i = 0; i < 100; i ++) {
  219. sum -= *(char *)(addr + i);
  220. }
  221. assert(sum == 0);
  222. page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
  223. free_page(pa2page(pgdir[0]));
  224. pgdir[0] = 0;
  225. mm->pgdir = NULL;
  226. mm_destroy(mm);
  227. check_mm_struct = NULL;
  228. assert(nr_free_pages_store == nr_free_pages());
  229. cprintf("check_pgfault() succeeded!\n");
  230. }
  231. //page fault number
  232. volatile unsigned int pgfault_num=0;
  233. /* do_pgfault - interrupt handler to process the page fault execption
  234. * @mm : the control struct for a set of vma using the same PDT
  235. * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware
  236. * @addr : the addr which causes a memory access exception, (the contents of the CR2 register)
  237. *
  238. * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault
  239. * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing
  240. * the exception and recovering from it.
  241. * (1) The contents of the CR2 register. The processor loads the CR2 register with the
  242. * 32-bit linear address that generated the exception. The do_pgfault fun can
  243. * use this address to locate the corresponding page directory and page-table
  244. * entries.
  245. * (2) An error code on the kernel stack. The error code for a page fault has a format different from
  246. * that for other exceptions. The error code tells the exception handler three things:
  247. * -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0)
  248. * or to either an access rights violation or the use of a reserved bit (1).
  249. * -- The W/R flag (bit 1) indicates whether the memory access that caused the exception
  250. * was a read (0) or write (1).
  251. * -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1)
  252. * or supervisor mode (0) at the time of the exception.
  253. */
  254. int
  255. do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
  256. int ret = -E_INVAL;
  257. //try to find a vma which include addr
  258. struct vma_struct *vma = find_vma(mm, addr);
  259. pgfault_num++;
  260. //If the addr is in the range of a mm's vma?
  261. if (vma == NULL || vma->vm_start > addr) {
  262. cprintf("not valid addr %x, and can not find it in vma\n", addr);
  263. goto failed;
  264. }
  265. //check the error_code
  266. switch (error_code & 3) {
  267. default:
  268. /* error code flag : default is 3 ( W/R=1, P=1): write, present */
  269. case 2: /* error code flag : (W/R=1, P=0): write, not present */
  270. if (!(vma->vm_flags & VM_WRITE)) {
  271. cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
  272. goto failed;
  273. }
  274. break;
  275. case 1: /* error code flag : (W/R=0, P=1): read, present */
  276. cprintf("do_pgfault failed: error code flag = read AND present\n");
  277. goto failed;
  278. case 0: /* error code flag : (W/R=0, P=0): read, not present */
  279. if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
  280. cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
  281. goto failed;
  282. }
  283. }
  284. /* IF (write an existed addr ) OR
  285. * (write an non_existed addr && addr is writable) OR
  286. * (read an non_existed addr && addr is readable)
  287. * THEN
  288. * continue process
  289. */
  290. uint32_t perm = PTE_U;
  291. if (vma->vm_flags & VM_WRITE) {
  292. perm |= PTE_W;
  293. }
  294. addr = ROUNDDOWN(addr, PGSIZE);
  295. ret = -E_NO_MEM;
  296. pte_t *ptep=NULL;
  297. /*LAB3 EXERCISE 1: YOUR CODE
  298. * Maybe you want help comment, BELOW comments can help you finish the code
  299. *
  300. * Some Useful MACROs and DEFINEs, you can use them in below implementation.
  301. * MACROs or Functions:
  302. * get_pte : get an pte and return the kernel virtual address of this pte for la
  303. * if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1')
  304. * pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup
  305. * an addr map pa<--->la with linear address la and the PDT pgdir
  306. * DEFINES:
  307. * VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable
  308. * PTE_W 0x002 // page table/directory entry flags bit : Writeable
  309. * PTE_U 0x004 // page table/directory entry flags bit : User can access
  310. * VARIABLES:
  311. * mm->pgdir : the PDT of these vma
  312. *
  313. */
  314. #if 0
  315. /*LAB3 EXERCISE 1: YOUR CODE*/
  316. ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
  317. if (*ptep == 0) {
  318. //(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
  319. }
  320. else {
  321. /*LAB3 EXERCISE 2: YOUR CODE
  322. * Now we think this pte is a swap entry, we should load data from disk to a page with phy addr,
  323. * and map the phy addr with logical addr, trigger swap manager to record the access situation of this page.
  324. *
  325. * Some Useful MACROs and DEFINEs, you can use them in below implementation.
  326. * MACROs or Functions:
  327. * swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr,
  328. * find the addr of disk page, read the content of disk page into this memroy page
  329. * page_insert build the map of phy addr of an Page with the linear addr la
  330. * swap_map_swappable set the page swappable
  331. */
  332. if(swap_init_ok) {
  333. struct Page *page=NULL;
  334. //(1)According to the mm AND addr, try to load the content of right disk page
  335. // into the memory which page managed.
  336. //(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr
  337. //(3) make the page swappable.
  338. }
  339. else {
  340. cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
  341. goto failed;
  342. }
  343. }
  344. #endif
  345. // try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
  346. // (notice the 3th parameter '1')
  347. if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
  348. cprintf("get_pte in do_pgfault failed\n");
  349. goto failed;
  350. }
  351. if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
  352. if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
  353. cprintf("pgdir_alloc_page in do_pgfault failed\n");
  354. goto failed;
  355. }
  356. }
  357. else { // if this pte is a swap entry, then load data from disk to a page with phy addr
  358. // and call page_insert to map the phy addr with logical addr
  359. if(swap_init_ok) {
  360. struct Page *page=NULL;
  361. if ((ret = swap_in(mm, addr, &page)) != 0) {
  362. cprintf("swap_in in do_pgfault failed\n");
  363. goto failed;
  364. }
  365. page_insert(mm->pgdir, page, addr, perm);
  366. swap_map_swappable(mm, addr, page, 1);
  367. }
  368. else {
  369. cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
  370. goto failed;
  371. }
  372. }
  373. ret = 0;
  374. failed:
  375. return ret;
  376. }