// 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);
// machine exception program counter, holds the // instruction address to which a return from // exception will go. staticinlinevoid w_mepc(uint64 x) { asmvolatile("csrw mepc, %0" : : "r" (x)); }
// machine exception program counter, holds the // instruction address to which a return from // exception will go. staticinlinevoid w_sepc(uint64 x) { asmvolatile("csrw sepc, %0" : : "r" (x)); }
// shift a physical address to the right place for a PTE. #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
#define PTE2PA(pte) (((pte) >> 10) << 12)
#define PTE_FLAGS(pte) ((pte) & 0x3FF)
// extract the three 9-bit page table indices from a virtual address. #define PXMASK 0x1FF // 9 bits #define PXSHIFT(level) (PGSHIFT+(9*(level))) #define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)
// one beyond the highest possible virtual address. // MAXVA is actually one bit less than the max allowed by // Sv39, to avoid having to sign-extend virtual addresses // that have the high bit set. #define MAXVA (1L << (9 + 9 + 9 + 12 - 1))