2020301918-os/kern/main.c
2022-11-04 13:57:40 +08:00

216 lines
8.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <assert.h>
#include <mmu.h>
#include <string.h>
#include <type.h>
#include <x86.h>
#include <elf.h>
#include <kern/fs.h>
#include <kern/kmalloc.h>
#include <kern/stdio.h>
#include <kern/pmap.h>
#include <kern/process.h>
#include <kern/protect.h>
#include <kern/trap.h>
#include <kern/syscall.h>
// 标志着内核是否处理完成
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;
}