code:
/*	$OpenBSD: cpu.c,v 1.58 2015/06/16 18:28:51 miod Exp $ */

/*
 * Copyright (c) 1997-2004 Opsycon AB (www.opsycon.se)
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/atomic.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/rndvar.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/autoconf.h>

int	cpumatch(struct device *, void *, void *);
void	cpuattach(struct device *, struct device *, void *);

struct cpu_info cpu_info_primary;
struct cpu_info *cpu_info_list = &cpu_info_primary;
struct cpu_info *cpu_info_secondaries;
#ifdef MULTIPROCESSOR
struct cpuset cpus_running;
#endif

vaddr_t	cache_valias_mask;
int	cpu_is_rm7k = 0;

struct cfattach cpu_ca = {
	sizeof(struct device), cpumatch, cpuattach
};
struct cfdriver cpu_cd = {
	NULL, "cpu", DV_DULL,
};

int
cpumatch(struct device *parent, void *match, void *aux)
{
	struct cpu_attach_args *caa = aux;

	/* make sure that we're looking for a CPU. */
	if (strcmp(caa->caa_maa.maa_name, cpu_cd.cd_name) != 0)
		return 0;

	return 20;	/* Make CPU probe first */
}

void
cpuattach(struct device *parent, struct device *dev, void *aux)
{
	struct cpu_attach_args *caa = aux;
	struct cpu_hwinfo *ch = caa->caa_hw;
	struct cpu_info *ci;
	int cpuno = dev->dv_unit;
	int isr16k = 0;
	int fptype, vers_maj, vers_min;
	int displayver;

	if (cpuno == 0) {
		ci = &cpu_info_primary;
#ifdef MULTIPROCESSOR
		ci->ci_flags |= CPUF_RUNNING | CPUF_PRESENT | CPUF_PRIMARY;
		cpuset_add(&cpus_running, ci);
		if (ncpusfound > 1) {
			cpu_info_secondaries = (struct cpu_info *)
			    alloc_contiguous_pages(sizeof(struct cpu_info) *
			      (ncpusfound - 1));
			if (cpu_info_secondaries == NULL)
				panic("unable to allocate cpu_info");
		}
#endif
	}
#ifdef MULTIPROCESSOR
	else {
		ci = &cpu_info_secondaries[cpuno - 1];
		ci->ci_next = cpu_info_list->ci_next;
		cpu_info_list->ci_next = ci;
		ci->ci_flags |= CPUF_PRESENT;
	}
#endif
	ci->ci_self = ci;
	ci->ci_cpuid = cpuno;
	ci->ci_dev = dev;
	bcopy(ch, &ci->ci_hw, sizeof(struct cpu_hwinfo));
#ifdef MULTIPROCESSOR
	/*
	 * When attaching secondary processors, cache information is not
	 * available yet.  Copy the cache information from the primary cpu
	 * instead.
	 * XXX The MP boot sequence needs to be reworked to avoid this.
	 */
	if (!ISSET(ci->ci_flags, CPUF_PRIMARY)) {
		ci->ci_l1inst = cpu_info_primary.ci_l1inst;
		ci->ci_l1data = cpu_info_primary.ci_l1data;
		ci->ci_l2 = cpu_info_primary.ci_l2;
		ci->ci_l3 = cpu_info_primary.ci_l3;
	}
#endif

	printf(": ");

	displayver = 1;
	fptype = (ch->c1prid >> 8) & 0xff;
	vers_maj = (ch->c0prid >> 4) & 0x0f;
	vers_min = ch->c0prid & 0x0f;
	switch (ch->type) {
	case MIPS_R4000:
		if (vers_maj < 4)
			printf("MIPS R4000 CPU");
		else {
			vers_maj -= 3;
			printf("MIPS R4400 CPU");
		}
		fptype = MIPS_R4000;
		break;
#if 0
	case MIPS_R4100:
		printf("NEC VR41xx CPU");
		break;
	case MIPS_R4200:
		printf("NEC VR4200 CPU (ICE)");
		break;
	case MIPS_R4300:
		printf("NEC VR4300 CPU");
		break;
#endif
	case MIPS_R4600:
		printf("QED R4600 Orion CPU");
		break;
#if 0
	case MIPS_R4700:
		printf("QED R4700 Orion CPU");
		break;
#endif
	case MIPS_R5000:
		printf("MIPS R5000 CPU");
		break;
	case MIPS_RM52X0:
		printf("PMC-Sierra RM52X0 CPU");
		break;
	case MIPS_RM7000:
		if (vers_maj < 2)
			printf("PMC-Sierra RM7000 CPU");
		else
			printf("PMC-Sierra RM7000A CPU");
		cpu_is_rm7k++;
		break;
	case MIPS_R8000:
		printf("MIPS R8000 CPU");
		break;
	case MIPS_RM9000:
		printf("PMC-Sierra RM9000 CPU");
		break;
	case MIPS_R10000:
		printf("MIPS R10000 CPU");
		break;
	case MIPS_R12000:
		printf("MIPS R12000 CPU");
		break;
	case MIPS_R14000:
		if (vers_maj > 2) {
			vers_maj -= 2;
			isr16k = 1;
		}
		printf("R1%d000 CPU", isr16k ? 6 : 4);
		break;
	case MIPS_LOONGSON2:
		switch (ch->c0prid & 0xff) {
		case 0x00:
		case 0x02:
		case 0x03:
			printf("STC Loongson2%c CPU", 'C' + vers_min);
			break;
		case 0x05:
			printf("STC Loongson3%c CPU", 'A' + vers_min - 5);
			break;
		default:
			printf("Unknown STC Loongson CPU type (%02x)",
			    ch->c0prid & 0xff);
			break;
		}
		displayver = 0;
		break;
	case MIPS_OCTEON:
		printf("Cavium OCTEON CPU");
		fptype = MIPS_SOFT;
		break;
	case MIPS_OCTEON2:
		printf("Cavium OCTEON II CPU");
		fptype = MIPS_SOFT;
		break;
	default:
		printf("Unknown CPU type (0x%x)", ch->type);
		break;
	}
	if (displayver != 0)
		printf(" rev %d.%d", vers_maj, vers_min);
	printf(" %d MHz, ", ch->clock / 1000000);

	displayver = 1;
	vers_maj = (ch->c1prid >> 4) & 0x0f;
	vers_min = ch->c1prid & 0x0f;
	switch (fptype) {
	case MIPS_SOFT:
#ifdef FPUEMUL
		printf("Software FP emulation");
#else
		printf("no FPU");
#endif
		displayver = 0;
		break;
	case MIPS_R4000:
		printf("R4010 FPC");
		break;
#if 0
	case MIPS_R4200:
		printf("VR4200 FPC (ICE)");
		break;
#endif
	case MIPS_R4600:
		printf("R4600 Orion FPC");
		break;
#if 0
	case MIPS_R4700:
		printf("R4700 Orion FPC");
		break;
#endif
	case MIPS_R5000:
		printf("R5000 based FPC");
		break;
	case MIPS_RM52X0:
		printf("RM52X0 FPC");
		break;
	case MIPS_RM7000:
		printf("RM7000 FPC");
		break;
	case MIPS_R8000:
		printf("R8010 FPU");
		break;
	case MIPS_RM9000:
		printf("RM9000 FPC");
		break;
	case MIPS_R10000:
		printf("R10000 FPU");
		break;
	case MIPS_R12000:
		printf("R12000 FPU");
		break;
	case MIPS_R14000:
		if (isr16k) {
			if (ch->c0prid == ch->c1prid)
				vers_maj -= 2;
			printf("R16000 FPU");
		} else
			printf("R14000 FPU");
		break;
	case MIPS_LOONGSON2:
		switch (ch->c1prid & 0xff) {
		case 0x00:
		case 0x02:
		case 0x03:
			printf("STC Loongson2%c FPU", 'C' + vers_min);
			break;
		case 0x05:
			printf("STC Loongson3%c FPU", 'A' + vers_min - 5);
			break;
		default:
			printf("Unknown STC Loongson FPU type (%02x)",
			    ch->c1prid & 0xff);
			break;
		}
		displayver = 0;
		break;
	default:
		printf("Unknown FPU type (0x%x)", fptype);
		break;
	}
	if (displayver != 0)
		printf(" rev %d.%d", vers_maj, vers_min);
	printf("\n");

	if (ci->ci_l1inst.sets == ci->ci_l1data.sets) {
		printf("cpu%d: cache L1-I %dKB D %dKB ", cpuno,
		    ci->ci_l1inst.size / 1024, ci->ci_l1data.size / 1024);
		if (ci->ci_l1inst.sets == 1)
			printf("direct");
		else
			printf("%d way", ci->ci_l1inst.sets);
	} else {
		printf("cpu%d: cache L1-I %dKB ", cpuno,
		    ci->ci_l1inst.size / 1024);
		if (ci->ci_l1inst.sets == 1)
			printf("direct");
		else
			printf("%d way", ci->ci_l1inst.sets);
		printf(" D %dKB ", ci->ci_l1data.size / 1024);
		if (ci->ci_l1data.sets == 1)
			printf("direct");
		else
			printf("%d way", ci->ci_l1data.sets);
	}

	if (ci->ci_l2.size != 0) {
		printf(", L2 %dKB ", ci->ci_l2.size / 1024);
		if (ci->ci_l2.sets == 1)
			printf("direct");
		else
			printf("%d way", ci->ci_l2.sets);
	}
	if (ci->ci_l3.size != 0) {
		printf(", L3 %dKB ", ci->ci_l3.size / 1024);
		if (ci->ci_l3.sets == 1)
			printf("direct");
		else
			printf("%d way", ci->ci_l3.sets);
	}
	printf("\n");

#ifdef DEBUG
	printf("cpu%d: L1 set size %d:%d\n", cpuno,
	    ci->ci_l1inst.setsize, ci->ci_l1data.setsize);
	printf("cpu%d: L1 line size %d:%d\n", cpuno,
	    ci->ci_l1inst.linesize, ci->ci_l1data.linesize);
	printf("cpu%d: L2 line size %d\n", cpuno, ci->ci_l2.linesize);
	printf("cpu%d: cache configuration %x\n",
	    cpuno, ci->ci_cacheconfiguration);
	printf("cpu%d: virtual alias mask 0x%lx\n", cpuno, cache_valias_mask);
	printf("cpu%d: config register %016lx, status register %016lx\n",
	    cpuno, cp0_get_config(), getsr());
#endif
}

void
cpu_switchto(struct proc *oldproc, struct proc *newproc)
{
#ifdef MULTIPROCESSOR
	struct cpu_info *ci = curcpu();
	if (ci->ci_fpuproc)
		save_fpu();
#endif

	cpu_switchto_asm(oldproc, newproc);
}

void
enable_fpu(struct proc *p)
{
#ifndef FPUEMUL
	struct cpu_info *ci = curcpu();

	if (p->p_md.md_regs->sr & SR_FR_32)
		MipsSwitchFPState(ci->ci_fpuproc, p->p_md.md_regs);
	else
		MipsSwitchFPState16(ci->ci_fpuproc, p->p_md.md_regs);
	atomic_inc_int(&uvmexp.fpswtch);

	ci->ci_fpuproc = p;
	p->p_md.md_regs->sr |= SR_COP_1_BIT;
	p->p_md.md_flags |= MDP_FPUSED;
#endif
}

void
save_fpu(void)
{
#ifndef FPUEMUL
	struct cpu_info *ci = curcpu();
	struct proc *p;

	KASSERT(ci->ci_fpuproc);
	p = ci->ci_fpuproc;
	if (p->p_md.md_regs->sr & SR_FR_32)
		MipsSaveCurFPState(p);
	else
		MipsSaveCurFPState16(p);
#endif
}

#ifdef MULTIPROCESSOR
struct cpu_info *
get_cpu_info(int cpuno)
{
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci) {
		if (ci->ci_cpuid == cpuno)
			return ci;
	}
	return NULL;
}

void
cpu_boot_secondary_processors(void)
{
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci) {
		if ((ci->ci_flags & CPUF_PRESENT) == 0)
			continue;
		if (ci->ci_flags & CPUF_PRIMARY)
			continue;

		ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;
		sched_init_cpu(ci);
		cpu_boot_secondary(ci);
	}

       /* This must called after xheart0 has initialized, so here is
	* the best place to do so.
	*/
       mips64_ipi_init();
}

void
cpu_unidle(struct cpu_info *ci)
{
	if (ci != curcpu())
		mips64_send_ipi(ci->ci_cpuid, MIPS64_IPI_NOP);
}

vaddr_t
alloc_contiguous_pages(size_t size)
{
	struct pglist mlist;
	struct vm_page *m;
	int error;
	paddr_t pa;

	TAILQ_INIT(&mlist);
	error = uvm_pglistalloc(round_page(size), 0, (paddr_t)-1, 0, 0,
		&mlist, 1, UVM_PLA_NOWAIT | UVM_PLA_ZERO);
	if (error)
		return 0;
	m = TAILQ_FIRST(&mlist);
	pa = VM_PAGE_TO_PHYS(m);

	return PHYS_TO_XKPHYS(pa, CCA_CACHED);
}
#endif


code:
/*	$OpenBSD: db_disasm.c,v 1.16 2012/09/29 21:37:03 miod Exp $	*/

/*
 * Copyright (c) 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*-
 * Copyright (c) 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. 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 REGENTS 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 REGENTS 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.
 *
 *	from: @(#)kadb.c	8.1 (Berkeley) 6/10/93
 *      $Id: db_disasm.c,v 1.16 2012/09/29 21:37:03 miod Exp $
 */

#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#else
#include <unistd.h>
#include <stdio.h>
#endif

#ifdef _KERNEL
#include <machine/db_machdep.h>
#endif
#include <machine/mips_opcode.h>
#include <machine/regnum.h>

#ifdef _KERNEL
#include <ddb/db_interface.h>
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#else
#define db_addr_t uint64_t
#define	db_printsym(addr,flags,fn)	(fn)("%p",addr)
#endif

static const char *op_name[64] = {
	NULL,	/* OP_SPECIAL */
	NULL,	/* OP_BCOND */
	"j",
	"jal",
	"beq",
	"bne",
	"blez",
	"bgtz",

	"addi",
	"addiu",
	"slti",
	"sltiu",
	"andi",
	"ori",
	"xori",
	"lui",

	"cop0",
	"cop1",
	"cop2",
	"cop1x",
	"beql",
	"bnel",
	"blezl",
	"bgtzl",

	"daddi",
	"daddiu",
	"ldl",
	"ldr",
	NULL,
	NULL,
	NULL,
	NULL,

	"lb",
	"lh",
	"lwl",
	"lw",
	"lbu",
	"lhu",
	"lwr",
	"lwu",

	"sb",
	"sh",
	"swl",
	"sw",
	"sdl",
	"sdr",
	"swr",
	"cache",

	"ll",
	"lwc1",
	"lwc2",
	"pref",
	"lld",
	"ldc1",
	"ldc2",
	"ld",

	"sc",
	"swc1",
	"swc2",
	"swc3",
	"scd",
	"sdc1",
	"sdc2",
	"sd"
};

static const char *special_name[64] = {
	"sll",
	NULL,
	"srl",
	"sra",
	"sllv",
	NULL,
	"srlv",
	"srav",

	"jr",
	"jalr",
	"movz",
	"movn",
	"syscall",
	"break",
	NULL,
	"sync",

	"mfhi",
	"mthi",
	"mflo",
	"mtlo",
	"dsllv",
	NULL,
	"dsrlv",
	"dsrav",

	"mult",
	"multu",
	"div",
	"divu",
	"dmult",
	"dmultu",
	"ddiv",
	"ddivu",

	"add",
	"addu",
	"sub",
	"subu",
	"and",
	"or",
	"xor",
	"nor",

	NULL,
	NULL,
	"slt",
	"sltu",
	"dadd",
	"daddu",
	"dsub",
	"dsubu",

	"tge",
	"tgeu",
	"tlt",
	"tltu",
	"teq",
	NULL,
	"tne",
	NULL,

	"dsll",
	NULL,
	"dsrl",
	"dsra",
	"dsll32",
	NULL,
	"dsrl32",
	"dsra32"
};

static const char *bcond_name[32] = {
	"bltz",
	"bgez",
	"bltzl",
	"bgezl",
	NULL,
	NULL,
	NULL,
	NULL,

	"tgei",
	"tgeiu",
	"tlti",
	"tltiu",
	"teqi",
	NULL,
	"tnei",
	NULL,

	"bltzal",
	"bgezal",
	"bltzall",
	"bgezall",
	NULL,
	NULL,
	NULL,
	NULL,

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"synci"
};

static const char *cop_std[OP_MTH + 1] = {
	"mfc",
	"dmfc",
	"cfc",
	"mfhc",
	"mtc",
	"dmtc",
	"ctc",
	"mthc"
};

static const char *cop1_name[64] = {
	"add",
	"sub",
	"mul",
	"div",
	"sqrt",
	"abs",
	"mov",
	"neg",

	"round.l",
	"trunc.l",
	"ceil.l",
	"floor.l",
	"round.w",
	"trunc.w",
	"ceil.w",
	"floor.w",

	NULL,
	NULL,	/* movf/movt */
	"movz",
	"movn",
	NULL,
	"recip",
	"rsqrt",
	NULL,

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	"cvt.s",
	"cvt.d",
	NULL,
	NULL,
	"cvt.w",
	"cvt.l",
	NULL,
	NULL,

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	"c.f",
	"c.un",
	"c.eq",
	"c.ueq",
	"c.olt",
	"c.ult",
	"c.ole",
	"c.ule",

	"c.sf",
	"c.ngle",
	"c.seq",
	"c.ngl",
	"c.lt",
	"c.nge",
	"c.le",
	"c.ngt"
};

static const char *fmt_name[16] = {
	"s",
	"d",
	NULL,
	NULL,
	"w",
	"l",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static const char *cop1x_op4[8] = {
	NULL,
	NULL,
	NULL,
	NULL,
	"madd",
	"msub",
	"nmadd",
	"nmsub"
};

static const char *cop1x_std[32] = {
	"lwxc1",
	"ldxc1",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	"swxc1",
	"sdxc1",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"prefx",

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static const char *reg_name[32] = {
	"zero",	"at",	"v0",	"v1",	"a0",	"a1",	"a2",	"a3",
	"ta0",	"ta1",	"ta2",	"ta3",	"t0",	"t1",	"t2",	"t3",
	"s0",	"s1",	"s2",	"s3",	"s4",	"s5",	"s6",	"s7",
	"t8",	"t9",	"k0",	"k1",	"gp",	"sp",	"s8",	"ra"
};

static const char *cop0_miscname[64] = {
	NULL,
	"tlbr",
	"tlbwi",
	NULL,
	NULL,
	NULL,
	"tlbwr",
	NULL,

	"tlbp",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	NULL,	/* rfe */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	"eret",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	"wait"	/* RM5200 */
};

static const char *cop0_tfp_miscname[64] = {
	NULL,
	"tlbr",
	"tlbw",
	NULL,
	NULL,
	NULL,
	"tlbwr",
	NULL,

	"tlbp",
	"dctr",
	"dctw"
};

static const char *cop0_reg0[32] = {
	"Index",
	"Random",
	"EntryLo0",
	"EntryLo1",
	"Context",
	"PageMask",
	"Wired",
	"Info",		/* RM7000 */

	"BadVAddr",
	"Count",
	"EntryHi",
	"Compare",
	"Status",
	"Cause",
	"EPC",
	"PRId",

	"Config",
	"LLAddr",
	"WatchLo",	/* RM7000 Watch1 */
	"WatchHi",	/* RM7000 Watch2 */
	"XContext",
	NULL,
	"PerfControl",	/* RM7000 */
	NULL,

	"WatchMask",	/* RM7000 */
	"PerfCount",	/* RM7000 */
	"ECC",
	"CacheErr",
	"TagLo",
	"TagHi",
	"ErrorEPC",
	NULL
};

static const char *cop0_reg1[32] = {
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	NULL,
	NULL,
	"IPLLo",
	"IPLHi",
	"IntCtl",
	NULL,
	NULL,
	NULL,

	NULL,
	NULL,
	"DErrAddr0",
	"DErrAddr1",
	NULL,
	NULL,
	NULL,
	NULL,
};

int
dbmd_print_insn(uint32_t ins, db_addr_t mdbdot, int (*pr)(const char *, ...))
{
	InstFmt i;
	int delay = 0;
	const char *insn, *descr;

	i.word = ins;
	insn = op_name[i.JType.op];

	switch (i.JType.op) {
	case OP_SPECIAL:
		/* recognize nop/ssnop variants of sll early */
		if (i.word == 0) {
			(*pr)("nop");
			break;
		} else if (i.word == 1 << 6) {
			(*pr)("ssnop");
			break;
		}

		/* display `daddu' involving zero as `move' */
		if (i.RType.func == OP_DADDU && i.RType.rt == 0) {
			(*pr)("move\t%s,%s",
			    reg_name[i.RType.rd], reg_name[i.RType.rs]);
			break;
		}

		if (i.RType.func == OP_MOVCI) {
			(*pr)("mov%c\t%s,%s,%d",
			    i.RType.rt & 1 ? 't' : 'f',
			    reg_name[i.RType.rd], reg_name[i.RType.rs],
			    i.RType.rt >> 2);
			break;
		}

		/* fix ambiguous opcode memonics */
		insn = special_name[i.RType.func];
		switch (i.RType.func) {
		case OP_SRL:
			if (i.RType.rs != 0)
				insn = "rotr";
			break;
		case OP_SRLV:
			if (i.RType.shamt != 0)
				insn = "rotrv";
			break;
		case OP_JR:
			if (i.RType.shamt != 0)
				insn = "jr.hb";
			break;
		case OP_JALR:
			if (i.RType.shamt != 0)
				insn = "jalr.hb";
			break;
		case OP_DSRL:
			if (i.RType.rs != 0)
				insn = "drotr";
			break;
		case OP_DSRLV:
			if (i.RType.shamt != 0)
				insn = "drotrv";
			break;
		}

		if (insn == NULL)
			goto unknown;
		(*pr)("%s", insn);

		switch (i.RType.func) {
		case OP_SLL:
		case OP_SRL:
		case OP_SRA:
		case OP_DSLL:
		case OP_DSRL:
		case OP_DSRA:
		case OP_DSLL32:
		case OP_DSRL32:
		case OP_DSRA32:
			(*pr)("\t%s,%s,%d",
			    reg_name[i.RType.rd], reg_name[i.RType.rt],
			    i.RType.shamt);
			break;
		case OP_SLLV:
		case OP_SRLV:
		case OP_SRAV:
		case OP_MOVZ:
		case OP_MOVN:
		case OP_DSLLV:
		case OP_DSRLV:
		case OP_DSRAV:
			(*pr)("\t%s,%s,%s",
			    reg_name[i.RType.rd], reg_name[i.RType.rt],
			    reg_name[i.RType.rs]);
			break;
		case OP_MFHI:
		case OP_MFLO:
			(*pr)("\t%s", reg_name[i.RType.rd]);
			break;
		case OP_JALR:
			delay = 1;
			if (i.RType.rd != RA)
				(*pr)("\t%s,%s",
				    reg_name[i.RType.rd], reg_name[i.RType.rs]);
			else
				(*pr)("\t%s", reg_name[i.RType.rs]);
			break;
		case OP_JR:
			delay = 1;
			/* FALLTHROUGH */
		case OP_MTLO:
		case OP_MTHI:
			(*pr)("\t%s", reg_name[i.RType.rs]);
			break;
		case OP_MULT:
		case OP_MULTU:
		case OP_DIV:
		case OP_DIVU:
		case OP_DMULT:
		case OP_DMULTU:
		case OP_DDIV:
		case OP_DDIVU:
		case OP_TGE:
		case OP_TGEU:
		case OP_TLT:
		case OP_TLTU:
		case OP_TEQ:
		case OP_TNE:
			(*pr)("\t%s,%s",
			    reg_name[i.RType.rs], reg_name[i.RType.rt]);
			break;
		case OP_SYSCALL:
			if ((ins >> 6) != 0)
				(*pr)("\t%d", ins >> 6);
			break;
		case OP_SYNC:
			break;
		case OP_BREAK:
			(*pr)("\t%d", ins >> 16);
			break;
		default:
			(*pr)("\t%s,%s,%s",
			    reg_name[i.RType.rd], reg_name[i.RType.rs],
			    reg_name[i.RType.rt]);
		};
		break;

	case OP_BCOND:
		insn = bcond_name[i.IType.rt];
		if (insn == NULL)
			goto unknown;
		if (i.IType.rt == 31) {	/* synci */
			(*pr)("%s\t", insn);
			goto loadstore;
		}
		(*pr)("%s\t%s,", insn, reg_name[i.IType.rs]);
		if ((i.IType.rt & 0x18) == 0x08)	/* trap, not branch */
			(*pr)("%d", i.IType.imm);
		else
			goto pr_displ;

	case OP_J:
	case OP_JAL:
		delay = 1;
		(*pr)("%s\t", insn);
		db_printsym((mdbdot & ~0x0fffffffUL) |
		    (db_addr_t)(i.JType.target << 2), DB_STGY_PROC, pr);
		break;

	case OP_BEQ:
	case OP_BEQL:
		if (i.IType.rs == ZERO && i.IType.rt == ZERO) {
			(*pr)("b\t");
			goto pr_displ;
		}
		/* FALLTHROUGH */
	case OP_BNE:
	case OP_BNEL:
		if (i.IType.rt == ZERO) {
			if (i.IType.op == OP_BEQL || i.IType.op == OP_BNEL) {
				/* get the non-l opcode name */
				insn = op_name[i.IType.op & 0x07];
				(*pr)("%szl\t%s,", insn, reg_name[i.IType.rs]);
			} else
				(*pr)("%sz\t%s,", insn, reg_name[i.IType.rs]);
		} else
			(*pr)("%s\t%s,%s,", insn,
			    reg_name[i.IType.rs], reg_name[i.IType.rt]);
pr_displ:
		delay = 1;
		db_printsym(mdbdot + 4 + ((int16_t)i.IType.imm << 2),
		    DB_STGY_PROC, pr);
		break;

	case OP_BLEZ:
	case OP_BGTZ:
	case OP_BLEZL:
	case OP_BGTZL:
		(*pr)("%s\t%s,", insn, reg_name[i.IType.rs]);
		goto pr_displ;

	case OP_ADDI:
	case OP_ADDIU:
	case OP_DADDI:
	case OP_DADDIU:
		if (i.IType.rs == 0) {
			(*pr)("li\t%s,%d",
			    reg_name[i.IType.rt], (int16_t)i.IType.imm);
			break;
		}
		/* FALLTHROUGH */
	case OP_SLTI:
	case OP_SLTIU:
	default:
		if (insn != NULL)
			(*pr)("%s\t%s,%s,%d", insn,
			    reg_name[i.IType.rt], reg_name[i.IType.rs],
			    (int16_t)i.IType.imm);
		else {
unknown:
			(*pr)(".word\t%08x", ins);
		}
		break;

	case OP_ORI:
	case OP_XORI:
		if (i.IType.rs == 0) {
			(*pr)("li\t%s,0x%x", reg_name[i.IType.rt], i.IType.imm);
			break;
		}
		/* FALLTHROUGH */
	case OP_ANDI:
		(*pr)("%s\t%s,%s,0x%x", insn,
		    reg_name[i.IType.rt], reg_name[i.IType.rs], i.IType.imm);
		break;

	case OP_LUI:
		(*pr)("%s\t%s,0x%x", insn, reg_name[i.IType.rt], i.IType.imm);
		break;

	case OP_COP0:
		switch (i.RType.rs) {
		case OP_MF:
		case OP_DMF:
		case OP_MT:
		case OP_DMT:
			if ((i.RType.func & 0x07) != 0) {
				insn = cop_std[i.RType.rs];
				descr = cop0_reg0[i.RType.rd];
				if (descr != NULL)
					(*pr)("%s0\t%s,%d,%d # %s.%d", insn,
					    reg_name[i.RType.rt], i.RType.rd,
					    i.RType.func & 0x07, descr,
					    i.RType.func & 0x07);
				else
					(*pr)("%s0\t%s,%d,%d", insn,
					    reg_name[i.RType.rt], i.RType.rd,
					    i.RType.func & 0x07);
				break;
			}
			/* FALLTHROUGH */
		case OP_CF:
		case OP_CT:
			insn = cop_std[i.RType.rs];
			if (i.RType.rs == OP_CF || i.RType.rs == OP_CT)
				descr = cop0_reg1[i.RType.rd];
			else
				descr = cop0_reg0[i.RType.rd];
			if (descr != NULL)
				(*pr)("%s0\t%s,%d # %s", insn,
				    reg_name[i.RType.rt], i.RType.rd, descr);
			else
				(*pr)("%s0\t%s,%d", insn,
				    reg_name[i.RType.rt], i.RType.rd);
			break;
		case OP_BC:
			(*pr)("bc0%c%c\t",
			    i.RType.rt & COPz_BC_TF_MASK ? 't' : 'f',
			    i.RType.rt & COPz_BCL_TF_MASK ? 'l' : ' ');
			goto pr_displ;
		case OP_C0MISC:
			if (i.FRType.func < nitems(cop0_miscname))
				insn = cop0_miscname[i.FRType.func];
			else
				insn = NULL;
			if (insn == NULL)
				goto unknown;
			else
				(*pr)("%s", insn);
		case OP_TFP_C0MISC:
			if (i.FRType.func < nitems(cop0_tfp_miscname))
				insn = cop0_tfp_miscname[i.FRType.func];
			else
				insn = NULL;
			if (insn == NULL)
				goto unknown;
			else
				(*pr)("%s", insn);
		default:
			goto unknown;
		};
		break;

	case OP_COP1:
		switch (i.RType.rs) {
		case OP_MF:
		case OP_DMF:
		case OP_CF:
		case OP_MFH:
		case OP_MT:
		case OP_DMT:
		case OP_CT:
		case OP_MTH:
			insn = cop_std[i.RType.rs];
			(*pr)("%s1\t%s,f%d", insn,
			    reg_name[i.RType.rt], i.RType.rd);
			break;
		case OP_BC:
			(*pr)("bc1%c%c\t",
			    i.RType.rt & COPz_BC_TF_MASK ? 't' : 'f',
			    i.RType.rt & COPz_BCL_TF_MASK ? 'l' : ' ');
			goto pr_displ;
		default:
			if (fmt_name[i.FRType.fmt] == NULL)
				goto unknown;
			if (i.FRType.func == 0x11) {	/* movcf */
				insn = i.FRType.ft & 1 ? "movt" : "movf";
				(*pr)("%s.%s\tf%d,f%d,%d",
				    insn, fmt_name[i.FRType.fmt],
				    i.FRType.fd, i.FRType.fs, i.FRType.ft >> 2);
				break;
			 }
			insn = cop1_name[i.FRType.func];
			if (insn == NULL)
				goto unknown;
			(*pr)("%s.%s\tf%d,f%d,f%d",
			    insn, fmt_name[i.FRType.fmt],
			    i.FRType.fd, i.FRType.fs, i.FRType.ft);
		};
		break;

	case OP_COP2:
		switch (i.RType.rs) {
		case OP_MF:
		case OP_DMF:
		case OP_CF:
		case OP_MFH:
		case OP_MT:
		case OP_DMT:
		case OP_CT:
		case OP_MTH:
			insn = cop_std[i.RType.rs];
			(*pr)("%s2\t%s,f%d", insn,
			    reg_name[i.RType.rt], i.RType.rd);
			break;
		case OP_BC:
			(*pr)("bc2%c%c\t",
			    i.RType.rt & COPz_BC_TF_MASK ? 't' : 'f',
			    i.RType.rt & COPz_BCL_TF_MASK ? 'l' : ' ');
			goto pr_displ;
		default:
			goto unknown;
		};
		break;

	case OP_COP1X:
		switch (i.FQType.op4) {
		case OP_MADD:
		case OP_MSUB:
		case OP_NMADD:
		case OP_NMSUB:
			if (fmt_name[i.FQType.fmt3] == NULL)
				goto unknown;
			insn = cop1x_op4[i.FQType.op4];
			(*pr)("%s.%s\tf%d,f%d,f%d,f%d",
			    insn, fmt_name[i.FQType.fmt3],
			    i.FQType.fd, i.FQType.fr,
			    i.FQType.fs, i.FQType.ft);
			break;
		default:
			insn = cop1x_std[i.FRType.func];
			switch (i.FRType.func) {
			case OP_LWXC1:
			case OP_LDXC1:
				(*pr)("%s\tf%d,%s(%s)", insn,
				    i.FQType.fd, reg_name[i.FQType.ft],
				    reg_name[i.FQType.fr]);
				break;
			case OP_SWXC1:
			case OP_SDXC1:
				(*pr)("%s\tf%d,%s(%s)", insn,
				    i.FQType.fs, reg_name[i.FQType.ft],
				    reg_name[i.FQType.fr]);
				break;
			case OP_PREFX:
				(*pr)("%s\t%d,%s(%s)", insn,
				    i.FQType.fs, reg_name[i.FQType.ft],
				    reg_name[i.FQType.fr]);
				break;
			}
			break;
		}
		break;

	case OP_LDL:
	case OP_LDR:
	case OP_LB:
	case OP_LH:
	case OP_LWL:
	case OP_LW:
	case OP_LBU:
	case OP_LHU:
	case OP_LWR:
	case OP_LWU:
	case OP_SB:
	case OP_SH:
	case OP_SWL:
	case OP_SW:
	case OP_SDL:
	case OP_SDR:
	case OP_SWR:
	case OP_LL:
	case OP_LLD:
	case OP_LD:
	case OP_SC:
	case OP_SCD:
	case OP_SD:
		(*pr)("%s\t%s,", insn, reg_name[i.IType.rt]);
loadstore:
		(*pr)("%d(%s)", (int16_t)i.IType.imm, reg_name[i.IType.rs]);
		break;

	case OP_CACHE:
		(*pr)("%s\t0x%x,", insn, i.IType.rt);
		goto loadstore;
		break;

	case OP_LWC1:
	case OP_LWC2:
	/* case OP_LWC3: superseded with OP_PREF */
	case OP_LDC1:
	case OP_LDC2:
	case OP_SWC1:
	case OP_SWC2:
	case OP_SWC3:
	case OP_SDC1:
	case OP_SDC2:
		(*pr)("%s\tf%d,", insn, i.IType.rt);
		goto loadstore;

	case OP_PREF:
		(*pr)("%s\t%d,", insn, i.IType.rt);
		goto loadstore;
	}
	(*pr)("\n");
	return delay;
}

#ifdef _KERNEL
db_addr_t
db_disasm(db_addr_t loc, boolean_t altfmt)
{
	extern uint32_t kdbpeek(vaddr_t);

	if (dbmd_print_insn(kdbpeek(loc), loc, db_printf)) {
		loc += 4;
		db_printsym(loc, DB_STGY_ANY, db_printf);
		db_printf(":\t ");
		dbmd_print_insn(kdbpeek(loc), loc, db_printf);
	}
	loc += 4;
	return loc;
}
#else
/*
 * Simple userspace test program (to confirm the logic never tries to print
 * NULL, to begin with...)
 */
int
main()
{
	uint32_t insn = 0;

	do {
		printf("%08x\t", insn);
		dbmd_print_insn(insn, 0, printf);
		insn++;
		if ((insn & 0x00ffffff) == 0)
			fprintf(stderr, "%08x\n", insn);
	} while (insn != 0);

	return 0;
}
#endif

license: unknown


code:
/*	$OpenBSD: db_machdep.c,v 1.43 2015/02/08 00:30:20 uebayasi Exp $ */

/*
 * Copyright (c) 1998-2003 Opsycon AB (www.opsycon.se)
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <dev/cons.h>

#include <mips64/cache.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/db_machdep.h>
#include <machine/mips_opcode.h>
#include <machine/pte.h>
#include <machine/frame.h>
#include <machine/regnum.h>

#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <ddb/db_access.h>
#include <ddb/db_command.h>
#include <ddb/db_output.h>
#include <ddb/db_variables.h>
#include <ddb/db_interface.h>
#include <ddb/db_run.h>

#define MIPS_JR_RA        0x03e00008      /* instruction code for jr ra */

void  stacktrace_subr(db_regs_t *, int, int (*)(const char*, ...));

uint32_t kdbpeek(vaddr_t);
uint64_t kdbpeekd(vaddr_t);
uint16_t kdbpeekw(vaddr_t);
uint8_t  kdbpeekb(vaddr_t);
void  kdbpoke(vaddr_t, uint32_t);
void  kdbpoked(vaddr_t, uint64_t);
void  kdbpokew(vaddr_t, uint16_t);
void  kdbpokeb(vaddr_t, uint8_t);
int   kdb_trap(int, struct trap_frame *);

void db_print_tlb(uint, uint64_t);
void db_trap_trace_cmd(db_expr_t, int, db_expr_t, char *);
void db_dump_tlb_cmd(db_expr_t, int, db_expr_t, char *);

#ifdef MULTIPROCESSOR
struct mutex ddb_mp_mutex = MUTEX_INITIALIZER(IPL_HIGH);
volatile int ddb_state = DDB_STATE_NOT_RUNNING;
volatile cpuid_t ddb_active_cpu;
boolean_t        db_switch_cpu;
long             db_switch_to_cpu;
#endif

int   db_active = 0;
db_regs_t ddb_regs;

#ifdef MULTIPROCESSOR
void db_cpuinfo_cmd(db_expr_t, int, db_expr_t, char *);
void db_startproc_cmd(db_expr_t, int, db_expr_t, char *);
void db_stopproc_cmd(db_expr_t, int, db_expr_t, char *);
void db_ddbproc_cmd(db_expr_t, int, db_expr_t, char *);
#endif

struct db_variable db_regs[] = {
    { "at",  (long *)&ddb_regs.ast,     FCN_NULL },
    { "v0",  (long *)&ddb_regs.v0,      FCN_NULL },
    { "v1",  (long *)&ddb_regs.v1,      FCN_NULL },
    { "a0",  (long *)&ddb_regs.a0,      FCN_NULL },
    { "a1",  (long *)&ddb_regs.a1,      FCN_NULL },
    { "a2",  (long *)&ddb_regs.a2,      FCN_NULL },
    { "a3",  (long *)&ddb_regs.a3,      FCN_NULL },
    { "a4",  (long *)&ddb_regs.a4,      FCN_NULL },
    { "a5",  (long *)&ddb_regs.a5,      FCN_NULL },
    { "a6",  (long *)&ddb_regs.a6,      FCN_NULL },
    { "a7",  (long *)&ddb_regs.a7,      FCN_NULL },
    { "t0",  (long *)&ddb_regs.t0,      FCN_NULL },
    { "t1",  (long *)&ddb_regs.t1,      FCN_NULL },
    { "t2",  (long *)&ddb_regs.t2,      FCN_NULL },
    { "t3",  (long *)&ddb_regs.t3,      FCN_NULL },
    { "s0",  (long *)&ddb_regs.s0,      FCN_NULL },
    { "s1",  (long *)&ddb_regs.s1,      FCN_NULL },
    { "s2",  (long *)&ddb_regs.s2,      FCN_NULL },
    { "s3",  (long *)&ddb_regs.s3,      FCN_NULL },
    { "s4",  (long *)&ddb_regs.s4,      FCN_NULL },
    { "s5",  (long *)&ddb_regs.s5,      FCN_NULL },
    { "s6",  (long *)&ddb_regs.s6,      FCN_NULL },
    { "s7",  (long *)&ddb_regs.s7,      FCN_NULL },
    { "t8",  (long *)&ddb_regs.t8,      FCN_NULL },
    { "t9",  (long *)&ddb_regs.t9,      FCN_NULL },
    { "k0",  (long *)&ddb_regs.k0,      FCN_NULL },
    { "k1",  (long *)&ddb_regs.k1,      FCN_NULL },
    { "gp",  (long *)&ddb_regs.gp,      FCN_NULL },
    { "sp",  (long *)&ddb_regs.sp,      FCN_NULL },
    { "s8",  (long *)&ddb_regs.s8,      FCN_NULL },
    { "ra",  (long *)&ddb_regs.ra,      FCN_NULL },
    { "sr",  (long *)&ddb_regs.sr,      FCN_NULL },
    { "lo",  (long *)&ddb_regs.mullo,   FCN_NULL },
    { "hi",  (long *)&ddb_regs.mulhi,   FCN_NULL },
    { "bad", (long *)&ddb_regs.badvaddr,FCN_NULL },
    { "cs",  (long *)&ddb_regs.cause,   FCN_NULL },
    { "pc",  (long *)&ddb_regs.pc,      FCN_NULL },
};
struct db_variable *db_eregs = db_regs + nitems(db_regs);

extern label_t  *db_recover;

int
kdb_trap(int type, struct trap_frame *fp)
{
	switch(type) {
	case T_BREAK:		/* breakpoint */
		if (db_get_value((fp)->pc, sizeof(int), FALSE) == BREAK_SOVER) {
			(fp)->pc += BKPT_SIZE;
		}
		break;
	case -1:
		break;
	default:
#if 0
		if (!db_panic)
			return (0);
#endif
		if (db_recover != 0) {
			db_error("Caught exception in ddb.\n");
			/*NOTREACHED*/
		}
		printf("stopped on non ddb fault\n");
	}

#ifdef MULTIPROCESSOR
	mtx_enter(&ddb_mp_mutex);
	if (ddb_state == DDB_STATE_EXITING)
		ddb_state = DDB_STATE_NOT_RUNNING;
	mtx_leave(&ddb_mp_mutex);

	while (db_enter_ddb()) {
#endif
		bcopy((void *)fp, (void *)&ddb_regs, NUMSAVEREGS * sizeof(register_t));

		db_active++;
		cnpollc(TRUE);
		db_trap(type, 0);
		cnpollc(FALSE);
		db_active--;

		bcopy((void *)&ddb_regs, (void *)fp, NUMSAVEREGS * sizeof(register_t));
#ifdef MULTIPROCESSOR
		if (!db_switch_cpu)
			ddb_state = DDB_STATE_EXITING;
	}
#endif
	return(TRUE);
}

#ifdef MULTIPROCESSOR
int
db_enter_ddb(void)
{
	int i;
	struct cpu_info *ci = curcpu();
	mtx_enter(&ddb_mp_mutex);

#ifdef DEBUG
	printf("db_enter_ddb %d: state %x pause %x\n", ci->ci_cpuid,
	    ddb_state, ci->ci_ddb);
#endif
	/* If we are first in, grab ddb and stop all other CPUs */
	if (ddb_state == DDB_STATE_NOT_RUNNING) {
		ddb_active_cpu = cpu_number();
		ddb_state = DDB_STATE_RUNNING;
		ci->ci_ddb = CI_DDB_INDDB;
		for (i = 0; i < ncpus; i++) {
			if (i != cpu_number() &&
			    get_cpu_info(i)->ci_ddb != CI_DDB_STOPPED) {
				get_cpu_info(i)->ci_ddb = CI_DDB_SHOULDSTOP;
				mips64_send_ipi(get_cpu_info(i)->ci_cpuid, MIPS64_IPI_DDB);
			}
		}
		mtx_leave(&ddb_mp_mutex);
		return (1);
	}

	/* Leaving ddb completely.  Start all other CPUs and return 0 */
	if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_EXITING) {
		for (i = 0; i < ncpus; i++) {
			get_cpu_info(i)->ci_ddb = CI_DDB_RUNNING;
		}
		mtx_leave(&ddb_mp_mutex);
		return (0);
	}

	/* We are switching to another CPU. ddb_ddbproc_cmd() has made sure
	 * it is waiting for ddb, we just have to set ddb_active_cpu. */
	if (ddb_active_cpu == cpu_number() && db_switch_cpu) {
		ci->ci_ddb = CI_DDB_SHOULDSTOP;
		db_switch_cpu = 0;
		ddb_active_cpu = db_switch_to_cpu;
		get_cpu_info(db_switch_to_cpu)->ci_ddb = CI_DDB_ENTERDDB;
	}

	/* Wait until we should enter ddb or resume */
	while (ddb_active_cpu != cpu_number() &&
	    ci->ci_ddb != CI_DDB_RUNNING) {
		if (ci->ci_ddb == CI_DDB_SHOULDSTOP)
			ci->ci_ddb = CI_DDB_STOPPED;
		mtx_leave(&ddb_mp_mutex);
		/* Busy wait without locking, we will confirm with lock later */
		while (ddb_active_cpu != cpu_number() &&
		    ci->ci_ddb != CI_DDB_RUNNING)
			;	/* Do nothing */
		mtx_enter(&ddb_mp_mutex);
	}

	/* Either enter ddb or exit */
	if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) {
		ci->ci_ddb = CI_DDB_INDDB;
		mtx_leave(&ddb_mp_mutex);
		return (1);
	} else {
		mtx_leave(&ddb_mp_mutex);
		return (0);
	}
}

void
db_cpuinfo_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	int i;

	for (i = 0; i < ncpus; i++) {
		db_printf("%c%4ld: ", (i == cpu_number()) ? '*' : ' ',
		    get_cpu_info(i)->ci_cpuid);
		switch(get_cpu_info(i)->ci_ddb) {
		case CI_DDB_RUNNING:
			db_printf("running\n");
			break;
		case CI_DDB_SHOULDSTOP:
			db_printf("stopping\n");
			break;
		case CI_DDB_STOPPED:
			db_printf("stopped\n");
			break;
		case CI_DDB_ENTERDDB:
			db_printf("entering ddb\n");
			break;
		case CI_DDB_INDDB:
			db_printf("ddb\n");
			break;
		default:
			db_printf("? (%d)\n",
			    get_cpu_info(i)->ci_ddb);
			break;
		}
	}
}
#endif

void
db_read_bytes(vaddr_t addr, size_t size, char *data)
{
	while (size >= sizeof(uint32_t)) {
		*(uint32_t *)data = kdbpeek(addr);
		data += sizeof(uint32_t);
		addr += sizeof(uint32_t);
		size -= sizeof(uint32_t);
	}

	if (size >= sizeof(uint16_t)) {
		*(uint16_t *)data = kdbpeekw(addr);
		data += sizeof(uint16_t);
		addr += sizeof(uint16_t);
		size -= sizeof(uint16_t);
	}

	if (size)
		*(uint8_t *)data = kdbpeekb(addr);
}

void
db_write_bytes(vaddr_t addr, size_t size, char *data)
{
	vaddr_t ptr = addr;
	size_t len = size;

	while (len >= sizeof(uint32_t)) {
		kdbpoke(ptr, *(uint32_t *)data);
		data += sizeof(uint32_t);
		ptr += sizeof(uint32_t);
		len -= sizeof(uint32_t);
	}

	if (len >= sizeof(uint16_t)) {
		kdbpokew(ptr, *(uint16_t *)data);
		data += sizeof(uint16_t);
		ptr += sizeof(uint16_t);
		len -= sizeof(uint16_t);
	}

	if (len)
		kdbpokeb(ptr, *(uint8_t *)data);

	if (addr < VM_MAXUSER_ADDRESS) {
		struct cpu_info *ci = curcpu();

		Mips_HitSyncDCache(ci, addr, size);
		Mips_InvalidateICache(ci, addr, size);
	}
}

void
db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count,
    char *modif, int (*pr)(const char *, ...))
{
	struct trap_frame *regs = &ddb_regs;

	if (have_addr) {
		(*pr)("mips trace requires a trap frame... giving up\n");
		return;
	}

	stacktrace_subr(regs, count, pr);
}

/*
 *	To do a single step ddb needs to know the next address
 *	that we will get to. It means that we need to find out
 *	both the address for a branch taken and for not taken, NOT! :-)
 *	MipsEmulateBranch will do the job to find out _exactly_ which
 *	address we will end up at so the 'dual bp' method is not
 *	required.
 */
db_addr_t
next_instr_address(db_addr_t pc, boolean_t bd)
{
	db_addr_t next;

	next = MipsEmulateBranch(&ddb_regs, (vaddr_t)pc, 0, 0);
	return(next);
}

/*
 *  MIPS machine dependent DDB commands.
 */

/*
 *  Do a trap traceback.
 */
void
db_trap_trace_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
{
	trapDump("ddb trap trace", db_printf);
}

void
db_print_tlb(uint tlbno, uint64_t tlblo)
{
	paddr_t pa;
#ifndef CPU_R8000
	/* short description of coherency attributes */
	static const char *attr[] = {
		"CCA 0",
		"CCA 1",
		"NC   ",
		"C    ",
		"CEX  ",
		"CEXW ",
		"CCA 6",
		"NCACC"
	};
#endif

	pa = pfn_to_pad(tlblo);
#ifdef CPU_R8000
	pa |= ptoa(tlbno % 128);
#endif
	if (tlblo & PG_V) {
		db_printf("%016lx ", pa);
		db_printf("%c", tlblo & PG_M ? 'M' : ' ');
#ifndef CPU_R8000
		db_printf("%c", tlblo & PG_G ? 'G' : ' ');
		db_printf("%s ", attr[(tlblo >> 3) & 7]);
#endif
	} else {
		db_printf("invalid                 ");
	}
}

/*
 *	Dump TLB contents.
 * Syntax: machine tlb [/p asid] [/c] [tlb#]
 *	/p: only display tlb entries matching the given asid
 *	/c: check for duplicate entries
 *	tlb#: display <count> entries starting from this index
 */
void
db_dump_tlb_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
{
	int tlbno, last, check, pid;
	struct tlb_entry tlb, tlbp;
	struct cpu_info *ci = curcpu();

	pid = -1;

	if (m[0] == 'p') {
		if (have_addr && addr < PG_ASID_COUNT) {
			pid = addr;
		}
		tlbno = 0;
		count = ci->ci_hw.tlbsize;
	} else if (m[0] == 'c') {
		last = ci->ci_hw.tlbsize;
		for (tlbno = 0; tlbno < last; tlbno++) {
			tlb_read(tlbno, &tlb);
			for (check = tlbno + 1; check < last; check++) {
				tlb_read(check, &tlbp);
				if ((tlbp.tlb_hi == tlb.tlb_hi &&
				    (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V)) ||
				    (pfn_to_pad(tlb.tlb_lo0) ==
				     pfn_to_pad(tlbp.tlb_lo0) &&
				     tlb.tlb_lo0 & PG_V) ||
				    (pfn_to_pad(tlb.tlb_lo1) ==
				     pfn_to_pad(tlbp.tlb_lo1) &&
				     tlb.tlb_lo1 & PG_V)) {
					db_printf("MATCH:\n");
					db_dump_tlb_cmd(tlbno, 1, 1, "");
					db_dump_tlb_cmd(check, 1, 1, "");
				}
			}
		}
		return;
	} else {
		if (have_addr && addr < ci->ci_hw.tlbsize) {
			tlbno = addr;
		} else {
			tlbno = 0;
			count = ci->ci_hw.tlbsize;
		}
	}
	last = tlbno + count;
	if (last > ci->ci_hw.tlbsize)
		last = ci->ci_hw.tlbsize;

	if (pid == -1)
		db_printf("current asid: %d\n", tlb_get_pid());
	for (; tlbno < last; tlbno++) {
		tlb_read(tlbno, &tlb);

		if (pid >= 0 &&
		    (tlb.tlb_hi & PG_ASID_MASK) != (pid << PG_ASID_SHIFT))
			continue;

		if (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V) {
			vaddr_t va;
			uint asid;

			asid = (tlb.tlb_hi & PG_ASID_MASK) >> PG_ASID_SHIFT;
			va = tlb.tlb_hi & ~((vaddr_t)PG_ASID_MASK);
#ifdef CPU_R8000
			if ((int64_t)va < 0)
				asid = 0;	/* KV1 addresses ignore ASID */
			va |= ptoa((tlbno ^ asid) % 128);
#endif
			db_printf("%3d v=%016lx", tlbno, va);
			db_printf("/%02x ", asid);

			db_print_tlb(tlbno, tlb.tlb_lo0);
#ifndef CPU_R8000
			db_print_tlb(tlbno, tlb.tlb_lo1);
			db_printf(" sz=%llx", tlb.tlb_mask);
#endif
		} else if (pid < 0) {
			db_printf("%3d v=invalid    ", tlbno);
		}
		db_printf("\n");
	}
}

struct db_command mips_db_command_table[] = {
	{ "tlb",	db_dump_tlb_cmd,	0,	NULL },
	{ "trap",	db_trap_trace_cmd,	0,	NULL },
#ifdef MULTIPROCESSOR
	{ "cpuinfo",    db_cpuinfo_cmd,         0,      NULL },
	{ "startcpu",   db_startproc_cmd,       0,      NULL },
	{ "stopcpu",    db_stopproc_cmd,        0,      NULL },
	{ "ddbcpu",     db_ddbproc_cmd,         0,      NULL },
#endif
	{ NULL,		NULL,			0,	NULL }
};

void
db_machine_init(void)
{
	extern char *ssym;
#ifdef MULTIPROCESSOR
	int i;
#endif

	db_machine_commands_install(mips_db_command_table);
#ifdef MULTIPROCESSOR
	for (i = 0; i < ncpus; i++) {
		get_cpu_info(i)->ci_ddb = CI_DDB_RUNNING;
	}
#endif
	if (ssym != NULL) {
		ddb_init();	/* Init symbols */
	}
}

#ifdef MULTIPROCESSOR
void
db_ddbproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	int cpu_n;

	if (have_addr) {
		cpu_n = addr;
		if (cpu_n >= 0 && cpu_n < ncpus &&
		    cpu_n != cpu_number()) {
			db_stopcpu(cpu_n);
			db_switch_to_cpu = cpu_n;
			db_switch_cpu = 1;
			db_cmd_loop_done = 1;
		} else {
			db_printf("Invalid cpu %d\n", (int)addr);
		}
	} else {
		db_printf("CPU not specified\n");
	}
}

void
db_startproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	int cpu_n;

	if (have_addr) {
		cpu_n = addr;
		if (cpu_n >= 0 && cpu_n < ncpus &&
		    cpu_n != cpu_number())
			db_startcpu(cpu_n);
		else
			db_printf("Invalid cpu %d\n", (int)addr);
	} else {
		for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
			if (cpu_n != cpu_number()) {
				db_startcpu(cpu_n);
			}
		}
	}
}

void
db_stopproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	int cpu_n;

	if (have_addr) {
		cpu_n = addr;
		if (cpu_n >= 0 && cpu_n < ncpus &&
		    cpu_n != cpu_number())
			db_stopcpu(cpu_n);
		else
			db_printf("Invalid cpu %d\n", (int)addr);
	} else {
		for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
			if (cpu_n != cpu_number()) {
				db_stopcpu(cpu_n);
			}
		}
	}
}

void
db_startcpu(int cpu)
{
	if (cpu != cpu_number() && cpu < ncpus) {
		mtx_enter(&ddb_mp_mutex);
		get_cpu_info(cpu)->ci_ddb = CI_DDB_RUNNING;
		mtx_leave(&ddb_mp_mutex);
	}
}

void
db_stopcpu(int cpu)
{
	mtx_enter(&ddb_mp_mutex);
	if (cpu != cpu_number() && cpu < ncpus &&
	    get_cpu_info(cpu)->ci_ddb != CI_DDB_STOPPED) {
		get_cpu_info(cpu)->ci_ddb = CI_DDB_SHOULDSTOP;
		mtx_leave(&ddb_mp_mutex);
		mips64_send_ipi(cpu, MIPS64_IPI_DDB);
	} else {
		mtx_leave(&ddb_mp_mutex);
	}
}
#endif


code:
/*	$OpenBSD: exception.S,v 1.38 2014/03/26 19:38:18 miod Exp $ */

/*
 * Copyright (c) 2002-2003 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 *  This code handles exceptions and dispatches to the
 *  correct handler depending on the exception type.
 *
 *  Exceptions are directed to the following addresses:
 *      0xffffffffbfc00000  Reset, NMI etc. Not handled by the kernel.
 *	0xffffffff80000000  TLB refill, not in exception.
 *	0xffffffff80000080  XTLB refill, not in exception.
 *	0xffffffffa0000100  Cache errors.
 *	0xffffffff80000180  Interrupts. Same as next.
 *	0xffffffff80000180  Everything else...
 */

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/regnum.h>
#include <machine/cpustate.h>
#ifdef CPU_LOONGSON2
#include <machine/loongson2.h>
#endif

#include "assym.h"

	.set	mips3

	.text

k_exception_table:
	PTR_VAL	k_intr			/* T_INT */
	PTR_VAL	k_general		/* T_TLB_MOD */
	PTR_VAL	k_tlb_inv		/* T_TLB_LD_MISS */
	PTR_VAL	k_tlb_inv		/* T_TLB_ST_MISS */
	PTR_VAL	k_general		/* T_ADDR_ERR_LD */
	PTR_VAL	k_general		/* T_ADDR_ERR_ST */
	PTR_VAL	k_general		/* T_BUS_ERR_IFETCH */
	PTR_VAL	k_general		/* T_BUS_ERR_LD_ST */
	PTR_VAL	k_general		/* T_SYSCALL */
	PTR_VAL	k_general		/* T_BREAK */
	PTR_VAL	k_general		/* T_RES_INST */
	PTR_VAL	k_general		/* T_COP_UNUSABLE */
	PTR_VAL	k_general		/* T_OVFLOW */
	PTR_VAL	k_general		/* T_TRAP */
#ifdef CPU_R4000
	PTR_VAL	vcei			/* T_VCEI */
#else
	PTR_VAL	k_general		/* T_VCEI */
#endif
	PTR_VAL	k_general		/* T_FPE */
	PTR_VAL	k_general		/* T_IWATCH */
	PTR_VAL	k_general
	PTR_VAL	k_general		/* T_C2E */
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general		/* T_MDMX */
	PTR_VAL	k_general		/* T_DWATCH */
	PTR_VAL	k_general		/* T_MCHECK */
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general		/* T_CACHEERR */
#ifdef CPU_R4000
	PTR_VAL	vced			/* T_VCED */
#else
	PTR_VAL	k_general		/* T_VCED */
#endif

u_exception_table:
	PTR_VAL	u_intr			/* T_INT */
	PTR_VAL	u_general		/* T_TLB_MOD */
	PTR_VAL	u_general		/* T_TLB_LD_MISS */
	PTR_VAL	u_general		/* T_TLB_ST_MISS */
	PTR_VAL	u_general		/* T_ADDR_ERR_LD */
	PTR_VAL	u_general		/* T_ADDR_ERR_ST */
	PTR_VAL	u_general		/* T_BUS_ERR_IFETCH */
	PTR_VAL	u_general		/* T_BUS_ERR_LD_ST */
	PTR_VAL	u_general		/* T_SYSCALL */
	PTR_VAL	u_general		/* T_BREAK */
	PTR_VAL	u_general		/* T_RES_INST */
	PTR_VAL	u_general		/* T_COP_UNUSABLE */
	PTR_VAL	u_general		/* T_OVFLOW */
	PTR_VAL	u_general		/* T_TRAP */
#ifdef CPU_R4000
	PTR_VAL	vcei			/* T_VCEI */
#else
	PTR_VAL	u_general		/* T_VCEI */
#endif
	PTR_VAL	u_general		/* T_FPE */
	PTR_VAL	u_general		/* T_IWATCH */
	PTR_VAL	u_general
	PTR_VAL	u_general		/* T_C2E */
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general		/* T_MDMX */
	PTR_VAL	u_general		/* T_DWATCH */
	PTR_VAL	u_general		/* T_MCHECK */
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general
	PTR_VAL	u_general		/* T_CACHEERR */
#ifdef CPU_R4000
	PTR_VAL	vced			/* T_VCED */
#else
	PTR_VAL	u_general		/* T_VCED */
#endif

	.set	noreorder		# Noreorder is default style!

/*---------------------------------------------------------------- exception
 *	General exception handler dispatcher. This code is copied
 *	to the vector area and must thus be PIC and less than 128
 *	bytes long to fit. Only k0 and k1 may be used at this time.
 */
	.globl	cache_err
cache_err:
#ifdef CPU_R4000
	nop
#endif
	.globl	exception
exception:
	.set	noat
#ifdef CPU_LOONGSON2
	/*
	 * To work around a branch prediction issue on earlier LS2F
	 * chips, it is necessary to clear the BTB upon
	 * userland->kernel boundaries.
	 */
	li	k0, COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif
	MFC0	k0, COP_0_STATUS_REG
	MFC0	k1, COP_0_CAUSE_REG
	and	k0, k0, SR_KSU_USER
	beqz	k0, k_exception		# Kernel mode mode
	and	k1, k1, CR_EXC_CODE

	LA	k0, u_exception_table
	PTR_ADDU k0, k0, k1
	PTR_ADDU k0, k0, k1		# yes, twice...
	PTR_L	k0, 0(k0)
	j	k0
	nop

k_exception:
	LA	k0, k_exception_table
	PTR_ADDU k0, k0, k1
	PTR_ADDU k0, k0, k1		# yes, twice...
	PTR_L	k0, 0(k0)
	j	k0
	nop
	.set	at
	.globl	e_exception
e_exception:

/*---------------------------------------------------------------- k_intr
 *	Handle an interrupt in kernel mode. This is easy since we
 *	just need to save away the 'save' registers and state.
 *	State is saved on kernel stack.
 */

NNON_LEAF(k_intr, FRAMESZ(KERN_EXC_FRAME_SIZE), ra)
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(KERN_EXC_FRAME_SIZE))
	PTR_SUB	k0, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	SAVE_CPU(k0, CF_RA_OFFS)
#ifdef RM7000_ICR
	cfc0	v1, COP_0_ICR
	SAVE_REG(v1, IC, k0, CF_RA_OFFS)
#endif
	.set	at
	move	sp, k0			# Already on kernel stack
	LA	gp, _gp
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	PTR_S	a0, 0(sp)
	jal	interrupt
	PTR_S	a3, CF_RA_OFFS + KERN_REG_SIZE(sp)

	PTR_L	a0, CF_RA_OFFS + KERN_REG_SIZE(sp)
	.set	noat
#ifdef RM7000_ICR
	RESTORE_REG(t0, IC, sp, CF_RA_OFFS)
	ctc0	t0, COP_0_ICR
#endif
	RESTORE_CPU(sp, CF_RA_OFFS)
	PTR_ADDU sp, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	ERET
	.set	at
END(k_intr)

/*---------------------------------------------------------------- u_intr
 *	Handle an interrupt in user mode. Save the relevant user
 *	registers into the u.u_pcb struct. This will allow us
 *	to preempt the interrupted process. Full save is held
 *	off though until a switch() really is required.
 */
NNON_LEAF(u_intr, FRAMESZ(CF_SZ), ra)
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(CF_SZ))
	GET_CPU_INFO(k1, k0)
	PTR_L	k0, CI_CURPROCPADDR(k1)
	SAVE_CPU(k0, 0)
#ifdef RM7000_ICR
	cfc0	v1, COP_0_ICR
	SAVE_REG(v1, IC, k0, 0)
#endif
	PTR_ADDU sp, k0, USPACE-FRAMESZ(CF_SZ)
	LA	gp, _gp
	.set	at
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	PTR_S	a0, 0(sp)
	jal	interrupt
	PTR_S	a3, CF_RA_OFFS(sp)	# for debugging

	MFC0	t0, COP_0_STATUS_REG	# enable interrupts before checking
	ori	t0, SR_INT_ENAB		# for ast.
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	GET_CPU_INFO(t1, t0)
0:
	PTR_L	t0, CI_CURPROC(t1)
	lw	v0, P_ASTPENDING(t0)	# any pending AST?
	beq	v0, zero, 4f
	nop

	PTR_L	t0, CI_CURPROCPADDR(t1)	# curprocpaddr
	SAVE_CPU_SREG(t0, 0)

#ifdef PERFCNTRS
	lw	t0, cpu_is_rm7k
	beqz	t0, 1f			# not an RM7K. Don't do perf save.
	nop

	mfc0	v0, COP_0_PC_CTRL
	PTR_L	t0, curproc
	sw	v0, P_PC_CTRL(t0)
	dmfc0	v0, COP_0_WATCH_1
	dmfc0	v1, COP_0_WATCH_2
	sd	v0, P_WATCH_1(t0)
	sd	v1, P_WATCH_2(t0)
	mfc0	v0, COP_0_WATCH_M
	mfc0	v1, COP_0_PC_COUNT
	sw	v0, P_WATCH_M(t0)
	sw	v1, P_PC_COUNT(t0)
	mtc0	zero, COP_0_PC_CTRL
	dmtc0	zero, COP_0_WATCH_1
	dmtc0	zero, COP_0_WATCH_2
	nop;nop;nop;nop
1:
#endif
	jal	ast
	nop

/*
 * Restore user registers and return. NOTE: interrupts are enabled.
 */
#ifdef PERFCNTRS
	lw	t0, cpu_is_rm7k
	beqz	t0, 1f			# not an RM7K. Don't do perf setup.

	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROC(t1)	# set up rm7k.
	ld	v0, P_WATCH_1(t1)
	dmtc0	v0, COP_0_WATCH_1
	ld	v0, P_WATCH_2(t1)
	dmtc0	v0, COP_0_WATCH_2
	lw	v0, P_WATCH_M(t1)
	mtc0	v0, COP_0_WATCH_M
	lw	v0, P_PC_CTRL(t1)
	lw	v1, P_PC_COUNT(t1)
	nop;nop
	mtc0	v0, COP_0_PC_CTRL
	nop;nop;nop;nop
	mtc0	v1, COP_0_PC_COUNT
	nop;nop;nop;nop
1:
#endif
	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROCPADDR(t1)
	RESTORE_CPU_SREG(t0, 0)

	b	0b
	nop

4:
	MFC0	t0, COP_0_STATUS_REG	# disable interrupts
	LI	v0, ~SR_INT_ENAB
	and	t0, t0, v0
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	ori	t0, SR_EXL		# restoring to user mode.
	MTC0	t0, COP_0_STATUS_REG	# must set exception level bit.
	MTC0_SR_IE_HAZARD

	# t1 is curcpu() from earlier
	move	k1, t1
	PTR_L	k0, CI_CURPROCPADDR(k1)
	RESTORE_REG(a3, CPL, k0, 0)
	sw	a3, CI_IPL(k1)
	.set	noat
	RESTORE_REG(a0, PC, k0, 0)
#ifdef RM7000_ICR
	RESTORE_REG(t0, IC, k0, 0)
	ctc0	t0, COP_0_ICR
#endif
	RESTORE_CPU(k0, 0)
	RESTORE_REG(sp, SP, k0, 0)
	LI	k0, 0
	LI	k1, 0
	ERET
	.set	at
END(u_intr)

/*---------------------------------------------------------------- k_general
 *	Handle a kernel general trap. This is very much like
 *	k_intr except that we call ktrap instead of interrupt.
 */

NNON_LEAF(k_general, FRAMESZ(KERN_EXC_FRAME_SIZE), ra)
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(KERN_EXC_FRAME_SIZE))
	PTR_SUB	k0, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	SAVE_CPU(k0, CF_RA_OFFS)
#ifdef RM7000_ICR
	cfc0	v1, COP_0_ICR
	SAVE_REG(v1, IC, k0, CF_RA_OFFS)
#endif
#if defined(DDB)
	SAVE_CPU_SREG(k0, CF_RA_OFFS)
#endif
	.set	at
	move	sp, k0			# Already on kernel stack
	LA	gp, _gp
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	PTR_S	a0, 0(sp)
	jal	trap
	PTR_S	a3, CF_RA_OFFS + KERN_REG_SIZE(sp)

	MFC0	t0, COP_0_STATUS_REG	# disable interrupts
	LI	t1, ~SR_INT_ENAB
	and	t0, t0, t1
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	.set	noat
#ifdef RM7000_ICR
	RESTORE_REG(t0, IC, sp, CF_RA_OFFS)
	ctc0	t0, COP_0_ICR
#endif
	RESTORE_REG(a0, PC, sp, CF_RA_OFFS)
	RESTORE_CPU(sp, CF_RA_OFFS)
	PTR_ADDU sp, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	ERET
	.set	at
END(k_general)

/*---------------------------------------------------------------- u_general
 *	Handle a user general trap.
 */
NNON_LEAF(u_general, FRAMESZ(CF_SZ), ra)
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(CF_SZ))

	GET_CPU_INFO(k1, k0)
	PTR_L	k0, CI_CURPROCPADDR(k1)
	SAVE_CPU(k0, 0)
#ifdef RM7000_ICR
	cfc0	v1, COP_0_ICR
	SAVE_REG(v1, IC, k0, 0)
#endif
	SAVE_CPU_SREG(k0, 0)
	PTR_ADDU sp, k0, USPACE-FRAMESZ(CF_SZ)
	LA	gp, _gp
	.set	at
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

#ifdef PERFCNTRS
	lw	t0, cpu_is_rm7k
	beqz	t0, 1f			# not an RM7K. Don't do perf save.
	nop

	mfc0	v0, COP_0_PC_CTRL
	PTR_L	t0, CI_CURPROC(k1)
	sw	v0, P_PC_CTRL(t0)
	dmfc0	v0, COP_0_WATCH_1
	dmfc0	v1, COP_0_WATCH_2
	sd	v0, P_WATCH_1(t0)
	sd	v1, P_WATCH_2(t0)
	mfc0	v0, COP_0_WATCH_M
	mfc0	v1, COP_0_PC_COUNT
	sw	v0, P_WATCH_M(t0)
	sw	v1, P_PC_COUNT(t0)
	mtc0	zero, COP_0_PC_CTRL
	nop;nop;nop;nop
1:
#endif

	jal	trap
	PTR_S	a3, CF_RA_OFFS(sp)		# for debugging

0:
	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROC(t1)
	lw	v0, P_ASTPENDING(t0)	# any pending AST?
	beq	v0, zero, 4f
	nop

	jal	ast
	nop

	b	0b
	nop

4:
#ifdef PERFCNTRS
	lw	t0, cpu_is_rm7k
	beqz	t0, 1f			# not an RM7K. Don't do perf setup.

	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROC(k1)	# set up rm7k.
	ld	v0, P_WATCH_1(t0)
	dmtc0	v0, COP_0_WATCH_1
	ld	v0, P_WATCH_2(t0)
	dmtc0	v0, COP_0_WATCH_2
	lw	v0, P_WATCH_M(t0)
	mtc0	v0, COP_0_WATCH_M
	lw	v0, P_PC_CTRL(t0)
	lw	v1, P_PC_COUNT(t0)
	nop;nop
	mtc0	v0, COP_0_PC_CTRL
	nop;nop;nop;nop
	mtc0	v1, COP_0_PC_COUNT
	nop;nop;nop;nop
1:
#endif
	MFC0	t0, COP_0_STATUS_REG	# disable interrupts
	LI	t1, ~SR_INT_ENAB
	and	t0, t0, t1
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	ori	t0, SR_EXL		# restoring to user mode.
	MTC0	t0, COP_0_STATUS_REG	# must set exception level bit.
	MTC0_SR_IE_HAZARD

	GET_CPU_INFO(k1, k0)
	PTR_L	k0, CI_CURPROCPADDR(k1)
	RESTORE_REG(a3, CPL, k0, 0)
	sw	a3, CI_IPL(k1)
	.set	noat
	RESTORE_CPU_SREG(k0, 0)
	RESTORE_REG(a0, PC, k0, 0)
#ifdef RM7000_ICR
	RESTORE_REG(t0, IC, k0, 0)
	ctc0	t0, COP_0_ICR
#endif
	RESTORE_CPU(k0, 0)
	RESTORE_REG(sp, SP, k0, 0)
	LI	k0, 0
	LI	k1, 0
	ERET
	.set	at
END(u_general)

#ifdef CPU_R4000
LEAF(vced, 0)
	.set	noat
	dmfc0	k0, COP_0_BAD_VADDR
	ori	k0, k0, 3		# need to align badvaddr...
	xori	k0, k0, 3		# ...to a word boundary.
	cache	0x17, 0(k0)		# HitWBInvalidate_S
	cache	0x11, 0(k0)		# HitWBInvalidate_D
	LI	k0, 0
	ERET
	.set	at
END(vced)

LEAF(vcei, 0)
	.set	noat
	dmfc0	k0, COP_0_BAD_VADDR
	cache	0x17, 0(k0)		# HitWBInvalidate_S
	cache	0x10, 0(k0)		# HitInvalidate_I
	LI	k0, 0
	ERET
	.set	at
END(vcei)
#endif


code:
/*	$OpenBSD: exception_tfp.S,v 1.3 2014/04/09 21:10:35 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * k_general and u_general are heavily based upon their counterparts in
 * exception.S under the following licence terms:
 */
/*
 * Copyright (c) 2002-2003 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 * Trap handling subpage for R8000 systems.
 */

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/pte.h>
#include <machine/regnum.h>
#include <machine/cpustate.h>

#include "assym.h"

#define	TLBW	.align 4; .word 0x43000002

	.set	mips4
	.set	noreorder

	.text

	.align	12
	.globl	tfp_trapbase
tfp_trapbase:

	/*
	 * 000: tlb miss handler for U region
	 */
	.align	10

	.ent	utlb_miss, 0
utlb_miss:
	.set	noat
	.align	4
	PRE_MFC0_ADDR_HAZARD
	DMFC0	k0, COP_0_VADDR
	MFC0_HAZARD
	DMTC0	k0, COP_0_WORK0
	MTC0_HAZARD
	PTR_SRL	k0, SEGSHIFT
	sltiu	k1, k0, PMAP_SEGTABSIZE
	beqz	k1, _inv_seg
	 NOP
	DMFC0	k1, COP_0_UBASE		# PCB_SEGTAB(CI_CURPROCPADDR(curcpu))
	MFC0_HAZARD
	PTR_SLL	k0, LOGREGSZ
	PTR_ADDU k1, k0
	PTR_L	k1, 0(k1)		# get pointer to page table
	DMFC0	k0, COP_0_WORK0		# saved COP_0_VADDR
	MFC0_HAZARD
	beqz	k1, _inv_seg
	 PTR_SRL k0, PAGE_SHIFT - 2
	andi	k0, ((NPTEPG / 2) - 1) << 2
	PTR_ADDU k1, k0
	lwu	k0, 0(k1)		# get pte
	DMTC0	k0, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW
	ERET

_inv_seg:
	DMFC0	k0, COP_0_STATUS_REG
	MFC0_HAZARD
	andi	k0, SR_KSU_USER
	bnez	k0, u_general
	 NOP
	b	k_general
	 NOP

	.set	at
	.end	utlb_miss

	/*
	 * 400: tlb miss handler for KV0 region
	 */
	.align	10

	.ent	kv0tlb_miss, 0
kv0tlb_miss:
	.set	noat
	/*
	 * Since we do not set up KV0 mappings, fall through directly
	 * into the `invalid address' fault handling.
	 */
	.align	4
	b	_inv_seg
	 NOP
	.set	at
	.end	kv0tlb_miss

	/*
	 * 800: tlb miss handler for KV1 region
	 */
	.align	10

	.ent	kv1tlb_miss, 0
kv1tlb_miss:
	.set	noat
	.align	4
	PRE_MFC0_ADDR_HAZARD
	DMFC0	k0, COP_0_VADDR
	MFC0_HAZARD
	DMFC0	k1, COP_0_STATUS_REG
	MFC0_HAZARD
	andi	k1, SR_KSU_USER
	bnez	k1, u_general
	 LA	k1, VM_MIN_KERNEL_ADDRESS
	PTR_SUBU k0, k1
	DMFC0	k1, COP_0_WORK1		# Sysmapsize
	MFC0_HAZARD
	PTR_SRL	k0, PAGE_SHIFT
	sltu	k1, k0, k1
	beqz	k1, k_general
	 PTR_SLL k0, 2
	DMFC0	k1, COP_0_GBASE		# Sysmap
	MFC0_HAZARD
	PTR_ADDU k1, k0
	lwu	k0, 0(k1)		# get pte
	DMTC0	k0, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW
	ERET
	.set	at
	.end	kv1tlb_miss

	/*
	 * C00: tlb miss exception while servicing an exception
	 *	tlb invalid exception
	 *	tlb modified exception
	 *	all other exceptions
	 */
	.align	10
	.ent	exception, 0
exception:
	.set	noat
	.align	4
	DMFC0	k0, COP_0_STATUS_REG
	MFC0_HAZARD
	andi	k0, SR_KSU_USER
	DMFC0	k1, COP_0_CAUSE_REG
	MFC0_HAZARD
	bnez	k0, u_general
	 and	k1, CR_EXC_CODE

	LA	k0, k_exception_table
	PTR_ADDU k0, k1
	PTR_L	k1, 0(k0)
	j	k1
	 NOP

	.set	at
	.end	exception

	/*
	 * Handle a TLB invalid exception from kernel mode. This implies there
	 * is a TLB match, but currently not valid. Check if the pte is now
	 * valid and update, or pass the mess to the regular exception handler.
	 */
k_tlb_inv:
	.set	noat
	.align	4
	PRE_MFC0_ADDR_HAZARD
	DMFC0	k0, COP_0_VADDR
	MFC0_HAZARD
	LA	k1, VM_MIN_KERNEL_ADDRESS
	PTR_SUBU k0, k1
	DMFC0	k1, COP_0_WORK1		# Sysmapsize
	MFC0_HAZARD
	PTR_SRL	k0, PAGE_SHIFT
	sltu	k1, k0, k1
	beqz	k1, k_general
	 PTR_SLL k0, 2
	DMFC0	k1, COP_0_GBASE		# Sysmap
	MFC0_HAZARD
	PTR_ADDU k1, k0
	lwu	k0, 0(k1)		# get pte
	andi	k1, k0, PG_V
	beqz	k1, k_general		# if not valid
	 NOP

	DMTC0	k0, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW
	ERET
	.set	at

k_exception_table:
	PTR_VAL	k_general	# interrupt
	PTR_VAL	k_general	# TLB modification
	PTR_VAL	k_tlb_inv
	PTR_VAL	k_tlb_inv
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general
	PTR_VAL	k_general

	.align	5
NNON_LEAF(u_general, FRAMESZ(CF_SZ), ra)
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(CF_SZ))

	GET_CPU_INFO(k1, k0)
	PTR_L	k0, CI_CURPROCPADDR(k1)
	SAVE_CPU(k0, 0)
	SAVE_CPU_SREG(k0, 0)
	PTR_ADDU sp, k0, USPACE-FRAMESZ(CF_SZ)
	LA	gp, _gp
	.set	at
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	DMTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	jal	trap
	 PTR_S	a3, CF_RA_OFFS(sp)		# for debugging

	DMFC0	t0, COP_0_STATUS_REG	# enable interrupts before checking
	ori	t0, SR_INT_ENAB		# for ast.
	DMTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

0:
	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROC(t1)
	lw	v0, P_ASTPENDING(t0)	# any pending AST?
	beq	v0, zero, 4f
	 NOP

	jal	ast
	 NOP

	b	0b
	 NOP

4:
	DMFC0	t0, COP_0_STATUS_REG	# disable interrupts
	MFC0_HAZARD
	ori	t0, SR_INT_ENAB
	xori	t0, SR_INT_ENAB
	DMTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	ori	t0, SR_EXL		# restoring to user mode.
	DMTC0	t0, COP_0_STATUS_REG	# must set exception level bit.
	MTC0_SR_IE_HAZARD

	# t1 is curcpu() from earlier
	move	k1, t1
	PTR_L	k0, CI_CURPROCPADDR(k1)
	RESTORE_REG(a3, CPL, k0, 0)
	sw	a3, CI_IPL(k1)
	.set	noat
	RESTORE_CPU_SREG(k0, 0)
	RESTORE_REG(a0, PC, k0, 0)
	RESTORE_CPU(k0, 0)
	RESTORE_REG(sp, SP, k0, 0)
	LI	k0, 0
	LI	k1, 0
	ERET
	.set	at
END(u_general)

	.align	5
NNON_LEAF(k_general, FRAMESZ(KERN_EXC_FRAME_SIZE), ra)
	.globl	k_intr				# for trap.c peace of mind
k_intr:
	.set	noat
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(KERN_EXC_FRAME_SIZE))

#ifdef DEBUG
	GET_CPU_INFO(k1, k0)
	PTR_L	k1, CI_CURPROCPADDR(k1)
	PTR_SUBU k0, sp, k1
	sltiu	k0, 2048
	beqz	k0, 8f
	 NOP

	LA	a0, start - FRAMESZ(CF_SZ) - 4 * REGSZ
	move	a6, sp
	move	sp, a0
	DMFC0	a1, COP_0_EXC_PC
	MFC0_HAZARD
	LA	a0, 1f
	PRE_MFC0_ADDR_HAZARD
	DMFC0	a3, COP_0_VADDR
	MFC0_HAZARD
	DMFC0	a4, COP_0_STATUS_REG
	MFC0_HAZARD
	DMFC0	a5, COP_0_CAUSE_REG
	MFC0_HAZARD
	jal	printf
	 move	a2, ra

	LA	sp, start-FRAMESZ(CF_SZ)
#ifdef DDB
	LA	a1, db_printf
	LA	a0, 2f
	jal	trapDump
	 NOP
#endif
	PANIC("kernel stack overflow")

	.data
1:
	.asciiz "\rtfptrap: PC %p RA %p ADR %p\nSR %p CR %p SP %p\n"
2:
	.asciiz	"stack oflow"

	.text
8:
#endif

	PTR_SUB	k0, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	SAVE_CPU(k0, CF_RA_OFFS)
#if defined(DDB)
	SAVE_CPU_SREG(k0, CF_RA_OFFS)
#endif
	.set	at
	move	sp, k0			# Already on kernel stack
	LA	gp, _gp
	and	t0, a1, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK)
	DMTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	PTR_S	a0, 0(sp)
	jal	trap
	 PTR_S	a3, CF_RA_OFFS + KERN_REG_SIZE(sp)

	DMFC0	t0, COP_0_STATUS_REG	# disable interrupts
	MFC0_HAZARD
	ori	t0, SR_INT_ENAB
	xori	t0, SR_INT_ENAB
	DMTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	.set	noat
	RESTORE_REG(a0, PC, sp, CF_RA_OFFS)
	RESTORE_CPU(sp, CF_RA_OFFS)
	PTR_ADDU sp, sp, FRAMESZ(KERN_EXC_FRAME_SIZE)
	ERET
	.set	at
END(k_general)