/*
 * 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 KDI, or kernel/debugger interface, is used to allow the kernel and the
 * debugger to communicate.  These communications take two forms:
 *
 *  1. kernel to debugger.  Interfaces of this type are used by the kernel to
 *     inform the debugger of changes in the state of the system that need to
 *     be noted by the debugger.  For example, the kernel uses one of these
 *     interfaces to tell debugger that the set of currently-loaded modules
 *     has changed.
 *
 *  2. debugger to kernel.  Interfaces of this type are used by the debugger
 *     to extract information from the kernel that would otherwise be difficult
 *     to get, or to perform services that are specific to the machine being
 *     used.  An example of the former is the module iterator, which is needed
 *     to allow symbol resolution, but which needs to resolve symbols prior
 *     to the iteration.  The latter class include machine-specific or
 *     cpu-type-specific functions, such as the I-cache flusher.  By directly
 *     using the kernel versions of these functions, we avoid the need to
 *     include multiple versions of each function - one per cpu and/or machine -
 *     in kmdb.
 */

#include <sys/kdi_impl.h>

#include <kmdb/kmdb_kdi.h>
#include <kmdb/kmdb_dpi.h>
#include <kmdb/kmdb_kvm.h>
#include <kmdb/kmdb_promif.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

static int kdi_unload_request;

typedef struct mod_interp_data {
	int	(*mid_usercb)(struct modctl *, void *);
	void	*mid_userarg;
	jmp_buf mid_pcb;
	jmp_buf *mid_oldpcb;
} mod_interp_data_t;

static kmdb_auxv_t *kdi_auxv;

int
kmdb_kdi_mods_changed(void)
{
	return (mdb.m_kdi->kdi_mods_changed());
}

static int
kmdb_kdi_mod_interp(struct modctl *mp, void *arg)
{
	mod_interp_data_t *mid = arg;
	int rc;

	kmdb_dpi_restore_fault_hdlr(mid->mid_oldpcb);
	rc = mid->mid_usercb(mp, mid->mid_userarg);
	mid->mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid->mid_pcb);

	return (rc);
}

/*
 * We need to protect ourselves against any problems that may occur while
 * executing the module iterator, currently located in krtld.  If, for
 * example, one of the next pointers in the module list points to an invalid
 * address, we don't want kmdb to explode.  As such, we protect ourselves
 * with the DPI fault-protection routines.  We don't want our fault-protection
 * callback to protect the callback that the kmdb consumer provided, so we
 * provide our own interposition callback that removes our fault-protector
 * before invoking the user's callback.
 */
int
kmdb_kdi_mod_iter(int (*cb)(struct modctl *, void *), void *arg)
{
	mod_interp_data_t mid;
	int rc;

	if (setjmp(mid.mid_pcb) != 0) {
		/* We took a fault while iterating through the modules */
		kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);
		return (-1);
	}

	mid.mid_usercb = cb;
	mid.mid_userarg = arg;
	mid.mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid.mid_pcb);

	rc = mdb.m_kdi->kdi_mod_iter(kmdb_kdi_mod_interp, &mid);

	kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);

	return (rc);
}

int
kmdb_kdi_mod_isloaded(struct modctl *modp)
{
	return (mdb.m_kdi->kdi_mod_isloaded(modp));
}

int
kmdb_kdi_mod_haschanged(struct modctl *mc1, struct module *mp1,
    struct modctl *mc2, struct module *mp2)
{
	return (mdb.m_kdi->kdi_mod_haschanged(mc1, mp1, mc2, mp2));
}

static ssize_t
kdi_prw(void *buf, size_t nbytes, physaddr_t addr, int (*rw)(caddr_t, size_t,
    physaddr_t, size_t *))
{
	size_t sz;
	int rc;

	kmdb_dpi_flush_slave_caches();
	if ((rc = rw(buf, nbytes, addr, &sz)) != 0)
		return (set_errno(rc));

	return (sz);
}

ssize_t
kmdb_kdi_pread(void *buf, size_t nbytes, physaddr_t addr)
{
	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pread));
}

ssize_t
kmdb_kdi_pwrite(void *buf, size_t nbytes, physaddr_t addr)
{
	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pwrite));
}

void
kmdb_kdi_flush_caches(void)
{
	mdb.m_kdi->kdi_flush_caches();
}

int
kmdb_kdi_get_unload_request(void)
{
	return (kdi_unload_request);
}

void
kmdb_kdi_set_unload_request(void)
{
	kdi_unload_request = 1;
}

int
kmdb_kdi_get_flags(void)
{
	uint_t flags = 0;

	if (mdb.m_flags & MDB_FL_NOCTF)
		flags |= KMDB_KDI_FL_NOCTF;
	if (mdb.m_flags & MDB_FL_NOMODS)
		flags |= KMDB_KDI_FL_NOMODS;

	return (flags);
}

size_t
kmdb_kdi_range_is_nontoxic(uintptr_t va, size_t sz, int write)
{
	return (mdb.m_kdi->kdi_range_is_nontoxic(va, sz, write));
}

void
kmdb_kdi_system_claim(void)
{
	(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_claim, 0, NULL);
	kmdb_prom_debugger_entry();
}

void
kmdb_kdi_system_release(void)
{
	kmdb_prom_debugger_exit();

	if (mdb.m_kdi->kdi_system_release != NULL) {
		(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_release,
		    0, NULL);
	}
}

struct cons_polledio *
kmdb_kdi_get_polled_io(void)
{
	return (mdb.m_kdi->kdi_get_polled_io());
}

void
kmdb_kdi_kmdb_enter(void)
{
	mdb.m_kdi->kdi_kmdb_enter();
}

int
kmdb_kdi_vtop(uintptr_t va, physaddr_t *pap)
{
	jmp_buf pcb, *oldpcb;
	int rc = 0;

	if (setjmp(pcb) == 0) {
		int err;

		oldpcb = kmdb_dpi_set_fault_hdlr(&pcb);

		if ((err = mdb.m_kdi->kdi_vtop(va, pap)) != 0)
			rc = set_errno(err == ENOENT ? EMDB_NOMAP : err);
	} else {
		/* We faulted during the translation */
		rc = set_errno(EMDB_NOMAP);
	}

	kmdb_dpi_restore_fault_hdlr(oldpcb);

	return (rc);
}

kdi_dtrace_state_t
kmdb_kdi_dtrace_get_state(void)
{
	return (mdb.m_kdi->kdi_dtrace_get_state());
}

int
kmdb_kdi_dtrace_set(int state)
{
	int err;

	if ((err = mdb.m_kdi->kdi_dtrace_set(state)) != 0)
		return (set_errno(err));

	return (0);
}

/*
 * This function is to be called only during kmdb initialization, as it
 * uses the running kernel for symbol translation facilities.
 */
uintptr_t
kmdb_kdi_lookup_by_name(char *modname, char *symname)
{
	ASSERT(kmdb_dpi_get_state(NULL) == DPI_STATE_INIT);

	return (kdi_auxv->kav_lookup_by_name(modname, symname));
}

void
kmdb_kdi_init(kdi_t *kdi, kmdb_auxv_t *kav)
{
	mdb.m_kdi = kdi;
	mdb.m_pagesize = kav->kav_pagesize;

	kdi_unload_request = 0;

	kdi_auxv = kav;

	kmdb_kdi_init_isadep(kdi, kav);
}

void
kmdb_kdi_end_init(void)
{
	kdi_auxv = NULL;
}