/*	$OpenBSD: db_machdep.c,v 1.2 1998/09/15 10:50:13 pefo Exp $ */

/*-
 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
 *
 * 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 under OpenBSD by
 *	Per Fogelstrom, Opsycon AB, Sweden.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 *
 *	JNPR: db_interface.c,v 1.6.2.1 2007/08/29 12:24:49 girish
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cons.h>
#include <sys/lock.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/reboot.h>

#include <machine/cache.h>
#include <machine/db_machdep.h>
#include <machine/mips_opcode.h>
#include <machine/vmparam.h>
#include <machine/md_var.h>
#define	NO_REG_DEFS	1	/* Prevent asm.h from including regdef.h */
#include <machine/asm.h>
#include <machine/setjmp.h>

#include <ddb/ddb.h>
#include <ddb/db_sym.h>
#include <ddb/db_access.h>
#include <ddb/db_output.h>
#include <ddb/db_variables.h>
#include <sys/kdb.h>

static db_varfcn_t db_frame;

#define	DB_OFFSET(x)	(db_expr_t *)offsetof(struct trapframe, x)
struct db_variable db_regs[] = {
	{ "at",  DB_OFFSET(ast),	db_frame },
	{ "v0",  DB_OFFSET(v0),		db_frame },
	{ "v1",  DB_OFFSET(v1),		db_frame },
	{ "a0",  DB_OFFSET(a0),		db_frame },
	{ "a1",  DB_OFFSET(a1),		db_frame },
	{ "a2",  DB_OFFSET(a2),		db_frame },
	{ "a3",  DB_OFFSET(a3),		db_frame },
	{ "t0",  DB_OFFSET(t0),		db_frame },
	{ "t1",  DB_OFFSET(t1),		db_frame },
	{ "t2",  DB_OFFSET(t2),		db_frame },
	{ "t3",  DB_OFFSET(t3),		db_frame },
	{ "t4",  DB_OFFSET(t4),		db_frame },
	{ "t5",  DB_OFFSET(t5),		db_frame },
	{ "t6",  DB_OFFSET(t6),		db_frame },
	{ "t7",  DB_OFFSET(t7),		db_frame },
	{ "s0",  DB_OFFSET(s0),		db_frame },
	{ "s1",  DB_OFFSET(s1),		db_frame },
	{ "s2",  DB_OFFSET(s2),		db_frame },
	{ "s3",  DB_OFFSET(s3),		db_frame },
	{ "s4",  DB_OFFSET(s4),		db_frame },
	{ "s5",  DB_OFFSET(s5),		db_frame },
	{ "s6",  DB_OFFSET(s6),		db_frame },
	{ "s7",  DB_OFFSET(s7),		db_frame },
	{ "t8",  DB_OFFSET(t8),		db_frame },
	{ "t9",  DB_OFFSET(t9),		db_frame },
	{ "k0",  DB_OFFSET(k0),		db_frame },
	{ "k1",  DB_OFFSET(k1),		db_frame },
	{ "gp",  DB_OFFSET(gp),		db_frame },
	{ "sp",  DB_OFFSET(sp),		db_frame },
	{ "s8",  DB_OFFSET(s8),		db_frame },
	{ "ra",  DB_OFFSET(ra),		db_frame },
	{ "sr",  DB_OFFSET(sr),		db_frame },
	{ "lo",  DB_OFFSET(mullo),	db_frame },
	{ "hi",  DB_OFFSET(mulhi),	db_frame },
	{ "bad", DB_OFFSET(badvaddr),	db_frame },
	{ "cs",  DB_OFFSET(cause),	db_frame },
	{ "pc",  DB_OFFSET(pc),		db_frame },
};
struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);

int (*do_db_log_stack_trace_cmd)(char *);

static int
db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
{
	register_t *reg;

	if (kdb_frame == NULL)
		return (0);

	reg = (register_t *)((uintptr_t)kdb_frame + (size_t)(intptr_t)vp->valuep);
	if (op == DB_VAR_GET)
		*valuep = *reg;
	else
		*reg = *valuep;
	return (1);
}

int
db_read_bytes(vm_offset_t addr, size_t size, char *data)
{
	jmp_buf jb;
	void *prev_jb;
	int ret;

	prev_jb = kdb_jmpbuf(jb);
	ret = setjmp(jb);
	if (ret == 0) {
		/*
		 * 'addr' could be a memory-mapped I/O address.  Try to
		 * do atomic load/store in unit of size requested.
		 */
		if ((size == 2 || size == 4 || size == 8) &&
		    ((addr & (size -1)) == 0) &&
		    (((vm_offset_t)data & (size -1)) == 0)) {
			switch (size) {
			case 2:
				*(uint16_t *)data = *(uint16_t *)addr;
				break;
			case 4:
				*(uint32_t *)data = *(uint32_t *)addr;
				break;
			case 8:
				atomic_load_64((volatile u_int64_t *)addr,
				    (u_int64_t *)data);
			break;
			}
		} else {
			char *src;

			src = (char *)addr;
			while (size-- > 0)
				*data++ = *src++;
		}
	}

	(void)kdb_jmpbuf(prev_jb);
	return (ret);
}

int
db_write_bytes(vm_offset_t addr, size_t size, char *data)
{
	int ret;
	jmp_buf jb;
	void *prev_jb;

	prev_jb = kdb_jmpbuf(jb);
	ret = setjmp(jb);

	if (ret == 0) {
		/*
		 * 'addr' could be a memory-mapped I/O address.  Try to
		 * do atomic load/store in unit of size requested.
		 */
		if ((size == 2 || size == 4 || size == 8) &&
		    ((addr & (size -1)) == 0) &&
		    (((vm_offset_t)data & (size -1)) == 0)) {
			switch (size) {
			case 2:
				*(uint16_t *)addr = *(uint16_t *)data;
				break;
			case 4:
				*(uint32_t *)addr = *(uint32_t *)data;
				break;
			case 8:
				atomic_store_64((volatile u_int64_t *)addr,
				    (u_int64_t *)data);
			break;
			}
		} else {
			char *dst;
			size_t len = size;

			dst = (char *)addr;
			while (len-- > 0)
				*dst++ = *data++;
		}

		mips_icache_sync_range((db_addr_t) addr, size);
		mips_dcache_wbinv_range((db_addr_t) addr, size);
	}
	(void)kdb_jmpbuf(prev_jb);
	return (ret);
}

/*
 *	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
 *	requiered.
 */
db_addr_t
next_instr_address(db_addr_t pc, boolean_t bd)
{
	db_addr_t next;

	next = (db_addr_t)MipsEmulateBranch(kdb_frame, pc, 0, 0);
	return (next);
}


/*
 *	Decode instruction and figure out type.
 */
int
db_inst_type(int ins)
{
	InstFmt inst;
	int	ityp = 0;

	inst.word = ins;
	switch ((int)inst.JType.op) {
	case OP_SPECIAL:
		switch ((int)inst.RType.func) {
		case OP_JR:
			ityp = IT_BRANCH;
			break;
		case OP_JALR:
		case OP_SYSCALL:
			ityp = IT_CALL;
			break;
		}
		break;

	case OP_BCOND:
		switch ((int)inst.IType.rt) {
		case OP_BLTZ:
		case OP_BLTZL:
		case OP_BGEZ:
		case OP_BGEZL:
			ityp = IT_BRANCH;
			break;

		case OP_BLTZAL:
		case OP_BLTZALL:
		case OP_BGEZAL:
		case OP_BGEZALL:
			ityp = IT_CALL;
			break;
		}
		break;

	case OP_JAL:
		ityp = IT_CALL;
		break;

	case OP_J:
	case OP_BEQ:
	case OP_BEQL:
	case OP_BNE:
	case OP_BNEL:
	case OP_BLEZ:
	case OP_BLEZL:
	case OP_BGTZ:
	case OP_BGTZL:
		ityp = IT_BRANCH;
		break;

	case OP_COP1:
		switch (inst.RType.rs) {
		case OP_BCx:
		case OP_BCy:
			ityp = IT_BRANCH;
			break;
		}
		break;

	case OP_LB:
	case OP_LH:
	case OP_LW:
	case OP_LD:
	case OP_LBU:
	case OP_LHU:
	case OP_LWU:
	case OP_LWC1:
		ityp = IT_LOAD;
		break;

	case OP_SB:
	case OP_SH:
	case OP_SW:
	case OP_SD:  
	case OP_SWC1:
		ityp = IT_STORE;
		break;
	}
	return (ityp);
}

/*
 * Return the next pc if the given branch is taken.
 * MachEmulateBranch() runs analysis for branch delay slot.
 */
db_addr_t
branch_taken(int inst, db_addr_t pc)
{
	db_addr_t ra;
	register_t fpucsr;

	/* TBD: when is fsr set */
	fpucsr = (curthread) ? curthread->td_pcb->pcb_regs.fsr : 0;
	ra = (db_addr_t)MipsEmulateBranch(kdb_frame, pc, fpucsr, 0);
	return (ra);
}
