code:
/*	$OpenBSD: openfirm.c,v 1.15 2014/03/29 18:09:30 guenther Exp $	*/
/*	$NetBSD: openfirm.c,v 1.13 2001/06/21 00:08:02 eeh Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * All rights reserved.
 *
 * 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/stdarg.h>
#include <machine/psl.h>

#include <machine/openfirm.h>

#define min(x,y)	((x<y)?(x):(y))

int
OF_peer(phandle)
	int phandle;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t sibling;
	} args;

	args.name = ADR2CELL("peer");
	args.nargs = 1;
	args.nreturns = 1;
	args.phandle = HDL2CELL(phandle);
	if (openfirmware(&args) == -1)
		return 0;
	return args.sibling;
}

int
OF_child(phandle)
	int phandle;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t child;
	} args;

	args.name = ADR2CELL("child");
	args.nargs = 1;
	args.nreturns = 1;
	args.phandle = HDL2CELL(phandle);
	if (openfirmware(&args) == -1)
		return 0;
	return args.child;
}

int
OF_parent(phandle)
	int phandle;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t parent;
	} args;

	args.name = ADR2CELL("parent");
	args.nargs = 1;
	args.nreturns = 1;
	args.phandle = HDL2CELL(phandle);
	if (openfirmware(&args) == -1)
		return 0;
	return args.parent;
}

int
OF_instance_to_package(ihandle)
	int ihandle;
{
	static struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t ihandle;
		cell_t phandle;
	} args;

	args.name = ADR2CELL("instance-to-package");
	args.nargs = 1;
	args.nreturns = 1;
	args.ihandle = HDL2CELL(ihandle);
	if (openfirmware(&args) == -1)
		return -1;
	return args.phandle;
}

/* Should really return a `long' */
int
OF_getproplen(handle, prop)
	int handle;
	char *prop;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t prop;
		cell_t size;
	} args;

	args.name = ADR2CELL("getproplen");
	args.nargs = 2;
	args.nreturns = 1;
	args.phandle = HDL2CELL(handle);
	args.prop = ADR2CELL(prop);
	if (openfirmware(&args) == -1)
		return -1;
	return args.size;
}

int
OF_getprop(handle, prop, buf, buflen)
	int handle;
	char *prop;
	void *buf;
	int buflen;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t prop;
		cell_t buf;
		cell_t buflen;
		cell_t size;
	} args;

	if (buflen > NBPG)
		return -1;
	args.name = ADR2CELL("getprop");
	args.nargs = 4;
	args.nreturns = 1;
	args.phandle = HDL2CELL(handle);
	args.prop = ADR2CELL(prop);
	args.buf = ADR2CELL(buf);
	args.buflen = buflen;
	if (openfirmware(&args) == -1)
		return -1;
	return args.size;
}

int
OF_setprop(handle, prop, buf, buflen)
	int handle;
	char *prop;
	const void *buf;
	int buflen;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t prop;
		cell_t buf;
		cell_t buflen;
		cell_t size;
	} args;

	if (buflen > NBPG)
		return -1;
	args.name = ADR2CELL("setprop");
	args.nargs = 4;
	args.nreturns = 1;
	args.phandle = HDL2CELL(handle);
	args.prop = ADR2CELL(prop);
	args.buf = ADR2CELL(buf);
	args.buflen = buflen;
	if (openfirmware(&args) == -1)
		return -1;
	return args.size;
}

int
OF_nextprop(handle, prop, buf)
	int handle;
	char *prop;
	void *buf;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t prev;
		cell_t buf;
		cell_t next;
	} args;

	args.name = ADR2CELL("nextprop");
	args.nargs = 3;
	args.nreturns = 1;
	args.phandle = HDL2CELL(handle);
	args.prev = ADR2CELL(prop);
	args.buf = ADR2CELL(buf);
	if (openfirmware(&args) == -1)
		return -1;
	return args.next;
}

int
OF_finddevice(name)
char *name;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t device;
		cell_t phandle;
	} args;

	args.name = ADR2CELL("finddevice");
	args.nargs = 1;
	args.nreturns = 1;
	args.device = ADR2CELL(name);
	if (openfirmware(&args) == -1)
		return -1;
	return args.phandle;
}

int
OF_instance_to_path(ihandle, buf, buflen)
	int ihandle;
	char *buf;
	int buflen;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t ihandle;
		cell_t buf;
		cell_t buflen;
		cell_t length;
	} args;

	if (buflen > NBPG)
		return -1;
	args.name = ADR2CELL("instance-to-path");
	args.nargs = 3;
	args.nreturns = 1;
	args.ihandle = HDL2CELL(ihandle);
	args.buf = ADR2CELL(buf);
	args.buflen = buflen;
	if (openfirmware(&args) < 0)
		return -1;
	return args.length;
}

int
OF_package_to_path(phandle, buf, buflen)
	int phandle;
	char *buf;
	int buflen;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t phandle;
		cell_t buf;
		cell_t buflen;
		cell_t length;
	} args;

	if (buflen > NBPG)
		return -1;
	args.name = ADR2CELL("package-to-path");
	args.nargs = 3;
	args.nreturns = 1;
	args.phandle = HDL2CELL(phandle);
	args.buf = ADR2CELL(buf);
	args.buflen = buflen;
	if (openfirmware(&args) < 0)
		return -1;
	return args.length;
}

/*
 * The following two functions may need to be re-worked to be 64-bit clean.
 */
int
OF_call_method(char *method, int ihandle, int nargs, int nreturns, ...)
{
	va_list ap;
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t method;
		cell_t ihandle;
		cell_t args_n_results[12];
	} args;
	long *ip, n;

	if (nargs > 6)
		return -1;
	args.name = ADR2CELL("call-method");
	args.nargs = nargs + 2;
	args.nreturns = nreturns + 1;
	args.method = ADR2CELL(method);
	args.ihandle = HDL2CELL(ihandle);
	va_start(ap, nreturns);
	for (ip = (long *)(args.args_n_results + (n = nargs)); --n >= 0;)
		*--ip = va_arg(ap, unsigned long);
	if (openfirmware(&args) == -1) {
		va_end(ap);
		return -1;
	}
	if (args.args_n_results[nargs]) {
		va_end(ap);
		return args.args_n_results[nargs];
	}
	for (ip = (long *)(args.args_n_results + nargs + (n = args.nreturns)); --n > 0;)
		*va_arg(ap, unsigned long *) = *--ip;
	va_end(ap);
	return 0;
}

int
OF_call_method_1(char *method, int ihandle, int nargs, ...)
{
	va_list ap;
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t method;
		cell_t ihandle;
		cell_t args_n_results[16];
	} args;
	long *ip, n;

	if (nargs > 6)
		return -1;
	args.name = ADR2CELL("call-method");
	args.nargs = nargs + 2;
	args.nreturns = 1;
	args.method = ADR2CELL(method);
	args.ihandle = HDL2CELL(ihandle);
	va_start(ap, nargs);
	for (ip = (long *)(args.args_n_results + (n = nargs)); --n >= 0;)
		*--ip = va_arg(ap, unsigned long);
	va_end(ap);
	if (openfirmware(&args) == -1)
		return -1;
	if (args.args_n_results[nargs])
		return -1;
	return args.args_n_results[nargs + 1];
}

int
OF_open(dname)
	char *dname;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t dname;
		cell_t handle;
	} args;
	int l;

	if ((l = strlen(dname)) >= NBPG)
		return -1;
	args.name = ADR2CELL("open");
	args.nargs = 1;
	args.nreturns = 1;
	args.dname = ADR2CELL(dname);
	if (openfirmware(&args) == -1)
		return -1;
	return args.handle;
}

void
OF_close(handle)
	int handle;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t handle;
	} args;

	args.name = ADR2CELL("close");
	args.nargs = 1;
	args.nreturns = 0;
	args.handle = HDL2CELL(handle);
	openfirmware(&args);
}

int
OF_test(service)
	char *service;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t service;
		cell_t status;
	} args;

	args.name = ADR2CELL("test");
	args.nargs = 1;
	args.nreturns = 1;
	args.service = ADR2CELL(service);
	if (openfirmware(&args) == -1)
		return -1;
	return args.status;
}

int
OF_test_method(service, method)
	int service;
	char *method;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t service;
		cell_t method;
		cell_t status;
	} args;

	args.name = ADR2CELL("test-method");
	args.nargs = 2;
	args.nreturns = 1;
	args.service = HDL2CELL(service);
	args.method = ADR2CELL(method);
	if (openfirmware(&args) == -1)
		return -1;
	return args.status;
}

/*
 * This assumes that character devices don't read in multiples of NBPG.
 */
int
OF_read(handle, addr, len)
	int handle;
	void *addr;
	int len;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t ihandle;
		cell_t addr;
		cell_t len;
		cell_t actual;
	} args;
	int l, act = 0;

	args.name = ADR2CELL("read");
	args.nargs = 3;
	args.nreturns = 1;
	args.ihandle = HDL2CELL(handle);
	args.addr = ADR2CELL(addr);
	for (; len > 0; len -= l) {
		l = min(NBPG, len);
		args.len = l;
		if (openfirmware(&args) == -1)
			return -1;
		if (args.actual > 0) {
			act += args.actual;
		}
		if (args.actual < l) {
			if (act)
				return act;
			else
				return args.actual;
		}
	}
	return act;
}

void prom_printf(const char *fmt, ...);	/* XXX for below */

int
OF_write(handle, addr, len)
	int handle;
	void *addr;
	int len;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t ihandle;
		cell_t addr;
		cell_t len;
		cell_t actual;
	} args;
	int l, act = 0;

	if (len > 1024) {
		panic("OF_write(len=%d)", len);
	}
	args.name = ADR2CELL("write");
	args.nargs = 3;
	args.nreturns = 1;
	args.ihandle = HDL2CELL(handle);
	args.addr = ADR2CELL(addr);
	for (; len > 0; len -= l) {
		l = min(NBPG, len);
		args.len = l;
		if (openfirmware(&args) == -1)
			return -1;
		l = args.actual;
		act += l;
	}
	return act;
}

int
OF_seek(handle, pos)
	int handle;
	u_quad_t pos;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t handle;
		cell_t poshi;
		cell_t poslo;
		cell_t status;
	} args;

	args.name = ADR2CELL("seek");
	args.nargs = 3;
	args.nreturns = 1;
	args.handle = HDL2CELL(handle);
	args.poshi = HDQ2CELL_HI(pos);
	args.poslo = HDQ2CELL_LO(pos);
	if (openfirmware(&args) == -1)
		return -1;
	return args.status;
}

void
OF_boot(bootspec)
	char *bootspec;
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t bootspec;
	} args;
	int l;

	if ((l = strlen(bootspec)) >= NBPG)
		panic("OF_boot");
	args.name = ADR2CELL("boot");
	args.nargs = 1;
	args.nreturns = 0;
	args.bootspec = ADR2CELL(bootspec);
	openfirmware(&args);
	panic("OF_boot failed");
}

void
OF_enter()
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
	} args;

	args.name = ADR2CELL("enter");
	args.nargs = 0;
	args.nreturns = 0;
	openfirmware(&args);
}

void
OF_exit()
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
	} args;

	args.name = ADR2CELL("exit");
	args.nargs = 0;
	args.nreturns = 0;
	openfirmware(&args);
	panic("OF_exit failed");
}

void
OF_poweroff()
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
	} args;

	args.name = ADR2CELL("SUNW,power-off");
	args.nargs = 0;
	args.nreturns = 0;
	openfirmware(&args);
}

void
(*OF_set_callback(newfunc))(void *)
	void (*newfunc)(void *);
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t newfunc;
		cell_t oldfunc;
	} args;

	args.name = ADR2CELL("set-callback");
	args.nargs = 1;
	args.nreturns = 1;
	args.newfunc = ADR2CELL(newfunc);
	if (openfirmware(&args) == -1)
		return (void *)(long)-1;
	return (void *)(long)args.oldfunc;
}

void
OF_set_symbol_lookup(s2v, v2s)
	void (*s2v)(void *);
	void (*v2s)(void *);
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t sym2val;
		cell_t val2sym;
	} args;

	args.name = ADR2CELL("set-symbol-lookup");
	args.nargs = 2;
	args.nreturns = 0;
	args.sym2val = ADR2CELL(s2v);
	args.val2sym = ADR2CELL(v2s);

	(void)openfirmware(&args);
}

int
OF_interpret(char *cmd, int nreturns, ...)
{
	va_list ap;
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t slot[16];
	} args;
	cell_t status;
	int i = 0;

	args.name = ADR2CELL("interpret");
	args.nargs = 1;
	args.nreturns = ++nreturns;
	args.slot[i++] = ADR2CELL(cmd);
	va_start(ap, nreturns);
	while (i < 1)
		args.slot[i++] = va_arg(ap, cell_t);
	if (openfirmware(&args) == -1) {
		va_end(ap);
		return (-1);
	}
	status = args.slot[i++];
	while (i < 1 + nreturns)
		*va_arg(ap, cell_t *) = args.slot[i++];
	va_end(ap);
	return (status);
}

int
OF_milliseconds()
{
	struct {
		cell_t name;
		cell_t nargs;
		cell_t nreturns;
		cell_t ticks;
	} args;

	args.name = ADR2CELL("milliseconds");
	args.nargs = 0;
	args.nreturns = 1;
	if (openfirmware(&args) == -1)
		return -1;
	return (args.ticks);
}

#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>

int obp_symbol_debug = 0;

void OF_sym2val(cells)
	void *cells;
{
	struct args {
		cell_t service;
		cell_t nargs;
		cell_t nreturns;
		cell_t symbol;
		cell_t result;
		cell_t value;
	} *args = (struct args*)cells;
	char *symbol;
	db_expr_t value;

	/* Set data segment pointer */
	__asm volatile("clr %%g4" : :);

	/* No args?  Nothing to do. */
	if (!args->nargs ||
	    !args->nreturns) return;

	/* Do we have a place for the value? */
	if (args->nreturns != 2) {
		args->nreturns = 1;
		args->result = -1;
		return;
	}
	symbol = (char *)(u_long)args->symbol;
	if (obp_symbol_debug)
		prom_printf("looking up symbol %s\r\n", symbol);
	args->result = (db_value_of_name(symbol, &value) == TRUE) ? 0 : -1;
	if (obp_symbol_debug)
		prom_printf("%s is %lx\r\n", symbol, value);
	args->value = ADR2CELL(value);
}

void OF_val2sym(cells)
	void *cells;
{
	struct args {
		cell_t service;
		cell_t nargs;
		cell_t nreturns;
		cell_t value;
		cell_t offset;
		cell_t symbol;
	} *args = (struct args*)cells;
	db_sym_t symbol;
	db_expr_t value;
	db_expr_t offset;

	/* Set data segment pointer */
	__asm volatile("clr %%g4" : :);

	if (obp_symbol_debug)
		prom_printf("OF_val2sym: nargs %lx nreturns %lx\r\n",
			args->nargs, args->nreturns);
	/* No args?  Nothing to do. */
	if (!args->nargs ||
	    !args->nreturns) return;

	/* Do we have a place for the value? */
	if (args->nreturns != 2) {
		args->nreturns = 1;
		args->offset = -1;
		return;
	}

	value = args->value;
	if (obp_symbol_debug)
		prom_printf("looking up value %ld\r\n", value);
	symbol = db_search_symbol(value, 0, &offset);
	if (symbol == DB_SYM_NULL) {
		if (obp_symbol_debug)
			prom_printf("OF_val2sym: not found\r\n");
		args->nreturns = 1;
		args->offset = -1;
		return;
	}
	args->offset = offset;
	args->symbol = ADR2CELL(symbol);

}
#endif


code:
/*	$OpenBSD: openprom.c,v 1.20 2015/02/11 01:14:06 miod Exp $	*/
/*	$NetBSD: openprom.c,v 1.4 2002/01/10 06:21:53 briggs Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * 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, Lawrence Berkeley Laboratory.
 *
 * 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 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.
 *
 *	@(#)openprom.c	8.1 (Berkeley) 6/11/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/device.h>

#include <machine/openpromio.h>
#include <machine/autoconf.h>
#include <machine/conf.h>
#include <machine/mdesc.h>

#include <dev/clock_subr.h>
#include <dev/ofw/openfirm.h>

extern todr_chip_handle_t todr_handle;

#define OPROMMAXPARAM		32

static	int lastnode;			/* speed hack */
extern	int optionsnode;		/* node ID of ROM's options */

static int openpromcheckid(int, int);
static int openpromgetstr(int, char *, char **);

int
openpromopen(dev, flags, mode, p)
	dev_t dev;
	int flags, mode;
	struct proc *p;
{

	return (0);
}

int
openpromclose(dev, flags, mode, p)
	dev_t dev;
	int flags, mode;
	struct proc *p;
{

	return (0);
}

int
openpromread(dev_t dev, struct uio *uio, int flags)
{
#ifdef SUN4V
	int error;
	size_t len;
	caddr_t v;

	if (minor(dev) != 1)
		return (ENXIO);

	if (uio->uio_offset < 0)
		return (EINVAL);

	while (uio->uio_resid > 0) {
		if (uio->uio_offset >= mdesc_len)
			break;

		v = mdesc + uio->uio_offset;
		len = mdesc_len - uio->uio_offset;
		if (len > uio->uio_resid)
			len = uio->uio_resid;

		error = uiomove(v, len, uio);
		if (error)
			return (error);
	}

	return (0);
#else
	return (ENXIO);
#endif
}

/*
 * Verify target ID is valid (exists in the OPENPROM tree), as
 * listed from node ID sid forward.
 */
static int
openpromcheckid(sid, tid)
	int sid, tid;
{
	for (; sid != 0; sid = OF_peer(sid))
		if (sid == tid || openpromcheckid(OF_child(sid), tid))
			return (1);

	return (0);
}

static int
openpromgetstr(len, user, cpp)
	int len;
	char *user, **cpp;
{
	int error;
	char *cp;

	/* Reject obvious bogus requests */
	if ((u_int)len > (8 * 1024) - 1)
		return (ENAMETOOLONG);

	*cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
	error = copyin(user, cp, len);
	cp[len] = '\0';
	return (error);
}

int
openpromioctl(dev, cmd, data, flags, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flags;
	struct proc *p;
{
	struct opiocdesc *op;
	int node, len, ok, error, s;
	char *name, *value, *nextprop;

	/* All too easy... */
	if (cmd == OPIOCGETOPTNODE) {
		*(int *)data = optionsnode;
		return (0);
	}

	/* Verify node id */
	op = (struct opiocdesc *)data;
	node = op->op_nodeid;
	if (node != 0 && node != lastnode && node != optionsnode) {
		/* Not an easy one, must search for it */
		s = splhigh();
		ok = openpromcheckid(findroot(), node);
		splx(s);
		if (!ok)
			return (EINVAL);
		lastnode = node;
	}

	name = value = NULL;
	error = 0;
	switch (cmd) {

	case OPIOCGET:
		if ((flags & FREAD) == 0)
			return (EBADF);
		if (node == 0)
			return (EINVAL);
		error = openpromgetstr(op->op_namelen, op->op_name, &name);
		if (error)
			break;
		s = splhigh();
		len = getproplen(node, name);
		splx(s);
		if (len > op->op_buflen) {
			error = ENOMEM;
			break;
		}
		op->op_buflen = len;
		/* -1 means no entry; 0 means no value */
		if (len <= 0)
			break;
		value = malloc(len, M_TEMP, M_WAITOK);
		s = splhigh();
		error = getprop(node, name, 1, &len, (void **)&value);
		splx(s);
		if (error != 0)
			break;
		error = copyout(value, op->op_buf, len);
		break;

	case OPIOCSET:
		if ((flags & FWRITE) == 0)
			return (EBADF);
		if (node == 0)
			return (EINVAL);
		error = openpromgetstr(op->op_namelen, op->op_name, &name);
		if (error)
			break;
		error = openpromgetstr(op->op_buflen, op->op_buf, &value);
		if (error)
			break;
		s = splhigh();
		/*
		 * Make sure we can write to nvram, which on many
		 * machines lives on the clock chip (and shares its
		 * address space with the clock).
		 */
		if (todr_handle)
			todr_wenable(todr_handle, 1);
		len = OF_setprop(node, name, value, op->op_buflen + 1);
		if (todr_handle)
			todr_wenable(todr_handle, 0);
		splx(s);
		if (len != op->op_buflen)
			error = EINVAL;
		break;

	case OPIOCNEXTPROP:
		if ((flags & FREAD) == 0)
			return (EBADF);
		if (node == 0)
			return (EINVAL);
		error = openpromgetstr(op->op_namelen, op->op_name, &name);
		if (error)
			break;
		if (op->op_buflen <= 0) {
			error = ENAMETOOLONG;
			break;
		}
		value = nextprop = malloc(OPROMMAXPARAM, M_TEMP,
		    M_WAITOK | M_CANFAIL);
		if (nextprop == NULL) {
			error = ENOMEM;
			break;
		}
		s = splhigh();
		error = OF_nextprop(node, name, nextprop);
		splx(s);
		if (error == -1) {
			error = EINVAL;
			break;
		}
		if (error == 0) {
			char nul = '\0';

			op->op_buflen = 0;
			error = copyout(&nul, op->op_buf, sizeof(char));
			break;
		}
		len = strlen(nextprop);
		if (len > op->op_buflen)
			len = op->op_buflen;
		else
			op->op_buflen = len;
		error = copyout(nextprop, op->op_buf, len);
		break;

	case OPIOCGETNEXT:
		if ((flags & FREAD) == 0)
			return (EBADF);
		s = splhigh();
		node = nextsibling(node);
		splx(s);
		*(int *)data = lastnode = node;
		break;

	case OPIOCGETCHILD:
		if ((flags & FREAD) == 0)
			return (EBADF);
		if (node == 0)
			return (EINVAL);
		s = splhigh();
		node = firstchild(node);
		splx(s);
		*(int *)data = lastnode = node;
		break;

	default:
		return (ENOTTY);
	}

	if (name)
		free(name, M_TEMP, 0);
	if (value)
		free(value, M_TEMP, 0);

	return (error);
}

license: unknown


code:
/*	$OpenBSD: pmap.c,v 1.92 2015/07/10 12:11:41 kettenis Exp $	*/
/*	$NetBSD: pmap.c,v 1.107 2001/08/31 16:47:41 eeh Exp $	*/
#undef	NO_VCACHE /* Don't forget the locked TLB in dostart */
/*
 *
 * Copyright (C) 1996-1999 Eduardo Horvath.
 * All rights reserved.
 *
 *
 * 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.
 *
 * 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/atomic.h>
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/pool.h>
#include <sys/exec.h>
#include <sys/core.h>
#include <sys/kcore.h>

#include <uvm/uvm.h>

#include <machine/pcb.h>
#include <machine/sparc64.h>
#include <machine/ctlreg.h>
#include <machine/hypervisor.h>
#include <machine/openfirm.h>
#include <machine/kcore.h>

#include "cache.h"

#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_command.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
#include <ddb/db_extern.h>
#include <ddb/db_access.h>
#include <ddb/db_output.h>
#define Debugger()	__asm volatile("ta 1; nop");
#else
#define Debugger()
#define db_printf	printf
#endif

#define	MEG		(1<<20) /* 1MB */
#define	KB		(1<<10)	/* 1KB */

paddr_t cpu0paddr;/* XXXXXXXXXXXXXXXX */

extern int64_t asmptechk(int64_t *pseg[], int addr); /* DEBUG XXXXX */

/* These routines are in assembly to allow access thru physical mappings */
extern int64_t pseg_get(struct pmap*, vaddr_t addr);
extern int pseg_set(struct pmap*, vaddr_t addr, int64_t tte, paddr_t spare);

/* XXX - temporary workaround for pmap_{copy,zero}_page api change */
void pmap_zero_phys(paddr_t pa);
void pmap_copy_phys(paddr_t src, paddr_t dst);

/*
 * Diatribe on ref/mod counting:
 *
 * First of all, ref/mod info must be non-volatile.  Hence we need to keep it
 * in the pv_entry structure for each page.  (We could bypass this for the
 * vm_page, but that's a long story....)
 *
 * This architecture has nice, fast traps with lots of space for software bits
 * in the TTE.  To accelerate ref/mod counts we make use of these features.
 *
 * When we map a page initially, we place a TTE in the page table.  It's
 * inserted with the TLB_W and TLB_ACCESS bits cleared.  If a page is really
 * writeable we set the TLB_REAL_W bit for the trap handler.
 *
 * Whenever we take a TLB miss trap, the trap handler will set the TLB_ACCESS
 * bit in the approprate TTE in the page table.  Whenever we take a protection
 * fault, if the TLB_REAL_W bit is set then we flip both the TLB_W and TLB_MOD
 * bits to enable writing and mark the page as modified.
 *
 * This means that we may have ref/mod information all over the place.  The
 * pmap routines must traverse the page tables of all pmaps with a given page
 * and collect/clear all the ref/mod information and copy it into the pv_entry.
 */

#ifdef NO_VCACHE
#define FORCE_ALIAS	1
#else
#define FORCE_ALIAS	0
#endif

#define	PV_ALIAS	0x1LL
#define PV_REF		0x2LL
#define PV_MOD		0x4LL
#define PV_NVC		0x8LL
#define PV_NC		0x10LL
#define PV_WE		0x20LL		/* Debug -- track if this page was ever writable */
#define PV_MASK		(0x03fLL)
#define PV_VAMASK	(~(NBPG - 1))
#define PV_MATCH(pv,va)	(!((((pv)->pv_va) ^ (va)) & PV_VAMASK))
#define PV_SETVA(pv,va) ((pv)->pv_va = (((va) & PV_VAMASK) | (((pv)->pv_va) & PV_MASK)))

pv_entry_t	pv_table;	/* array of entries, one per page */
static struct pool pv_pool;
static struct pool pmap_pool;

pv_entry_t pmap_remove_pv(struct pmap *pm, vaddr_t va, paddr_t pa);
pv_entry_t pmap_enter_pv(struct pmap *pm, pv_entry_t, vaddr_t va, paddr_t pa);
void	pmap_page_cache(struct pmap *pm, paddr_t pa, int mode);

void	pmap_bootstrap_cpu(paddr_t);

void	pmap_pinit(struct pmap *);
void	pmap_release(struct pmap *);
pv_entry_t pa_to_pvh(paddr_t);

u_int64_t first_phys_addr;

pv_entry_t
pa_to_pvh(paddr_t pa)
{
	struct vm_page *pg;

	pg = PHYS_TO_VM_PAGE(pa);
	return pg ? &pg->mdpage.pvent : NULL;
}

static __inline u_int
pmap_tte2flags(u_int64_t tte)
{
	if (CPU_ISSUN4V)
		return (((tte & SUN4V_TLB_ACCESS) ? PV_REF : 0) |
		    ((tte & SUN4V_TLB_MODIFY) ? PV_MOD : 0));
	else
		return (((tte & SUN4U_TLB_ACCESS) ? PV_REF : 0) |
		    ((tte & SUN4U_TLB_MODIFY) ? PV_MOD : 0));
}

/*
 * Here's the CPU TSB stuff.  It's allocated in pmap_bootstrap.
 */
pte_t *tsb_dmmu;
pte_t *tsb_immu;
int tsbsize;		/* tsbents = 512 * 2^tsbsize */
#define TSBENTS (512 << tsbsize)
#define	TSBSIZE	(TSBENTS * 16)

/*
 * The invalid tsb tag uses the fact that the last context we have is
 * never allocated.
 */
#define TSB_TAG_INVALID	(~0LL << 48)

#define TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie) \
  (CPU_ISSUN4V ?\
    SUN4V_TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie) : \
    SUN4U_TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie))

/* The same for sun4u and sun4v. */
#define TLB_V		SUN4U_TLB_V

/* Only used for DEBUG. */
#define TLB_NFO		(CPU_ISSUN4V ? SUN4V_TLB_NFO : SUN4U_TLB_NFO)

/*
 * UltraSPARC T1 & T2 implement only a 40-bit real address range, just
 * like older UltraSPARC CPUs.
 */
#define TLB_PA_MASK	SUN4U_TLB_PA_MASK

/* XXX */
#define TLB_TSB_LOCK	(CPU_ISSUN4V ? SUN4V_TLB_TSB_LOCK : SUN4U_TLB_TSB_LOCK)

#ifdef SUN4V
struct tsb_desc *tsb_desc;
#endif

struct pmap kernel_pmap_;

extern int physmem;
/*
 * Virtual and physical addresses of the start and end of kernel text
 * and data segments.
 */
vaddr_t ktext;
paddr_t ktextp;
vaddr_t ektext;
paddr_t ektextp;
vaddr_t kdata;
paddr_t kdatap;
vaddr_t ekdata;
paddr_t ekdatap;

static int npgs;
static struct mem_region memlist[8]; /* Pick a random size here */

vaddr_t	vmmap;			/* one reserved MI vpage for /dev/mem */

struct mem_region *mem, *avail, *orig;
int memsize;

static int memh = 0, vmemh = 0;	/* Handles to OBP devices */

static int ptelookup_va(vaddr_t va); /* sun4u */

static __inline void
tsb_invalidate(int ctx, vaddr_t va)
{
	int i;
	int64_t tag;

	i = ptelookup_va(va);
	tag = TSB_TAG(0, ctx, va);
	if (tsb_dmmu[i].tag == tag)
		atomic_cas_ulong((volatile unsigned long *)&tsb_dmmu[i].tag,
		    tag, TSB_TAG_INVALID);
	if (tsb_immu[i].tag == tag)
		atomic_cas_ulong((volatile unsigned long *)&tsb_immu[i].tag,
		    tag, TSB_TAG_INVALID);
}

struct prom_map *prom_map;
int prom_map_size;

#ifdef DEBUG
#define	PDB_BOOT	0x20000
#define	PDB_BOOT1	0x40000
int	pmapdebug = 0;

#define	BDPRINTF(n, f)	if (pmapdebug & (n)) prom_printf f
#else
#define	BDPRINTF(n, f)
#endif

/*
 *
 * A context is simply a small number that differentiates multiple mappings
 * of the same address.  Contexts on the spitfire are 13 bits, but could
 * be as large as 17 bits.
 *
 * Each context is either free or attached to a pmap.
 *
 * The context table is an array of pointers to psegs.  Just dereference
 * the right pointer and you get to the pmap segment tables.  These are
 * physical addresses, of course.
 *
 */
paddr_t *ctxbusy;
int numctx;
#define CTXENTRY	(sizeof(paddr_t))
#define CTXSIZE		(numctx * CTXENTRY)

int pmap_get_page(paddr_t *, const char *, struct pmap *);
void pmap_free_page(paddr_t, struct pmap *);

/*
 * Support for big page sizes.  This maps the page size to the
 * page bits.  That is: these are the bits between 8K pages and
 * larger page sizes that cause aliasing.
 */
struct page_size_map page_size_map[] = {
	{ (4*1024*1024-1) & ~(8*1024-1), PGSZ_4M },
	{ (512*1024-1) & ~(8*1024-1), PGSZ_512K  },
	{ (64*1024-1) & ~(8*1024-1), PGSZ_64K  },
	{ (8*1024-1) & ~(8*1024-1), PGSZ_8K  },
	{ 0, PGSZ_8K&0  }
};

/*
 * Enter a TTE into the kernel pmap only.  Don't do anything else.
 *
 * Use only during bootstrapping since it does no locking and
 * can lose ref/mod info!!!!
 *
 */
static void
pmap_enter_kpage(vaddr_t va, int64_t data)
{
	paddr_t newp;

	newp = 0;
	while (pseg_set(pmap_kernel(), va, data, newp) == 1) {
		newp = 0;
		if (!pmap_get_page(&newp, NULL, pmap_kernel())) {
			prom_printf("pmap_enter_kpage: out of pages\n");
			panic("pmap_enter_kpage");
		}
		pmap_kernel()->pm_stats.resident_count++;

		BDPRINTF(PDB_BOOT1,
			 ("pseg_set: pm=%p va=%p data=%lx newp %lx\r\n",
			  pmap_kernel(), va, (long)data, (long)newp));
	}
}

/*
 * Check bootargs to see if we need to enable bootdebug.
 */
#ifdef DEBUG
void
pmap_bootdebug(void)
{
	int chosen;
	char *cp;
	char buf[128];

	/*
	 * Grab boot args from PROM
	 */
	chosen = OF_finddevice("/chosen");
	/* Setup pointer to boot flags */
	OF_getprop(chosen, "bootargs", buf, sizeof(buf));
	cp = buf;
	while (*cp != '-')
		if (*cp++ == '\0')
			return;
	for (;;)
		switch (*++cp) {
		case '\0':
			return;
		case 'V':
			pmapdebug |= PDB_BOOT|PDB_BOOT1;
			break;
		case 'D':
			pmapdebug |= PDB_BOOT1;
			break;
		}
}
#endif

/*
 * This is called during bootstrap, before the system is really initialized.
 *
 * It's called with the start and end virtual addresses of the kernel.  We
 * bootstrap the pmap allocator now.  We will allocate the basic structures we
 * need to bootstrap the VM system here: the page frame tables, the TSB, and
 * the free memory lists.
 *
 * Now all this is becoming a bit obsolete.  maxctx is still important, but by
 * separating the kernel text and data segments we really would need to
 * provide the start and end of each segment.  But we can't.  The rodata
 * segment is attached to the end of the kernel segment and has nothing to
 * delimit its end.  We could still pass in the beginning of the kernel and
 * the beginning and end of the data segment but we could also just as easily
 * calculate that all in here.
 *
 * To handle the kernel text, we need to do a reverse mapping of the start of
 * the kernel, then traverse the free memory lists to find out how big it is.
 */

void
pmap_bootstrap(u_long kernelstart, u_long kernelend, u_int maxctx, u_int numcpus)
{
	extern int data_start[], end[];	/* start of data segment */
	extern int msgbufmapped;
	struct mem_region *mp, *mp1;
	int msgbufsiz;
	int pcnt;
	size_t s, sz;
	int i, j;
	int64_t data;
	vaddr_t va;
	u_int64_t phys_msgbuf;
	paddr_t newkp;
	vaddr_t newkv, firstaddr, intstk;
	vsize_t kdsize, ktsize;

#ifdef DEBUG
	pmap_bootdebug();
#endif

	BDPRINTF(PDB_BOOT, ("Entered pmap_bootstrap.\r\n"));
	/*
	 * set machine page size
	 */
	uvmexp.pagesize = NBPG;
	uvm_setpagesize();

	/*
	 * Find out how big the kernel's virtual address
	 * space is.  The *$#@$ prom loses this info
	 */
	if ((vmemh = OF_finddevice("/virtual-memory")) == -1) {
		prom_printf("no virtual-memory?");
		OF_exit();
	}
	bzero((caddr_t)memlist, sizeof(memlist));
	if (OF_getprop(vmemh, "available", memlist, sizeof(memlist)) <= 0) {
		prom_printf("no vmemory avail?");
		OF_exit();
	}

#ifdef DEBUG
	if (pmapdebug & PDB_BOOT) {
		/* print out mem list */
		prom_printf("Available virtual memory:\r\n");
		for (mp = memlist; mp->size; mp++) {
			prom_printf("memlist start %p size %lx\r\n",
				    (void *)(u_long)mp->start,
				    (u_long)mp->size);
		}
		prom_printf("End of available virtual memory\r\n");
	}
#endif
	/*
	 * Get hold or the message buffer.
	 */
	msgbufp = (struct msgbuf *)(vaddr_t)MSGBUF_VA;
/* XXXXX -- increase msgbufsiz for uvmhist printing */
	msgbufsiz = 4*NBPG /* round_page(sizeof(struct msgbuf)) */;
	BDPRINTF(PDB_BOOT, ("Trying to allocate msgbuf at %lx, size %lx\r\n",
			    (long)msgbufp, (long)msgbufsiz));
	if ((long)msgbufp !=
	    (long)(phys_msgbuf = prom_claim_virt((vaddr_t)msgbufp, msgbufsiz)))
		prom_printf(
		    "cannot get msgbuf VA, msgbufp=%p, phys_msgbuf=%lx\r\n",
		    (void *)msgbufp, (long)phys_msgbuf);
	phys_msgbuf = prom_get_msgbuf(msgbufsiz, MMU_PAGE_ALIGN);
	BDPRINTF(PDB_BOOT,
		("We should have the memory at %lx, let's map it in\r\n",
			phys_msgbuf));
	if (prom_map_phys(phys_msgbuf, msgbufsiz, (vaddr_t)msgbufp,
			  -1/* sunos does this */) == -1)
		prom_printf("Failed to map msgbuf\r\n");
	else
		BDPRINTF(PDB_BOOT, ("msgbuf mapped at %p\r\n",
			(void *)msgbufp));
	msgbufmapped = 1;	/* enable message buffer */
	initmsgbuf((caddr_t)msgbufp, msgbufsiz);

	/*
	 * Record kernel mapping -- we will map these with a permanent 4MB
	 * TLB entry when we initialize the CPU later.
	 */
	BDPRINTF(PDB_BOOT, ("translating kernelstart %p\r\n",
		(void *)kernelstart));
	ktext = kernelstart;
	ktextp = prom_vtop(kernelstart);

	kdata = (vaddr_t)data_start;
	kdatap = prom_vtop(kdata);
	ekdata = (vaddr_t)end;

	/*
	 * Find the real size of the kernel.  Locate the smallest starting
	 * address > kernelstart.
	 */
	for (mp1 = mp = memlist; mp->size; mp++) {
		/*
		 * Check whether this region is at the end of the kernel.
		 */
		if (mp->start >= ekdata && (mp1->start < ekdata ||
						mp1->start > mp->start))
			mp1 = mp;
	}
	if (mp1->start < kdata)
		prom_printf("Kernel at end of vmem???\r\n");

	BDPRINTF(PDB_BOOT1,
		("Kernel data is mapped at %lx, next free seg: %lx, %lx\r\n",
			(long)kdata, (u_long)mp1->start, (u_long)mp1->size));

	/*
	 * We save where we can start allocating memory.
	 */
	firstaddr = (ekdata + 07) & ~ 07;	/* Longword align */

	/*
	 * We reserve 100K to grow.
	 */
	ekdata += 100*KB;

	/*
	 * And set the end of the data segment to the end of what our
	 * bootloader allocated for us, if we still fit in there.
	 */
	if (ekdata < mp1->start)
		ekdata = mp1->start;

#define	valloc(name, type, num) (name) = (type *)firstaddr; firstaddr += (num)

	/*
	 * Since we can't always give the loader the hint to align us on a 4MB
	 * boundary, we will need to do the alignment ourselves.  First
	 * allocate a new 4MB aligned segment for the kernel, then map it
	 * in, copy the kernel over, swap mappings, then finally, free the
	 * old kernel.  Then we can continue with this.
	 *
	 * We'll do the data segment up here since we know how big it is.
	 * We'll do the text segment after we've read in the PROM translations
	 * so we can figure out its size.
	 *
	 * The ctxbusy table takes about 64KB, the TSB up to 32KB, and the
	 * rest should be less than 1K, so 100KB extra should be plenty.
	 */
	kdsize = round_page(ekdata - kdata);
	BDPRINTF(PDB_BOOT1, ("Kernel data size is %lx\r\n", (long)kdsize));

	if ((kdatap & (4*MEG-1)) == 0) {
		/* We were at a 4MB boundary -- claim the rest */
		psize_t szdiff = (4*MEG - kdsize) & (4*MEG - 1);

		BDPRINTF(PDB_BOOT1, ("Need to extend dseg by %lx\r\n",
			(long)szdiff));
		if (szdiff) {
			/* Claim the rest of the physical page. */
			newkp = kdatap + kdsize;
			newkv = kdata + kdsize;
			if (newkp != prom_claim_phys(newkp, szdiff)) {
				prom_printf("pmap_bootstrap: could not claim "
					"physical dseg extension "
					"at %lx size %lx\r\n",
					newkp, szdiff);
				goto remap_data;
			}

			/* And the rest of the virtual page. */
			if (prom_claim_virt(newkv, szdiff) != newkv)
			prom_printf("pmap_bootstrap: could not claim "
				"virtual dseg extension "
				"at size %lx\r\n", newkv, szdiff);

			/* Make sure all 4MB are mapped */
			prom_map_phys(newkp, szdiff, newkv, -1);
		}
	} else {
		psize_t sz;
remap_data:
		/*
		 * Either we're not at a 4MB boundary or we can't get the rest
		 * of the 4MB extension.  We need to move the data segment.
		 * Leave 1MB of extra fiddle space in the calculations.
		 */

		sz = (kdsize + 4*MEG - 1) & ~(4*MEG-1);
		BDPRINTF(PDB_BOOT1,
			 ("Allocating new %lx kernel data at 4MB boundary\r\n",
			  (u_long)sz));
		if ((newkp = prom_alloc_phys(sz, 4*MEG)) == (paddr_t)-1 ) {
			prom_printf("Cannot allocate new kernel\r\n");
			OF_exit();
		}
		BDPRINTF(PDB_BOOT1, ("Allocating new va for buffer at %llx\r\n",
				     (u_int64_t)newkp));
		if ((newkv = (vaddr_t)prom_alloc_virt(sz, 8)) ==
		    (vaddr_t)-1) {
			prom_printf("Cannot allocate new kernel va\r\n");
			OF_exit();
		}
		BDPRINTF(PDB_BOOT1, ("Mapping in buffer %llx at %llx\r\n",
		    (u_int64_t)newkp, (u_int64_t)newkv));
		prom_map_phys(newkp, sz, (vaddr_t)newkv, -1);
		BDPRINTF(PDB_BOOT1, ("Copying %ld bytes kernel data...",
			kdsize));
		bzero((void *)newkv, sz);
		bcopy((void *)kdata, (void *)newkv, kdsize);
		BDPRINTF(PDB_BOOT1, ("done.  Swapping maps..unmap new\r\n"));
		prom_unmap_virt((vaddr_t)newkv, sz);
		BDPRINTF(PDB_BOOT, ("remap old "));
#if 0
		/*
		 * calling the prom will probably require reading part of the
		 * data segment so we can't do this.  */
		prom_unmap_virt((vaddr_t)kdatap, kdsize);
#endif
		prom_map_phys(newkp, sz, kdata, -1);
		/*
		 * we will map in 4MB, more than we allocated, to allow
		 * further allocation
		 */
		BDPRINTF(PDB_BOOT1, ("free old\r\n"));
		prom_free_phys(kdatap, kdsize);
		kdatap = newkp;
		BDPRINTF(PDB_BOOT1,
			 ("pmap_bootstrap: firstaddr is %lx virt (%lx phys)"
			  "avail for kernel\r\n", (u_long)firstaddr,
			  (u_long)prom_vtop(firstaddr)));
	}

	/*
	 * Find out how much RAM we have installed.
	 */
	BDPRINTF(PDB_BOOT, ("pmap_bootstrap: getting phys installed\r\n"));
	if ((memh = OF_finddevice("/memory")) == -1) {
		prom_printf("no memory?");
		OF_exit();
	}
	memsize = OF_getproplen(memh, "reg") + 2 * sizeof(struct mem_region);
	valloc(mem, struct mem_region, memsize);
	bzero((caddr_t)mem, memsize);
	if (OF_getprop(memh, "reg", mem, memsize) <= 0) {
		prom_printf("no memory installed?");
		OF_exit();
	}

#ifdef DEBUG
	if (pmapdebug & PDB_BOOT1) {
		/* print out mem list */
		prom_printf("Installed physical memory:\r\n");
		for (mp = mem; mp->size; mp++) {
			prom_printf("memlist start %lx size %lx\r\n",
				    (u_long)mp->start, (u_long)mp->size);
		}
	}
#endif
	BDPRINTF(PDB_BOOT1, ("Calculating physmem:"));

	for (mp = mem; mp->size; mp++)
		physmem += atop(mp->size);
	BDPRINTF(PDB_BOOT1, (" result %x or %d pages\r\n",
			     (int)physmem, (int)physmem));

	/*
	 * Calculate approx TSB size.
	 */
	tsbsize = 0;
#ifdef SMALL_KERNEL
	while ((physmem >> tsbsize) > atop(64 * MEG) && tsbsize < 2)
#else
	while ((physmem >> tsbsize) > atop(64 * MEG) && tsbsize < 7)
#endif
		tsbsize++;

	/*
	 * Save the prom translations
	 */
	sz = OF_getproplen(vmemh, "translations");
	valloc(prom_map, struct prom_map, sz);
	if (OF_getprop(vmemh, "translations", (void *)prom_map, sz) <= 0) {
		prom_printf("no translations installed?");
		OF_exit();
	}
	prom_map_size = sz / sizeof(struct prom_map);
#ifdef DEBUG
	if (pmapdebug & PDB_BOOT) {
		/* print out mem list */
		prom_printf("Prom xlations:\r\n");
		for (i = 0; i < prom_map_size; i++) {
			prom_printf("start %016lx size %016lx tte %016lx\r\n",
				    (u_long)prom_map[i].vstart,
				    (u_long)prom_map[i].vsize,
				    (u_long)prom_map[i].tte);
		}
		prom_printf("End of prom xlations\r\n");
	}
#endif
	/*
	 * Hunt for the kernel text segment and figure out it size and
	 * alignment.
	 */
	ktsize = 0;
	for (i = 0; i < prom_map_size; i++)
		if (prom_map[i].vstart == ktext + ktsize)
			ktsize += prom_map[i].vsize;
	if (ktsize == 0)
		panic("No kernel text segment!");
	ektext = ktext + ktsize;

	if (ktextp & (4*MEG-1)) {
		/* Kernel text is not 4MB aligned -- need to fix that */
		BDPRINTF(PDB_BOOT1,
			 ("Allocating new %lx kernel text at 4MB boundary\r\n",
			  (u_long)ktsize));
		if ((newkp = prom_alloc_phys(ktsize, 4*MEG)) == 0 ) {
			prom_printf("Cannot allocate new kernel text\r\n");
			OF_exit();
		}
		BDPRINTF(PDB_BOOT1, ("Allocating new va for buffer at %llx\r\n",
				     (u_int64_t)newkp));
		if ((newkv = (vaddr_t)prom_alloc_virt(ktsize, 8)) ==
		    (vaddr_t)-1) {
			prom_printf("Cannot allocate new kernel text va\r\n");
			OF_exit();
		}
		BDPRINTF(PDB_BOOT1, ("Mapping in buffer %lx at %lx\r\n",
				     (u_long)newkp, (u_long)newkv));
		prom_map_phys(newkp, ktsize, (vaddr_t)newkv, -1);
		BDPRINTF(PDB_BOOT1, ("Copying %ld bytes kernel text...",
			ktsize));
		bcopy((void *)ktext, (void *)newkv,
		    ktsize);
		BDPRINTF(PDB_BOOT1, ("done.  Swapping maps..unmap new\r\n"));
		prom_unmap_virt((vaddr_t)newkv, 4*MEG);
		BDPRINTF(PDB_BOOT, ("remap old "));
#if 0
		/*
		 * calling the prom will probably require reading part of the
		 * text segment so we can't do this.
		 */
		prom_unmap_virt((vaddr_t)ktextp, ktsize);
#endif
		prom_map_phys(newkp, ktsize, ktext, -1);
		/*
		 * we will map in 4MB, more than we allocated, to allow
		 * further allocation
		 */
		BDPRINTF(PDB_BOOT1, ("free old\r\n"));
		prom_free_phys(ktextp, ktsize);
		ktextp = newkp;

		BDPRINTF(PDB_BOOT1,
			 ("pmap_bootstrap: firstaddr is %lx virt (%lx phys)"
			  "avail for kernel\r\n", (u_long)firstaddr,
			  (u_long)prom_vtop(firstaddr)));

		/*
		 * Re-fetch translations -- they've certainly changed.
		 */
		if (OF_getprop(vmemh, "translations", (void *)prom_map, sz) <=
			0) {
			prom_printf("no translations installed?");
			OF_exit();
		}
#ifdef DEBUG
		if (pmapdebug & PDB_BOOT) {
			/* print out mem list */
			prom_printf("New prom xlations:\r\n");
			for (i = 0; i < prom_map_size; i++) {
				prom_printf("start %016lx size %016lx tte %016lx\r\n",
					    (u_long)prom_map[i].vstart,
					    (u_long)prom_map[i].vsize,
					    (u_long)prom_map[i].tte);
			}
			prom_printf("End of prom xlations\r\n");
		}
#endif
	}
	ektextp = ktextp + ktsize;

	/*
	 * Here's a quick in-lined reverse bubble sort.  It gets rid of
	 * any translations inside the kernel data VA range.
	 */
	for(i = 0; i < prom_map_size; i++) {
		if (prom_map[i].vstart >= kdata &&
		    prom_map[i].vstart <= firstaddr) {
			prom_map[i].vstart = 0;
			prom_map[i].vsize = 0;
		}
		if (prom_map[i].vstart >= ktext &&
		    prom_map[i].vstart <= ektext) {
			prom_map[i].vstart = 0;
			prom_map[i].vsize = 0;
		}
		for(j = i; j < prom_map_size; j++) {
			if (prom_map[j].vstart >= kdata &&
			    prom_map[j].vstart <= firstaddr)
				continue;	/* this is inside the kernel */
			if (prom_map[j].vstart >= ktext &&
			    prom_map[j].vstart <= ektext)
				continue;	/* this is inside the kernel */
			if (prom_map[j].vstart > prom_map[i].vstart) {
				struct prom_map tmp;
				tmp = prom_map[i];
				prom_map[i] = prom_map[j];
				prom_map[j] = tmp;
			}
		}
	}
#ifdef DEBUG
	if (pmapdebug & PDB_BOOT) {
		/* print out mem list */
		prom_printf("Prom xlations:\r\n");
		for (i = 0; i < prom_map_size; i++) {
			prom_printf("start %016lx size %016lx tte %016lx\r\n",
				    (u_long)prom_map[i].vstart,
				    (u_long)prom_map[i].vsize,
				    (u_long)prom_map[i].tte);
		}
		prom_printf("End of prom xlations\r\n");
	}
#endif

	/*
	 * Allocate a 64KB page for the cpu_info structure now.
	 */
	if ((cpu0paddr = prom_alloc_phys(numcpus * 8*NBPG, 8*NBPG)) == 0 ) {
		prom_printf("Cannot allocate new cpu_info\r\n");
		OF_exit();
	}

	/*
	 * Now the kernel text segment is in its final location we can try to
	 * find out how much memory really is free.
	 */
	sz = OF_getproplen(memh, "available") + sizeof(struct mem_region);
	valloc(orig, struct mem_region, sz);
	bzero((caddr_t)orig, sz);
	if (OF_getprop(memh, "available", orig, sz) <= 0) {
		prom_printf("no available RAM?");
		OF_exit();
	}
#ifdef DEBUG
	if (pmapdebug & PDB_BOOT1) {
		/* print out mem list */
		prom_printf("Available physical memory:\r\n");
		for (mp = orig; mp->size; mp++) {
			prom_printf("memlist start %lx size %lx\r\n",
				    (u_long)mp->start, (u_long)mp->size);
		}
		prom_printf("End of available physical memory\r\n");
	}
#endif
	valloc(avail, struct mem_region, sz);
	bzero((caddr_t)avail, sz);
	for (pcnt = 0, mp = orig, mp1 = avail; (mp1->size = mp->size);
	    mp++, mp1++) {
		mp1->start = mp->start;
		pcnt++;
	}

	/*
	 * Allocate and initialize a context table
	 */
	numctx = maxctx;
	valloc(ctxbusy, paddr_t, CTXSIZE);
	bzero((caddr_t)ctxbusy, CTXSIZE);

	/*
	 * Allocate our TSB.
	 *
	 * We will use the left over space to flesh out the kernel pmap.
	 */
	BDPRINTF(PDB_BOOT1, ("firstaddr before TSB=%lx\r\n",
		(u_long)firstaddr));
	firstaddr = ((firstaddr + TSBSIZE - 1) & ~(TSBSIZE-1));
#ifdef DEBUG
	i = (firstaddr + (NBPG-1)) & ~(NBPG-1);	/* First, page align */
	if ((int)firstaddr < i) {
		prom_printf("TSB alloc fixup failed\r\n");
		prom_printf("frobbed i, firstaddr before TSB=%x, %lx\r\n",
		    (int)i, (u_long)firstaddr);
		panic("TSB alloc");
		OF_exit();
	}
#endif
	BDPRINTF(PDB_BOOT, ("frobbed i, firstaddr before TSB=%x, %lx\r\n",
			    (int)i, (u_long)firstaddr));
	valloc(tsb_dmmu, pte_t, TSBSIZE);
	bzero(tsb_dmmu, TSBSIZE);
	valloc(tsb_immu, pte_t, TSBSIZE);
	bzero(tsb_immu, TSBSIZE);

	BDPRINTF(PDB_BOOT1, ("firstaddr after TSB=%lx\r\n", (u_long)firstaddr));
	BDPRINTF(PDB_BOOT1, ("TSB allocated at %p size %08x\r\n", (void *)tsb_dmmu,
	    (int)TSBSIZE));

#ifdef SUN4V
	if (CPU_ISSUN4V) {
		valloc(tsb_desc, struct tsb_desc, sizeof(struct tsb_desc));
		bzero(tsb_desc, sizeof(struct tsb_desc));
		tsb_desc->td_idxpgsz = 0;
		tsb_desc->td_assoc = 1;
		tsb_desc->td_size = TSBENTS;
		tsb_desc->td_ctxidx = -1;
		tsb_desc->td_pgsz = 0xf;
		tsb_desc->td_pa = (paddr_t)tsb_dmmu + kdatap - kdata;
	}
#endif

	first_phys_addr = mem->start;
	BDPRINTF(PDB_BOOT1, ("firstaddr after pmap=%08lx\r\n",
		(u_long)firstaddr));

	/*
	 * Page align all regions.
	 * Non-page memory isn't very interesting to us.
	 * Also, sort the entries for ascending addresses.
	 *
	 * And convert from virtual to physical addresses.
	 */

	BDPRINTF(PDB_BOOT, ("kernel virtual size %08lx - %08lx\r\n",
			    (u_long)kernelstart, (u_long)firstaddr));
	kdata = kdata & ~PGOFSET;
	ekdata = firstaddr;
	ekdata = (ekdata + PGOFSET) & ~PGOFSET;
	BDPRINTF(PDB_BOOT1, ("kernel virtual size %08lx - %08lx\r\n",
			     (u_long)kernelstart, (u_long)kernelend));
	ekdatap = ekdata - kdata + kdatap;
	/* Switch from vaddrs to paddrs */
	if(ekdatap > (kdatap + 4*MEG)) {
		prom_printf("Kernel size exceeds 4MB\r\n");
	}

#ifdef DEBUG
	if (pmapdebug & PDB_BOOT1) {
		/* print out mem list */
		prom_printf("Available %lx physical memory before cleanup:\r\n",
			    (u_long)avail);
		for (mp = avail; mp->size; mp++) {
			prom_printf("memlist start %lx size %lx\r\n",
				    (u_long)mp->start,
				    (u_long)mp->size);
		}
		prom_printf("End of available physical memory before cleanup\r\n");
		prom_printf("kernel physical text size %08lx - %08lx\r\n",
			    (u_long)ktextp, (u_long)ektextp);
		prom_printf("kernel physical data size %08lx - %08lx\r\n",
			    (u_long)kdatap, (u_long)ekdatap);
	}
#endif
	/*
	 * Here's a another quick in-lined bubble sort.
	 */
	for (i = 0; i < pcnt; i++) {
		for (j = i; j < pcnt; j++) {
			if (avail[j].start < avail[i].start) {
				struct mem_region tmp;
				tmp = avail[i];
				avail[i] = avail[j];
				avail[j] = tmp;
			}
		}
	}

	/* Throw away page zero if we have it. */
	if (avail->start == 0) {
		avail->start += NBPG;
		avail->size -= NBPG;
	}
	/*
	 * Now we need to remove the area we valloc'ed from the available
	 * memory lists.  (NB: we may have already alloc'ed the entire space).
	 */
	npgs = 0;
	for (mp = avail; mp->size; mp++) {
		/*
		 * Check whether this region holds all of the kernel.
		 */
		s = mp->start + mp->size;
		if (mp->start < kdatap && s > roundup(ekdatap, 4*MEG)) {
			avail[pcnt].start = roundup(ekdatap, 4*MEG);
			avail[pcnt++].size = s - kdatap;
			mp->size = kdatap - mp->start;
		}
		/*
		 * Look whether this regions starts within the kernel.
		 */
		if (mp->start >= kdatap &&
			mp->start < roundup(ekdatap, 4*MEG)) {
			s = ekdatap - mp->start;
			if (mp->size > s)
				mp->size -= s;
			else
				mp->size = 0;
			mp->start = roundup(ekdatap, 4*MEG);
		}
		/*
		 * Now look whether this region ends within the kernel.
		 */
		s = mp->start + mp->size;
		if (s > kdatap && s < roundup(ekdatap, 4*MEG))
			mp->size -= s - kdatap;
		/*
		 * Now page align the start of the region.
		 */
		s = mp->start % NBPG;
		if (mp->size >= s) {
			mp->size -= s;
			mp->start += s;
		}
		/*
		 * And now align the size of the region.
		 */
		mp->size -= mp->size % NBPG;
		/*
		 * Check whether some memory is left here.
		 */
		if (mp->size == 0) {
			bcopy(mp + 1, mp,
			      (pcnt - (mp - avail)) * sizeof *mp);
			pcnt--;
			mp--;
			continue;
		}
		s = mp->start;
		sz = mp->size;
		npgs += atop(sz);
		for (mp1 = avail; mp1 < mp; mp1++)
			if (s < mp1->start)
				break;
		if (mp1 < mp) {
			bcopy(mp1, mp1 + 1, (char *)mp - (char *)mp1);
			mp1->start = s;
			mp1->size = sz;
		}
		/*
		 * In future we should be able to specify both allocated
		 * and free.
		 */
		uvm_page_physload(
			atop(mp->start),
			atop(mp->start+mp->size),
			atop(mp->start),
			atop(mp->start+mp->size), 0);
	}

#if 0
	/* finally, free up any space that valloc did not use */
	prom_unmap_virt((vaddr_t)ekdata, roundup(ekdata, 4*MEG) - ekdata);
	if (ekdatap < roundup(kdatap, 4*MEG))) {
		uvm_page_physload(atop(ekdatap),
			atop(roundup(ekdatap, (4*MEG))),
			atop(ekdatap),
			atop(roundup(ekdatap, (4*MEG))), 0);
	}
#endif

#ifdef DEBUG
	if (pmapdebug & PDB_BOOT) {
		/* print out mem list */
		prom_printf("Available physical memory after cleanup:\r\n");
		for (mp = avail; mp->size; mp++) {
			prom_printf("avail start %lx size %lx\r\n",
				    (long)mp->start, (long)mp->size);
		}
		prom_printf("End of available physical memory after cleanup\r\n");
	}
#endif
	/*
	 * Allocate and clear out pmap_kernel()->pm_segs[]
	 */
	pmap_kernel()->pm_refs = 1;
	pmap_kernel()->pm_ctx = 0;
	{
		paddr_t newp;

		do {
			pmap_get_page(&newp, NULL, pmap_kernel());
		} while (!newp); /* Throw away page zero */
		pmap_kernel()->pm_segs=(int64_t *)(u_long)newp;
		pmap_kernel()->pm_physaddr = newp;
		/* mark kernel context as busy */
		((paddr_t*)ctxbusy)[0] = pmap_kernel()->pm_physaddr;
	}
	/*
	 * finish filling out kernel pmap.
	 */

	BDPRINTF(PDB_BOOT, ("pmap_kernel()->pm_physaddr = %lx\r\n",
	    (long)pmap_kernel()->pm_physaddr));
	/*
	 * Tell pmap about our mesgbuf -- Hope this works already
	 */
#ifdef DEBUG
	BDPRINTF(PDB_BOOT1, ("Calling consinit()\r\n"));
	if (pmapdebug & PDB_BOOT1) consinit();
	BDPRINTF(PDB_BOOT1, ("Inserting mesgbuf into pmap_kernel()\r\n"));
#endif
	/* it's not safe to call pmap_enter so we need to do this ourselves */
	va = (vaddr_t)msgbufp;
	prom_map_phys(phys_msgbuf, msgbufsiz, (vaddr_t)msgbufp, -1);
	while (msgbufsiz) {
		data = TSB_DATA(0 /* global */,
			PGSZ_8K,
			phys_msgbuf,
			1 /* priv */,
			1 /* Write */,
			1 /* Cacheable */,
			FORCE_ALIAS /* ALIAS -- Disable D$ */,
			1 /* valid */,
			0 /* IE */);
		pmap_enter_kpage(va, data);
		va += PAGE_SIZE;
		msgbufsiz -= PAGE_SIZE;
		phys_msgbuf += PAGE_SIZE;
	}
	BDPRINTF(PDB_BOOT1, ("Done inserting mesgbuf into pmap_kernel()\r\n"));

	BDPRINTF(PDB_BOOT1, ("Inserting PROM mappings into pmap_kernel()\r\n"));
	data = 0;
	if (CPU_ISSUN4U || CPU_ISSUN4US)
		data = SUN4U_TLB_EXEC;
	for (i = 0; i < prom_map_size; i++) {
		if (prom_map[i].vstart && ((prom_map[i].vstart>>32) == 0)) {
			for (j = 0; j < prom_map[i].vsize; j += NBPG) {
				int k;

				for (k = 0; page_size_map[k].mask; k++) {
					if (((prom_map[i].vstart |
					      prom_map[i].tte) &
					      page_size_map[k].mask) == 0 &&
					      page_size_map[k].mask <
					      prom_map[i].vsize)
						break;
				}
				/* Enter PROM map into pmap_kernel() */
				pmap_enter_kpage(prom_map[i].vstart + j,
					(prom_map[i].tte + j)|data|
					page_size_map[k].code);
			}
		}
	}
	BDPRINTF(PDB_BOOT1, ("Done inserting PROM mappings into pmap_kernel()\r\n"));

	/*
	 * Fix up start of kernel heap.
	 */
	vmmap = (vaddr_t)roundup(ekdata, 4*MEG);
	/* Let's keep 1 page of redzone after the kernel */
	vmmap += NBPG;
	{
		extern vaddr_t u0[2];
		extern struct pcb* proc0paddr;
		extern void main(void);
		paddr_t pa;

		/* Initialize all the pointers to u0 */
		u0[0] = vmmap;
		/* Allocate some VAs for u0 */
		u0[1] = vmmap + 2*USPACE;

		BDPRINTF(PDB_BOOT1,
			("Inserting stack 0 into pmap_kernel() at %p\r\n",
				vmmap));

		while (vmmap < u0[1]) {
			int64_t data;

			pmap_get_page(&pa, NULL, pmap_kernel());
			prom_map_phys(pa, NBPG, vmmap, -1);
			data = TSB_DATA(0 /* global */,
				PGSZ_8K,
				pa,
				1 /* priv */,
				1 /* Write */,
				1 /* Cacheable */,
				FORCE_ALIAS /* ALIAS -- Disable D$ */,
				1 /* valid */,
				0 /* IE */);
			pmap_enter_kpage(vmmap, data);
			vmmap += NBPG;
		}
		BDPRINTF(PDB_BOOT1,
			 ("Done inserting stack 0 into pmap_kernel()\r\n"));

		/* Now map in and initialize our cpu_info structure */
#ifdef DIAGNOSTIC
		vmmap += NBPG; /* redzone -- XXXX do we need one? */
#endif
		intstk = vmmap = roundup(vmmap, 64*KB);
		cpus = (struct cpu_info *)(intstk + CPUINFO_VA - INTSTACK);

		BDPRINTF(PDB_BOOT1,
			("Inserting cpu_info into pmap_kernel() at %p\r\n",
				 cpus));
		/* Now map in all 8 pages of cpu_info */
		pa = cpu0paddr;
		prom_map_phys(pa, 64*KB, vmmap, -1);
		/*
		 * Also map it in as the interrupt stack.
		 * This lets the PROM see this if needed.
		 *
		 * XXXX locore.s does not flush these mappings
		 * before installing the locked TTE.
		 */
		prom_map_phys(pa, 64*KB, CPUINFO_VA, -1);
		for (i=0; i<8; i++) {
			int64_t data;

			data = TSB_DATA(0 /* global */,
				PGSZ_8K,
				pa,
				1 /* priv */,
				1 /* Write */,
				1 /* Cacheable */,
				FORCE_ALIAS /* ALIAS -- Disable D$ */,
				1 /* valid */,
				0 /* IE */);
			pmap_enter_kpage(vmmap, data);
			vmmap += NBPG;
			pa += NBPG;
		}
		BDPRINTF(PDB_BOOT1, ("Initializing cpu_info\r\n"));

		/* Initialize our cpu_info structure */
		bzero((void *)intstk, 8*NBPG);
		cpus->ci_self = cpus;
		cpus->ci_next = NULL; /* Redundant, I know. */
		cpus->ci_curproc = &proc0;
		cpus->ci_cpcb = (struct pcb *)u0[0]; /* Need better source */
		cpus->ci_upaid = cpu_myid();
		cpus->ci_number = 0;
		cpus->ci_flags = CPUF_RUNNING;
		cpus->ci_fpproc = NULL;
		cpus->ci_spinup = main; /* Call main when we're running. */
		cpus->ci_initstack = (void *)u0[1];
		cpus->ci_paddr = cpu0paddr;
#ifdef SUN4V
		cpus->ci_mmfsa = cpu0paddr;
#endif
		proc0paddr = cpus->ci_cpcb;

		cpu0paddr += 64 * KB;

		/* The rest will be done at CPU attach time. */
		BDPRINTF(PDB_BOOT1,
			 ("Done inserting cpu_info into pmap_kernel()\r\n"));
	}

	vmmap = (vaddr_t)reserve_dumppages((caddr_t)(u_long)vmmap);
	BDPRINTF(PDB_BOOT1, ("Finished pmap_bootstrap()\r\n"));

	pmap_bootstrap_cpu(cpus->ci_paddr);
}

void sun4u_bootstrap_cpu(paddr_t);
void sun4v_bootstrap_cpu(paddr_t);

void
pmap_bootstrap_cpu(paddr_t intstack)
{
	if (CPU_ISSUN4V)
		sun4v_bootstrap_cpu(intstack);
	else
		sun4u_bootstrap_cpu(intstack);
}

extern void sun4u_set_tsbs(void);

void
sun4u_bootstrap_cpu(paddr_t intstack)
{
	u_int64_t data;
	paddr_t pa;
	vaddr_t va;
	int index;
	int impl;

	impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;

	/*
	 * Establish the 4MB locked mappings for kernel data and text.
	 *
	 * The text segment needs to be mapped into the DTLB too,
	 * because of .rodata.
	 */

	index = 15; /* XXX */
	for (va = ktext, pa = ktextp; va < ektext; va += 4*MEG, pa += 4*MEG) {
		data = SUN4U_TSB_DATA(0, PGSZ_4M, pa, 1, 0, 1, FORCE_ALIAS, 1, 0);
		data |= SUN4U_TLB_L;
		prom_itlb_load(index, data, va);
		prom_dtlb_load(index, data, va);
		index--;
	}

	for (va = kdata, pa = kdatap; va < ekdata; va += 4*MEG, pa += 4*MEG) {
		data = SUN4U_TSB_DATA(0, PGSZ_4M, pa, 1, 1, 1, FORCE_ALIAS, 1, 0);
		data |= SUN4U_TLB_L;
		prom_dtlb_load(index, data, va);
		index--;
	}

#ifdef MULTIPROCESSOR
	if (impl >= IMPL_OLYMPUS_C && impl <= IMPL_JUPITER) {
		/*
		 * On SPARC64-VI and SPARC64-VII processors, the MMU is
		 * shared between threads, so we can't establish a locked
		 * mapping for the interrupt stack since the mappings would
		 * conflict.  Instead we stick the address in a scratch
		 * register, like we do for sun4v.
		 */
		pa = intstack + (CPUINFO_VA - INTSTACK);
		pa += offsetof(struct cpu_info, ci_self);
		va = ldxa(pa, ASI_PHYS_CACHED);
		stxa(0x00, ASI_SCRATCH, va);

		if ((CPU_JUPITERID % 2) == 1)
			index--;

		data = SUN4U_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, FORCE_ALIAS, 1, 0);
		data |= SUN4U_TLB_L;
		prom_dtlb_load(index, data, va - (CPUINFO_VA - INTSTACK));

		sun4u_set_tsbs();
		return;
	}
#endif

	/*
	 * Establish the 64KB locked mapping for the interrupt stack.
	 */

	data = SUN4U_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, FORCE_ALIAS, 1, 0);
	data |= SUN4U_TLB_L;
	prom_dtlb_load(index, data, INTSTACK);

	sun4u_set_tsbs();
}

void
sun4v_bootstrap_cpu(paddr_t intstack)
{
#ifdef SUN4V
	u_int64_t data;
	paddr_t pa;
	vaddr_t va;
	int err;

	/*
	 * Establish the 4MB locked mappings for kernel data and text.
	 *
	 * The text segment needs to be mapped into the DTLB too,
	 * because of .rodata.
	 */

	for (va = ktext, pa = ktextp; va < ektext; va += 4*MEG, pa += 4*MEG) {
		data = SUN4V_TSB_DATA(0, PGSZ_4M, pa, 1, 0, 1, 0, 1, 0);
		data |= SUN4V_TLB_X;
		err = hv_mmu_map_perm_addr(va, data, MAP_ITLB|MAP_DTLB);
		if (err != H_EOK)
			prom_printf("err: %d\r\n", err);
	}

	for (va = kdata, pa = kdatap; va < ekdata; va += 4*MEG, pa += 4*MEG) {
		data = SUN4V_TSB_DATA(0, PGSZ_4M, pa, 1, 1, 1, 0, 1, 0);
		err = hv_mmu_map_perm_addr(va, data, MAP_DTLB);
		if (err != H_EOK)
			prom_printf("err: %d\r\n", err);
	}

#ifndef MULTIPROCESSOR
	/*
	 * Establish the 64KB locked mapping for the interrupt stack.
	 */
	data = SUN4V_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, 0, 1, 0);
	err = hv_mmu_map_perm_addr(INTSTACK, data, MAP_DTLB);
	if (err != H_EOK)
		prom_printf("err: %d\r\n", err);
#else
	pa = intstack + (CPUINFO_VA - INTSTACK);
	pa += offsetof(struct cpu_info, ci_self);
	stxa(0x00, ASI_SCRATCHPAD, ldxa(pa, ASI_PHYS_CACHED));
#endif

	stxa(0x10, ASI_SCRATCHPAD, intstack + (CPUINFO_VA - INTSTACK));

	err = hv_mmu_tsb_ctx0(1, (paddr_t)tsb_desc + kdatap - kdata);
	if (err != H_EOK)
		prom_printf("err: %d\r\n", err);
	err = hv_mmu_tsb_ctxnon0(1, (paddr_t)tsb_desc + kdatap - kdata);
	if (err != H_EOK)
		prom_printf("err: %d\r\n", err);
#endif
}

/*
 * Initialize anything else for pmap handling.
 * Called during uvm_init().
 */
void
pmap_init(void)
{
	BDPRINTF(PDB_BOOT1, ("pmap_init()\r\n"));
	if (PAGE_SIZE != NBPG)
		panic("pmap_init: CLSIZE!=1");

	/* Setup a pool for additional pvlist structures */
	pool_init(&pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pv_entry", NULL);
	pool_setipl(&pv_pool, IPL_VM);
	pool_init(&pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pool_allocator_nointr);
}

/* Start of non-cachable physical memory on UltraSPARC-III. */
#define VM_MAXPHYS_ADDRESS	((vaddr_t)0x0000040000000000L)

static vaddr_t kbreak; /* End of kernel VA */

/*
 * How much virtual space is available to the kernel?
 */
void
pmap_virtual_space(vaddr_t *start, vaddr_t *end)
{
	/*
	 * Make sure virtual memory and physical memory don't overlap
	 * to avoid problems with ASI_PHYS_CACHED on UltraSPARC-III.
	 */
	if (vmmap < VM_MAXPHYS_ADDRESS)
		vmmap = VM_MAXPHYS_ADDRESS;

	/* Reserve two pages for pmap_copy_page && /dev/mem */
	*start = kbreak = (vaddr_t)(vmmap + 2*NBPG);
	*end = VM_MAX_KERNEL_ADDRESS;
	BDPRINTF(PDB_BOOT1, ("pmap_virtual_space: %x-%x\r\n", *start, *end));
}

/*
 * Preallocate kernel page tables to a specified VA.
 * This simply loops through the first TTE for each
 * page table from the beginning of the kernel pmap,
 * reads the entry, and if the result is
 * zero (either invalid entry or no page table) it stores
 * a zero there, populating page tables in the process.
 * This is not the most efficient technique but i don't
 * expect it to be called that often.
 */
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
	paddr_t pg;
	struct pmap *pm = pmap_kernel();

	if (maxkvaddr >= VM_MAX_KERNEL_ADDRESS) {
		printf("WARNING: cannot extend kernel pmap beyond %p to %p\n",
		       (void *)VM_MAX_KERNEL_ADDRESS, (void *)maxkvaddr);
		return (kbreak);
	}

	/* Align with the start of a page table */
	for (kbreak &= (-1<<PDSHIFT); kbreak < maxkvaddr;
	     kbreak += (1<<PDSHIFT)) {
		if (pseg_get(pm, kbreak))
			continue;

		pg = 0;
		while (pseg_set(pm, kbreak, 0, pg) == 1) {
			pg = 0;
			pmap_get_page(&pg, "growk", pm);
		}

	}

	return (kbreak);
}

/*
 * Create and return a physical map.
 */
struct pmap *
pmap_create(void)
{
	struct pmap *pm;

	pm = pool_get(&pmap_pool, PR_WAITOK | PR_ZERO);

	mtx_init(&pm->pm_mtx, IPL_VM);
	pm->pm_refs = 1;
	pmap_get_page(&pm->pm_physaddr, "pmap_create", pm);
	pm->pm_segs = (int64_t *)(u_long)pm->pm_physaddr;
	ctx_alloc(pm);

	return (pm);
}

/*
 * Add a reference to the given pmap.
 */
void
pmap_reference(struct pmap *pm)
{
	atomic_inc_int(&pm->pm_refs);
}

/*
 * Retire the given pmap from service.
 * Should only be called if the map contains no valid mappings.
 */
void
pmap_destroy(struct pmap *pm)
{
	if (atomic_dec_int_nv(&pm->pm_refs) == 0) {
		pmap_release(pm);
		pool_put(&pmap_pool, pm);
	}
}

/*
 * Release any resources held by the given physical map.
 * Called when a pmap initialized by pmap_pinit is being released.
 */
void
pmap_release(struct pmap *pm)
{
	int i, j, k;
	paddr_t *pdir, *ptbl, tmp;

#ifdef DIAGNOSTIC
	if(pm == pmap_kernel())
		panic("pmap_release: releasing pmap_kernel()");
#endif

	mtx_enter(&pm->pm_mtx);
	for(i=0; i<STSZ; i++) {
		paddr_t psegentp = (paddr_t)(u_long)&pm->pm_segs[i];
		if((pdir = (paddr_t *)(u_long)ldxa((vaddr_t)psegentp,
		    ASI_PHYS_CACHED))) {
			for (k=0; k<PDSZ; k++) {
				paddr_t pdirentp = (paddr_t)(u_long)&pdir[k];
				if ((ptbl = (paddr_t *)(u_long)ldxa(
					(vaddr_t)pdirentp, ASI_PHYS_CACHED))) {
					for (j=0; j<PTSZ; j++) {
						int64_t data;
						paddr_t pa;
						pv_entry_t pv;

						data  = ldxa((vaddr_t)&ptbl[j],
							ASI_PHYS_CACHED);
						if (!(data & TLB_V))
							continue;
						pa = data & TLB_PA_MASK;
						pv = pa_to_pvh(pa);
						if (pv != NULL) {
							printf("pmap_release: pm=%p page %llx still in use\n", pm,
							       (unsigned long long)(((u_int64_t)i<<STSHIFT)|((u_int64_t)k<<PDSHIFT)|((u_int64_t)j<<PTSHIFT)));
							Debugger();
						}
					}
					stxa(pdirentp, ASI_PHYS_CACHED, 0);
					pmap_free_page((paddr_t)ptbl, pm);
				}
			}
			stxa(psegentp, ASI_PHYS_CACHED, 0);
			pmap_free_page((paddr_t)pdir, pm);
		}
	}
	tmp = (paddr_t)(u_long)pm->pm_segs;
	pm->pm_segs = NULL;
	pmap_free_page(tmp, pm);
	mtx_leave(&pm->pm_mtx);
	ctx_free(pm);
}

/*
 * Copy the range specified by src_addr/len
 * from the source map to the range dst_addr/len
 * in the destination map.
 *
 * This routine is only advisory and need not do anything.
 */
void
pmap_copy(struct pmap *dst_pmap, struct pmap *src_pmap, vaddr_t dst_addr,
    vsize_t len, vaddr_t src_addr)
{
}

/*
 * Garbage collects the physical map system for
 * pages which are no longer used.
 * Success need not be guaranteed -- that is, there
 * may well be pages which are not referenced, but
 * others may be collected.
 * Called by the pageout daemon when pages are scarce.
 */
void
pmap_collect(struct pmap *pm)
{
#if 1
	int i, j, k, n, m, s;
	paddr_t *pdir, *ptbl;
	/* This is a good place to scan the pmaps for page tables with
	 * no valid mappings in them and free them. */

	/* NEVER GARBAGE COLLECT THE KERNEL PMAP */
	if (pm == pmap_kernel())
		return;

	s = splvm();
	for (i=0; i<STSZ; i++) {
		if ((pdir = (paddr_t *)(u_long)ldxa((vaddr_t)&pm->pm_segs[i], ASI_PHYS_CACHED))) {
			m = 0;
			for (k=0; k<PDSZ; k++) {
				if ((ptbl = (paddr_t *)(u_long)ldxa((vaddr_t)&pdir[k], ASI_PHYS_CACHED))) {
					m++;
					n = 0;
					for (j=0; j<PTSZ; j++) {
						int64_t data = ldxa((vaddr_t)&ptbl[j], ASI_PHYS_CACHED);
						if (data&TLB_V)
							n++;
					}
					if (!n) {
						/* Free the damn thing */
						stxa((paddr_t)(u_long)&pdir[k], ASI_PHYS_CACHED, 0);
						pmap_free_page((paddr_t)ptbl, pm);
					}
				}
			}
			if (!m) {
				/* Free the damn thing */
				stxa((paddr_t)(u_long)&pm->pm_segs[i], ASI_PHYS_CACHED, 0);
				pmap_free_page((paddr_t)pdir, pm);
			}
		}
	}
	splx(s);
#endif
}

void
pmap_zero_page(struct vm_page *pg)
{
	pmap_zero_phys(VM_PAGE_TO_PHYS(pg));
}

void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
	paddr_t src = VM_PAGE_TO_PHYS(srcpg);
	paddr_t dst = VM_PAGE_TO_PHYS(dstpg);

	pmap_copy_phys(src, dst);
}

/*
 * Activate the address space for the specified process.  If the
 * process is the current process, load the new MMU context.
 */
void
pmap_activate(struct proc *p)
{
	struct pmap *pmap = p->p_vmspace->vm_map.pmap;
	int s;

	/*
	 * This is essentially the same thing that happens in cpu_switch()
	 * when the newly selected process is about to run, except that we
	 * have to make sure to clean the register windows before we set
	 * the new context.
	 */

	s = splvm();
	if (p == curproc) {
		write_user_windows();
		if (pmap->pm_ctx == 0)
			ctx_alloc(pmap);
		if (CPU_ISSUN4V)
			stxa(CTX_SECONDARY, ASI_MMU_CONTEXTID, pmap->pm_ctx);
		else
			stxa(CTX_SECONDARY, ASI_DMMU, pmap->pm_ctx);
	}
	splx(s);
}

/*
 * Deactivate the address space of the specified process.
 */
void
pmap_deactivate(struct proc *p)
{
}

/*
 * pmap_kenter_pa:		[ INTERFACE ]
 *
 *	Enter a va -> pa mapping into the kernel pmap without any
 *	physical->virtual tracking.
 *
 *	Note: no locking is necessary in this function.
 */
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	pte_t tte;
	struct pmap *pm = pmap_kernel();
	int s;

	KDASSERT(va < INTSTACK || va > EINTSTACK);
	KDASSERT(va < kdata || va > ekdata);

#ifdef DIAGNOSTIC
	if (pa & (PMAP_NVC|PMAP_NC|PMAP_LITTLE))
		panic("pmap_kenter_pa: illegal cache flags %ld", pa);
#endif

	/*
	 * Construct the TTE.
	 */
	s = splvm();
	tte.tag = TSB_TAG(0,pm->pm_ctx,va);
	if (CPU_ISSUN4V) {
		tte.data = SUN4V_TSB_DATA(0, PGSZ_8K, pa, 1 /* Privileged */,
		    (PROT_WRITE & prot), 1, 0, 1, 0);
		/*
		 * We don't track modification on kenter mappings.
		 */
		if (prot & PROT_WRITE)
			tte.data |= SUN4V_TLB_REAL_W|SUN4V_TLB_W;
		if (prot & PROT_EXEC)
			tte.data |= SUN4V_TLB_EXEC;
		tte.data |= SUN4V_TLB_TSB_LOCK;	/* wired */
	} else {
		tte.data = SUN4U_TSB_DATA(0, PGSZ_8K, pa, 1 /* Privileged */,
		    (PROT_WRITE & prot), 1, 0, 1, 0);
		/*
		 * We don't track modification on kenter mappings.
		 */
		if (prot & PROT_WRITE)
			tte.data |= SUN4U_TLB_REAL_W|SUN4U_TLB_W;
		if (prot & PROT_EXEC)
			tte.data |= SUN4U_TLB_EXEC;
		tte.data |= SUN4U_TLB_TSB_LOCK;	/* wired */
	}
	KDASSERT((tte.data & TLB_NFO) == 0);

	/* Kernel page tables are pre-allocated. */
	if (pseg_set(pmap_kernel(), va, tte.data, 0) != 0)
		panic("pmap_kenter_pa: no pseg");

	pmap_kernel()->pm_stats.resident_count++;

	splx(s);
	/* this is correct */
	dcache_flush_page(pa);
}

/*
 * pmap_kremove:		[ INTERFACE ]
 *
 *	Remove a mapping entered with pmap_kenter_pa() starting at va,
 *	for size bytes (assumed to be page rounded).
 */
void
pmap_kremove(vaddr_t va, vsize_t size)
{
	struct pmap *pm = pmap_kernel();
	int64_t data;
	int s;

	KDASSERT(va < INTSTACK || va > EINTSTACK);
	KDASSERT(va < kdata || va > ekdata);

	s = splvm();
	while (size >= NBPG) {
		/*
		 * Is this part of the permanent 4MB mapping?
		 */
#ifdef DIAGNOSTIC
		if (pm == pmap_kernel() &&
			(va >= ktext && va < roundup(ekdata, 4*MEG)))
			panic("pmap_kremove: va=%08x in locked TLB",
				(u_int)va);
#endif
		/* Shouldn't need to do this if the entry's not valid. */
		if ((data = pseg_get(pm, va))) {
			/* We need to flip the valid bit and clear the access statistics. */
			if (pseg_set(pm, va, 0, 0)) {
				printf("pmap_kremove: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}

			pmap_kernel()->pm_stats.resident_count--;
			tsb_invalidate(pm->pm_ctx, va);
			/* Here we assume nothing can get into the TLB unless it has a PTE */
			tlb_flush_pte(va, pm->pm_ctx);
		}
		va += NBPG;
		size -= NBPG;
	}
	splx(s);
}

/*
 * Insert physical page at pa into the given pmap at virtual address va.
 * Supports 64-bit pa so we can map I/O space.
 */
int
pmap_enter(struct pmap *pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
	pte_t tte;
	paddr_t pg;
	int aliased = 0;
	pv_entry_t pv, npv;
	int size = 0; /* PMAP_SZ_TO_TTE(pa); */
	boolean_t wired = (flags & PMAP_WIRED) != 0;

	/*
	 * Is this part of the permanent mappings?
	 */
	KDASSERT(pm != pmap_kernel() || va < INTSTACK || va > EINTSTACK);
	KDASSERT(pm != pmap_kernel() || va < kdata || va > ekdata);

	npv = pool_get(&pv_pool, PR_NOWAIT);

	/*
	 * XXXX If a mapping at this address already exists, remove it.
	 */
	mtx_enter(&pm->pm_mtx);
	tte.data = pseg_get(pm, va);
	if (tte.data & TLB_V) {
		mtx_leave(&pm->pm_mtx);
		pmap_remove(pm, va, va + NBPG-1);
		mtx_enter(&pm->pm_mtx);
		tte.data = pseg_get(pm, va);
	}

	/*
	 * Construct the TTE.
	 */
	pv = pa_to_pvh(pa);
	if (pv != NULL) {
		struct vm_page *pg = PHYS_TO_VM_PAGE(pa);

		mtx_enter(&pg->mdpage.pvmtx);
		aliased = (pv->pv_va & (PV_ALIAS|PV_NVC));
#ifdef DIAGNOSTIC
		if ((flags & PROT_MASK) & ~prot)
			panic("pmap_enter: access_type exceeds prot");
#endif
		/* If we don't have the traphandler do it, set the ref/mod bits now */
		if (flags & PROT_MASK)
			pv->pv_va |= PV_REF;
		if (flags & PROT_WRITE)
			pv->pv_va |= PV_MOD;
		pv->pv_va |= pmap_tte2flags(tte.data);
		mtx_leave(&pg->mdpage.pvmtx);
	} else {
		aliased = 0;
	}
	if (pa & PMAP_NVC)
		aliased = 1;
#ifdef NO_VCACHE
	aliased = 1; /* Disable D$ */
#endif
	if (CPU_ISSUN4V) {
		tte.data = SUN4V_TSB_DATA(0, size, pa, pm == pmap_kernel(),
		    (flags & PROT_WRITE), (!(pa & PMAP_NC)),
		    aliased, 1, (pa & PMAP_LITTLE));
		if (prot & PROT_WRITE)
			tte.data |= SUN4V_TLB_REAL_W;
		if (prot & PROT_EXEC)
			tte.data |= SUN4V_TLB_EXEC;
		if (wired)
			tte.data |= SUN4V_TLB_TSB_LOCK;
	} else {
		tte.data = SUN4U_TSB_DATA(0, size, pa, pm == pmap_kernel(),
		    (flags & PROT_WRITE), (!(pa & PMAP_NC)),
		    aliased, 1, (pa & PMAP_LITTLE));
		if (prot & PROT_WRITE)
			tte.data |= SUN4U_TLB_REAL_W;
		if (prot & PROT_EXEC)
			tte.data |= SUN4U_TLB_EXEC;
		if (wired)
			tte.data |= SUN4U_TLB_TSB_LOCK;
	}
	KDASSERT((tte.data & TLB_NFO) == 0);

	pg = 0;
	while (pseg_set(pm, va, tte.data, pg) == 1) {
		pg = 0;
		if (!pmap_get_page(&pg, NULL, pm)) {
			if ((flags & PMAP_CANFAIL) == 0)
				panic("pmap_enter: no memory");
			mtx_leave(&pm->pm_mtx);
			if (npv != NULL)
				pool_put(&pv_pool, npv);
			return (ENOMEM);
		}
	}

	if (pv != NULL)
		npv = pmap_enter_pv(pm, npv, va, pa);
	pm->pm_stats.resident_count++;
	mtx_leave(&pm->pm_mtx);
	if (pm->pm_ctx || pm == pmap_kernel()) {
		tsb_invalidate(pm->pm_ctx, va);

		/* Force reload -- protections may be changed */
		tlb_flush_pte(va, pm->pm_ctx);
	}
	/* this is correct */
	dcache_flush_page(pa);

	if (npv != NULL)
		pool_put(&pv_pool, npv);

	/* We will let the fast mmu miss interrupt load the new translation */
	return 0;
}

/*
 * Remove the given range of mapping entries.
 */
void
pmap_remove(struct pmap *pm, vaddr_t va, vaddr_t endva)
{
	pv_entry_t pv, freepvs = NULL;
	int flush = 0;
	int64_t data;
	vaddr_t flushva = va;

	/*
	 * In here we should check each pseg and if there are no more entries,
	 * free it.  It's just that linear scans of 8K pages gets expensive.
	 */

	KDASSERT(pm != pmap_kernel() || endva < INTSTACK || va > EINTSTACK);
	KDASSERT(pm != pmap_kernel() || endva < kdata || va > ekdata);

	mtx_enter(&pm->pm_mtx);

	/* Now do the real work */
	while (va < endva) {
		/*
		 * Is this part of the permanent 4MB mapping?
		 */
#ifdef DIAGNOSTIC
		if (pm == pmap_kernel() && va >= ktext &&
			va < roundup(ekdata, 4*MEG))
			panic("pmap_remove: va=%08x in locked TLB", (u_int)va);
#endif
		/* We don't really need to do this if the valid bit is not set... */
		if ((data = pseg_get(pm, va)) && (data & TLB_V) != 0) {
			paddr_t entry;

			flush |= 1;
			/* First remove it from the pv_table */
			entry = (data & TLB_PA_MASK);
			pv = pa_to_pvh(entry);
			if (pv != NULL) {
				pv = pmap_remove_pv(pm, va, entry);
				if (pv != NULL) {
					pv->pv_next = freepvs;
					freepvs = pv;
				}
			}
			/* We need to flip the valid bit and clear the access statistics. */
			if (pseg_set(pm, va, 0, 0)) {
				printf("pmap_remove: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}
			pm->pm_stats.resident_count--;
			if (!pm->pm_ctx && pm != pmap_kernel())
				continue;
			tsb_invalidate(pm->pm_ctx, va);
			/* Here we assume nothing can get into the TLB unless it has a PTE */
			tlb_flush_pte(va, pm->pm_ctx);
		}
		va += NBPG;
	}

	mtx_leave(&pm->pm_mtx);

	while ((pv = freepvs) != NULL) {
		freepvs = pv->pv_next;
		pool_put(&pv_pool, pv);
	}

	if (flush)
		cache_flush_virt(flushva, endva - flushva);
}

/*
 * Change the protection on the specified range of this pmap.
 */
void
pmap_protect(struct pmap *pm, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
	paddr_t pa;
	pv_entry_t pv;
	int64_t data;

	KDASSERT(pm != pmap_kernel() || eva < INTSTACK || sva > EINTSTACK);
	KDASSERT(pm != pmap_kernel() || eva < kdata || sva > ekdata);

	if ((prot & (PROT_WRITE | PROT_EXEC)) ==
	    (PROT_WRITE | PROT_EXEC))
		return;

	if (prot == PROT_NONE) {
		pmap_remove(pm, sva, eva);
		return;
	}

	mtx_enter(&pm->pm_mtx);
	sva = sva & ~PGOFSET;
	while (sva < eva) {
		/*
		 * Is this part of the permanent 4MB mapping?
		 */
		if (pm == pmap_kernel() && sva >= ktext &&
			sva < roundup(ekdata, 4*MEG)) {
			prom_printf("pmap_protect: va=%08x in locked TLB\r\n", sva);
			OF_enter();
			mtx_leave(&pm->pm_mtx);
			return;
		}

		if (((data = pseg_get(pm, sva))&TLB_V) /*&& ((data&TLB_TSB_LOCK) == 0)*/) {
			pa = data & TLB_PA_MASK;
			pv = pa_to_pvh(pa);
			if (pv != NULL) {
				struct vm_page *pg = PHYS_TO_VM_PAGE(pa);

				/* Save REF/MOD info */
				mtx_enter(&pg->mdpage.pvmtx);
				pv->pv_va |= pmap_tte2flags(data);
				mtx_leave(&pg->mdpage.pvmtx);
			}
			/* Just do the pmap and TSB, not the pv_list */
			if (CPU_ISSUN4V) {
				if ((prot & PROT_WRITE) == 0)
					data &= ~(SUN4V_TLB_W|SUN4V_TLB_REAL_W);
				if ((prot & PROT_EXEC) == 0)
					data &= ~(SUN4V_TLB_EXEC);
			} else {
				if ((prot & PROT_WRITE) == 0)
					data &= ~(SUN4U_TLB_W|SUN4U_TLB_REAL_W);
				if ((prot & PROT_EXEC) == 0)
					data &= ~(SUN4U_TLB_EXEC);
			}
			KDASSERT((data & TLB_NFO) == 0);
			if (pseg_set(pm, sva, data, 0)) {
				printf("pmap_protect: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}

			if (!pm->pm_ctx && pm != pmap_kernel())
				continue;
			tsb_invalidate(pm->pm_ctx, sva);
			tlb_flush_pte(sva, pm->pm_ctx);
		}
		sva += NBPG;
	}
	mtx_leave(&pm->pm_mtx);
}

/*
 * Extract the physical page address associated
 * with the given map/virtual_address pair.
 */
boolean_t
pmap_extract(struct pmap *pm, vaddr_t va, paddr_t *pap)
{
	paddr_t pa;

	if (pm == pmap_kernel() && va >= kdata &&
		va < roundup(ekdata, 4*MEG)) {
		/* Need to deal w/locked TLB entry specially. */
		pa = (paddr_t) (kdatap - kdata + va);
	} else if( pm == pmap_kernel() && va >= ktext && va < ektext ) {
		/* Need to deal w/locked TLB entry specially. */
		pa = (paddr_t) (ktextp - ktext + va);
	} else if (pm == pmap_kernel() && va >= INTSTACK && va < EINTSTACK) {
		pa = curcpu()->ci_paddr + va - INTSTACK;
	} else {
		int s;

		s = splvm();
		pa = (pseg_get(pm, va) & TLB_PA_MASK) + (va & PGOFSET);
		splx(s);
	}
	if (pa == 0)
		return (FALSE);
	if (pap != NULL)
		*pap = pa;
	return (TRUE);
}

/*
 * Return the number bytes that pmap_dumpmmu() will dump.
 */
int
pmap_dumpsize(void)
{
	int	sz;

	sz = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t));
	sz += memsize * sizeof(phys_ram_seg_t);

	return btodb(sz + DEV_BSIZE - 1);
}

/*
 * Write the mmu contents to the dump device.
 * This gets appended to the end of a crash dump since
 * there is no in-core copy of kernel memory mappings on a 4/4c machine.
 *
 * Write the core dump headers and MD data to the dump device.
 * We dump the following items:
 *
 *	kcore_seg_t		 MI header defined in <sys/kcore.h>)
 *	cpu_kcore_hdr_t		 MD header defined in <machine/kcore.h>)
 *	phys_ram_seg_t[memsize]  physical memory segments
 */
int
pmap_dumpmmu(int (*dump)(dev_t, daddr_t, caddr_t, size_t), daddr_t blkno)
{
	kcore_seg_t	*kseg;
	cpu_kcore_hdr_t	*kcpu;
	phys_ram_seg_t	memseg;
	register int	error = 0;
	register int	i, memsegoffset;
	int		buffer[dbtob(1) / sizeof(int)];
	int		*bp, *ep;

#define EXPEDITE(p,n) do {						\
	int *sp = (int *)(p);						\
	int sz = (n);							\
	while (sz > 0) {						\
		*bp++ = *sp++;						\
		if (bp >= ep) {						\
			error = (*dump)(dumpdev, blkno,			\
					(caddr_t)buffer, dbtob(1));	\
			if (error != 0)					\
				return (error);				\
			++blkno;					\
			bp = buffer;					\
		}							\
		sz -= 4;						\
	}								\
} while (0)

	/* Setup bookkeeping pointers */
	bp = buffer;
	ep = &buffer[sizeof(buffer) / sizeof(buffer[0])];

	/* Fill in MI segment header */
	kseg = (kcore_seg_t *)bp;
	CORE_SETMAGIC(*kseg, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
	kseg->c_size = dbtob(pmap_dumpsize()) - ALIGN(sizeof(kcore_seg_t));

	/* Fill in MD segment header (interpreted by MD part of libkvm) */
	kcpu = (cpu_kcore_hdr_t *)((long)bp + ALIGN(sizeof(kcore_seg_t)));
	kcpu->cputype = CPU_SUN4U;
	kcpu->kernbase = (u_int64_t)KERNBASE;
	kcpu->cpubase = (u_int64_t)CPUINFO_VA;

	/* Describe the locked text segment */
	kcpu->ktextbase = (u_int64_t)ktext;
	kcpu->ktextp = (u_int64_t)ktextp;
	kcpu->ktextsz = (u_int64_t)(roundup(ektextp, 4*MEG) - ktextp);

	/* Describe locked data segment */
	kcpu->kdatabase = (u_int64_t)kdata;
	kcpu->kdatap = (u_int64_t)kdatap;
	kcpu->kdatasz = (u_int64_t)(roundup(ekdatap, 4*MEG) - kdatap);

	/* Now the memsegs */
	kcpu->nmemseg = memsize;
	kcpu->memsegoffset = memsegoffset = ALIGN(sizeof(cpu_kcore_hdr_t));

	/* Now we need to point this at our kernel pmap. */
	kcpu->nsegmap = STSZ;
	kcpu->segmapoffset = (u_int64_t)pmap_kernel()->pm_physaddr;

	/* Note: we have assumed everything fits in buffer[] so far... */
	bp = (int *)((long)kcpu + ALIGN(sizeof(cpu_kcore_hdr_t)));

	for (i = 0; i < memsize; i++) {
		memseg.start = mem[i].start;
		memseg.size = mem[i].size;
		EXPEDITE(&memseg, sizeof(phys_ram_seg_t));
	}

	if (bp != buffer)
		error = (*dump)(dumpdev, blkno++, (caddr_t)buffer, dbtob(1));

	return (error);
}

/*
 * Determine (non)existence of physical page
 */
int pmap_pa_exists(paddr_t pa)
{
	struct mem_region *mp;

	/* Just go through physical memory list & see if we're there */
	for (mp = mem; mp->size && mp->start <= pa; mp++)
		if (mp->start <= pa && mp->start + mp->size >= pa)
			return 1;
	return 0;
}

/*
 * Lookup the appropriate TSB entry.
 *
 * Here is the full official pseudo code:
 *
 */

#ifdef NOTYET
int64 GenerateTSBPointer(
 	int64 va,		/* Missing VA			*/
 	PointerType type,	/* 8K_POINTER or 16K_POINTER	*/
 	int64 TSBBase,		/* TSB Register[63:13] << 13	*/
 	Boolean split,		/* TSB Register[12]		*/
 	int TSBSize)		/* TSB Register[2:0]		*/
{
 	int64 vaPortion;
 	int64 TSBBaseMask;
 	int64 splitMask;

	/* TSBBaseMask marks the bits from TSB Base Reg		*/
	TSBBaseMask = 0xffffffffffffe000 <<
		(split? (TSBsize + 1) : TSBsize);

	/* Shift va towards lsb appropriately and		*/
	/* zero out the original va page offset			*/
	vaPortion = (va >> ((type == 8K_POINTER)? 9: 12)) &
		0xfffffffffffffff0;

	if (split) {
		/* There's only one bit in question for split	*/
		splitMask = 1 << (13 + TSBsize);
		if (type == 8K_POINTER)
			/* Make sure we're in the lower half	*/
			vaPortion &= ~splitMask;
		else
			/* Make sure we're in the upper half	*/
			vaPortion |= splitMask;
	}
	return (TSBBase & TSBBaseMask) | (vaPortion & ~TSBBaseMask);
}
#endif
/*
 * Of course, since we are not using a split TSB or variable page sizes,
 * we can optimize this a bit.
 *
 * The following only works for a unified 8K TSB.  It will find the slot
 * for that particular va and return it.  IT MAY BE FOR ANOTHER MAPPING!
 */
int
ptelookup_va(vaddr_t va)
{
	long tsbptr;
#define TSBBASEMASK	(0xffffffffffffe000LL<<tsbsize)

	tsbptr = (((va >> 9) & 0xfffffffffffffff0LL) & ~TSBBASEMASK );
	return (tsbptr/sizeof(pte_t));
}

/*
 * Do whatever is needed to sync the MOD/REF flags
 */

boolean_t
pmap_clear_modify(struct vm_page *pg)
{
	paddr_t pa = VM_PAGE_TO_PHYS(pg);
	int changed = 0;
	pv_entry_t pv;

	/* Clear all mappings */
	mtx_enter(&pg->mdpage.pvmtx);
	pv = pa_to_pvh(pa);
	if (pv->pv_va & PV_MOD)
		changed |= 1;
	pv->pv_va &= ~(PV_MOD);
	if (pv->pv_pmap != NULL) {
		for (; pv; pv = pv->pv_next) {
			int64_t data;

			/* First clear the mod bit in the PTE and make it R/O */
			data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);

			/* Need to both clear the modify and write bits */
			if (CPU_ISSUN4V) {
				if (data & (SUN4V_TLB_MODIFY))
					changed |= 1;
				data &= ~(SUN4V_TLB_MODIFY|SUN4V_TLB_W);
			} else {
				if (data & (SUN4U_TLB_MODIFY))
					changed |= 1;
				data &= ~(SUN4U_TLB_MODIFY|SUN4U_TLB_W);
			}
			KDASSERT((data & TLB_NFO) == 0);
			if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
				printf("pmap_clear_modify: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}
			if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
				tsb_invalidate(pv->pv_pmap->pm_ctx,
				    (pv->pv_va & PV_VAMASK));
				tlb_flush_pte((pv->pv_va & PV_VAMASK),
				    pv->pv_pmap->pm_ctx);
			}
			/* Then clear the mod bit in the pv */
			if (pv->pv_va & PV_MOD)
				changed |= 1;
			pv->pv_va &= ~(PV_MOD);
			dcache_flush_page(pa);
		}
	}
	mtx_leave(&pg->mdpage.pvmtx);

	return (changed);
}

boolean_t
pmap_clear_reference(struct vm_page *pg)
{
	paddr_t pa = VM_PAGE_TO_PHYS(pg);
	int changed = 0;
	pv_entry_t pv;

	/* Clear all references */
	mtx_enter(&pg->mdpage.pvmtx);
	pv = pa_to_pvh(pa);
	if (pv->pv_va & PV_REF)
		changed = 1;
	pv->pv_va &= ~(PV_REF);
	if (pv->pv_pmap != NULL) {
		for (; pv; pv = pv->pv_next) {
			int64_t data;

			data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
			if (CPU_ISSUN4V) {
				if (data & SUN4V_TLB_ACCESS)
					changed = 1;
				data &= ~SUN4V_TLB_ACCESS;
			} else {
				if (data & SUN4U_TLB_ACCESS)
					changed = 1;
				data &= ~SUN4U_TLB_ACCESS;
			}
			KDASSERT((data & TLB_NFO) == 0);
			if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
				printf("pmap_clear_reference: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}
			if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
				tsb_invalidate(pv->pv_pmap->pm_ctx,
				    (pv->pv_va & PV_VAMASK));
/*
				tlb_flush_pte(pv->pv_va & PV_VAMASK,
					pv->pv_pmap->pm_ctx);
*/
			}
			if (pv->pv_va & PV_REF)
				changed = 1;
			pv->pv_va &= ~(PV_REF);
		}
	}
	/* Stupid here will take a cache hit even on unmapped pages 8^( */
	dcache_flush_page(VM_PAGE_TO_PHYS(pg));
	mtx_leave(&pg->mdpage.pvmtx);

	return (changed);
}

boolean_t
pmap_is_modified(struct vm_page *pg)
{
	pv_entry_t pv, npv;
	int mod = 0;

	/* Check if any mapping has been modified */
	mtx_enter(&pg->mdpage.pvmtx);
	pv = &pg->mdpage.pvent;
	if (pv->pv_va & PV_MOD)
		mod = 1;
	if (!mod && (pv->pv_pmap != NULL)) {
		for (npv = pv; mod == 0 && npv && npv->pv_pmap; npv = npv->pv_next) {
			int64_t data;

			data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);
			if (pmap_tte2flags(data) & PV_MOD)
				mod = 1;
			/* Migrate modify info to head pv */
			if (npv->pv_va & PV_MOD)
				mod = 1;
			npv->pv_va &= ~PV_MOD;
		}
	}
	/* Save modify info */
	if (mod)
		pv->pv_va |= PV_MOD;
	mtx_leave(&pg->mdpage.pvmtx);

	return (mod);
}

boolean_t
pmap_is_referenced(struct vm_page *pg)
{
	pv_entry_t pv, npv;
	int ref = 0;

	/* Check if any mapping has been referenced */
	mtx_enter(&pg->mdpage.pvmtx);
	pv = &pg->mdpage.pvent;
	if (pv->pv_va & PV_REF)
		ref = 1;
	if (!ref && (pv->pv_pmap != NULL)) {
		for (npv = pv; npv; npv = npv->pv_next) {
			int64_t data;

			data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);
			if (pmap_tte2flags(data) & PV_REF)
				ref = 1;
			/* Migrate modify info to head pv */
			if (npv->pv_va & PV_REF)
				ref = 1;
			npv->pv_va &= ~PV_REF;
		}
	}
	/* Save ref info */
	if (ref)
		pv->pv_va |= PV_REF;
	mtx_leave(&pg->mdpage.pvmtx);

	return (ref);
}

/*
 *	Routine:	pmap_unwire
 *	Function:	Clear the wired attribute for a map/virtual-address
 *			pair.
 *	In/out conditions:
 *			The mapping must already exist in the pmap.
 */
void
pmap_unwire(struct pmap *pmap, vaddr_t va)
{
	int64_t data;

	if (pmap == NULL)
		return;

	/*
	 * Is this part of the permanent 4MB mapping?
	 */
	if (pmap == pmap_kernel() && va >= ktext &&
		va < roundup(ekdata, 4*MEG)) {
		prom_printf("pmap_unwire: va=%08x in locked TLB\r\n", va);
		OF_enter();
		return;
	}
	mtx_enter(&pmap->pm_mtx);
	data = pseg_get(pmap, va & PV_VAMASK);

	if (CPU_ISSUN4V)
		data &= ~SUN4V_TLB_TSB_LOCK;
	else
		data &= ~SUN4U_TLB_TSB_LOCK;

	if (pseg_set(pmap, va & PV_VAMASK, data, 0)) {
		printf("pmap_unwire: gotten pseg empty!\n");
		Debugger();
		/* panic? */
	}
	mtx_leave(&pmap->pm_mtx);
}

/*
 * Lower the protection on the specified physical page.
 *
 * Never enable writing as it will break COW
 */
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
	paddr_t pa = VM_PAGE_TO_PHYS(pg);
	pv_entry_t pv;
	int64_t data, clear, set;

	if (prot & PROT_WRITE)
		return;

	if (prot & (PROT_READ | PROT_EXEC)) {
		/* copy_on_write */

		set = TLB_V;
		if (CPU_ISSUN4V) {
			clear = SUN4V_TLB_REAL_W|SUN4V_TLB_W;
			if (PROT_EXEC & prot)
				set |= SUN4V_TLB_EXEC;
			else
				clear |= SUN4V_TLB_EXEC;
		} else {
			clear = SUN4U_TLB_REAL_W|SUN4U_TLB_W;
			if (PROT_EXEC & prot)
				set |= SUN4U_TLB_EXEC;
			else
				clear |= SUN4U_TLB_EXEC;
			if (PROT_EXEC == prot)
				set |= SUN4U_TLB_EXEC_ONLY;
		}

		pv = pa_to_pvh(pa);
		mtx_enter(&pg->mdpage.pvmtx);
		if (pv->pv_pmap != NULL) {
			for (; pv; pv = pv->pv_next) {
				data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);

				/* Save REF/MOD info */
				pv->pv_va |= pmap_tte2flags(data);

				data &= ~(clear);
				data |= (set);
				KDASSERT((data & TLB_NFO) == 0);
				if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
					printf("pmap_page_protect: gotten pseg empty!\n");
					Debugger();
					/* panic? */
				}
				if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
					tsb_invalidate(pv->pv_pmap->pm_ctx,
					    (pv->pv_va & PV_VAMASK));
					tlb_flush_pte(pv->pv_va & PV_VAMASK, pv->pv_pmap->pm_ctx);
				}
			}
		}
		mtx_leave(&pg->mdpage.pvmtx);
	} else {
		pv_entry_t firstpv;
		/* remove mappings */

		firstpv = pa_to_pvh(pa);
		mtx_enter(&pg->mdpage.pvmtx);

		/* First remove the entire list of continuation pv's*/
		while ((pv = firstpv->pv_next) != NULL) {
			data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);

			/* Save REF/MOD info */
			firstpv->pv_va |= pmap_tte2flags(data);

			/* Clear mapping */
			if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, 0, 0)) {
				printf("pmap_page_protect: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}
			if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
				tsb_invalidate(pv->pv_pmap->pm_ctx,
				    (pv->pv_va & PV_VAMASK));
				tlb_flush_pte(pv->pv_va & PV_VAMASK, pv->pv_pmap->pm_ctx);
			}
			pv->pv_pmap->pm_stats.resident_count--;

			/* free the pv */
			firstpv->pv_next = pv->pv_next;
			mtx_leave(&pg->mdpage.pvmtx);
			pool_put(&pv_pool, pv);
			mtx_enter(&pg->mdpage.pvmtx);
		}

		pv = firstpv;

		/* Then remove the primary pv */
		if (pv->pv_pmap != NULL) {
			data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);

			/* Save REF/MOD info */
			pv->pv_va |= pmap_tte2flags(data);
			if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, 0, 0)) {
				printf("pmap_page_protect: gotten pseg empty!\n");
				Debugger();
				/* panic? */
			}
			if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
				tsb_invalidate(pv->pv_pmap->pm_ctx,
				    (pv->pv_va & PV_VAMASK));
				tlb_flush_pte(pv->pv_va & PV_VAMASK,
				    pv->pv_pmap->pm_ctx);
			}
			pv->pv_pmap->pm_stats.resident_count--;
			KASSERT(pv->pv_next == NULL);
			/* dump the first pv */
			pv->pv_pmap = NULL;
		}
		dcache_flush_page(pa);
		mtx_leave(&pg->mdpage.pvmtx);
	}
	/* We should really only flush the pages we demapped. */
}

/*
 * Allocate a context.  If necessary, steal one from someone else.
 * Changes hardware context number and loads segment map.
 *
 * This routine is only ever called from locore.s just after it has
 * saved away the previous process, so there are no active user windows.
 *
 * The new context is flushed from the TLB before returning.
 */
int
ctx_alloc(struct pmap *pm)
{
	int s, cnum;
	static int next = 0;

	if (pm == pmap_kernel()) {
#ifdef DIAGNOSTIC
		printf("ctx_alloc: kernel pmap!\n");
#endif
		return (0);
	}
	s = splvm();
	cnum = next;
	do {
		/*
		 * We use the last context as an "invalid" context in
		 * TSB tags. Never allocate (or bad things will happen).
		 */
		if (cnum >= numctx - 2)
			cnum = 0;
	} while (ctxbusy[++cnum] != 0 && cnum != next);
	if (cnum==0) cnum++; /* Never steal ctx 0 */
	if (ctxbusy[cnum]) {
		int i;
		/* We gotta steal this context */
		for (i = 0; i < TSBENTS; i++) {
			if (TSB_TAG_CTX(tsb_dmmu[i].tag) == cnum)
				tsb_dmmu[i].tag = TSB_TAG_INVALID;
			if (TSB_TAG_CTX(tsb_immu[i].tag) == cnum)
				tsb_immu[i].tag = TSB_TAG_INVALID;
		}
		tlb_flush_ctx(cnum);
	}
	ctxbusy[cnum] = pm->pm_physaddr;
	next = cnum;
	splx(s);
	pm->pm_ctx = cnum;
	return cnum;
}

/*
 * Give away a context.
 */
void
ctx_free(struct pmap *pm)
{
	int oldctx;

	oldctx = pm->pm_ctx;

	if (oldctx == 0)
		panic("ctx_free: freeing kernel context");
#ifdef DIAGNOSTIC
	if (ctxbusy[oldctx] == 0)
		printf("ctx_free: freeing free context %d\n", oldctx);
	if (ctxbusy[oldctx] != pm->pm_physaddr) {
		printf("ctx_free: freeing someone esle's context\n "
		       "ctxbusy[%d] = %p, pm(%p)->pm_ctx = %p\n",
		       oldctx, (void *)(u_long)ctxbusy[oldctx], pm,
		       (void *)(u_long)pm->pm_physaddr);
		Debugger();
	}
#endif
	/* We should verify it has not been stolen and reallocated... */
	ctxbusy[oldctx] = 0;
}

/*
 * Enter the pmap and virtual address into the
 * physical to virtual map table.
 */
pv_entry_t
pmap_enter_pv(struct pmap *pmap, pv_entry_t npv, vaddr_t va, paddr_t pa)
{
	struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
	pv_entry_t pv = &pg->mdpage.pvent;

	mtx_enter(&pg->mdpage.pvmtx);

	if (pv->pv_pmap == NULL) {
		/*
		 * No entries yet, use header as the first entry
		 */
		PV_SETVA(pv, va);
		pv->pv_pmap = pmap;
		pv->pv_next = NULL;

		mtx_leave(&pg->mdpage.pvmtx);
		return (npv);
	}

	if (npv == NULL)
		panic("%s: no pv entries available", __func__);

	if (!(pv->pv_va & PV_ALIAS)) {
		/*
		 * There is at least one other VA mapping this page.
		 * Check if they are cache index compatible. If not
		 * remove all mappings, flush the cache and set page
		 * to be mapped uncached. Caching will be restored
		 * when pages are mapped compatible again.
		 * XXX - caching is not currently being restored, but
		 * XXX - I haven't seen the pages uncached since
		 * XXX - using pmap_prefer().	mhitch
		 */
		if ((pv->pv_va ^ va) & VA_ALIAS_MASK) {
			pv->pv_va |= PV_ALIAS;
			pmap_page_cache(pmap, pa, 0);
		}
	}

	/*
	 * There is at least one other VA mapping this page.
	 * Place this entry after the header.
	 */
	npv->pv_va = va & PV_VAMASK;
	npv->pv_pmap = pmap;
	npv->pv_next = pv->pv_next;
	pv->pv_next = npv;

	mtx_leave(&pg->mdpage.pvmtx);
	return (NULL);
}

/*
 * Remove a physical to virtual address translation.
 */
pv_entry_t
pmap_remove_pv(struct pmap *pmap, vaddr_t va, paddr_t pa)
{
	pv_entry_t pv, opv, npv = NULL;
	struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
	int64_t data = 0LL;

	opv = pv = &pg->mdpage.pvent;
	mtx_enter(&pg->mdpage.pvmtx);

	/*
	 * If it is the first entry on the list, it is actually
	 * in the header and we must copy the following entry up
	 * to the header.  Otherwise we must search the list for
	 * the entry.  In either case we free the now unused entry.
	 */
	if (pmap == pv->pv_pmap && PV_MATCH(pv, va)) {
		/* Save modified/ref bits */
		data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
		npv = pv->pv_next;
		if (npv) {
			/* First save mod/ref bits */
			pv->pv_va = (pv->pv_va & PV_MASK) | npv->pv_va;
			pv->pv_next = npv->pv_next;
			pv->pv_pmap = npv->pv_pmap;
		} else {
			pv->pv_pmap = NULL;
			pv->pv_next = NULL;
			pv->pv_va &= (PV_REF|PV_MOD); /* Only save ref/mod bits */
		}
	} else {
		for (npv = pv->pv_next; npv; pv = npv, npv = npv->pv_next) {
			if (pmap == npv->pv_pmap && PV_MATCH(npv, va))
				goto found;
		}

		/*
		 * Sometimes UVM gets confused and calls pmap_remove() instead
		 * of pmap_kremove()
		 */
		mtx_leave(&pg->mdpage.pvmtx);
		return (NULL);
found:
		pv->pv_next = npv->pv_next;

		/*
		 * move any referenced/modified info to the base pv
		 */
		data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);

		/*
		 * Here, if this page was aliased, we should try clear out any
		 * alias that may have occurred.  However, that's a complicated
		 * operation involving multiple scans of the pv list.
		 */
	}

	/* Save REF/MOD info */
	opv->pv_va |= pmap_tte2flags(data);

	/* Check to see if the alias went away */
	if (opv->pv_va & PV_ALIAS) {
		opv->pv_va &= ~PV_ALIAS;
		for (pv = opv; pv; pv = pv->pv_next) {
			if ((pv->pv_va ^ opv->pv_va) & VA_ALIAS_MASK) {
				opv->pv_va |= PV_ALIAS;
			}
		}
		if (!(opv->pv_va & PV_ALIAS))
			pmap_page_cache(pmap, pa, 1);
	}

	mtx_leave(&pg->mdpage.pvmtx);
	return (npv);
}

/*
 *	pmap_page_cache:
 *
 *	Change all mappings of a page to cached/uncached.
 */
void
pmap_page_cache(struct pmap *pm, paddr_t pa, int mode)
{
	pv_entry_t pv;
	struct vm_page *pg = PHYS_TO_VM_PAGE(pa);

	if (CPU_ISSUN4US || CPU_ISSUN4V)
		return;

	pv = &pg->mdpage.pvent;
	if (pv == NULL)
		return;

	MUTEX_ASSERT_LOCKED(&pg->mdpage.pvmtx);

	while (pv) {
		vaddr_t va;

		va = (pv->pv_va & PV_VAMASK);
		if (pv->pv_va & PV_NC) {
			/* Non-cached -- I/O mapping */
			if (pseg_set(pv->pv_pmap, va,
			    pseg_get(pv->pv_pmap, va) & ~(SUN4U_TLB_CV|SUN4U_TLB_CP),
				     0)) {
				printf("pmap_page_cache: aliased pseg empty!\n");
				Debugger();
				/* panic? */
			}
		} else if (mode && (!(pv->pv_va & PV_NVC))) {
			/* Enable caching */
			if (pseg_set(pv->pv_pmap, va,
			    pseg_get(pv->pv_pmap, va) | SUN4U_TLB_CV, 0)) {
				printf("pmap_page_cache: aliased pseg empty!\n");
				Debugger();
				/* panic? */
			}
		} else {
			/* Disable caching */
			if (pseg_set(pv->pv_pmap, va,
			    pseg_get(pv->pv_pmap, va) & ~SUN4U_TLB_CV, 0)) {
				printf("pmap_page_cache: aliased pseg empty!\n");
				Debugger();
				/* panic? */
			}
		}
		if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
			tsb_invalidate(pv->pv_pmap->pm_ctx, va);
			/* Force reload -- protections may be changed */
			tlb_flush_pte(va, pv->pv_pmap->pm_ctx);
		}

		pv = pv->pv_next;
	}
}

int
pmap_get_page(paddr_t *pa, const char *wait, struct pmap *pm)
{
	int reserve = pm == pmap_kernel() ? UVM_PGA_USERESERVE : 0;

	if (uvm.page_init_done) {
		struct vm_page *pg;

		while ((pg = uvm_pagealloc(NULL, 0, NULL,
		    UVM_PGA_ZERO|reserve)) == NULL) {
			if (wait == NULL)
				return 0;
			uvm_wait(wait);
		}
		pg->wire_count++;
		atomic_clearbits_int(&pg->pg_flags, PG_BUSY);
		*pa = VM_PAGE_TO_PHYS(pg);
	} else {
		uvm_page_physget(pa);
		pmap_zero_phys(*pa);
	}

	return (1);
}

void
pmap_free_page(paddr_t pa, struct pmap *pm)
{
	struct vm_page *pg = PHYS_TO_VM_PAGE(pa);

	pg->wire_count = 0;
	uvm_pagefree(pg);
}

void
pmap_remove_holes(struct vmspace *vm)
{
	vaddr_t shole, ehole;
	struct vm_map *map = &vm->vm_map;

	/*
	 * Although the hardware only supports 44-bit virtual addresses
	 * (and thus a hole from 1 << 43 to -1 << 43), this pmap
	 * implementation itself only supports 43-bit virtual addresses,
	 * so we have to narrow the hole a bit more.
	 */
	shole = 1L << (HOLESHIFT - 1);
	ehole = -1L << (HOLESHIFT - 1);

	shole = ulmax(vm_map_min(map), shole);
	ehole = ulmin(vm_map_max(map), ehole);

	if (ehole <= shole)
		return;

	(void)uvm_map(map, &shole, ehole - shole, NULL, UVM_UNKNOWN_OFFSET, 0,
	    UVM_MAPFLAG(PROT_NONE, PROT_NONE, MAP_INHERIT_SHARE, MADV_RANDOM,
	      UVM_FLAG_NOMERGE | UVM_FLAG_HOLE | UVM_FLAG_FIXED));
}

#ifdef DDB

void
db_dump_pv(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	struct pv_entry *pv;

	if (!have_addr) {
		db_printf("Need addr for pv\n");
		return;
	}

	for (pv = pa_to_pvh(addr); pv; pv = pv->pv_next)
		db_printf("pv@%p: next=%p pmap=%p va=0x%llx\n",
			  pv, pv->pv_next, pv->pv_pmap,
			  (unsigned long long)pv->pv_va);

}

#endif