#include #include #include #include #include #include #include #include #include #include #include #include #include #include // 标志着内核是否处理完成 bool init_kernel; // 指向当前进程pcb的指针 PROCESS *p_proc_ready; // pcb表 PROCESS proc_table[PCB_SIZE]; static inline void init_segment_regs(PROCESS *p_proc) { p_proc->pcb.user_regs.cs = (SELECTOR_FLAT_C & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_USER; p_proc->pcb.user_regs.ds = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_USER; p_proc->pcb.user_regs.es = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_USER; p_proc->pcb.user_regs.fs = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_USER; p_proc->pcb.user_regs.ss = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_USER; p_proc->pcb.user_regs.gs = (SELECTOR_VIDEO & SA_RPL_MASK & SA_TI_MASK) | RPL_USER; } static inline void lml_map(phyaddr_t cr3, uintptr_t vaddr, phyaddr_t paddr, u32 flags) { uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3); // get pdx item, pde_ptr + PDX(vaddr) pde_ptr += PDX(vaddr); if (!(*pde_ptr & PTE_P)) { // pdx ot exists, allocate a page and assign to pde *pde_ptr = phy_malloc_4k() | PTE_P | flags; } uintptr_t *ptx_ptr = (uintptr_t *)K_PHY2LIN(*pde_ptr & (~0xfff)); // get ptx item ptx_ptr += PTX(vaddr); // assume paddr is allocated, just set it to paddr[31:12] | FLAGS *ptx_ptr = (paddr & (~0xfff)) | PTE_P | flags; } /* * 内核的main函数 * 用于初始化用户进程,然后将执行流交给用户进程 */ void kernel_main(void) { kprintf("---start kernel main---\n"); PROCESS *p_proc = proc_table; for (int i = 0; i < PCB_SIZE; i++, p_proc++) { // 初始化进程段寄存器 init_segment_regs(p_proc); // 为进程分配cr3物理内存 p_proc->pcb.cr3 = phy_malloc_4k(); // 将3GB~3GB+128MB的线性地址映射到0~128MB的物理地址 map_kern(p_proc->pcb.cr3); // 在map_kern之后,就内核程序对应的页表已经被映射了 // 就可以直接lcr3,于此同时执行流不会触发page fault // 如果不先map_kern,执行流会发现执行的代码的线性地址不存在爆出Page Fault // 当然选不选择看个人的想法,评价是都行,各有各的优缺点 // lcr3(p_proc->pcb.cr3); static char filename[PCB_SIZE][12] = { // "TESTPID BIN", // "TESTKEY BIN", // "DELAY BIN", // "DELAY BIN", // "DELAY BIN", "SNAKE BIN", "HACK BIN", }; // 从磁盘中将文件读出,需要注意的是要满足短目录项的文件名长度11, // 前八个为文件名,后三个为后缀名,跟BootLoader做法一致 // 推荐将文件加载到3GB + 48MB处,应用程序保证不会有16MB那么大 read_file(filename[i], (void *)K_PHY2LIN(48 * MB)); // 现在你就需要将从磁盘中读出的ELF文件解析到用户进程的地址空间中 // panic("unimplement! load elf file"); // ---------------------------- load elf -------------------------------------- void *elf_start = (void *)K_PHY2LIN(48 * MB); assert(*(u32 *)elf_start == ELF_MAGIC); Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elf_start; for (int i = 0; i < ehdr->e_phnum; ++i) { Elf32_Phdr *phdr = elf_start + i * ehdr->e_phentsize + ehdr->e_phoff; if (phdr->p_type == PT_LOAD) { u32 vpage_fst = phdr->p_va & (~0xfff); // clear low 12 bit, start of the first pg u32 vpage_lst = (phdr->p_va + phdr->p_memsz - 1) & (~0xfff); // start of the last pg // in fact, we allocate 4k aligned vpage to every segment; // its ok because elf32 segment align is 0x1000 u32 page_num = ((vpage_lst - vpage_fst) >> 12) + 1; phyaddr_t newpage = 0; // useless initialization for (int j = 0; j < page_num; ++j) { newpage = phy_malloc_4k(); lml_map(p_proc->pcb.cr3, vpage_fst + (j << 12), newpage, PTE_P | PTE_W | PTE_U); } phyaddr_t page_start = newpage - (page_num - 1) * 4 * KB; memcpy((void *)K_PHY2LIN(page_start), (void *)(elf_start + phdr->p_offset), phdr->p_filesz); memset((void *)K_PHY2LIN(page_start) + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } } // 上一个实验中,我们开栈是用内核中的一个数组临时充当栈 // 但是现在就不行了,用户是无法访问内核的地址空间(3GB ~ 3GB + 128MB) // 需要你自行处理给用户分配用户栈。 // ---------------------------- alloc user stack ------------------------------ phyaddr_t ustack_low = phy_malloc_4k(); for (int i = 0; i < 6; ++i) phy_malloc_4k(); phyaddr_t ustack_high = phy_malloc_4k(); // alloc 8 pages, 32KB stack for each program for (int i = 0; i < 8; ++i) { lml_map(p_proc->pcb.cr3, (3 * GB - (8 - i) * PGSIZE), ustack_low + i * PGSIZE, PTE_P | PTE_W | PTE_U); } p_proc->pcb.user_regs.esp = 3 * GB; // high to low stack p_proc->pcb.user_regs.eip = ehdr->e_entry; // refer to restart: // 初始化用户寄存器 p_proc->pcb.user_regs.eflags = 0x1202; /* IF=1, IOPL=1 */ // panic("unimplement! init user stack and esp"); // 接下来初始化内核寄存器, // 为什么需要初始化内核寄存器原因是加入了系统调用后 // 非常有可能出现系统调用执行过程中插入其余中断的情况, // 如果跟之前一样所有进程共享一个内核栈会发生不可想象的结果, // 为了避免这种情况,就需要给每个进程分配一个进程栈。 // 当触发时钟中断发生调度的时候,不再是简单的切换p_proc_ready, // 而是需要将内核栈进行切换,而且需要切换执行流到另一个进程的内核栈。 // 所以需要一个地方存放当前进程的寄存器上下文,这是一个富有技巧性的活,深入研究会觉得很妙, // 如果需要深入了解,去查看kern/process.c中的schedule函数了解切换细节。 p_proc->pcb.kern_regs.esp = (u32)(p_proc + 1) - 8; // 保证切换内核栈后执行流进入的是restart函数。 *(u32 *)(p_proc->pcb.kern_regs.esp + 0) = (u32)restart; // 这里是因为restart要用`pop esp`确认esp该往哪里跳。 *(u32 *)(p_proc->pcb.kern_regs.esp + 4) = (u32)&p_proc->pcb; // 初始化其余量 p_proc->pcb.pid = i; static int priority_table[PCB_SIZE] = {1, 1}; // priority 预计给每个进程分配的时间片 // ticks 进程剩余的进程片 p_proc->pcb.priority = p_proc->pcb.ticks = priority_table[i]; p_proc->pcb.target_tick = 0; } p_proc_ready = proc_table; // 切换进程页表和tss lcr3(p_proc_ready->pcb.cr3); tss.esp0 = (u32)(&p_proc_ready->pcb.user_regs + 1); init_kernel = true; // 开个无用的kern_context存当前执行流的寄存器上下文(之后就没用了,直接放在临时变量中) struct kern_context idle; switch_kern_context(&idle, &p_proc_ready->pcb.kern_regs); assert(0); } static inline void sys_mmap(phyaddr_t cr3_src, phyaddr_t cr3_dst, uintptr_t va_src, uintptr_t va_dst) { uintptr_t *pde_ptr_src = (uintptr_t *)K_PHY2LIN(cr3_src); uintptr_t *pde_ptr_dst = (uintptr_t *)K_PHY2LIN(cr3_dst); // its quite okay, kernel space is mapped to all processes pde_ptr_src += PDX(va_src); pde_ptr_dst += PDX(va_dst); assert((*pde_ptr_dst & PTE_P) && (*pde_ptr_src & PTE_P)); // it is not a map procedure, so pde should be present uintptr_t *pte_ptr_src = (uintptr_t *)K_PHY2LIN(*pde_ptr_src & (~0xfff)); uintptr_t *pte_ptr_dst = (uintptr_t *)K_PHY2LIN(*pde_ptr_dst & (~0xfff)); pte_ptr_src += PTX(va_src); pte_ptr_dst += PTX(va_dst); assert((*pte_ptr_dst & PTE_P) && (*pte_ptr_src & PTE_P)); // make should page table entry also exists *pte_ptr_dst = *pte_ptr_src; // simple assign src to dst, no need to recycle, though should be } #define PG_MASK 0x0fff // 12bit mask ssize_t do_mmap(int pid, void *src, void *dst, size_t length) { if ((length & PG_MASK) || ((u32)src & PG_MASK) || ((u32)dst & PG_MASK)) { // make sure src,dst,len are 4K aligned assert(0); return -1; } if (pid > PCB_SIZE) { assert(0); return -1; } disable_int(); for (int off = 0; off < length; off += PGSIZE) { sys_mmap(proc_table[pid].pcb.cr3, p_proc_ready->pcb.cr3, (uintptr_t)src + off, (uintptr_t)dst + off); } enable_int(); return 0; }