336 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 * 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
 |