/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <kern/mips/regdefs.h> #include <mips/specialreg.h> .set noreorder .text .globl __start .type __start,@function .ent __start __start: /* * Stack frame. We save the return address register, even though * it contains nothing useful. This is for gdb's benefit when it * comes disassembling. We also need 16 bytes for making a call, * and we have to align to an 8-byte (64-bit) boundary, so the * total frame size is 24. * * Note that the frame here must match the frame we set up below * when we switch off the bootup stack. Otherwise, gdb gets very * confused. */ .frame sp, 24, $0 /* 24-byte sp-relative frame; return addr on stack */ .mask 0x80000000, -4 /* register 31 (ra) saved at (sp+24)-4 */ addiu sp, sp, -24 sw ra, 20(sp) /* * The System/161 loader sets up a boot stack for the first * processor at the top of physical memory, and passes us a single * string argument. The string lives on the very top of the stack. * We get its address in a0. * * The kernel loads at virtual address 0x80000200, which is * physical address 0x00000200. The space immediately below this * is reserved for the exception vector code. * * The symbol _end is generated by the linker. It's the address of * the end of the kernel. It's not a variable; the *value* of the * _end symbol itself is this address. In C you'd use "&_end". * * We set up the memory map like this: * * top of memory * free memory * P + 0x1000 * first thread's stack (1 page) * P * wasted space (< 1 page) * copy of the boot string * _end * kernel * 0x80000200 * exception handlers * 0x80000000 * * where P is the next whole page after copying the argument string. */ la s0, _end /* stash _end in a saved register */ move a1, a0 /* move bootstring to the second argument */ move a0, s0 /* make _end the first argument */ jal strcpy /* call strcpy(_end, bootstring) */ nop /* delay slot */ move a0, s0 /* make _end the first argument again */ jal strlen /* call strlen(_end) */ nop add t0, s0, v0 /* add in the length of the string */ addi t0, t0, 1 /* and the null terminator */ addi t0, t0, 4095 /* round up to next page boundary */ li t1, 0xfffff000 and t0, t0, t1 addi t0, t0, 4096 /* add one page to hold the stack */ move sp, t0 /* start the kernel stack for the first thread here */ sw t0, firstfree /* remember the first free page for later */ /* * At this point, s0 contains the boot argument string, and no other * registers contain anything interesting (except the stack pointer). */ /* * Now set up a stack frame on the real kernel stack: a dummy saved * return address and four argument slots for making function calls, * plus a wasted slot for alignment. * * (This needs to match the stack frame set up at the top of the * function, or the debugger gets confused.) */ addiu sp, sp, -24 sw $0, 20(sp) /* * Now, copy the exception handler code onto the first page of memory. */ li a0, EXADDR_UTLB la a1, mips_utlb_handler la a2, mips_utlb_end sub a2, a2, a1 jal memmove nop li a0, EXADDR_GENERAL la a1, mips_general_handler la a2, mips_general_end sub a2, a2, a1 jal memmove nop /* * Flush the instruction cache to make sure the above changes show * through to instruction fetch. */ jal mips_flushicache nop /* * Initialize the TLB. */ jal tlb_reset nop /* * Load NULL into the register we use for curthread. */ li s7, 0 /* * Set up the status register. * * The MIPS has six hardware interrupt lines and two software interrupts. * These are individually maskable in the status register. However, we * don't use this feature (for simplicity) - we only use the master * interrupt enable/disable flag in bit 0. So enable all of those bits * now and forget about them. * * The BEV bit in the status register, if set, causes the processor to * jump to a different set of hardwired exception handling addresses. * This is so that the kernel's exception handling code can be loaded * into RAM and that the boot ROM's exception handling code can be ROM. * This flag is normally set at boot time, and we need to be sure to * clear it. * * The KUo/IEo/KUp/IEp/KUc/IEc bits should all start at zero. * * We also want all the other random control bits (mostly for cache * stuff) set to zero. * * Thus, the actual value we write is CST_IRQMASK. */ li t0, CST_IRQMASK /* get value */ mtc0 t0, c0_status /* set status register */ /* * Load the CPU number into the PTBASE field of the CONTEXT * register. This is necessary to read from cpustacks[] and * cputhreads[] on trap entry from user mode. See further * discussions elsewhere. * * Because the boot CPU is CPU 0, we can just send 0. */ mtc0 $0, c0_context /* * Load the GP register. This is a MIPS ABI feature; the GP * register points to an address in the middle of the data segment, * so data can be accessed relative to GP using one instruction * instead of the two it takes to set up a full 32-bit address. */ la gp, _gp /* * We're all set up! * Fetch the copy of the bootstring as the argument, and call main. */ jal kmain move a0, s0 /* in delay slot */ /* * kmain shouldn't return. panic. * Loop back just in case panic returns too. */ 1: la a0, panicstr jal panic nop /* delay slot */ j 1b nop /* delay slot */ .end __start .rdata panicstr: .asciz "kmain returned\n" /* * CPUs started after the boot CPU come here. */ .text .globl cpu_start_secondary .type cpu_start_secondary,@function .ent cpu_start_secondary cpu_start_secondary: /* * When we get here our stack points to the CRAM area of the bus * controller per-CPU space. This means we can, with a bit of * caution, call C functions, but nothing very deeply nesting. * However, we don't need to. * * The a0 register contains the value that was put in the second * word of the CRAM area, which is the (software) cpu number for * indexing cpustacks[]. None of the other registers contain * anything useful. */ /* * Stack frame. We save the return address register, even though * it contains nothing useful. This is for gdb's benefit when it * comes disassembling. We also need 16 bytes for making a call, * and 4 bytes for alignment, so the total frame size is 24. * * Note that the frame here must match the frame we set up below * when we switch stacks. Otherwise, gdb gets very confused. */ .frame sp, 24, $0 /* 24-byte sp-relative frame; return addr on stack */ .mask 0x80000000, -4 /* register 31 (ra) saved at (sp+24)-4 */ addiu sp, sp, -24 sw ra, 20(sp) /* * Fetch the stack out of cpustacks[]. */ lui t0, %hi(cpustacks) /* load upper half of cpustacks base addr */ sll v0, a0, 2 /* get byte index for array (multiply by 4) */ addu t0, t0, v0 /* add it in */ lw sp, %lo(cpustacks)(t0) /* get the stack pointer */ /* * Now fetch curthread out of cputhreads[]. */ lui t0, %hi(cputhreads) /* load upper half of cpustacks base addr */ sll v0, a0, 2 /* get byte index for array (multiply by 4) */ addu t0, t0, v0 /* add it in */ lw s7, %lo(cputhreads)(t0) /* load curthread register */ /* * Initialize the TLB. */ jal tlb_reset nop /* * Set up the status register, as described above. */ li t0, CST_IRQMASK /* get value */ mtc0 t0, c0_status /* set status register */ /* * Load the CPU number into the PTBASE field of the CONTEXT * register, as described above. */ sll v0, a0, CTX_PTBASESHIFT mtc0 v0, c0_context /* * Initialize the on-chip timer interrupt. * * This should be set to CPU_FREQUENCY/HZ, but we don't have either * of those values here, so we'll arbitrarily set it to 100,000. It * will get reset to the right thing after it first fires. */ li v0, 100000 mtc0 v0, c0_compare /* * Load the GP register. */ la gp, _gp /* * Set up a stack frame. Store zero into the return address slot so * we show as the top of the stack. */ addiu sp, sp, -24 sw z0, 20(sp) /* * Off to MI code. Pass the cpu number as the argument; it's already * in the a0 register. */ j cpu_hatch nop /* delay slot for jump */ .end cpu_start_secondary