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

1115 lines
34 KiB

10 years ago
  1. #include <proc.h>
  2. #include <kmalloc.h>
  3. #include <string.h>
  4. #include <sync.h>
  5. #include <pmm.h>
  6. #include <error.h>
  7. #include <sched.h>
  8. #include <elf.h>
  9. #include <vmm.h>
  10. #include <trap.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <assert.h>
  14. #include <unistd.h>
  15. #include <fs.h>
  16. #include <vfs.h>
  17. #include <sysfile.h>
  18. /* ------------- process/thread mechanism design&implementation -------------
  19. (an simplified Linux process/thread mechanism )
  20. introduction:
  21. ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads
  22. for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to
  23. manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory).
  24. ------------------------------
  25. process state : meaning -- reason
  26. PROC_UNINIT : uninitialized -- alloc_proc
  27. PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep
  28. PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc,
  29. PROC_ZOMBIE : almost dead -- do_exit
  30. -----------------------------
  31. process state changing:
  32. alloc_proc RUNNING
  33. + +--<----<--+
  34. + + proc_run +
  35. V +-->---->--+
  36. PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --
  37. A + +
  38. | +--- do_exit --> PROC_ZOMBIE +
  39. + +
  40. -----------------------wakeup_proc----------------------------------
  41. -----------------------------
  42. process relations
  43. parent: proc->parent (proc is children)
  44. children: proc->cptr (proc is parent)
  45. older sibling: proc->optr (proc is younger sibling)
  46. younger sibling: proc->yptr (proc is older sibling)
  47. -----------------------------
  48. related syscall for process:
  49. SYS_exit : process exit, -->do_exit
  50. SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc
  51. SYS_wait : wait process -->do_wait
  52. SYS_exec : after fork, process execute a program -->load a program and refresh the mm
  53. SYS_clone : create child thread -->do_fork-->wakeup_proc
  54. SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process
  55. SYS_sleep : process sleep -->do_sleep
  56. SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING
  57. -->wakeup_proc-->do_wait-->do_exit
  58. SYS_getpid : get the process's pid
  59. */
  60. // the process set's list
  61. list_entry_t proc_list;
  62. #define HASH_SHIFT 10
  63. #define HASH_LIST_SIZE (1 << HASH_SHIFT)
  64. #define pid_hashfn(x) (hash32(x, HASH_SHIFT))
  65. // has list for process set based on pid
  66. static list_entry_t hash_list[HASH_LIST_SIZE];
  67. // idle proc
  68. struct proc_struct *idleproc = NULL;
  69. // init proc
  70. struct proc_struct *initproc = NULL;
  71. // current proc
  72. struct proc_struct *current = NULL;
  73. static int nr_process = 0;
  74. void kernel_thread_entry(void);
  75. void forkrets(struct trapframe *tf);
  76. void switch_to(struct context *from, struct context *to);
  77. // alloc_proc - alloc a proc_struct and init all fields of proc_struct
  78. static struct proc_struct *
  79. alloc_proc(void) {
  80. struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
  81. if (proc != NULL) {
  82. //LAB4:EXERCISE1 YOUR CODE
  83. /*
  84. * below fields in proc_struct need to be initialized
  85. * enum proc_state state; // Process state
  86. * int pid; // Process ID
  87. * int runs; // the running times of Proces
  88. * uintptr_t kstack; // Process kernel stack
  89. * volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
  90. * struct proc_struct *parent; // the parent process
  91. * struct mm_struct *mm; // Process's memory management field
  92. * struct context context; // Switch here to run process
  93. * struct trapframe *tf; // Trap frame for current interrupt
  94. * uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
  95. * uint32_t flags; // Process flag
  96. * char name[PROC_NAME_LEN + 1]; // Process name
  97. */
  98. //LAB5 YOUR CODE : (update LAB4 steps)
  99. /*
  100. * below fields(add in LAB5) in proc_struct need to be initialized
  101. * uint32_t wait_state; // waiting state
  102. * struct proc_struct *cptr, *yptr, *optr; // relations between processes
  103. */
  104. //LAB8:EXERCISE2 YOUR CODE HINT:need add some code to init fs in proc_struct, ...
  105. proc->state = PROC_UNINIT;
  106. proc->pid = -1;
  107. proc->runs = 0;
  108. proc->kstack = 0;
  109. proc->need_resched = 0;
  110. proc->parent = NULL;
  111. proc->mm = NULL;
  112. memset(&(proc->context), 0, sizeof(struct context));
  113. proc->tf = NULL;
  114. proc->cr3 = boot_cr3;
  115. proc->flags = 0;
  116. memset(proc->name, 0, PROC_NAME_LEN);
  117. proc->wait_state = 0;
  118. proc->cptr = proc->optr = proc->yptr = NULL;
  119. proc->rq = NULL;
  120. proc->run_link.prev = proc->run_link.next = NULL;
  121. proc->time_slice = 0;
  122. proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL;
  123. proc->lab6_stride = 0;
  124. proc->lab6_priority = 0;
  125. proc->filesp = NULL;
  126. }
  127. return proc;
  128. }
  129. // set_proc_name - set the name of proc
  130. char *
  131. set_proc_name(struct proc_struct *proc, const char *name) {
  132. memset(proc->name, 0, sizeof(proc->name));
  133. return memcpy(proc->name, name, PROC_NAME_LEN);
  134. }
  135. // get_proc_name - get the name of proc
  136. char *
  137. get_proc_name(struct proc_struct *proc) {
  138. static char name[PROC_NAME_LEN + 1];
  139. memset(name, 0, sizeof(name));
  140. return memcpy(name, proc->name, PROC_NAME_LEN);
  141. }
  142. // set_links - set the relation links of process
  143. static void
  144. set_links(struct proc_struct *proc) {
  145. list_add(&proc_list, &(proc->list_link));
  146. proc->yptr = NULL;
  147. if ((proc->optr = proc->parent->cptr) != NULL) {
  148. proc->optr->yptr = proc;
  149. }
  150. proc->parent->cptr = proc;
  151. nr_process ++;
  152. }
  153. // remove_links - clean the relation links of process
  154. static void
  155. remove_links(struct proc_struct *proc) {
  156. list_del(&(proc->list_link));
  157. if (proc->optr != NULL) {
  158. proc->optr->yptr = proc->yptr;
  159. }
  160. if (proc->yptr != NULL) {
  161. proc->yptr->optr = proc->optr;
  162. }
  163. else {
  164. proc->parent->cptr = proc->optr;
  165. }
  166. nr_process --;
  167. }
  168. // get_pid - alloc a unique pid for process
  169. static int
  170. get_pid(void) {
  171. static_assert(MAX_PID > MAX_PROCESS);
  172. struct proc_struct *proc;
  173. list_entry_t *list = &proc_list, *le;
  174. static int next_safe = MAX_PID, last_pid = MAX_PID;
  175. if (++ last_pid >= MAX_PID) {
  176. last_pid = 1;
  177. goto inside;
  178. }
  179. if (last_pid >= next_safe) {
  180. inside:
  181. next_safe = MAX_PID;
  182. repeat:
  183. le = list;
  184. while ((le = list_next(le)) != list) {
  185. proc = le2proc(le, list_link);
  186. if (proc->pid == last_pid) {
  187. if (++ last_pid >= next_safe) {
  188. if (last_pid >= MAX_PID) {
  189. last_pid = 1;
  190. }
  191. next_safe = MAX_PID;
  192. goto repeat;
  193. }
  194. }
  195. else if (proc->pid > last_pid && next_safe > proc->pid) {
  196. next_safe = proc->pid;
  197. }
  198. }
  199. }
  200. return last_pid;
  201. }
  202. // proc_run - make process "proc" running on cpu
  203. // NOTE: before call switch_to, should load base addr of "proc"'s new PDT
  204. void
  205. proc_run(struct proc_struct *proc) {
  206. if (proc != current) {
  207. bool intr_flag;
  208. struct proc_struct *prev = current, *next = proc;
  209. local_intr_save(intr_flag);
  210. {
  211. current = proc;
  212. load_esp0(next->kstack + KSTACKSIZE);
  213. lcr3(next->cr3);
  214. switch_to(&(prev->context), &(next->context));
  215. }
  216. local_intr_restore(intr_flag);
  217. }
  218. }
  219. // forkret -- the first kernel entry point of a new thread/process
  220. // NOTE: the addr of forkret is setted in copy_thread function
  221. // after switch_to, the current proc will execute here.
  222. static void
  223. forkret(void) {
  224. forkrets(current->tf);
  225. }
  226. // hash_proc - add proc into proc hash_list
  227. static void
  228. hash_proc(struct proc_struct *proc) {
  229. list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link));
  230. }
  231. // unhash_proc - delete proc from proc hash_list
  232. static void
  233. unhash_proc(struct proc_struct *proc) {
  234. list_del(&(proc->hash_link));
  235. }
  236. // find_proc - find proc frome proc hash_list according to pid
  237. struct proc_struct *
  238. find_proc(int pid) {
  239. if (0 < pid && pid < MAX_PID) {
  240. list_entry_t *list = hash_list + pid_hashfn(pid), *le = list;
  241. while ((le = list_next(le)) != list) {
  242. struct proc_struct *proc = le2proc(le, hash_link);
  243. if (proc->pid == pid) {
  244. return proc;
  245. }
  246. }
  247. }
  248. return NULL;
  249. }
  250. // kernel_thread - create a kernel thread using "fn" function
  251. // NOTE: the contents of temp trapframe tf will be copied to
  252. // proc->tf in do_fork-->copy_thread function
  253. int
  254. kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
  255. struct trapframe tf;
  256. memset(&tf, 0, sizeof(struct trapframe));
  257. tf.tf_cs = KERNEL_CS;
  258. tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;
  259. tf.tf_regs.reg_ebx = (uint32_t)fn;
  260. tf.tf_regs.reg_edx = (uint32_t)arg;
  261. tf.tf_eip = (uint32_t)kernel_thread_entry;
  262. return do_fork(clone_flags | CLONE_VM, 0, &tf);
  263. }
  264. // setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack
  265. static int
  266. setup_kstack(struct proc_struct *proc) {
  267. struct Page *page = alloc_pages(KSTACKPAGE);
  268. if (page != NULL) {
  269. proc->kstack = (uintptr_t)page2kva(page);
  270. return 0;
  271. }
  272. return -E_NO_MEM;
  273. }
  274. // put_kstack - free the memory space of process kernel stack
  275. static void
  276. put_kstack(struct proc_struct *proc) {
  277. free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE);
  278. }
  279. // setup_pgdir - alloc one page as PDT
  280. static int
  281. setup_pgdir(struct mm_struct *mm) {
  282. struct Page *page;
  283. if ((page = alloc_page()) == NULL) {
  284. return -E_NO_MEM;
  285. }
  286. pde_t *pgdir = page2kva(page);
  287. memcpy(pgdir, boot_pgdir, PGSIZE);
  288. pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_P | PTE_W;
  289. mm->pgdir = pgdir;
  290. return 0;
  291. }
  292. // put_pgdir - free the memory space of PDT
  293. static void
  294. put_pgdir(struct mm_struct *mm) {
  295. free_page(kva2page(mm->pgdir));
  296. }
  297. // copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags
  298. // - if clone_flags & CLONE_VM, then "share" ; else "duplicate"
  299. static int
  300. copy_mm(uint32_t clone_flags, struct proc_struct *proc) {
  301. struct mm_struct *mm, *oldmm = current->mm;
  302. /* current is a kernel thread */
  303. if (oldmm == NULL) {
  304. return 0;
  305. }
  306. if (clone_flags & CLONE_VM) {
  307. mm = oldmm;
  308. goto good_mm;
  309. }
  310. int ret = -E_NO_MEM;
  311. if ((mm = mm_create()) == NULL) {
  312. goto bad_mm;
  313. }
  314. if (setup_pgdir(mm) != 0) {
  315. goto bad_pgdir_cleanup_mm;
  316. }
  317. lock_mm(oldmm);
  318. {
  319. ret = dup_mmap(mm, oldmm);
  320. }
  321. unlock_mm(oldmm);
  322. if (ret != 0) {
  323. goto bad_dup_cleanup_mmap;
  324. }
  325. good_mm:
  326. mm_count_inc(mm);
  327. proc->mm = mm;
  328. proc->cr3 = PADDR(mm->pgdir);
  329. return 0;
  330. bad_dup_cleanup_mmap:
  331. exit_mmap(mm);
  332. put_pgdir(mm);
  333. bad_pgdir_cleanup_mm:
  334. mm_destroy(mm);
  335. bad_mm:
  336. return ret;
  337. }
  338. // copy_thread - setup the trapframe on the process's kernel stack top and
  339. // - setup the kernel entry point and stack of process
  340. static void
  341. copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
  342. proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;
  343. *(proc->tf) = *tf;
  344. proc->tf->tf_regs.reg_eax = 0;
  345. proc->tf->tf_esp = esp;
  346. proc->tf->tf_eflags |= FL_IF;
  347. proc->context.eip = (uintptr_t)forkret;
  348. proc->context.esp = (uintptr_t)(proc->tf);
  349. }
  350. //copy_fs&put_fs function used by do_fork in LAB8
  351. static int
  352. copy_fs(uint32_t clone_flags, struct proc_struct *proc) {
  353. struct files_struct *filesp, *old_filesp = current->filesp;
  354. assert(old_filesp != NULL);
  355. if (clone_flags & CLONE_FS) {
  356. filesp = old_filesp;
  357. goto good_files_struct;
  358. }
  359. int ret = -E_NO_MEM;
  360. if ((filesp = files_create()) == NULL) {
  361. goto bad_files_struct;
  362. }
  363. if ((ret = dup_fs(filesp, old_filesp)) != 0) {
  364. goto bad_dup_cleanup_fs;
  365. }
  366. good_files_struct:
  367. files_count_inc(filesp);
  368. proc->filesp = filesp;
  369. return 0;
  370. bad_dup_cleanup_fs:
  371. files_destroy(filesp);
  372. bad_files_struct:
  373. return ret;
  374. }
  375. static void
  376. put_fs(struct proc_struct *proc) {
  377. struct files_struct *filesp = proc->filesp;
  378. if (filesp != NULL) {
  379. if (files_count_dec(filesp) == 0) {
  380. files_destroy(filesp);
  381. }
  382. }
  383. }
  384. /* do_fork - parent process for a new child process
  385. * @clone_flags: used to guide how to clone the child process
  386. * @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
  387. * @tf: the trapframe info, which will be copied to child process's proc->tf
  388. */
  389. int
  390. do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
  391. int ret = -E_NO_FREE_PROC;
  392. struct proc_struct *proc;
  393. if (nr_process >= MAX_PROCESS) {
  394. goto fork_out;
  395. }
  396. ret = -E_NO_MEM;
  397. //LAB4:EXERCISE2 YOUR CODE
  398. //LAB8:EXERCISE2 YOUR CODE HINT:how to copy the fs in parent's proc_struct?
  399. /*
  400. * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation.
  401. * MACROs or Functions:
  402. * alloc_proc: create a proc struct and init fields (lab4:exercise1)
  403. * setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack
  404. * copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags
  405. * if clone_flags & CLONE_VM, then "share" ; else "duplicate"
  406. * copy_thread: setup the trapframe on the process's kernel stack top and
  407. * setup the kernel entry point and stack of process
  408. * hash_proc: add proc into proc hash_list
  409. * get_pid: alloc a unique pid for process
  410. * wakup_proc: set proc->state = PROC_RUNNABLE
  411. * VARIABLES:
  412. * proc_list: the process set's list
  413. * nr_process: the number of process set
  414. */
  415. // 1. call alloc_proc to allocate a proc_struct
  416. // 2. call setup_kstack to allocate a kernel stack for child process
  417. // 3. call copy_mm to dup OR share mm according clone_flag
  418. // 4. call copy_thread to setup tf & context in proc_struct
  419. // 5. insert proc_struct into hash_list && proc_list
  420. // 6. call wakup_proc to make the new child process RUNNABLE
  421. // 7. set ret vaule using child proc's pid
  422. //LAB5 YOUR CODE : (update LAB4 steps)
  423. /* Some Functions
  424. * set_links: set the relation links of process. ALSO SEE: remove_links: lean the relation links of process
  425. * -------------------
  426. * update step 1: set child proc's parent to current process, make sure current process's wait_state is 0
  427. * update step 5: insert proc_struct into hash_list && proc_list, set the relation links of process
  428. */
  429. if ((proc = alloc_proc()) == NULL) {
  430. goto fork_out;
  431. }
  432. proc->parent = current;
  433. assert(current->wait_state == 0);
  434. if (setup_kstack(proc) != 0) {
  435. goto bad_fork_cleanup_proc;
  436. }
  437. if (copy_fs(clone_flags, proc) != 0) { //for LAB8
  438. goto bad_fork_cleanup_kstack;
  439. }
  440. if (copy_mm(clone_flags, proc) != 0) {
  441. goto bad_fork_cleanup_kstack;
  442. }
  443. copy_thread(proc, stack, tf);
  444. bool intr_flag;
  445. local_intr_save(intr_flag);
  446. {
  447. proc->pid = get_pid();
  448. hash_proc(proc);
  449. set_links(proc);
  450. }
  451. local_intr_restore(intr_flag);
  452. wakeup_proc(proc);
  453. ret = proc->pid;
  454. fork_out:
  455. return ret;
  456. bad_fork_cleanup_fs: //for LAB8
  457. put_fs(proc);
  458. bad_fork_cleanup_kstack:
  459. put_kstack(proc);
  460. bad_fork_cleanup_proc:
  461. kfree(proc);
  462. goto fork_out;
  463. }
  464. // do_exit - called by sys_exit
  465. // 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process
  466. // 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
  467. // 3. call scheduler to switch to other process
  468. int
  469. do_exit(int error_code) {
  470. if (current == idleproc) {
  471. panic("idleproc exit.\n");
  472. }
  473. if (current == initproc) {
  474. panic("initproc exit.\n");
  475. }
  476. struct mm_struct *mm = current->mm;
  477. if (mm != NULL) {
  478. lcr3(boot_cr3);
  479. if (mm_count_dec(mm) == 0) {
  480. exit_mmap(mm);
  481. put_pgdir(mm);
  482. mm_destroy(mm);
  483. }
  484. current->mm = NULL;
  485. }
  486. put_fs(current); //for LAB8
  487. current->state = PROC_ZOMBIE;
  488. current->exit_code = error_code;
  489. bool intr_flag;
  490. struct proc_struct *proc;
  491. local_intr_save(intr_flag);
  492. {
  493. proc = current->parent;
  494. if (proc->wait_state == WT_CHILD) {
  495. wakeup_proc(proc);
  496. }
  497. while (current->cptr != NULL) {
  498. proc = current->cptr;
  499. current->cptr = proc->optr;
  500. proc->yptr = NULL;
  501. if ((proc->optr = initproc->cptr) != NULL) {
  502. initproc->cptr->yptr = proc;
  503. }
  504. proc->parent = initproc;
  505. initproc->cptr = proc;
  506. if (proc->state == PROC_ZOMBIE) {
  507. if (initproc->wait_state == WT_CHILD) {
  508. wakeup_proc(initproc);
  509. }
  510. }
  511. }
  512. }
  513. local_intr_restore(intr_flag);
  514. schedule();
  515. panic("do_exit will not return!! %d.\n", current->pid);
  516. }
  517. //load_icode_read is used by load_icode in LAB8
  518. static int
  519. load_icode_read(int fd, void *buf, size_t len, off_t offset) {
  520. int ret;
  521. if ((ret = sysfile_seek(fd, offset, LSEEK_SET)) != 0) {
  522. return ret;
  523. }
  524. if ((ret = sysfile_read(fd, buf, len)) != len) {
  525. return (ret < 0) ? ret : -1;
  526. }
  527. return 0;
  528. }
  529. // load_icode - called by sys_exec-->do_execve
  530. static int
  531. load_icode(int fd, int argc, char **kargv) {
  532. /* LAB8:EXERCISE2 YOUR CODE HINT:how to load the file with handler fd in to process's memory? how to setup argc/argv?
  533. * MACROs or Functions:
  534. * mm_create - create a mm
  535. * setup_pgdir - setup pgdir in mm
  536. * load_icode_read - read raw data content of program file
  537. * mm_map - build new vma
  538. * pgdir_alloc_page - allocate new memory for TEXT/DATA/BSS/stack parts
  539. * lcr3 - update Page Directory Addr Register -- CR3
  540. */
  541. /* (1) create a new mm for current process
  542. * (2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
  543. * (3) copy TEXT/DATA/BSS parts in binary to memory space of process
  544. * (3.1) read raw data content in file and resolve elfhdr
  545. * (3.2) read raw data content in file and resolve proghdr based on info in elfhdr
  546. * (3.3) call mm_map to build vma related to TEXT/DATA
  547. * (3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file
  548. * and copy them into the new allocated pages
  549. * (3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages
  550. * (4) call mm_map to setup user stack, and put parameters into user stack
  551. * (5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO)
  552. * (6) setup uargc and uargv in user stacks
  553. * (7) setup trapframe for user environment
  554. * (8) if up steps failed, you should cleanup the env.
  555. */
  556. assert(argc >= 0 && argc <= EXEC_MAX_ARG_NUM);
  557. if (current->mm != NULL) {
  558. panic("load_icode: current->mm must be empty.\n");
  559. }
  560. int ret = -E_NO_MEM;
  561. struct mm_struct *mm;
  562. if ((mm = mm_create()) == NULL) {
  563. goto bad_mm;
  564. }
  565. if (setup_pgdir(mm) != 0) {
  566. goto bad_pgdir_cleanup_mm;
  567. }
  568. struct Page *page;
  569. struct elfhdr __elf, *elf = &__elf;
  570. if ((ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0)) != 0) {
  571. goto bad_elf_cleanup_pgdir;
  572. }
  573. if (elf->e_magic != ELF_MAGIC) {
  574. ret = -E_INVAL_ELF;
  575. goto bad_elf_cleanup_pgdir;
  576. }
  577. struct proghdr __ph, *ph = &__ph;
  578. uint32_t vm_flags, perm, phnum;
  579. for (phnum = 0; phnum < elf->e_phnum; phnum ++) {
  580. off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum;
  581. if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) {
  582. goto bad_cleanup_mmap;
  583. }
  584. if (ph->p_type != ELF_PT_LOAD) {
  585. continue ;
  586. }
  587. if (ph->p_filesz > ph->p_memsz) {
  588. ret = -E_INVAL_ELF;
  589. goto bad_cleanup_mmap;
  590. }
  591. if (ph->p_filesz == 0) {
  592. continue ;
  593. }
  594. vm_flags = 0, perm = PTE_U;
  595. if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
  596. if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
  597. if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ;
  598. if (vm_flags & VM_WRITE) perm |= PTE_W;
  599. if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) {
  600. goto bad_cleanup_mmap;
  601. }
  602. off_t offset = ph->p_offset;
  603. size_t off, size;
  604. uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);
  605. ret = -E_NO_MEM;
  606. end = ph->p_va + ph->p_filesz;
  607. while (start < end) {
  608. if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
  609. ret = -E_NO_MEM;
  610. goto bad_cleanup_mmap;
  611. }
  612. off = start - la, size = PGSIZE - off, la += PGSIZE;
  613. if (end < la) {
  614. size -= la - end;
  615. }
  616. if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) {
  617. goto bad_cleanup_mmap;
  618. }
  619. start += size, offset += size;
  620. }
  621. end = ph->p_va + ph->p_memsz;
  622. if (start < la) {
  623. /* ph->p_memsz == ph->p_filesz */
  624. if (start == end) {
  625. continue ;
  626. }
  627. off = start + PGSIZE - la, size = PGSIZE - off;
  628. if (end < la) {
  629. size -= la - end;
  630. }
  631. memset(page2kva(page) + off, 0, size);
  632. start += size;
  633. assert((end < la && start == end) || (end >= la && start == la));
  634. }
  635. while (start < end) {
  636. if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
  637. ret = -E_NO_MEM;
  638. goto bad_cleanup_mmap;
  639. }
  640. off = start - la, size = PGSIZE - off, la += PGSIZE;
  641. if (end < la) {
  642. size -= la - end;
  643. }
  644. memset(page2kva(page) + off, 0, size);
  645. start += size;
  646. }
  647. }
  648. sysfile_close(fd);
  649. vm_flags = VM_READ | VM_WRITE | VM_STACK;
  650. if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {
  651. goto bad_cleanup_mmap;
  652. }
  653. assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);
  654. assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);
  655. assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);
  656. assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);
  657. mm_count_inc(mm);
  658. current->mm = mm;
  659. current->cr3 = PADDR(mm->pgdir);
  660. lcr3(PADDR(mm->pgdir));
  661. //setup argc, argv
  662. uint32_t argv_size=0, i;
  663. for (i = 0; i < argc; i ++) {
  664. argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
  665. }
  666. uintptr_t stacktop = USTACKTOP - (argv_size/sizeof(long)+1)*sizeof(long);
  667. char** uargv=(char **)(stacktop - argc * sizeof(char *));
  668. argv_size = 0;
  669. for (i = 0; i < argc; i ++) {
  670. uargv[i] = strcpy((char *)(stacktop + argv_size ), kargv[i]);
  671. argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
  672. }
  673. stacktop = (uintptr_t)uargv - sizeof(int);
  674. *(int *)stacktop = argc;
  675. struct trapframe *tf = current->tf;
  676. memset(tf, 0, sizeof(struct trapframe));
  677. tf->tf_cs = USER_CS;
  678. tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
  679. tf->tf_esp = stacktop;
  680. tf->tf_eip = elf->e_entry;
  681. tf->tf_eflags = FL_IF;
  682. ret = 0;
  683. out:
  684. return ret;
  685. bad_cleanup_mmap:
  686. exit_mmap(mm);
  687. bad_elf_cleanup_pgdir:
  688. put_pgdir(mm);
  689. bad_pgdir_cleanup_mm:
  690. mm_destroy(mm);
  691. bad_mm:
  692. goto out;
  693. }
  694. // this function isn't very correct in LAB8
  695. static void
  696. put_kargv(int argc, char **kargv) {
  697. while (argc > 0) {
  698. kfree(kargv[-- argc]);
  699. }
  700. }
  701. static int
  702. copy_kargv(struct mm_struct *mm, int argc, char **kargv, const char **argv) {
  703. int i, ret = -E_INVAL;
  704. if (!user_mem_check(mm, (uintptr_t)argv, sizeof(const char *) * argc, 0)) {
  705. return ret;
  706. }
  707. for (i = 0; i < argc; i ++) {
  708. char *buffer;
  709. if ((buffer = kmalloc(EXEC_MAX_ARG_LEN + 1)) == NULL) {
  710. goto failed_nomem;
  711. }
  712. if (!copy_string(mm, buffer, argv[i], EXEC_MAX_ARG_LEN + 1)) {
  713. kfree(buffer);
  714. goto failed_cleanup;
  715. }
  716. kargv[i] = buffer;
  717. }
  718. return 0;
  719. failed_nomem:
  720. ret = -E_NO_MEM;
  721. failed_cleanup:
  722. put_kargv(i, kargv);
  723. return ret;
  724. }
  725. // do_execve - call exit_mmap(mm)&pug_pgdir(mm) to reclaim memory space of current process
  726. // - call load_icode to setup new memory space accroding binary prog.
  727. int
  728. do_execve(const char *name, int argc, const char **argv) {
  729. static_assert(EXEC_MAX_ARG_LEN >= FS_MAX_FPATH_LEN);
  730. struct mm_struct *mm = current->mm;
  731. if (!(argc >= 1 && argc <= EXEC_MAX_ARG_NUM)) {
  732. return -E_INVAL;
  733. }
  734. char local_name[PROC_NAME_LEN + 1];
  735. memset(local_name, 0, sizeof(local_name));
  736. char *kargv[EXEC_MAX_ARG_NUM];
  737. const char *path;
  738. int ret = -E_INVAL;
  739. lock_mm(mm);
  740. if (name == NULL) {
  741. snprintf(local_name, sizeof(local_name), "<null> %d", current->pid);
  742. }
  743. else {
  744. if (!copy_string(mm, local_name, name, sizeof(local_name))) {
  745. unlock_mm(mm);
  746. return ret;
  747. }
  748. }
  749. if ((ret = copy_kargv(mm, argc, kargv, argv)) != 0) {
  750. unlock_mm(mm);
  751. return ret;
  752. }
  753. path = argv[0];
  754. unlock_mm(mm);
  755. files_closeall(current->filesp);
  756. /* sysfile_open will check the first argument path, thus we have to use a user-space pointer, and argv[0] may be incorrect */
  757. int fd;
  758. if ((ret = fd = sysfile_open(path, O_RDONLY)) < 0) {
  759. goto execve_exit;
  760. }
  761. if (mm != NULL) {
  762. lcr3(boot_cr3);
  763. if (mm_count_dec(mm) == 0) {
  764. exit_mmap(mm);
  765. put_pgdir(mm);
  766. mm_destroy(mm);
  767. }
  768. current->mm = NULL;
  769. }
  770. ret= -E_NO_MEM;;
  771. if ((ret = load_icode(fd, argc, kargv)) != 0) {
  772. goto execve_exit;
  773. }
  774. put_kargv(argc, kargv);
  775. set_proc_name(current, local_name);
  776. return 0;
  777. execve_exit:
  778. put_kargv(argc, kargv);
  779. do_exit(ret);
  780. panic("already exit: %e.\n", ret);
  781. }
  782. // do_yield - ask the scheduler to reschedule
  783. int
  784. do_yield(void) {
  785. current->need_resched = 1;
  786. return 0;
  787. }
  788. // do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack
  789. // - proc struct of this child.
  790. // NOTE: only after do_wait function, all resources of the child proces are free.
  791. int
  792. do_wait(int pid, int *code_store) {
  793. struct mm_struct *mm = current->mm;
  794. if (code_store != NULL) {
  795. if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) {
  796. return -E_INVAL;
  797. }
  798. }
  799. struct proc_struct *proc;
  800. bool intr_flag, haskid;
  801. repeat:
  802. haskid = 0;
  803. if (pid != 0) {
  804. proc = find_proc(pid);
  805. if (proc != NULL && proc->parent == current) {
  806. haskid = 1;
  807. if (proc->state == PROC_ZOMBIE) {
  808. goto found;
  809. }
  810. }
  811. }
  812. else {
  813. proc = current->cptr;
  814. for (; proc != NULL; proc = proc->optr) {
  815. haskid = 1;
  816. if (proc->state == PROC_ZOMBIE) {
  817. goto found;
  818. }
  819. }
  820. }
  821. if (haskid) {
  822. current->state = PROC_SLEEPING;
  823. current->wait_state = WT_CHILD;
  824. schedule();
  825. if (current->flags & PF_EXITING) {
  826. do_exit(-E_KILLED);
  827. }
  828. goto repeat;
  829. }
  830. return -E_BAD_PROC;
  831. found:
  832. if (proc == idleproc || proc == initproc) {
  833. panic("wait idleproc or initproc.\n");
  834. }
  835. if (code_store != NULL) {
  836. *code_store = proc->exit_code;
  837. }
  838. local_intr_save(intr_flag);
  839. {
  840. unhash_proc(proc);
  841. remove_links(proc);
  842. }
  843. local_intr_restore(intr_flag);
  844. put_kstack(proc);
  845. kfree(proc);
  846. return 0;
  847. }
  848. // do_kill - kill process with pid by set this process's flags with PF_EXITING
  849. int
  850. do_kill(int pid) {
  851. struct proc_struct *proc;
  852. if ((proc = find_proc(pid)) != NULL) {
  853. if (!(proc->flags & PF_EXITING)) {
  854. proc->flags |= PF_EXITING;
  855. if (proc->wait_state & WT_INTERRUPTED) {
  856. wakeup_proc(proc);
  857. }
  858. return 0;
  859. }
  860. return -E_KILLED;
  861. }
  862. return -E_INVAL;
  863. }
  864. // kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread
  865. static int
  866. kernel_execve(const char *name, const char **argv) {
  867. int argc = 0, ret;
  868. while (argv[argc] != NULL) {
  869. argc ++;
  870. }
  871. asm volatile (
  872. "int %1;"
  873. : "=a" (ret)
  874. : "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (argc), "b" (argv)
  875. : "memory");
  876. return ret;
  877. }
  878. #define __KERNEL_EXECVE(name, path, ...) ({ \
  879. const char *argv[] = {path, ##__VA_ARGS__, NULL}; \
  880. cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \
  881. current->pid, name); \
  882. kernel_execve(name, argv); \
  883. })
  884. #define KERNEL_EXECVE(x, ...) __KERNEL_EXECVE(#x, #x, ##__VA_ARGS__)
  885. #define KERNEL_EXECVE2(x, ...) KERNEL_EXECVE(x, ##__VA_ARGS__)
  886. #define __KERNEL_EXECVE3(x, s, ...) KERNEL_EXECVE(x, #s, ##__VA_ARGS__)
  887. #define KERNEL_EXECVE3(x, s, ...) __KERNEL_EXECVE3(x, s, ##__VA_ARGS__)
  888. // user_main - kernel thread used to exec a user program
  889. static int
  890. user_main(void *arg) {
  891. #ifdef TEST
  892. #ifdef TESTSCRIPT
  893. KERNEL_EXECVE3(TEST, TESTSCRIPT);
  894. #else
  895. KERNEL_EXECVE2(TEST);
  896. #endif
  897. #else
  898. KERNEL_EXECVE(sh);
  899. #endif
  900. panic("user_main execve failed.\n");
  901. }
  902. // init_main - the second kernel thread used to create user_main kernel threads
  903. static int
  904. init_main(void *arg) {
  905. int ret;
  906. if ((ret = vfs_set_bootfs("disk0:")) != 0) {
  907. panic("set boot fs failed: %e.\n", ret);
  908. }
  909. size_t nr_free_pages_store = nr_free_pages();
  910. size_t kernel_allocated_store = kallocated();
  911. int pid = kernel_thread(user_main, NULL, 0);
  912. if (pid <= 0) {
  913. panic("create user_main failed.\n");
  914. }
  915. extern void check_sync(void);
  916. check_sync(); // check philosopher sync problem
  917. while (do_wait(0, NULL) == 0) {
  918. schedule();
  919. }
  920. fs_cleanup();
  921. cprintf("all user-mode processes have quit.\n");
  922. assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL);
  923. assert(nr_process == 2);
  924. assert(list_next(&proc_list) == &(initproc->list_link));
  925. assert(list_prev(&proc_list) == &(initproc->list_link));
  926. assert(nr_free_pages_store == nr_free_pages());
  927. assert(kernel_allocated_store == kallocated());
  928. cprintf("init check memory pass.\n");
  929. return 0;
  930. }
  931. // proc_init - set up the first kernel thread idleproc "idle" by itself and
  932. // - create the second kernel thread init_main
  933. void
  934. proc_init(void) {
  935. int i;
  936. list_init(&proc_list);
  937. for (i = 0; i < HASH_LIST_SIZE; i ++) {
  938. list_init(hash_list + i);
  939. }
  940. if ((idleproc = alloc_proc()) == NULL) {
  941. panic("cannot alloc idleproc.\n");
  942. }
  943. idleproc->pid = 0;
  944. idleproc->state = PROC_RUNNABLE;
  945. idleproc->kstack = (uintptr_t)bootstack;
  946. idleproc->need_resched = 1;
  947. if ((idleproc->filesp = files_create()) == NULL) {
  948. panic("create filesp (idleproc) failed.\n");
  949. }
  950. files_count_inc(idleproc->filesp);
  951. set_proc_name(idleproc, "idle");
  952. nr_process ++;
  953. current = idleproc;
  954. int pid = kernel_thread(init_main, NULL, 0);
  955. if (pid <= 0) {
  956. panic("create init_main failed.\n");
  957. }
  958. initproc = find_proc(pid);
  959. set_proc_name(initproc, "init");
  960. assert(idleproc != NULL && idleproc->pid == 0);
  961. assert(initproc != NULL && initproc->pid == 1);
  962. }
  963. // cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
  964. void
  965. cpu_idle(void) {
  966. while (1) {
  967. if (current->need_resched) {
  968. schedule();
  969. }
  970. }
  971. }
  972. //FOR LAB6, set the process's priority (bigger value will get more CPU time)
  973. void
  974. lab6_set_priority(uint32_t priority)
  975. {
  976. if (priority == 0)
  977. current->lab6_priority = 1;
  978. else current->lab6_priority = priority;
  979. }
  980. // do_sleep - set current process state to sleep and add timer with "time"
  981. // - then call scheduler. if process run again, delete timer first.
  982. int
  983. do_sleep(unsigned int time) {
  984. if (time == 0) {
  985. return 0;
  986. }
  987. bool intr_flag;
  988. local_intr_save(intr_flag);
  989. timer_t __timer, *timer = timer_init(&__timer, current, time);
  990. current->state = PROC_SLEEPING;
  991. current->wait_state = WT_TIMER;
  992. add_timer(timer);
  993. local_intr_restore(intr_flag);
  994. schedule();
  995. del_timer(timer);
  996. return 0;
  997. }