310 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * CPU control functions.
 | 
						|
 */
 | 
						|
 | 
						|
#include <types.h>
 | 
						|
#include <lib.h>
 | 
						|
#include <mips/specialreg.h>
 | 
						|
#include <mips/trapframe.h>
 | 
						|
#include <platform/maxcpus.h>
 | 
						|
#include <cpu.h>
 | 
						|
#include <thread.h>
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
 * Startup and exception-time stack hook.
 | 
						|
 *
 | 
						|
 * The MIPS lacks a good way to find the current CPU, current thread,
 | 
						|
 * or current thread stack upon trap entry from user mode. To deal
 | 
						|
 * with this, we store the CPU number (our number, not the hardware
 | 
						|
 * number) in a nonessential field in the MMU, which is about the only
 | 
						|
 * place possible, and then use that to index cpustacks[]. This gets
 | 
						|
 * us the value to load as the stack pointer. We can then also load
 | 
						|
 * curthread from cputhreads[] by parallel indexing.
 | 
						|
 *
 | 
						|
 * These arrays are also used to start up new CPUs, for roughly the
 | 
						|
 * same reasons.
 | 
						|
 *
 | 
						|
 * The values in the current cpu's slots in these arrays are updated
 | 
						|
 * with the current thread's information in trap.c before heading to
 | 
						|
 * userlevel, as well as being initialized in cpu_machdep_init below.
 | 
						|
 * This means that (unless something really horrible happens) on entry
 | 
						|
 * to the kernel, and when a new CPU starts up in cpu_start_secondary,
 | 
						|
 * they will have the information needed to figure out who we are and
 | 
						|
 * proceed.
 | 
						|
 */
 | 
						|
 | 
						|
vaddr_t cpustacks[MAXCPUS];
 | 
						|
vaddr_t cputhreads[MAXCPUS];
 | 
						|
 | 
						|
/*
 | 
						|
 * Do machine-dependent initialization of the cpu structure or things
 | 
						|
 * associated with a new cpu. Note that we're not running on the new
 | 
						|
 * cpu when this is called.
 | 
						|
 */
 | 
						|
void
 | 
						|
cpu_machdep_init(struct cpu *c)
 | 
						|
{
 | 
						|
	vaddr_t stackpointer;
 | 
						|
 | 
						|
	KASSERT(c->c_number < MAXCPUS);
 | 
						|
 | 
						|
	if (c->c_curthread->t_stack == NULL) {
 | 
						|
		/* boot cpu; don't need to do anything here */
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/*
 | 
						|
		 * Stick the stack in cpustacks[], and thread pointer
 | 
						|
		 * in cputhreads[].
 | 
						|
		 */
 | 
						|
 | 
						|
		/* stack base address */
 | 
						|
		stackpointer = (vaddr_t) c->c_curthread->t_stack;
 | 
						|
		/* since stacks grow down, get the top */
 | 
						|
		stackpointer += STACK_SIZE;
 | 
						|
 | 
						|
		cpustacks[c->c_number] = stackpointer;
 | 
						|
		cputhreads[c->c_number] = (vaddr_t)c->c_curthread;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
 * Return the type name of the currently running CPU.
 | 
						|
 *
 | 
						|
 * For now, assume we're running on System/161 so we can use the
 | 
						|
 * System/161 processor-ID values.
 | 
						|
 */
 | 
						|
 | 
						|
#define SYS161_PRID_ORIG	0x000003ff
 | 
						|
#define SYS161_PRID_2X		0x000000a1
 | 
						|
 | 
						|
static inline
 | 
						|
uint32_t
 | 
						|
cpu_getprid(void)
 | 
						|
{
 | 
						|
	uint32_t prid;
 | 
						|
 | 
						|
	__asm volatile("mfc0 %0,$15" : "=r" (prid));
 | 
						|
	return prid;
 | 
						|
}
 | 
						|
 | 
						|
static inline
 | 
						|
uint32_t
 | 
						|
cpu_getfeatures(void)
 | 
						|
{
 | 
						|
	uint32_t features;
 | 
						|
 | 
						|
	__asm volatile(".set push;"		/* save assembler mode */
 | 
						|
		       ".set mips32;"		/* allow mips32 instructions */
 | 
						|
		       "mfc0 %0,$15,1;"		/* get cop0 reg 15 sel 1 */
 | 
						|
		       ".set pop"		/* restore assembler mode */
 | 
						|
		       : "=r" (features));
 | 
						|
	return features;
 | 
						|
}
 | 
						|
 | 
						|
static inline
 | 
						|
uint32_t
 | 
						|
cpu_getifeatures(void)
 | 
						|
{
 | 
						|
	uint32_t features;
 | 
						|
 | 
						|
	__asm volatile(".set push;"		/* save assembler mode */
 | 
						|
		       ".set mips32;"		/* allow mips32 instructions */
 | 
						|
		       "mfc0 %0,$15,2;"		/* get cop0 reg 15 sel 2 */
 | 
						|
		       ".set pop"		/* restore assembler mode */
 | 
						|
		       : "=r" (features));
 | 
						|
	return features;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
cpu_identify(char *buf, size_t max)
 | 
						|
{
 | 
						|
	uint32_t prid;
 | 
						|
	uint32_t features;
 | 
						|
 | 
						|
	prid = cpu_getprid();
 | 
						|
	switch (prid) {
 | 
						|
	    case SYS161_PRID_ORIG:
 | 
						|
		snprintf(buf, max, "MIPS/161 (System/161 1.x and pre-2.x)");
 | 
						|
		break;
 | 
						|
	    case SYS161_PRID_2X:
 | 
						|
		features = cpu_getfeatures();
 | 
						|
		snprintf(buf, max, "MIPS/161 (System/161 2.x) features 0x%x",
 | 
						|
			 features);
 | 
						|
		features = cpu_getifeatures();
 | 
						|
		if (features != 0) {
 | 
						|
			kprintf("WARNING: unknown CPU incompatible features "
 | 
						|
				"0x%x\n", features);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    default:
 | 
						|
		snprintf(buf, max, "32-bit MIPS (unknown type, CPU ID 0x%x)",
 | 
						|
			 prid);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
 * Interrupt control.
 | 
						|
 *
 | 
						|
 * While the mips actually has on-chip interrupt priority masking, in
 | 
						|
 * the interests of simplicity, we don't use it. Instead we use
 | 
						|
 * coprocessor 0 register 12 (the system coprocessor "status"
 | 
						|
 * register) bit 0, IEc, which is the global interrupt enable flag.
 | 
						|
 * (IEc stands for interrupt-enable-current.)
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * gcc inline assembly to get at the status register.
 | 
						|
 *
 | 
						|
 * Pipeline hazards:
 | 
						|
 *    - there must be at least one cycle between GET_STATUS
 | 
						|
 *      and SET_STATUS;
 | 
						|
 *    - it may take up to three cycles after SET_STATUS for the
 | 
						|
 *      interrupt state to really change.
 | 
						|
 *
 | 
						|
 * These considerations do not (currently) apply to System/161,
 | 
						|
 * however.
 | 
						|
 */
 | 
						|
#define GET_STATUS(x) __asm volatile("mfc0 %0,$12" : "=r" (x))
 | 
						|
#define SET_STATUS(x) __asm volatile("mtc0 %0,$12" :: "r" (x))
 | 
						|
 | 
						|
/*
 | 
						|
 * Interrupts on.
 | 
						|
 */
 | 
						|
void
 | 
						|
cpu_irqon(void)
 | 
						|
{
 | 
						|
        uint32_t x;
 | 
						|
 | 
						|
        GET_STATUS(x);
 | 
						|
        x |= CST_IEc;
 | 
						|
        SET_STATUS(x);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Interrupts off.
 | 
						|
 */
 | 
						|
void
 | 
						|
cpu_irqoff(void)
 | 
						|
{
 | 
						|
        uint32_t x;
 | 
						|
 | 
						|
        GET_STATUS(x);
 | 
						|
        x &= ~(uint32_t)CST_IEc;
 | 
						|
        SET_STATUS(x);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Used below.
 | 
						|
 */
 | 
						|
static
 | 
						|
void
 | 
						|
cpu_irqonoff(void)
 | 
						|
{
 | 
						|
        uint32_t x, xon, xoff;
 | 
						|
 | 
						|
        GET_STATUS(x);
 | 
						|
        xon = x | CST_IEc;
 | 
						|
        xoff = x & ~(uint32_t)CST_IEc;
 | 
						|
        SET_STATUS(xon);
 | 
						|
	__asm volatile("nop; nop; nop; nop");
 | 
						|
        SET_STATUS(xoff);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
 * Idling.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * gcc inline assembly for the WAIT instruction.
 | 
						|
 *
 | 
						|
 * mips r2k/r3k has no idle instruction at all.
 | 
						|
 *
 | 
						|
 * However, to avoid completely overloading the computing cluster, we
 | 
						|
 * appropriate the mips32 WAIT instruction.
 | 
						|
 */
 | 
						|
 | 
						|
static
 | 
						|
inline
 | 
						|
void
 | 
						|
wait(void)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * The WAIT instruction goes into powersave mode until an
 | 
						|
	 * interrupt is trying to occur.
 | 
						|
	 *
 | 
						|
	 * Then switch interrupts on and off again, so we actually
 | 
						|
	 * take the interrupt.
 | 
						|
	 *
 | 
						|
	 * Note that the precise behavior of this instruction in the
 | 
						|
	 * System/161 simulator is partly guesswork. This code may not
 | 
						|
	 * work on a real mips.
 | 
						|
	 */
 | 
						|
	__asm volatile(
 | 
						|
		".set push;"		/* save assembler mode */
 | 
						|
		".set mips32;"		/* allow MIPS32 instructions */
 | 
						|
		".set volatile;"	/* avoid unwanted optimization */
 | 
						|
		"wait;"			/* suspend until interrupted */
 | 
						|
		".set pop"		/* restore assembler mode */
 | 
						|
	      );
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Idle the processor until something happens.
 | 
						|
 */
 | 
						|
void
 | 
						|
cpu_idle(void)
 | 
						|
{
 | 
						|
	wait();
 | 
						|
        cpu_irqonoff();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Halt the CPU permanently.
 | 
						|
 */
 | 
						|
void
 | 
						|
cpu_halt(void)
 | 
						|
{
 | 
						|
        cpu_irqoff();
 | 
						|
        while (1) {
 | 
						|
		wait();
 | 
						|
        }
 | 
						|
}
 |