/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * The debugger/PROM interface layer - debugger activation
 */

#include <kmdb/kmdb_promif_isadep.h>
#include <kmdb/kmdb_start.h>
#include <kmdb/kmdb_kdi.h>
#include <kmdb/kmdb_asmutil.h>
#include <kmdb/kaif.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_kreg.h>

#include <sys/cpuvar.h>
#include <sys/kdi_impl.h>
#include <sys/machtrap.h>

kaif_cpusave_t kaif_cb_save;

static const char kaif_defer_word_tmpl[] =
	/*  1 */ ": kmdb_callback "

	/*
	 * Don't hand control to the debugger if we're coming from OBP's text.
	 */

	/*  2 */ "  %%pc f000.0000 ffff.ffff between if exit then "

	/*
	 * Save registers
	 */

	/*  3 */ "  %%pc h# %x x! "
	/*  4 */ "  %%npc h# %x x! "
	/*  5 */ "  %%g1 h# %x x! "
	/*  6 */ "  %%g2 h# %x x! "
	/*  7 */ "  %%g3 h# %x x! "
	/*  8 */ "  %%g4 h# %x x! "
	/*  9 */ "  %%g5 h# %x x! "
	/* 10 */ "  %%g6 h# %x x! "
	/* 11 */ "  %%g7 h# %x x! "
	/* 12 */ "  1 %%tstate h# %x x! "
	/* 13 */ "  1 %%tt h# %x x! "
	/* 14 */ "  %%tba h# %x x! "
	/* 15 */ "  h# %x set-pc "
	/* 16 */ "    go "
	/* 17 */ "; ";

/*
 * Format the Forth word which tells the prom how to save state for
 * giving control to us.
 */
static char *
kaif_format_word(void)
{
	static char prom_str[550];
	kreg_t *kregs = kaif_cb_save.krs_gregs.kregs;
	int len;

	len = mdb_snprintf(prom_str, sizeof (prom_str), kaif_defer_word_tmpl,
	    &kregs[KREG_PC],			/*  3 */
	    &kregs[KREG_NPC],			/*  4 */
	    &kregs[KREG_G1],			/*  5 */
	    &kregs[KREG_G2],			/*  6 */
	    &kregs[KREG_G3],			/*  7 */
	    &kregs[KREG_G4],			/*  8 */
	    &kregs[KREG_G5],			/*  9 */
	    &kregs[KREG_G6],			/* 10 */
	    &kregs[KREG_G7],			/* 11 */
	    &kaif_cb_save.krs_tstate,		/* 12 */
	    &kregs[KREG_TT],			/* 13 */
	    &kregs[KREG_TBA],			/* 14 */
	    kaif_trap_obp);			/* 15 */

	ASSERT(len <= sizeof (prom_str));

	return (prom_str);
}

static void
kaif_prom_install(void)
{
	kmdb_prom_interpret(kaif_format_word());
	kmdb_prom_interpret(" ['] kmdb_callback init-debugger-hook ");
}

void
kaif_prom_rearm(void)
{
	kmdb_prom_interpret(" ['] kmdb_callback is debugger-hook ");
}

/*ARGSUSED*/
static void
kaif_cpu_init(cpu_t *cp)
{
	kaif_wapt_set_regs();
}

/*ARGSUSED*/
static void
kaif_install_generic(caddr_t tgt, caddr_t arg)
{
	bcopy((caddr_t)kaif_hdlr_generic, tgt, 32);
}

#ifdef	sun4v

/*ARGSUSED*/
static void
kaif_install_goto_tt64(caddr_t tgt, caddr_t arg)
{
	/* LINTED - pointer alignment */
	uint32_t *hdlr = (uint32_t *)tgt;
	uint32_t disp = (T_FAST_INSTR_MMU_MISS - T_INSTR_MMU_MISS) * 0x20;

	*hdlr++ = 0x10480000 | (disp >> 2);	/* ba,pt (to tt64) */
	*hdlr++ = 0x01000000;			/* nop */
}

/*ARGSUSED*/
static void
kaif_install_goto_tt68(caddr_t tgt, caddr_t arg)
{
	/* LINTED - pointer alignment */
	uint32_t *hdlr = (uint32_t *)tgt;
	uint32_t disp = (T_FAST_DATA_MMU_MISS - T_DATA_MMU_MISS) * 0x20;

	*hdlr++ = 0x10480000 | (disp >> 2);	/* ba,pt (to tt68) */
	*hdlr++ = 0x01000000;			/* nop */
}

#endif	/* sun4v */

static void
kaif_install_dmmumiss(caddr_t tgt, caddr_t vatotte)
{
	uint32_t *patch;

	bcopy((caddr_t)kaif_hdlr_dmiss, tgt, 128);

	/* LINTED - pointer alignment */
	patch = (uint32_t *)(tgt + ((uintptr_t)&kaif_hdlr_dmiss_patch -
	    (uintptr_t)kaif_hdlr_dmiss));
	*patch++ |= (uintptr_t)vatotte >> 10;
	*patch |= ((uintptr_t)vatotte) & 0x3ff;
}

static void
kaif_install_immumiss(caddr_t tgt, caddr_t vatotte)
{
	uint32_t *patch;

	bcopy((caddr_t)kaif_hdlr_imiss, tgt, 128);

	/* LINTED - pointer alignment */
	patch = (uint32_t *)(tgt + ((uintptr_t)&kaif_hdlr_imiss_patch -
	    (uintptr_t)kaif_hdlr_imiss));
	*patch++ |= (uintptr_t)vatotte >> 10;
	*patch |= ((uintptr_t)vatotte) & 0x3ff;
}

static struct kaif_trap_handlers {
	uint_t th_tt;
	void (*th_install)(caddr_t, caddr_t);
} kaif_trap_handlers[] = {
	{ T_INSTR_EXCEPTION,			kaif_install_generic },
#ifdef sun4v
	{ T_INSTR_MMU_MISS,			kaif_install_goto_tt64 },
#endif
	{ T_IDIV0,				kaif_install_generic },
	{ T_DATA_EXCEPTION,			kaif_install_generic },
#ifdef sun4v
	{ T_DATA_MMU_MISS,			kaif_install_goto_tt68 },
#endif
	{ T_DATA_ERROR,				kaif_install_generic },
	{ T_ALIGNMENT,				kaif_install_generic },
	{ T_FAST_INSTR_MMU_MISS,		kaif_install_immumiss },
	{ T_FAST_DATA_MMU_MISS,			kaif_install_dmmumiss },
	{ T_FAST_DATA_MMU_PROT,			kaif_install_generic },
#ifdef sun4v
	{ T_INSTR_MMU_MISS + T_TL1,		kaif_install_goto_tt64 },
	{ T_DATA_MMU_MISS + T_TL1,		kaif_install_goto_tt68 },
#endif
	{ T_FAST_INSTR_MMU_MISS + T_TL1,	kaif_install_immumiss },
	{ T_FAST_DATA_MMU_MISS + T_TL1,		kaif_install_dmmumiss },
	{ 0 }
};

static void
kaif_trap_init(void)
{
	caddr_t vatotte = kmdb_kdi_get_trap_vatotte();
	uintptr_t brtgt;
	int i;

	/*
	 * sun4u:
	 * We rely upon OBP for the handling of a great many traps.  As such,
	 * we begin by populating our table with pointers to OBP's handlers.
	 * We then copy in our own handlers where appropriate.  At some point,
	 * when we provide the bulk of the handlers, this process will be
	 * reversed.
	 *
	 * sun4v:
	 * The sun4v kernel dismisses OBP at boot. Both fast and slow TLB
	 * misses are handled by KMDB. Breakpoint traps go directly KMDB.
	 * All other trap entries are redirected to their respective
	 * trap implemenation within the Solaris trap table.
	 */
	for (i = 0; i < kaif_tba_native_sz; i += 0x20) {
		/* LINTED - pointer alignment */
		uint32_t *hdlr = (uint32_t *)(kaif_tba_native + i);
#ifdef	sun4v
		brtgt = (uintptr_t)(kaif_tba_kernel + i);
#else
		brtgt = (uintptr_t)(kaif_tba_obp + i);
#endif
		*hdlr++ = 0x03000000 | (brtgt >> 10);	/* sethi brtgt, %g1 */
		*hdlr++ = 0x81c06000 | (brtgt & 0x3ff);	/* jmp %g1 + brtgt */
		*hdlr++ = 0x01000000;			/* nop */
	}

	for (i = 0; kaif_trap_handlers[i].th_tt != 0; i++) {
		struct kaif_trap_handlers *th = &kaif_trap_handlers[i];
		th->th_install(kaif_tba_native + th->th_tt * 0x20, vatotte);
	}
	membar_producer();
}

/*
 * The kernel is ready for us to switch to our table (the HAT has been
 * initialized, the hments are walkable, and the trap table's pages
 * have been locked into the TLBs.
 */
static void
kaif_vmready(void)
{
	kaif_tba = kaif_tba_native;
}

/*
 * Called on the CPR master CPU.  The driver has taken care of locking the
 * TLB entries.  CPR restored the OBP image which contains kmdb_callback,
 * so there's nothing we need to do.  This function should be removed entirely
 * in a future release.
 */
static void
kaif_cpr_restart(void)
{
}

static kdi_debugvec_t kaif_dvec = {
	NULL,			/* dv_kctl_vmready */
	NULL,			/* dv_kctl_memavail */
	NULL,			/* dv_kctl_modavail */
	NULL,			/* dv_kctl_thravail */
	kaif_vmready,
	NULL,			/* dv_memavail */
	kaif_mod_loaded,
	kaif_mod_unloading,
	NULL,			/* dv_kctl_cpu_init */
	kaif_cpu_init,
	kaif_cpr_restart
};

/*ARGSUSED1*/
void
kaif_activate(kdi_debugvec_t **dvecp, uint_t flags)
{
	kaif_prom_install();

	kaif_ktrap_install(0, kaif_ktrap);
	kaif_trap_init();

	*dvecp = &kaif_dvec;
}

void
kaif_deactivate(void)
{
	kmdb_prom_interpret(" ['] noop is debugger-hook ");

	kaif_ktrap_restore();
}