# qemu -kernel loads the kernel at 0x80000000 # and causes each CPU to jump there. # kernel.ld causes the following code to # be placed at 0x80000000. .section .text _entry: # set up a stack for C. # stack0 is declared in start.c, # with a 4096-byte stack per CPU. # sp = stack0 + (hartid * 4096) la sp, stack0 li a0, 1024*4 csrr a1, mhartid addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 # jump to start() in start.c call start spin: j spin
// entry.S needs one stack per CPU. __attribute__ ((aligned (16))) char stack0[4096 * NCPU];
// scratch area for timer interrupt, one per CPU. uint64 mscratch0[NCPU * 32];
// assembly code in kernelvec.S for machine-mode timer interrupt. externvoidtimervec();
// entry.S jumps here in machine mode on stack0. void start() { // set M Previous Privilege mode to Supervisor, for mret. unsignedlong x = r_mstatus(); x &= ~MSTATUS_MPP_MASK; x |= MSTATUS_MPP_S; w_mstatus(x);
// set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany w_mepc((uint64)main);
// disable paging for now. w_satp(0);
// delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
// ask for clock interrupts. timerinit();
// keep each CPU's hartid in its tp register, for cpuid(). int id = r_mhartid(); w_tp(id);
// switch to supervisor mode and jump to main(). asmvolatile("mret"); }
// set up to receive timer interrupts in machine mode, // which arrive at timervec in kernelvec.S, // which turns them into software interrupts for // devintr() in trap.c. void timerinit() { // each CPU has a separate source of timer interrupts. int id = r_mhartid();
// ask the CLINT for a timer interrupt. int interval = 1000000; // cycles; about 1/10th second in qemu. *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;
// prepare information in scratch[] for timervec. // scratch[0..3] : space for timervec to save registers. // scratch[4] : address of CLINT MTIMECMP register. // scratch[5] : desired interval (in cycles) between timer interrupts. uint64 *scratch = &mscratch0[32 * id]; scratch[4] = CLINT_MTIMECMP(id); scratch[5] = interval; w_mscratch((uint64)scratch);
// set the machine-mode trap handler. w_mtvec((uint64)timervec);
// Set up first user process. void userinit(void) { structproc *p;
p = allocproc(); initproc = p; // allocate one user page and copy init's instructions // and data into it. uvminit(p->pagetable, initcode, sizeof(initcode)); p->sz = PGSIZE;
// prepare for the very first "return" from kernel to user. p->trapframe->epc = 0; // user program counter p->trapframe->sp = PGSIZE; // user stack pointer
for(;;){ // this call to wait() returns if the shell exits, // or if a parentless process exits. wpid = wait((int *) 0); if(wpid == pid){ // the shell exited; restart it. break; } elseif(wpid < 0){ printf("init: wait returned an error\n"); exit(1); } else { // it was a parentless process; do nothing. } } } }