#pypp 0 // Also declare things which only work during kernel init. #define INIT #define ARCH #include "kernel.hh" #include "elf.h" static void init_idle (): // initialize idle task as if it is currently running. idle.prev_obj = NULL idle.next_obj = NULL idle.prev = NULL idle.next = NULL idle.schedule_prev = NULL idle.schedule_next = NULL idle.address_space = &idle_memory // initialize idle_memory. idle_memory.prev_obj = NULL idle_memory.next_obj = NULL idle_memory.prev = NULL idle_memory.next = NULL idle_memory.pages = &idle_page idle_memory.threads = &idle idle_memory.memories = NULL idle_memory.limit = 0 idle_memory.used = 0 idle_memory.arch.directory = (unsigned **)0x80000000 idle_memory.arch.asid = 0 // initialize idle_page idle_page.prev_obj = NULL idle_page.next_obj = NULL idle_page.prev = NULL idle_page.next = NULL idle_page.physical = 0x80000000 current = &idle static void init_cp0 (): // Set timer to a defined value cp0_set (CP0_COMPARE, 1000000) // Reset timer cp0_set0 (CP0_COUNT) // Use the interrupt vector for interrupts cp0_set (CP0_CAUSE, 1 << 23) // clear the tlb, hardwire page 0 to 0xffffffff // and soft-wire it to (0x294 << 20) + (0x290 << 10) // (for the idle task). cp0_set (CP0_WIRED, 1) cp0_set0 (CP0_PAGE_MASK) cp0_set0 (CP0_ENTRY_LO0) cp0_set0 (CP0_ENTRY_LO1) // Get number of tlb entries (is 31). unsigned num cp0_get (CP0_CONFIG1, num) num >>= 25 num &= 0x3f // Clear the tlb. for unsigned i = 1; i <= num; ++i: // with asid 0, no page faults will be triggered, so it's safe to map memory anywhere. cp0_set (CP0_ENTRY_HI, 0x2000 * i) cp0_set (CP0_INDEX, i) // write the data. __asm__ volatile ("tlbwi") // Fill the upper page in kseg3. cp0_set (CP0_ENTRY_HI, 0xffffe000) cp0_set (CP0_ENTRY_LO0, 0x1d) cp0_set (CP0_ENTRY_LO1, 0x1f) cp0_set0 (CP0_INDEX) __asm__ volatile ("tlbwi") // Fill the idle task's page in useg. Set it to non-cachable. cp0_set (CP0_ENTRY_HI, 0x284a0000) cp0_set (CP0_ENTRY_LO0, 0x16) cp0_set (CP0_ENTRY_LO1, 0x14) __asm__ volatile ("tlbwr") // Allow eret to be used to jump to the idle task. cp0_set (CP0_EPC, 0x284a0288) // Wait with initializing the status register until the last moment, so that // exceptions in the bootup code will fill EPC and friends. static void init_threads (): Thread *previous = NULL first_scheduled = NULL for unsigned i = 0; i < NUM_THREADS; ++i: Memory *mem = top_memory.alloc_memory () Thread *thread = mem->alloc_thread () Page **pages = (Page **)mem->zalloc () Elf32_Ehdr *header = (Elf32_Ehdr *)thread_start[i] for unsigned j = 0; j < SELFMAG; ++j: if header->e_ident[j] != ELFMAG[j]: panic (i * 0x1000 + j, "invalid ELF magic") if header->e_ident[EI_CLASS] != ELFCLASS32: panic (i * 0x1000 + EI_CLASS, "invalid ELF class") if header->e_ident[EI_DATA] != ELFDATA2LSB: panic (i * 0x1000 + EI_DATA, "invalid ELF data") if header->e_ident[EI_VERSION] != EV_CURRENT: panic (i * 0x1000 + EI_VERSION, "invalid ELF version") if header->e_type != ET_EXEC: panic (i * 0x1000 + 0x10, "invalid ELF type") if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS: panic (i * 0x1000 + 0x12, "invalid ELF machine") thread->pc = header->e_entry thread->sp = 0x80000000 for unsigned section = 0; section < header->e_shnum; ++section: Elf32_Shdr *shdr = (Elf32_Shdr *)(thread_start[i] + header->e_shoff + section * header->e_shentsize) if !(shdr->sh_flags & SHF_ALLOC): continue bool writable = shdr->sh_flags & SHF_WRITE //bool executable = shdr->sh_flags & SHF_EXEC_INSTR if shdr->sh_type != SHT_NOBITS: for unsigned p = (shdr->sh_addr & PAGE_MASK); p <= ((shdr->sh_addr + shdr->sh_size - 1) & PAGE_MASK); p += PAGE_SIZE: unsigned idx = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS if !pages[idx]: pages[idx] = mem->alloc_page () pages[idx]->physical = thread_start[i] + (idx << PAGE_BITS) ++top_memory.limit if !mem->map (pages[idx], p, writable): panic (0x22446688, "unable to map initial page") else: for unsigned p = (shdr->sh_addr & PAGE_MASK); p <= ((shdr->sh_addr + shdr->sh_size - 1) & PAGE_MASK); p += PAGE_SIZE: Page *page = mem->get_mapping (p) if !page: page = mem->alloc_page () if !page: panic (0x00220022, "out of memory") page->physical = mem->zalloc () if !page->physical || !mem->map (page, p, true): panic (0x33557799, "unable to map initial bss page") else: for unsigned a = p; a < p + PAGE_SIZE; a += 4: if a >= shdr->sh_addr + shdr->sh_size: break if a < shdr->sh_addr: continue ((unsigned *)page->physical)[(a & ~PAGE_MASK) >> 2] = 0 for unsigned p = 0; p <= ((thread_start[i + 1] - thread_start[i] - 1) >> PAGE_BITS); ++p: // TODO: this also skips pages where new space is allocated. if pages[p]: continue ++top_memory.limit top_memory.zfree (thread_start[i] + (p << PAGE_BITS)) Page *stackpage = mem->alloc_page () stackpage->physical = mem->zalloc () if !stackpage || !mem->map (stackpage, 0x7ffff000, true): panic (0x13151719, "unable to map initial stack page") thread->arch.a0 = (unsigned)mem->alloc_receiver () thread->arch.a1 = (unsigned)&top_memory thread->arch.a2 = (unsigned)mem Capability *admin = mem->alloc_capability ((Receiver *)(CAPTYPE_ADMIN | ~PAGE_MASK), &mem->capabilities, ~0) thread->arch.a3 = (unsigned)admin mem->pfree ((unsigned)pages) thread->schedule_next = NULL thread->schedule_prev = previous if previous: previous->schedule_next = thread else: first_scheduled = thread previous = thread // Initialize the kernel, finish by falling into the idle task. extern unsigned _end void init (): // Disable interrupts and set interrupt vectors to normal. cp0_set0 (CP0_STATUS) // Initialize kernel variables to empty. sleepers = NULL runners = NULL zero_pages = NULL // Fill junk pages with all memory not currently used. junk_pages = (FreePage *)(((unsigned)&_end + (1 << 12) - 1) & ~((1 << 12) - 1)) FreePage *p, *next unsigned count = 1 for p = junk_pages, next = p; (unsigned)next - 0x80000000 < (1 << 27); p = next, next = (FreePage *)((unsigned)p + (1 << 12)): p->next = next ++count p->next = NULL // initialize system control coprocessor. init_cp0 () // initialize everything about the idle task. init_idle () // initialize top_memory. top_memory.prev_obj = NULL top_memory.next_obj = NULL top_memory.prev = NULL top_memory.next = NULL top_memory.parent = NULL top_memory.pages = NULL top_memory.threads = NULL top_memory.memories = NULL top_memory.limit = count top_memory.used = 0 top_memory.arch.directory = NULL top_memory.arch.asid = 0 init_threads () // Enable all interrupts and say we're handling an exception. // Since we're going to enter the idle task, allow access to cp0. cp0_set (CP0_STATUS, 0x1000ff13) // Done; return to user space (the idle task). __asm__ volatile ("eret")