/*-
 * Copyright (c) 1998 Doug Rabson
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 */
/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center and by Chris G. Demetriou.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the NetBSD
 *	Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */
/*-
 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/alpha/alpha/machdep.c,v 1.236 2005/10/14 12:43:42 davidxu Exp $");

#include "opt_compat.h"
#include "opt_ddb.h"
#include "opt_kstack_pages.h"
#include "opt_msgbuf.h"
#include "opt_maxmem.h"

#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/bus.h>
#include <sys/cons.h>
#include <sys/cpu.h>
#include <sys/eventhandler.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/msgbuf.h>
#include <sys/mutex.h>
#include <sys/pcpu.h>
#include <sys/ptrace.h>
#include <sys/reboot.h>
#include <sys/signalvar.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <sys/sysproto.h>
#include <sys/ucontext.h>
#include <sys/uio.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>

#include <net/netisr.h>

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>

#include <machine/bootinfo.h>
#include <machine/chipset.h>
#include <machine/clock.h>
#include <machine/cpuconf.h>
#include <machine/elf.h>
#include <machine/fpu.h>
#include <machine/md_var.h>
#include <machine/pal.h>
#include <machine/pcb.h>
#include <machine/prom.h>
#include <machine/rpb.h>
#include <machine/sigframe.h>
#include <machine/vmparam.h>

#include <alpha/alpha/db_instruction.h>

u_int64_t cycles_per_usec;
u_int32_t cycles_per_sec;
int cold = 1;
struct platform platform;
alpha_chipset_t chipset;
struct bootinfo_kernel bootinfo;

struct mtx icu_lock;

vm_offset_t proc0kstack;

char machine[] = "alpha";
SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, "");

static char cpu_model[128];
SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, cpu_model, 0, "");

#ifdef DDB
extern vm_offset_t ksym_start, ksym_end;
#endif

int	alpha_unaligned_print = 1;	/* warn about unaligned accesses */
int	alpha_unaligned_fix = 1;	/* fix up unaligned accesses */
int	alpha_unaligned_sigbus = 0;	/* don't SIGBUS on fixed-up accesses */

SYSCTL_INT(_machdep, CPU_UNALIGNED_PRINT, unaligned_print,
	CTLFLAG_RW, &alpha_unaligned_print, 0, "");

SYSCTL_INT(_machdep, CPU_UNALIGNED_FIX, unaligned_fix,
	CTLFLAG_RW, &alpha_unaligned_fix, 0, "");

SYSCTL_INT(_machdep, CPU_UNALIGNED_SIGBUS, unaligned_sigbus,
	CTLFLAG_RW, &alpha_unaligned_sigbus, 0, "");

static void cpu_startup(void *);
SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL)

struct msgbuf *msgbufp=0;

long Maxmem = 0;
long realmem = 0;

long	totalphysmem;		/* total amount of physical memory in system */
long	resvmem;		/* amount of memory reserved for PROM */
long	unusedmem;		/* amount of memory for OS that we don't use */
long	unknownmem;		/* amount of memory with an unknown use */
int	ncpus;			/* number of cpus */

int	promcons_dly_mkdev = 1;	/* need to delay call to make_dev() */
void	promcons_delayed_makedev(void);

vm_offset_t phys_avail[10];

/* must be 2 less so 0 0 can signal end of chunks */
#define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2)

#ifdef COMPAT_43
void osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
#endif
#ifdef COMPAT_FREEBSD4
static void freebsd4_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
#endif

static void get_fpcontext(struct thread *td, mcontext_t *mcp);
static void identifycpu(void);
static int  set_fpcontext(struct thread *td, const mcontext_t *mcp);

struct kva_md_info kmi;

/*
 * Hooked into the shutdown chain; if the system is to be halted,
 * unconditionally drop back to the SRM console.
 */
static void
alpha_srm_shutdown(void *junk, int howto)
{
	if (howto & RB_HALT) {
		cpu_halt();
	}
}

static void
cpu_startup(dummy)
	void *dummy;
{
	/*
	 * Good {morning,afternoon,evening,night}.
	 */
	identifycpu();

	/* startrtclock(); */
#ifdef PERFMON
	perfmon_init();
#endif
	printf("real memory  = %ld (%ld MB)\n", alpha_ptob(Maxmem),
	    alpha_ptob(Maxmem) / 1048576);
	realmem = alpha_ptob(Maxmem);

	/*
	 * Display any holes after the first chunk of extended memory.
	 */
	if (bootverbose) {
		int indx;

		printf("Physical memory chunk(s):\n");
		for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) {
			int size1 = phys_avail[indx + 1] - phys_avail[indx];

			printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx],
			    phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE);
		}
	}

	vm_ksubmap_init(&kmi);

	printf("avail memory = %ld (%ld MB)\n", ptoa(cnt.v_free_count),
	    ptoa(cnt.v_free_count) / 1048576);

	/*
	 * Set up buffers, so they can be used to read disk labels.
	 */
	bufinit();
	vm_pager_bufferinit();
	EVENTHANDLER_REGISTER(shutdown_final, alpha_srm_shutdown, 0,
			      SHUTDOWN_PRI_LAST);
}

/*
 * Retrieve the platform name from the DSR.
 */
const char *
alpha_dsr_sysname()
{
	struct dsrdb *dsr;
	const char *sysname;

	/*
	 * DSR does not exist on early HWRPB versions.
	 */
	if (hwrpb->rpb_version < HWRPB_DSRDB_MINVERS)
		return (NULL);

	dsr = (struct dsrdb *)(((caddr_t)hwrpb) + hwrpb->rpb_dsrdb_off);
	sysname = (const char *)((caddr_t)dsr + (dsr->dsr_sysname_off +
	    sizeof(u_int64_t)));
	return (sysname);
}

/*
 * Lookup the system specified system variation in the provided table,
 * returning the model string on match.
 */
const char *
alpha_variation_name(u_int64_t variation,
		     const struct alpha_variation_table *avtp)
{
	int i;

	for (i = 0; avtp[i].avt_model != NULL; i++)
		if (avtp[i].avt_variation == variation)
			return (avtp[i].avt_model);
	return (NULL);
}

/*
 * Generate a default platform name based for unknown system variations.
 */
const char *
alpha_unknown_sysname()
{
	static char s[128];		/* safe size */

	snprintf(s, sizeof(s), "%s family, unknown model variation 0x%lx",
	    platform.family, hwrpb->rpb_variation & SV_ST_MASK);
	return ((const char *)s);
}

static void
identifycpu(void)
{
	u_int64_t type, major, minor;
	u_int64_t amask;
	struct pcs *pcsp;
	char *cpuname[] = {
		"unknown",		/* 0 */
		"EV3",			/* 1 */
		"EV4 (21064)",		/* 2 */
		"Simulation",		/* 3 */
		"LCA (21066/21068)",	/* 4 */
		"EV5 (21164)",		/* 5 */
		"EV45 (21064A)",	/* 6 */
		"EV56 (21164A)",	/* 7 */
		"EV6 (21264)",		/* 8 */
		"PCA56 (21164PC)",	/* 9 */
		"PCA57 (21164PC)",	/* 10 */
		"EV67 (21264A)",	/* 11 */
		"EV68CB (21264C)"	/* 12 */
		"EV68AL (21264B)",	/* 13 */
		"EV68CX (21264D)",	/* 14 */
		"EV7 (21364)"		/* 15 */
	};

	/*
	 * print out CPU identification information.
	 */
	printf("%s\n%s, %ldMHz\n", platform.family, platform.model,
	    hwrpb->rpb_cc_freq / 1000000);	/* XXX true for 21164? */
	printf("%ld byte page size, %d processor%s.\n",
	    hwrpb->rpb_page_size, ncpus, ncpus == 1 ? "" : "s");
#if 0
	/* this isn't defined for any systems that we run on? */
	printf("serial number 0x%lx 0x%lx\n",
	    ((long *)hwrpb->rpb_ssn)[0], ((long *)hwrpb->rpb_ssn)[1]);

	/* and these aren't particularly useful! */
	printf("variation: 0x%lx, revision 0x%lx\n",
	    hwrpb->rpb_variation, *(long *)hwrpb->rpb_revision);
#endif
 	pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
	/* cpu type */
	type = pcsp->pcs_proc_type;
	major = (type & PCS_PROC_MAJOR) >> PCS_PROC_MAJORSHIFT;
	minor = (type & PCS_PROC_MINOR) >> PCS_PROC_MINORSHIFT;
	if (major < sizeof(cpuname)/sizeof(char *))
		printf("CPU: %s major=%lu minor=%lu",
			cpuname[major], major, minor);
	else
		printf("CPU: major=%lu minor=%lu\n", major, minor);
	/* amask */
	if (major >= PCS_PROC_EV56) {
		amask = 0xffffffff; /* 32 bit for printf */
		amask = (~alpha_amask(amask)) & amask;
		printf(" extensions=0x%b\n", (u_int32_t) amask,
			"\020"
			"\001BWX"
			"\002FIX"
			"\003CIX"
			"\011MVI"
			"\012PRECISE"
		);
	} else
		printf("\n");	
	/* PAL code */
	printf("OSF PAL rev: 0x%lx\n", pcsp->pcs_palrevisions[PALvar_OSF1]);
}

extern char kernel_text[], _end[];

void
alpha_init(pfn, ptb, bim, bip, biv)
	u_long pfn;		/* first free PFN number */
	u_long ptb;		/* PFN of current level 1 page table */
	u_long bim;		/* bootinfo magic */
	u_long bip;		/* bootinfo pointer */
	u_long biv;		/* bootinfo version */
{
	int phys_avail_cnt;
	char *bootinfo_msg, *bootinfo_booted_kernel;
	vm_offset_t kernstart, kernend;
	vm_offset_t kernstartpfn, kernendpfn, pfn0, pfn1;
	u_long physmem_tunable;
	struct mddt *mddtp;
	struct mddt_cluster *memc;
	int i, mddtweird;
	int cputype;
	char *p;

	/* NO OUTPUT ALLOWED UNTIL FURTHER NOTICE */

	/*
	 * Turn off interrupts (not mchecks) and floating point.
	 * Make sure the instruction and data streams are consistent.
	 */
	(void)alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
	/* alpha_pal_wrfen(0); */
	ALPHA_TBIA();
	alpha_pal_imb();

	/*
	 * Get critical system information (if possible, from the
	 * information provided by the boot program).
	 */
	bootinfo_msg = NULL;
	bootinfo_booted_kernel = NULL;
	if (bim == BOOTINFO_MAGIC) {
		if (biv == 0) {		/* backward compat */
			biv = *(u_long *)bip;
			bip += 8;
		}
		switch (biv) {
		case 1: {
			struct bootinfo_v1 *v1p = (struct bootinfo_v1 *)bip;

			bootinfo.ssym = v1p->ssym;
			bootinfo.esym = v1p->esym;
			bootinfo.kernend = v1p->kernend;
			bootinfo.modptr = v1p->modptr;
			bootinfo.envp = v1p->envp;
			/* hwrpb may not be provided by boot block in v1 */
			if (v1p->hwrpb != NULL) {
				bootinfo.hwrpb_phys =
				    ((struct rpb *)v1p->hwrpb)->rpb_phys;
				bootinfo.hwrpb_size = v1p->hwrpbsize;
			} else {
				bootinfo.hwrpb_phys =
				    ((struct rpb *)HWRPB_ADDR)->rpb_phys;
				bootinfo.hwrpb_size =
				    ((struct rpb *)HWRPB_ADDR)->rpb_size;
			}
			bcopy(v1p->boot_flags, bootinfo.boot_flags,
			    min(sizeof v1p->boot_flags,
			      sizeof bootinfo.boot_flags));
			bcopy(v1p->booted_kernel, bootinfo.booted_kernel,
			    min(sizeof v1p->booted_kernel,
			      sizeof bootinfo.booted_kernel));
			bootinfo_booted_kernel = bootinfo.booted_kernel;
			/* booted dev not provided in bootinfo */
			init_prom_interface((struct rpb *)
			    ALPHA_PHYS_TO_K0SEG(bootinfo.hwrpb_phys));
                	prom_getenv(PROM_E_BOOTED_DEV, bootinfo.booted_dev,
			    sizeof bootinfo.booted_dev);
			break;
		}
		default:
			bootinfo_msg = "unknown bootinfo version";
			goto nobootinfo;
		}
	} else {
		bootinfo_msg = "boot program did not pass bootinfo";
	nobootinfo:
		bootinfo.ssym = (u_long)&_end;
		bootinfo.esym = (u_long)&_end;
		bootinfo.hwrpb_phys = ((struct rpb *)HWRPB_ADDR)->rpb_phys;
		bootinfo.hwrpb_size = ((struct rpb *)HWRPB_ADDR)->rpb_size;
		init_prom_interface((struct rpb *)HWRPB_ADDR);
		prom_getenv(PROM_E_BOOTED_OSFLAGS, bootinfo.boot_flags,
			    sizeof bootinfo.boot_flags);
		prom_getenv(PROM_E_BOOTED_FILE, bootinfo.booted_kernel,
			    sizeof bootinfo.booted_kernel);
		prom_getenv(PROM_E_BOOTED_DEV, bootinfo.booted_dev,
			    sizeof bootinfo.booted_dev);
	}

	/*
	 * Initialize the kernel's mapping of the RPB.  It's needed for
	 * lots of things.
	 */
	hwrpb = (struct rpb *)ALPHA_PHYS_TO_K0SEG(bootinfo.hwrpb_phys);

	/*
	 * Remember how many cycles there are per microsecond, 
	 * so that we can use delay().  Round up, for safety.
	 */
	cycles_per_usec = (hwrpb->rpb_cc_freq + 999999) / 1000000;

	/*
	 * Remember how many cycles per closk for coping with missed
	 * clock interrupts.
	 */
	cycles_per_sec = hwrpb->rpb_cc_freq;

	/* Get the loader(8) metadata */
	preload_metadata = (caddr_t)bootinfo.modptr;
	if (envmode == 1)
		kern_envp = static_env;
	else
		kern_envp = bootinfo.envp;

	/* Do basic tuning, hz etc */
	init_param1();

	/*
	 * Initalize the (temporary) bootstrap console interface, so
	 * we can use printf until the VM system starts being setup.
	 * The real console is initialized before then.
	 */
	init_bootstrap_console();

	/* OUTPUT NOW ALLOWED */

	/* delayed from above */
	if (bootinfo_msg)
		printf("WARNING: %s (0x%lx, 0x%lx, 0x%lx)\n",
		       bootinfo_msg, bim, bip, biv);

	/*
	 * Point interrupt/exception vectors to our own.
	 */
	alpha_pal_wrent(XentInt, ALPHA_KENTRY_INT);
	alpha_pal_wrent(XentArith, ALPHA_KENTRY_ARITH);
	alpha_pal_wrent(XentMM, ALPHA_KENTRY_MM);
	alpha_pal_wrent(XentIF, ALPHA_KENTRY_IF);
	alpha_pal_wrent(XentUna, ALPHA_KENTRY_UNA);
	alpha_pal_wrent(XentSys, ALPHA_KENTRY_SYS);

	/*
	 * Clear pending machine checks and error reports, and enable
	 * system- and processor-correctable error reporting.
	 */
	alpha_pal_wrmces(alpha_pal_rdmces() &
			 ~(ALPHA_MCES_DSC|ALPHA_MCES_DPC));

	/* Clear userland thread pointer */
	alpha_pal_wrunique(0);

	/*
	 * Find out what hardware we're on, and do basic initialization.
	 */
	cputype = hwrpb->rpb_type;
	if (cputype < 0) {
		/*
		 * At least some white-box (NT) systems have SRM which
		 * reports a systype that's the negative of their
		 * blue-box (UNIX/OVMS) counterpart.
		 */
		cputype = -cputype;
	}
	
	if (cputype >= API_ST_BASE) {
		if (cputype >= napi_cpuinit + API_ST_BASE) {
			platform_not_supported(cputype);
			/* NOTREACHED */
		}
		cputype -= API_ST_BASE;
		api_cpuinit[cputype].init(cputype);
	} else {
		if (cputype >= ncpuinit) {
			platform_not_supported(cputype);
			/* NOTREACHED */
		}	
		cpuinit[cputype].init(cputype);
	}
	snprintf(cpu_model, sizeof(cpu_model), "%s", platform.model);

	/* NO MORE FIRMWARE ACCESS ALLOWED */
#ifdef _PMAP_MAY_USE_PROM_CONSOLE
	/*
	 * XXX (unless _PMAP_MAY_USE_PROM_CONSOLE is defined and
	 * XXX pmap_uses_prom_console() evaluates to non-zero.)
	 */
#endif

	/*
	 * find out this system's page size
	 */
	if (hwrpb->rpb_page_size != PAGE_SIZE)
		panic("page size %ld != 8192?!", hwrpb->rpb_page_size);


	/*
	 * Find the beginning and end of the kernel (and leave a
	 * bit of space before the beginning for the bootstrap
	 * stack).
	 */
	kernstart = trunc_page(kernel_text) - 2 * PAGE_SIZE;
#ifdef DDB
	ksym_start = bootinfo.ssym;
	ksym_end   = bootinfo.esym;
	kernend = (vm_offset_t)round_page(ksym_end);
#else
	kernend = (vm_offset_t)round_page(_end);
#endif
	/* But if the bootstrap tells us otherwise, believe it! */
	if (bootinfo.kernend)
		kernend = round_page(bootinfo.kernend);
	if (preload_metadata == NULL)
		printf("WARNING: loader(8) metadata is missing!\n");

	kernstartpfn = atop(ALPHA_K0SEG_TO_PHYS(kernstart));
	kernendpfn = atop(ALPHA_K0SEG_TO_PHYS(kernend));

	/*
	 * Find out how much memory is available, by looking at
	 * the memory cluster descriptors.  This also tries to do
	 * its best to detect things things that have never been seen
	 * before...
	 */
	mddtp = (struct mddt *)(((caddr_t)hwrpb) + hwrpb->rpb_memdat_off);

	/* MDDT SANITY CHECKING */
	mddtweird = 0;
	if (mddtp->mddt_cluster_cnt < 2) {
		mddtweird = 1;
		printf("WARNING: weird number of mem clusters: %ld\n",
		       mddtp->mddt_cluster_cnt);
	}

#ifdef DEBUG_CLUSTER
	printf("Memory cluster count: %d\n", mddtp->mddt_cluster_cnt);
#endif

	phys_avail_cnt = 0;
	for (i = 0; i < mddtp->mddt_cluster_cnt; i++) {
		memc = &mddtp->mddt_clusters[i];
#ifdef DEBUG_CLUSTER
		printf("MEMC %d: pfn 0x%lx cnt 0x%lx usage 0x%lx\n", i,
		       memc->mddt_pfn, memc->mddt_pg_cnt, memc->mddt_usage);
#endif
		totalphysmem += memc->mddt_pg_cnt;

		if (memc->mddt_usage & MDDT_mbz) {
			mddtweird = 1;
			printf("WARNING: mem cluster %d has weird "
			       "usage 0x%lx\n", i, memc->mddt_usage);
			unknownmem += memc->mddt_pg_cnt;
			continue;
		}
		if (memc->mddt_usage & MDDT_NONVOLATILE) {
			/* XXX should handle these... */
			printf("WARNING: skipping non-volatile mem "
			       "cluster %d\n", i);
			unusedmem += memc->mddt_pg_cnt;
			continue;
		}
		if (memc->mddt_usage & MDDT_PALCODE) {
			resvmem += memc->mddt_pg_cnt;
			continue;
		}

		/*
		 * We have a memory cluster available for system
		 * software use.  We must determine if this cluster
		 * holds the kernel.
		 */
		/*
		 * XXX If the kernel uses the PROM console, we only use the
		 * XXX memory after the kernel in the first system segment,
		 * XXX to avoid clobbering prom mapping, data, etc.
		 */
		physmem += memc->mddt_pg_cnt;
		pfn0 = memc->mddt_pfn;
		pfn1 = memc->mddt_pfn + memc->mddt_pg_cnt;
		if (pfn0 <= kernendpfn && kernstartpfn <= pfn1) {
			/*
			 * Must compute the location of the kernel
			 * within the segment.
			 */
#ifdef DEBUG_CLUSTER
			printf("Cluster %d contains kernel\n", i);
#endif
			if (!pmap_uses_prom_console()) {
				if (pfn0 < kernstartpfn) {
					/*
					 * There is a chunk before the kernel.
					 */
#ifdef DEBUG_CLUSTER
					printf("Loading chunk before kernel: "
					       "0x%lx / 0x%lx\n", pfn0, kernstartpfn);
#endif
					phys_avail[phys_avail_cnt] = alpha_ptob(pfn0);
					phys_avail[phys_avail_cnt+1] = alpha_ptob(kernstartpfn);
					phys_avail_cnt += 2;
				}
			}
			if (kernendpfn < pfn1) {
				/*
				 * There is a chunk after the kernel.
				 */
#ifdef DEBUG_CLUSTER
				printf("Loading chunk after kernel: "
				       "0x%lx / 0x%lx\n", kernendpfn, pfn1);
#endif
				phys_avail[phys_avail_cnt] = alpha_ptob(kernendpfn);
				phys_avail[phys_avail_cnt+1] = alpha_ptob(pfn1);
				phys_avail_cnt += 2;
			}
		} else {
			/*
			 * Just load this cluster as one chunk.
			 */
#ifdef DEBUG_CLUSTER
			printf("Loading cluster %d: 0x%lx / 0x%lx\n", i,
			       pfn0, pfn1);
#endif
			phys_avail[phys_avail_cnt] = alpha_ptob(pfn0);
			phys_avail[phys_avail_cnt+1] = alpha_ptob(pfn1);
			phys_avail_cnt += 2;
			
		}
	}
	phys_avail[phys_avail_cnt] = 0;

	/*
	 * Dump out the MDDT if it looks odd...
	 */
	if (mddtweird) {
		printf("\n");
		printf("complete memory cluster information:\n");
		for (i = 0; i < mddtp->mddt_cluster_cnt; i++) {
			printf("mddt %d:\n", i);
			printf("\tpfn %lx\n",
			       mddtp->mddt_clusters[i].mddt_pfn);
			printf("\tcnt %lx\n",
			       mddtp->mddt_clusters[i].mddt_pg_cnt);
			printf("\ttest %lx\n",
			       mddtp->mddt_clusters[i].mddt_pg_test);
			printf("\tbva %lx\n",
			       mddtp->mddt_clusters[i].mddt_v_bitaddr);
			printf("\tbpa %lx\n",
			       mddtp->mddt_clusters[i].mddt_p_bitaddr);
			printf("\tbcksum %lx\n",
			       mddtp->mddt_clusters[i].mddt_bit_cksum);
			printf("\tusage %lx\n",
			       mddtp->mddt_clusters[i].mddt_usage);
		}
		printf("\n");
	}

	Maxmem = physmem;

#ifdef MAXMEM
	/*
	 * MAXMEM define is in kilobytes.
	 */
	Maxmem = alpha_btop(MAXMEM * 1024);
#endif

	if (TUNABLE_ULONG_FETCH("hw.physmem", &physmem_tunable))
		Maxmem = alpha_btop(physmem_tunable);

	while (physmem > Maxmem) {
		int i = phys_avail_cnt - 2;
		size_t sz = alpha_btop(phys_avail[i+1] - phys_avail[i]);
		size_t nsz;
		if (physmem - sz > Maxmem) {
			phys_avail[i] = 0;
			phys_avail[i+1] = 0;
			phys_avail_cnt -= 2;
			physmem -= sz;
		} else {
			nsz = sz - (physmem - Maxmem);
			phys_avail[i+1] = phys_avail[i] + alpha_ptob(nsz);
			physmem -= (sz - nsz);
		}
	}
	init_param2(physmem);

	/*
	 * Initialize error message buffer (at end of core).
	 */
	{
		size_t sz = round_page(MSGBUF_SIZE);
		int i = phys_avail_cnt - 2;

		/* shrink so that it'll fit in the last segment */
		if (phys_avail[i+1] - phys_avail[i] < sz)
			sz = phys_avail[i+1] - phys_avail[i];

		phys_avail[i+1] -= sz;
		msgbufp = (struct msgbuf*) ALPHA_PHYS_TO_K0SEG(phys_avail[i+1]);

		msgbufinit(msgbufp, sz);

		/* Remove the last segment if it now has no pages. */
		if (phys_avail[i] == phys_avail[i+1]) {
			phys_avail[i] = 0;
			phys_avail[i+1] = 0;
		}

		/* warn if the message buffer had to be shrunk */
		if (sz != round_page(MSGBUF_SIZE))
			printf("WARNING: %ld bytes not available for msgbuf in last cluster (%ld used)\n",
			    round_page(MSGBUF_SIZE), sz);

	}

	proc_linkup(&proc0, &ksegrp0, &thread0);
	/*
	 * Init mapping for kernel stack for proc 0
	 */
	proc0kstack = pmap_steal_memory(KSTACK_PAGES * PAGE_SIZE);
	thread0.td_kstack = proc0kstack;
	thread0.td_pcb = (struct pcb *)
	    (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1;

	/*
	 * Setup the per-CPU data for the bootstrap cpu.
	 */
	{
		size_t sz = round_page(KSTACK_PAGES * PAGE_SIZE);
		pcpup = (struct pcpu *) pmap_steal_memory(sz);
		pcpu_init(pcpup, 0, sz);
		pcpup->pc_pal_id = alpha_pal_whami();
		alpha_pal_wrval((u_int64_t) pcpup);
		PCPU_GET(next_asn) = 1;	/* 0 used for proc0 pmap */
		PCPU_SET(curthread, &thread0);
#ifdef SMP
		thread0.td_md.md_kernnest = 1;
#endif
	}

	/*
	 * Initalize the real console, so the the bootstrap console is
	 * no longer necessary.  Note this now involves mutexes as part
	 * of some operations so needs to be after proc0/thread0/curthread
	 * become valid.
	 */
	if (platform.cons_init)
		platform.cons_init();
	promcndetach();
	cninit();

	/*
	 * Check to see if promcons needs to make_dev() now,
	 * doing it before now crashes with kernel stack issues.
	 */
	if (promcons_dly_mkdev > 1)
		promcons_delayed_makedev();
	promcons_dly_mkdev = 0;

	/*
	 * Initialize the virtual memory system, and set the
	 * page table base register in proc 0's PCB.
	 */
	pmap_bootstrap(ALPHA_PHYS_TO_K0SEG(alpha_ptob(ptb)),
	    hwrpb->rpb_max_asn);
	hwrpb->rpb_vptb = VPTBASE;
	hwrpb->rpb_checksum = hwrpb_checksum();


	/*
	 * Initialize the rest of proc 0's PCB, and cache its physical
	 * address.
	 */
	thread0.td_md.md_pcbpaddr =
	    (struct pcb *)ALPHA_K0SEG_TO_PHYS((vm_offset_t)thread0.td_pcb);

	/*
	 * Set the kernel sp, reserving space for an (empty) trapframe,
	 * and make proc0's trapframe pointer point to it for sanity.
	 */
	thread0.td_frame = (struct trapframe *)thread0.td_pcb - 1;
	thread0.td_pcb->pcb_hw.apcb_ksp = (u_int64_t)thread0.td_frame;

	mutex_init();
	mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_RECURSE);
	mtx_init(&icu_lock, "icu", NULL, MTX_SPIN);

	/*
	 * Look at arguments passed to us and compute boothowto.
	 */
	for (p = bootinfo.boot_flags; p && *p != '\0'; p++) {
		/*
		 * Note that we'd really like to differentiate case here,
		 * but the Alpha AXP Architecture Reference Manual
		 * says that we shouldn't.
		 */
		switch (*p) {
		case 'a': /* autoboot */
		case 'A':
			boothowto &= ~RB_SINGLE;
			break;

#ifdef DEBUG
		case 'c': /* crash dump immediately after autoconfig */
		case 'C':
			boothowto |= RB_DUMP;
			break;
#endif

		case 'd': /* break into the kernel debugger ASAP */
		case 'D':
			boothowto |= RB_KDB;
			break;
		case 'g': /* use kernel gdb */
		case 'G':
			boothowto |= RB_GDB;
			break;

		case 'h': /* always halt, never reboot */
		case 'H':
			boothowto |= RB_HALT;
			break;

#if 0
		case 'm': /* mini root present in memory */
		case 'M':
			boothowto |= RB_MINIROOT;
			break;
#endif

		case 'n': /* askname */
		case 'N':
			boothowto |= RB_ASKNAME;
			break;

		case 's': /* single-user (default, supported for sanity) */
		case 'S':
			boothowto |= RB_SINGLE;
			break;

		case 'v':
		case 'V':
			boothowto |= RB_VERBOSE;
			bootverbose = 1;
			break;

		default:
			printf("Unrecognized boot flag '%c'.\n", *p);
			break;
		}
	}

	/*
	 * Catch case of boot_verbose set in environment.
	 */
	if ((p = getenv("boot_verbose")) != NULL) {
		if (strcmp(p, "yes") == 0 || strcmp(p, "YES") == 0) {
			boothowto |= RB_VERBOSE;
			bootverbose = 1;
		}
		freeenv(p);
	}

	/*
	 * Pick up kernelname.
	 */
	if (bootinfo_booted_kernel) {
		strncpy(kernelname, bootinfo_booted_kernel,
		   min(sizeof(kernelname), sizeof bootinfo.booted_kernel) - 1);
	} else if ((p = getenv("kernelname")) != NULL) {
		strncpy(kernelname, p, sizeof(kernelname) - 1);
		freeenv(p);
	}

	/*
	 * Initialize debuggers, and break into them if appropriate.
	 */
	if (getenv("boot_gdb") != NULL)
		boothowto |= RB_GDB;
	kdb_init();

#ifdef KDB
	if (boothowto & RB_KDB)
		kdb_enter("Boot flags requested debugger\n");
#endif

	/*
	 * Figure out the number of cpus in the box, from RPB fields.
	 * Really.  We mean it.
	 */
	for (i = 0; i < hwrpb->rpb_pcs_cnt; i++) {
		struct pcs *pcsp;

		pcsp = (struct pcs *)((char *)hwrpb + hwrpb->rpb_pcs_off +
		    (i * hwrpb->rpb_pcs_size));
		if ((pcsp->pcs_flags & PCS_PP) != 0)
			ncpus++;
	}

	/*
	 * Figure out our clock frequency, from RPB fields.
	 */
	hz = hwrpb->rpb_intr_freq >> 12;
	if (!(60 <= hz && hz <= 10240)) {
		hz = 1024;
#ifdef DIAGNOSTIC
		printf("WARNING: unbelievable rpb_intr_freq: %ld (%d hz)\n",
			hwrpb->rpb_intr_freq, hz);
#endif
	}

	hwrpb_restart_setup();

	alpha_pal_wrfen(0);
}

void
bzero(void *buf, size_t len)
{
	caddr_t p = buf;

	while (((vm_offset_t) p & (sizeof(u_long) - 1)) && len) {
		*p++ = 0;
		len--;
	}
	while (len >= sizeof(u_long) * 8) {
		*(u_long*) p = 0;
		*((u_long*) p + 1) = 0;
		*((u_long*) p + 2) = 0;
		*((u_long*) p + 3) = 0;
		len -= sizeof(u_long) * 8;
		*((u_long*) p + 4) = 0;
		*((u_long*) p + 5) = 0;
		*((u_long*) p + 6) = 0;
		*((u_long*) p + 7) = 0;
		p += sizeof(u_long) * 8;
	}
	while (len >= sizeof(u_long)) {
		*(u_long*) p = 0;
		len -= sizeof(u_long);
		p += sizeof(u_long);
	}
	while (len) {
		*p++ = 0;
		len--;
	}
}

void
DELAY(int n)
{
	unsigned long pcc0, pcc1, curcycle, cycles;
        int usec;

	if (n == 0)
		return;

        pcc0 = alpha_rpcc() & 0xffffffffUL;
	cycles = 0;
	usec = 0;

        while (usec <= n) {
		/*
		 * Get the next CPU cycle count. The assumption here
		 * is that we can't have wrapped twice past 32 bits worth
		 * of CPU cycles since we last checked.
		 */
		pcc1 = alpha_rpcc() & 0xffffffffUL;
		if (pcc1 < pcc0) {
			curcycle = (pcc1 + 0x100000000UL) - pcc0;
		} else {
			curcycle = pcc1 - pcc0;
		}

		/*
		 * We now have the number of processor cycles since we
		 * last checked. Add the current cycle count to the
		 * running total. If it's over cycles_per_usec, increment
		 * the usec counter.
		 */
		cycles += curcycle;
		while (cycles > cycles_per_usec) {
			usec++;
			cycles -= cycles_per_usec;
		}
		pcc0 = pcc1;
        }
}

/*
 * Send an interrupt to process.
 *
 * Stack is set up to allow sigcode stored
 * at top to call routine, followed by kcall
 * to sigreturn routine below.  After sigreturn
 * resets the signal mask, the stack, and the
 * frame pointer, it returns to the user
 * specified pc, psl.
 */
#ifdef COMPAT_43
void
osendsig(sig_t catcher, ksiginfo_t *kp, sigset_t *mask)
{
	struct proc *p;
	struct thread *td;
	osiginfo_t *sip, ksi;
	struct trapframe *frame;
	struct sigacts *psp;
	int oonstack, fsize, rndfsize;
	int sig;
	int code;

	td = curthread;
	p = td->td_proc;
	PROC_LOCK_ASSERT(p, MA_OWNED);
	sig = kp->ksi_signo;
	code = kp->ksi_code;
	psp = p->p_sigacts;
	mtx_assert(&psp->ps_mtx, MA_OWNED);

	frame = td->td_frame;
	fsize = sizeof ksi;
	rndfsize = ((fsize + 15) / 16) * 16;
	oonstack = sigonstack(alpha_pal_rdusp());

	/*
	 * Allocate and validate space for the signal handler
	 * context. Note that if the stack is in P0 space, the
	 * call to grow() is a nop, and the useracc() check
	 * will fail if the process has not already allocated
	 * the space with a `brk'.
	 */
	if ((td->td_pflags & TDP_ALTSTACK) && !oonstack &&
	    SIGISMEMBER(psp->ps_sigonstack, sig)) {
		sip = (osiginfo_t *)((caddr_t)td->td_sigstk.ss_sp +
		    td->td_sigstk.ss_size - rndfsize);
#if defined(COMPAT_43)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
#endif
	} else
		sip = (osiginfo_t *)(alpha_pal_rdusp() - rndfsize);
	mtx_unlock(&psp->ps_mtx);
	PROC_UNLOCK(p);

	/*
	 * Build the signal context to be used by sigreturn.
	 */
	ksi.si_sc.sc_onstack = (oonstack) ? 1 : 0;
	SIG2OSIG(*mask, ksi.si_sc.sc_mask);
	ksi.si_sc.sc_pc = frame->tf_regs[FRAME_PC];
	ksi.si_sc.sc_ps = frame->tf_regs[FRAME_PS];

	/* copy the registers. */
	fill_regs(td, (struct reg *)ksi.si_sc.sc_regs);
	ksi.si_sc.sc_regs[R_ZERO] = 0xACEDBADE;		/* magic number */
	ksi.si_sc.sc_regs[R_SP] = alpha_pal_rdusp();

	/* save the floating-point state, if necessary, then copy it. */
	alpha_fpstate_save(td, 1);		/* XXX maybe write=0 */
	ksi.si_sc.sc_ownedfp = td->td_md.md_flags & MDTD_FPUSED;
	bcopy(&td->td_pcb->pcb_fp, (struct fpreg *)ksi.si_sc.sc_fpregs,
	    sizeof(struct fpreg));
	ksi.si_sc.sc_fp_control = td->td_pcb->pcb_fp_control;
	bzero(ksi.si_sc.sc_reserved, sizeof ksi.si_sc.sc_reserved); /* XXX */
	ksi.si_sc.sc_xxx1[0] = 0;				/* XXX */
	ksi.si_sc.sc_xxx1[1] = 0;				/* XXX */
	ksi.si_sc.sc_traparg_a0 = frame->tf_regs[FRAME_TRAPARG_A0];
	ksi.si_sc.sc_traparg_a1 = frame->tf_regs[FRAME_TRAPARG_A1];
	ksi.si_sc.sc_traparg_a2 = frame->tf_regs[FRAME_TRAPARG_A2];
	ksi.si_sc.sc_xxx2[0] = 0;				/* XXX */
	ksi.si_sc.sc_xxx2[1] = 0;				/* XXX */
	ksi.si_sc.sc_xxx2[2] = 0;				/* XXX */
	/* Fill in POSIX parts */
	ksi.si_signo = sig;
	ksi.si_code = code;
	ksi.si_value = kp->ksi_value;

	/*
	 * copy the frame out to userland.
	 */
	if (copyout((caddr_t)&ksi, (caddr_t)sip, fsize) != 0) {
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
		PROC_LOCK(p);
		sigexit(td, SIGILL);
		return;
	}

	/*
	 * Set up the registers to return to sigcode.
	 */
	frame->tf_regs[FRAME_PC] = PS_STRINGS - szosigcode;
	frame->tf_regs[FRAME_A0] = sig;
	frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
	PROC_LOCK(p);
	mtx_lock(&psp->ps_mtx);
	if (SIGISMEMBER(psp->ps_siginfo, sig))
		frame->tf_regs[FRAME_A1] = (u_int64_t)sip;
	else
		frame->tf_regs[FRAME_A1] = code;
	frame->tf_regs[FRAME_A2] = (u_int64_t)&sip->si_sc;
	frame->tf_regs[FRAME_T12] = (u_int64_t)catcher;	/* t12 is pv */
	alpha_pal_wrusp((unsigned long)sip);
}
#endif

#ifdef COMPAT_FREEBSD4
static void
freebsd4_sendsig(sig_t catcher, ksiginfo_t *kp, sigset_t *mask)
{
	struct proc *p;
	struct thread *td;
	struct trapframe *frame;
	struct sigacts *psp;
	struct sigframe4 sf, *sfp;
	int oonstack, rndfsize;
	int sig;
	int code;

	td = curthread;
	p = td->td_proc;
	PROC_LOCK_ASSERT(p, MA_OWNED);
	sig = kp->ksi_signo;
	code = kp->ksi_code;
	psp = p->p_sigacts;
	mtx_assert(&psp->ps_mtx, MA_OWNED);

	frame = td->td_frame;
	oonstack = sigonstack(alpha_pal_rdusp());
	rndfsize = ((sizeof(sf) + 15) / 16) * 16;

	/* save user context */
	bzero(&sf, sizeof(sf));
	sf.sf_uc.uc_sigmask = *mask;
	sf.sf_uc.uc_stack = td->td_sigstk;
	sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK)
	    ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE;
	sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0;

	fill_regs(td, (struct reg *)sf.sf_uc.uc_mcontext.mc_regs);
	sf.sf_uc.uc_mcontext.mc_regs[R_SP] = alpha_pal_rdusp();
	sf.sf_uc.uc_mcontext.mc_regs[R_ZERO] = 0xACEDBADE;   /* magic number */
	sf.sf_uc.uc_mcontext.mc_regs[R_PS] = frame->tf_regs[FRAME_PS];
	sf.sf_uc.uc_mcontext.mc_regs[R_PC] = frame->tf_regs[FRAME_PC];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A0] =
	    frame->tf_regs[FRAME_TRAPARG_A0];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A1] =
	    frame->tf_regs[FRAME_TRAPARG_A1];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A2] =
	    frame->tf_regs[FRAME_TRAPARG_A2];

	/*
	 * Allocate and validate space for the signal handler
	 * context. Note that if the stack is in P0 space, the
	 * call to grow() is a nop, and the useracc() check
	 * will fail if the process has not already allocated
	 * the space with a `brk'.
	 */
	if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack &&
	    SIGISMEMBER(psp->ps_sigonstack, sig)) {
		sfp = (struct sigframe4 *)((caddr_t)td->td_sigstk.ss_sp +
		    td->td_sigstk.ss_size - rndfsize);
#if defined(COMPAT_43)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
#endif
	} else
		sfp = (struct sigframe4 *)(alpha_pal_rdusp() - rndfsize);
	mtx_unlock(&psp->ps_mtx);
	PROC_UNLOCK(p);

	/* save the floating-point state, if necessary, then copy it. */
	alpha_fpstate_save(td, 1);
	sf.sf_uc.uc_mcontext.mc_ownedfp = td->td_md.md_flags & MDTD_FPUSED;
	bcopy(&td->td_pcb->pcb_fp,
	      (struct fpreg *)sf.sf_uc.uc_mcontext.mc_fpregs,
	      sizeof(struct fpreg));
	sf.sf_uc.uc_mcontext.mc_fp_control = td->td_pcb->pcb_fp_control;

#ifdef COMPAT_OSF1
	/*
	 * XXX Create an OSF/1-style sigcontext and associated goo.
	 */
#endif

	/*
	 * copy the frame out to userland.
	 */
	if (copyout((caddr_t)&sf, (caddr_t)sfp, sizeof(sf)) != 0) {
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
		PROC_LOCK(p);
		sigexit(td, SIGILL);
		return;
	}

	/*
	 * Set up the registers to return to sigcode.
	 */
	frame->tf_regs[FRAME_PC] = PS_STRINGS - szfreebsd4_sigcode;
	frame->tf_regs[FRAME_A0] = sig;
	PROC_LOCK(p);
	mtx_lock(&psp->ps_mtx);
	if (SIGISMEMBER(psp->ps_siginfo, sig)) {
		frame->tf_regs[FRAME_A1] = (u_int64_t)&(sfp->sf_si);

		/* Fill in POSIX parts */
		sf.sf_si = kp->ksi_info;
	}
	else
		frame->tf_regs[FRAME_A1] = code;

	frame->tf_regs[FRAME_A2] = (u_int64_t)&(sfp->sf_uc);
	frame->tf_regs[FRAME_T12] = (u_int64_t)catcher;	/* t12 is pv */
	frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
	alpha_pal_wrusp((unsigned long)sfp);
}
#endif	/* COMPAT_FREEBSD4 */

void
sendsig(sig_t catcher, ksiginfo_t *kp, sigset_t *mask)
{
	struct proc *p;
	struct thread *td;
	struct trapframe *frame;
	struct sigacts *psp;
	struct sigframe sf, *sfp;
	int oonstack, rndfsize;
	int sig;
	int code;

	td = curthread;
	p = td->td_proc;
	PROC_LOCK_ASSERT(p, MA_OWNED);
	sig = kp->ksi_signo;
	code = kp->ksi_code;
	psp = p->p_sigacts;
	mtx_assert(&psp->ps_mtx, MA_OWNED);
#ifdef COMPAT_FREEBSD4
	if (SIGISMEMBER(psp->ps_freebsd4, sig)) {
		freebsd4_sendsig(catcher, kp, mask);
		return;
	}
#endif
#ifdef COMPAT_43
	if (SIGISMEMBER(psp->ps_osigset, sig)) {
		osendsig(catcher, kp, mask);
		return;
	}
#endif

	frame = td->td_frame;
	oonstack = sigonstack(alpha_pal_rdusp());
	rndfsize = ((sizeof(sf) + 15) / 16) * 16;

	/* save user context */
	bzero(&sf, sizeof(struct sigframe));
	sf.sf_uc.uc_sigmask = *mask;
	sf.sf_uc.uc_stack = td->td_sigstk;
	sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK)
	    ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE;
	sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0;

	fill_regs(td, (struct reg *)sf.sf_uc.uc_mcontext.mc_regs);
	sf.sf_uc.uc_mcontext.mc_regs[R_SP] = alpha_pal_rdusp();
	sf.sf_uc.uc_mcontext.mc_regs[R_ZERO] = 0xACEDBADE;   /* magic number */
	sf.sf_uc.uc_mcontext.mc_regs[R_PS] = frame->tf_regs[FRAME_PS];
	sf.sf_uc.uc_mcontext.mc_regs[R_PC] = frame->tf_regs[FRAME_PC];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A0] =
	    frame->tf_regs[FRAME_TRAPARG_A0];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A1] =
	    frame->tf_regs[FRAME_TRAPARG_A1];
	sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A2] =
	    frame->tf_regs[FRAME_TRAPARG_A2];
	sf.sf_uc.uc_mcontext.mc_format = _MC_REV0_SIGFRAME;

	/*
	 * Allocate and validate space for the signal handler
	 * context. Note that if the stack is in P0 space, the
	 * call to grow() is a nop, and the useracc() check
	 * will fail if the process has not already allocated
	 * the space with a `brk'.
	 */
	if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack &&
	    SIGISMEMBER(psp->ps_sigonstack, sig)) {
		sfp = (struct sigframe *)((caddr_t)td->td_sigstk.ss_sp +
		    td->td_sigstk.ss_size - rndfsize);
#if defined(COMPAT_43)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
#endif
	} else
		sfp = (struct sigframe *)(alpha_pal_rdusp() - rndfsize);
	mtx_unlock(&psp->ps_mtx);
	PROC_UNLOCK(p);

	/* save the floating-point state, if necessary, then copy it. */
	alpha_fpstate_save(td, 1);
	sf.sf_uc.uc_mcontext.mc_ownedfp = td->td_md.md_flags & MDTD_FPUSED;
	bcopy(&td->td_pcb->pcb_fp,
	      (struct fpreg *)sf.sf_uc.uc_mcontext.mc_fpregs,
	      sizeof(struct fpreg));
	sf.sf_uc.uc_mcontext.mc_fp_control = td->td_pcb->pcb_fp_control;

#ifdef COMPAT_OSF1
	/*
	 * XXX Create an OSF/1-style sigcontext and associated goo.
	 */
#endif

	/*
	 * copy the frame out to userland.
	 */
	if (copyout((caddr_t)&sf, (caddr_t)sfp, sizeof(sf)) != 0) {
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
		PROC_LOCK(p);
		sigexit(td, SIGILL);
		return;
	}

	/*
	 * Set up the registers to return to sigcode.
	 */
	frame->tf_regs[FRAME_PC] = PS_STRINGS - szsigcode;
	frame->tf_regs[FRAME_A0] = sig;
	PROC_LOCK(p);
	mtx_lock(&psp->ps_mtx);
	if (SIGISMEMBER(psp->ps_siginfo, sig)) {
		frame->tf_regs[FRAME_A1] = (u_int64_t)&(sfp->sf_si);

		/* Fill in POSIX parts */
		sf.sf_si = kp->ksi_info;
	}
	else
		frame->tf_regs[FRAME_A1] = code;

	frame->tf_regs[FRAME_A2] = (u_int64_t)&(sfp->sf_uc);
	frame->tf_regs[FRAME_T12] = (u_int64_t)catcher;	/* t12 is pv */
	frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
	alpha_pal_wrusp((unsigned long)sfp);
}

/*
 * System call to cleanup state after a signal
 * has been taken.  Reset signal mask and
 * stack state from context left by sendsig (above).
 * Return to previous pc and psl as specified by
 * context left by sendsig. Check carefully to
 * make sure that the user has not modified the
 * state to gain improper privileges.
 *
 * MPSAFE
 */
#ifdef COMPAT_43
int
osigreturn(struct thread *td,
	struct osigreturn_args /* {
		struct osigcontext *sigcntxp;
	} */ *uap)
{
	struct osigcontext *scp, ksc;
	struct proc *p = td->td_proc;

	scp = uap->sigcntxp;

	/*
	 * Fetch the entire context structure at once for speed.
	 */
	if (copyin((caddr_t)scp, (caddr_t)&ksc, sizeof ksc))
		return (EFAULT);

	/*
	 * XXX - Should we do this. What if we get a "handcrafted"
	 * but valid sigcontext that hasn't the magic number?
	 */
	if (ksc.sc_regs[R_ZERO] != 0xACEDBADE)		/* magic number */
		return (EINVAL);

	PROC_LOCK(p);
#if defined(COMPAT_43)
	/*
	 * Restore the user-supplied information
	 */
	if (ksc.sc_onstack)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
	else
		td->td_sigstk.ss_flags &= ~SS_ONSTACK;
#endif

	/*
	 * longjmp is still implemented by calling osigreturn. The new
	 * sigmask is stored in sc_reserved, sc_mask is only used for
	 * backward compatibility.
	 */
	SIGSETOLD(td->td_sigmask, ksc.sc_mask);
	SIG_CANTMASK(td->td_sigmask);
	signotify(td);
	PROC_UNLOCK(p);

	set_regs(td, (struct reg *)ksc.sc_regs);
	td->td_frame->tf_regs[FRAME_PC] = ksc.sc_pc;
	td->td_frame->tf_regs[FRAME_PS] =
	    (ksc.sc_ps | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR;
	td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */

	alpha_pal_wrusp(ksc.sc_regs[R_SP]);

	/* XXX ksc.sc_ownedfp ? */
	alpha_fpstate_drop(td);
	bcopy((struct fpreg *)ksc.sc_fpregs, &td->td_pcb->pcb_fp,
	    sizeof(struct fpreg));
	td->td_pcb->pcb_fp_control = ksc.sc_fp_control;
	return (EJUSTRETURN);
}
#endif /* COMPAT_43 */

#ifdef COMPAT_FREEBSD4
/*
 * MPSAFE
 */
int
freebsd4_sigreturn(struct thread *td,
	struct freebsd4_sigreturn_args /* {
		const struct ucontext4 *sigcntxp;
	} */ *uap)
{
	struct ucontext4 uc;
	const struct ucontext4 *ucp;
	struct pcb *pcb;
	unsigned long val;
	struct proc *p;
	int error;

	ucp = uap->sigcntxp;
	pcb = td->td_pcb;
	p = td->td_proc;

	/*
	 * Fetch the entire context structure at once for speed.
	 * Note that struct osigcontext is smaller than a ucontext_t,
	 * so even if copyin() faults, we may have actually gotten a complete
	 * struct osigcontext.
	 */
	error = copyin(ucp, &uc, sizeof(ucontext_t));
	if (error != 0) {
#ifdef COMPAT_43
		if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE)
			return osigreturn(td, (struct osigreturn_args *)uap);
#endif
		return (error);
	}

#ifdef COMPAT_43
	 if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE)
		  return osigreturn(td, (struct osigreturn_args *)uap);
#endif

	/*
	 * Restore the user-supplied information
	 */
	set_regs(td, (struct reg *)uc.uc_mcontext.mc_regs);
	val = (uc.uc_mcontext.mc_regs[R_PS] | ALPHA_PSL_USERSET) &
	    ~ALPHA_PSL_USERCLR;
	td->td_frame->tf_regs[FRAME_PS] = val;
	td->td_frame->tf_regs[FRAME_PC] = uc.uc_mcontext.mc_regs[R_PC];
	td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
	alpha_pal_wrusp(uc.uc_mcontext.mc_regs[R_SP]);

	PROC_LOCK(p);
#if defined(COMPAT_43)
	if (uc.uc_mcontext.mc_onstack & 1)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
	else
		td->td_sigstk.ss_flags &= ~SS_ONSTACK;
#endif

	td->td_sigmask = uc.uc_sigmask;
	SIG_CANTMASK(td->td_sigmask);
	signotify(td);
	PROC_UNLOCK(p);

	/* XXX ksc.sc_ownedfp ? */
	alpha_fpstate_drop(td);
	bcopy((struct fpreg *)uc.uc_mcontext.mc_fpregs,
	      &td->td_pcb->pcb_fp, sizeof(struct fpreg));
	td->td_pcb->pcb_fp_control = uc.uc_mcontext.mc_fp_control;
	return (EJUSTRETURN);
}
#endif /* COMPAT_FREEBSD4 */

/*
 * MPSAFE
 */
int
sigreturn(struct thread *td,
	struct sigreturn_args /* {
		const struct __ucontext *sigcntxp;
	} */ *uap)
{
	ucontext_t uc;
	const ucontext_t *ucp;
	struct pcb *pcb;
	unsigned long val;
	struct proc *p;
	int error;

	ucp = uap->sigcntxp;
	pcb = td->td_pcb;
	p = td->td_proc;

	/*
	 * Fetch the entire context structure at once for speed.
	 * Note that struct osigcontext is smaller than a ucontext_t,
	 * so even if copyin() faults, we may have actually gotten a complete
	 * struct osigcontext.
	 * XXX we'll *still* be getting osigcontext's here due to longjmp(3)
	 * brain damage.
	 */
	error = copyin(ucp, &uc, sizeof(ucontext_t));
	if (error != 0) {
#ifdef COMPAT_43
		if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE)
			return osigreturn(td, (struct osigreturn_args *)uap);
#endif
		return (error);
	}
#ifdef COMPAT_43
	if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE)
		return osigreturn(td, (struct osigreturn_args *)uap);
#endif

	/*
	 * Restore the user-supplied information
	 */
	if ((error = set_fpcontext(td, &uc.uc_mcontext)) != 0)
		return (error);
	set_regs(td, (struct reg *)uc.uc_mcontext.mc_regs);
	val = (uc.uc_mcontext.mc_regs[R_PS] | ALPHA_PSL_USERSET) &
	    ~ALPHA_PSL_USERCLR;
	td->td_frame->tf_regs[FRAME_PS] = val;
	td->td_frame->tf_regs[FRAME_PC] = uc.uc_mcontext.mc_regs[R_PC];
	td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
	alpha_pal_wrusp(uc.uc_mcontext.mc_regs[R_SP]);

	PROC_LOCK(p);
#if defined(COMPAT_43)
	if (uc.uc_mcontext.mc_onstack & 1)
		td->td_sigstk.ss_flags |= SS_ONSTACK;
	else
		td->td_sigstk.ss_flags &= ~SS_ONSTACK;
#endif

	td->td_sigmask = uc.uc_sigmask;
	SIG_CANTMASK(td->td_sigmask);
	signotify(td);
	PROC_UNLOCK(p);

	return (EJUSTRETURN);
}

/*
 * Machine dependent boot() routine
 *
 * I haven't seen anything to put here yet
 * Possibly some stuff might be grafted back here from boot()
 */
void
cpu_boot(int howto)
{
}

/* Get current clock frequency for the given cpu id. */
int
cpu_est_clockrate(int cpu_id, uint64_t *rate)
{

	return (ENXIO);
}

/*
 * Shutdown the CPU as much as possible
 */
void
cpu_halt(void)
{
	prom_halt(1);
}

static int	cpu_idle_hlt = 1;
SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW,
    &cpu_idle_hlt, 0, "Idle loop HLT enable");

/*
 * call platform specific code to halt (until next interrupt) for the idle loop
 */
void
cpu_idle(void)
{
	if (cpu_idle_hlt && platform.cpu_idle != NULL)
		platform.cpu_idle();
}

/*
 * Clear registers on exec
 */
void
exec_setregs(struct thread *td, u_long entry, u_long stack, u_long ps_strings)
{
	struct trapframe *tfp = td->td_frame;

	bzero(tfp->tf_regs, FRAME_SIZE * sizeof tfp->tf_regs[0]);
	bzero(&td->td_pcb->pcb_fp, sizeof td->td_pcb->pcb_fp);
	td->td_pcb->pcb_fp_control = 0;
	td->td_pcb->pcb_fp.fpr_cr = (FPCR_DYN_NORMAL
					  | FPCR_INVD | FPCR_DZED
					  | FPCR_OVFD | FPCR_INED
					  | FPCR_UNFD);

	alpha_pal_wrusp(stack);
	tfp->tf_regs[FRAME_PS] = ALPHA_PSL_USERSET;
	tfp->tf_regs[FRAME_PC] = entry & ~3;

	tfp->tf_regs[FRAME_A0] = stack;			/* a0 = sp */
	tfp->tf_regs[FRAME_A1] = 0;			/* a1 = rtld cleanup */
	tfp->tf_regs[FRAME_A2] = 0;			/* a2 = rtld object */
	tfp->tf_regs[FRAME_A3] = PS_STRINGS;		/* a3 = ps_strings */
	tfp->tf_regs[FRAME_T12] = tfp->tf_regs[FRAME_PC];	/* a.k.a. PV */
	tfp->tf_regs[FRAME_FLAGS] = 0;			/* full restore */

	td->td_md.md_flags &= ~MDTD_FPUSED;
	alpha_fpstate_drop(td);
}

int
ptrace_set_pc(struct thread *td, unsigned long addr)
{
	struct trapframe *tp = td->td_frame;
	tp->tf_regs[FRAME_PC] = addr;
	return 0;
}

static int
ptrace_read_int(struct thread *td, vm_offset_t addr, u_int32_t *v)
{
	struct iovec iov;
	struct uio uio;
	iov.iov_base = (caddr_t) v;
	iov.iov_len = sizeof(u_int32_t);
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = (off_t)addr;
	uio.uio_resid = sizeof(u_int32_t);
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_rw = UIO_READ;
	uio.uio_td = td;
	return proc_rwmem(td->td_proc, &uio);
}

static int
ptrace_write_int(struct thread *td, vm_offset_t addr, u_int32_t v)
{
	struct iovec iov;
	struct uio uio;
	iov.iov_base = (caddr_t) &v;
	iov.iov_len = sizeof(u_int32_t);
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = (off_t)addr;
	uio.uio_resid = sizeof(u_int32_t);
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_rw = UIO_WRITE;
	uio.uio_td = td;
	return proc_rwmem(td->td_proc, &uio);
}

static u_int64_t
ptrace_read_register(struct thread *td, int regno)
{
	static int reg_to_frame[32] = {
		FRAME_V0,
		FRAME_T0,
		FRAME_T1,
		FRAME_T2,
		FRAME_T3,
		FRAME_T4,
		FRAME_T5,
		FRAME_T6,
		FRAME_T7,

		FRAME_S0,
		FRAME_S1,
		FRAME_S2,
		FRAME_S3,
		FRAME_S4,
		FRAME_S5,
		FRAME_S6,

		FRAME_A0,
		FRAME_A1,
		FRAME_A2,
		FRAME_A3,
		FRAME_A4,
		FRAME_A5,

		FRAME_T8,
		FRAME_T9,
		FRAME_T10,
		FRAME_T11,
		FRAME_RA,
		FRAME_T12,
		FRAME_AT,
		FRAME_GP,
		FRAME_SP,
		-1,		/* zero */
	};

	if (regno == R_ZERO)
		return 0;

	return td->td_frame->tf_regs[reg_to_frame[regno]];
}


static int
ptrace_clear_bpt(struct thread *td, struct mdbpt *bpt)
{
	return ptrace_write_int(td, bpt->addr, bpt->contents);
}

static int
ptrace_set_bpt(struct thread *td, struct mdbpt *bpt)
{
	int error;
	u_int32_t bpins = 0x00000080;
	error = ptrace_read_int(td, bpt->addr, &bpt->contents);
	if (error)
		return error;
	return ptrace_write_int(td, bpt->addr, bpins);
}

int
ptrace_clear_single_step(struct thread *td)
{
	if (td->td_md.md_flags & MDTD_STEP2) {
		ptrace_clear_bpt(td, &td->td_md.md_sstep[1]);
		ptrace_clear_bpt(td, &td->td_md.md_sstep[0]);
		td->td_md.md_flags &= ~MDTD_STEP2;
	} else if (td->td_md.md_flags & MDTD_STEP1) {
		ptrace_clear_bpt(td, &td->td_md.md_sstep[0]);
		td->td_md.md_flags &= ~MDTD_STEP1;
	}
	return 0;
}

int
ptrace_single_step(struct thread *td)
{
	int error;
	vm_offset_t pc = td->td_frame->tf_regs[FRAME_PC];
	alpha_instruction ins;
	vm_offset_t addr[2];	/* places to set breakpoints */
	int count = 0;		/* count of breakpoints */

	if (td->td_md.md_flags & (MDTD_STEP1|MDTD_STEP2))
		panic("ptrace_single_step: step breakpoints not removed");

	error = ptrace_read_int(td, pc, &ins.bits);
	if (error)
		return (error);

	switch (ins.branch_format.opcode) {

	case op_j:
		/* Jump: target is register value */
		addr[0] = ptrace_read_register(td, ins.jump_format.rs) & ~3;
		count = 1;
		break;

	case op_br:
	case op_fbeq:
	case op_fblt:
	case op_fble:
	case op_bsr:
	case op_fbne:
	case op_fbge:
	case op_fbgt:
	case op_blbc:
	case op_beq:
	case op_blt:
	case op_ble:
	case op_blbs:
	case op_bne:
	case op_bge:
	case op_bgt:
		/* Branch: target is pc+4+4*displacement */
		addr[0] = pc + 4;
		addr[1] = pc + 4 + 4 * ins.branch_format.displacement;
		count = 2;
		break;

	default:
		addr[0] = pc + 4;
		count = 1;
	}

	td->td_md.md_sstep[0].addr = addr[0];
	error = ptrace_set_bpt(td, &td->td_md.md_sstep[0]);
	if (error)
		return (error);
	if (count == 2) {
		td->td_md.md_sstep[1].addr = addr[1];
		error = ptrace_set_bpt(td, &td->td_md.md_sstep[1]);
		if (error) {
			ptrace_clear_bpt(td, &td->td_md.md_sstep[0]);
			return (error);
		}
		td->td_md.md_flags |= MDTD_STEP2;
	} else
		td->td_md.md_flags |= MDTD_STEP1;

	return (error);
}

int
alpha_pa_access(vm_offset_t pa)
{
#if 0
	int i;

	for (i = 0; phys_avail[i] != 0; i += 2) {
		if (pa < phys_avail[i])
			continue;
		if (pa < phys_avail[i+1])
			return VM_PROT_READ|VM_PROT_WRITE;
	}
	return 0;
#else
	return VM_PROT_READ|VM_PROT_WRITE;
#endif
}

/*
 * Construct a PCB from a trapframe. This is called from kdb_trap() where
 * we want to start a backtrace from the function that caused us to enter
 * the debugger. We have the context in the trapframe, but base the trace
 * on the PCB. The PCB doesn't have to be perfect, as long as it contains
 * enough for a backtrace.
 */
void
makectx(struct trapframe *tf, struct pcb *pcb)
{

	pcb->pcb_context[0] = tf->tf_regs[FRAME_S0];
	pcb->pcb_context[1] = tf->tf_regs[FRAME_S1];
	pcb->pcb_context[2] = tf->tf_regs[FRAME_S2];
	pcb->pcb_context[3] = tf->tf_regs[FRAME_S3];
	pcb->pcb_context[4] = tf->tf_regs[FRAME_S4];
	pcb->pcb_context[5] = tf->tf_regs[FRAME_S5];
	pcb->pcb_context[6] = tf->tf_regs[FRAME_S6];
	pcb->pcb_context[7] = tf->tf_regs[FRAME_PC];
	pcb->pcb_context[8] = tf->tf_regs[FRAME_PS];
	pcb->pcb_hw.apcb_ksp = tf->tf_regs[FRAME_SP];
}

int
fill_regs(td, regs)
	struct thread *td;
	struct reg *regs;
{
	struct pcb *pcb = td->td_pcb;
	struct trapframe *tp = td->td_frame;

#define C(r)	regs->r_regs[R_ ## r] = tp->tf_regs[FRAME_ ## r]

	C(V0);
	C(T0); C(T1); C(T2); C(T3); C(T4); C(T5); C(T6); C(T7);
	C(S0); C(S1); C(S2); C(S3); C(S4); C(S5); C(S6);
	C(A0); C(A1); C(A2); C(A3); C(A4); C(A5);
	C(T8); C(T9); C(T10); C(T11);
	C(RA); C(T12); C(AT); C(GP);

#undef C

	regs->r_regs[R_ZERO] = tp->tf_regs[FRAME_PC];
	regs->r_regs[R_SP] = pcb->pcb_hw.apcb_usp;

	return (0);
}

int
set_regs(td, regs)
	struct thread *td;
	struct reg *regs;
{
	struct pcb *pcb = td->td_pcb;
	struct trapframe *tp = td->td_frame;

#define C(r)	tp->tf_regs[FRAME_ ## r] = regs->r_regs[R_ ## r]

	C(V0);
	C(T0); C(T1); C(T2); C(T3); C(T4); C(T5); C(T6); C(T7);
	C(S0); C(S1); C(S2); C(S3); C(S4); C(S5); C(S6);
	C(A0); C(A1); C(A2); C(A3); C(A4); C(A5);
	C(T8); C(T9); C(T10); C(T11);
	C(RA); C(T12); C(AT); C(GP);

#undef C

	tp->tf_regs[FRAME_PC] = regs->r_regs[R_ZERO];
	pcb->pcb_hw.apcb_usp = regs->r_regs[R_SP];

	return (0);
}

int
get_mcontext(struct thread *td, mcontext_t *mcp, int flags)
{
	/*
	 * Use a trapframe for getsetcontext, so just copy the
	 * threads trapframe.
	 */
	bcopy(td->td_frame, &mcp->mc_regs, sizeof(struct trapframe));
	if (flags & GET_MC_CLEAR_RET) {
		mcp->mc_regs[FRAME_V0] = 0;
		mcp->mc_regs[FRAME_A4] = 0;
		mcp->mc_regs[FRAME_A3] = 0;
	}

	/*
	 * When the thread is the current thread, the user stack pointer
	 * is not in the PCB; it must be read from the PAL.
	 */
	if (td == curthread) {
		mcp->mc_regs[FRAME_SP] = alpha_pal_rdusp();
		mcp->mc_thrptr = alpha_pal_rdunique();
	} else
		mcp->mc_thrptr = 0;

	mcp->mc_format = _MC_REV0_TRAPFRAME;
	PROC_LOCK(curthread->td_proc);
	mcp->mc_onstack = sigonstack(alpha_pal_rdusp()) ? 1 : 0;
	PROC_UNLOCK(curthread->td_proc);
	get_fpcontext(td, mcp);
	return (0);
}

int
set_mcontext(struct thread *td, const mcontext_t *mcp)
{
	int ret;
	unsigned long val;

	if ((mcp->mc_format != _MC_REV0_TRAPFRAME) &&
	    (mcp->mc_format != _MC_REV0_SIGFRAME))
		return (EINVAL);
	else if ((ret = set_fpcontext(td, mcp)) != 0)
		return (ret);

	/*
	 * NOTE: We only need to restore mc_thrptr when the ucontext format
	 * is _MC_REV0_TRAPFRAME. Only get_mcontext() above creates such
	 * contexts and that's also the only place where we save the thread
	 * pointer in the context.
	 */
	if (mcp->mc_format == _MC_REV0_SIGFRAME) {
		set_regs(td, (struct reg *)&mcp->mc_regs);
		val = (mcp->mc_regs[R_PS] | ALPHA_PSL_USERSET) &
		    ~ALPHA_PSL_USERCLR;
		td->td_frame->tf_regs[FRAME_PS] = val;
		td->td_frame->tf_regs[FRAME_PC] = mcp->mc_regs[R_PC];
		td->td_frame->tf_regs[FRAME_FLAGS] = 0;
		if (td == curthread)
			alpha_pal_wrusp(mcp->mc_regs[R_SP]);
	} else {
		if (td == curthread) {
			alpha_pal_wrusp(mcp->mc_regs[FRAME_SP]);
			alpha_pal_wrunique(mcp->mc_thrptr);
		} else {
			td->td_pcb->pcb_hw.apcb_usp = mcp->mc_regs[FRAME_SP];
			td->td_pcb->pcb_hw.apcb_unique = mcp->mc_thrptr;
		}

		/*
		 * The context is a trapframe, so just copy it over the
		 * threads frame.
		 */
		bcopy(&mcp->mc_regs, td->td_frame, sizeof(struct trapframe));
	}
	return (0);
}

static void
get_fpcontext(struct thread *td, mcontext_t *mcp)
{
	register_t s;

	s = intr_disable();
	if ((td->td_md.md_flags & MDTD_FPUSED) == 0) {
		intr_restore(s);
		mcp->mc_ownedfp = _MC_FPOWNED_NONE;
	} else if (PCPU_GET(fpcurthread) == td) {
		/* See comments in alpha_fpstate_save() regarding FEN. */
		if (td != curthread)
			alpha_pal_wrfen(1);
		/*
		 * The last field (fpr_cr) of struct fpreg isn't
		 * included in mc_fpregs, but it immediately follows
		 * it in mcontext_t.
		 */
		savefpstate((struct fpreg *)&mcp->mc_fpregs);
		if (td != curthread)
			alpha_pal_wrfen(0);
		intr_restore(s);
		mcp->mc_ownedfp = _MC_FPOWNED_FPU;
	} else {
		/*
		 * The thread doesn't own the FPU so get the state from
		 * the PCB.
		 */
		intr_restore(s);
		bcopy(&td->td_pcb->pcb_fp, &mcp->mc_fpregs,
		    sizeof(td->td_pcb->pcb_fp));
		mcp->mc_ownedfp = _MC_FPOWNED_PCB;
	}
	/* There's no harm in always doing the following. */
	mcp->mc_fp_control = td->td_pcb->pcb_fp_control;
}

static int
set_fpcontext(struct thread *td, const mcontext_t *mcp)
{
	register_t s;

	if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) {
		/* XXX - Drop fpu state so we get a clean state? */
		alpha_fpstate_drop(td);
	}
	else if ((mcp->mc_ownedfp != _MC_FPOWNED_FPU) &&
	    (mcp->mc_ownedfp != _MC_FPOWNED_PCB))
		return (EINVAL);
	else {
		s = intr_disable();
		if (PCPU_GET(fpcurthread) == td) {
			/*
			 * The last field (fpr_cr) of struct fpreg isn't
			 * included in mc_fpregs, but it immediately follows
			 * it in mcontext_t.
			 */
			restorefpstate((struct fpreg *)&mcp->mc_fpregs);
			intr_restore(s);
		}
		else {
			/* Just save the state in the PCB. */
			intr_restore(s);
			bcopy(&mcp->mc_fpregs, &td->td_pcb->pcb_fp,
			    sizeof (td->td_pcb->pcb_fp));
		}
		td->td_pcb->pcb_fp_control = mcp->mc_fp_control;
	}
	return (0);
}

int
fill_dbregs(struct thread *td, struct dbreg *dbregs)
{

	return (ENOSYS);
}

int
set_dbregs(struct thread *td, struct dbreg *dbregs)
{

	return (ENOSYS);
}

int
fill_fpregs(td, fpregs)
	struct thread *td;
	struct fpreg *fpregs;
{
	alpha_fpstate_save(td, 0);

	bcopy(&td->td_pcb->pcb_fp, fpregs, sizeof *fpregs);
	return (0);
}

int
set_fpregs(td, fpregs)
	struct thread *td;
	struct fpreg *fpregs;
{
	alpha_fpstate_drop(td);

	bcopy(fpregs, &td->td_pcb->pcb_fp, sizeof *fpregs);
	return (0);
}

static int
sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS)
{
	int error;
	error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2,
		req);
	if (!error && req->newptr)
		resettodr();
	return (error);
}

SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW,
	&adjkerntz, 0, sysctl_machdep_adjkerntz, "I", "");

SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
	CTLFLAG_RW, &disable_rtc_set, 0, "");

SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock,
	CTLFLAG_RW, &wall_cmos_clock, 0, "");

void
alpha_fpstate_check(struct thread *td)
{
	/*
	 * For SMP, we should check the fpcurthread of each cpu.
	 */
#ifndef SMP
	register_t s;

	s = intr_disable();
	if (td->td_pcb->pcb_hw.apcb_flags & ALPHA_PCB_FLAGS_FEN)
		if (td != PCPU_GET(fpcurthread))
			panic("alpha_check_fpcurthread: bogus");
	intr_restore(s);
#endif
}

#define SET_FEN(td) \
	(td)->td_pcb->pcb_hw.apcb_flags |= ALPHA_PCB_FLAGS_FEN

#define CLEAR_FEN(td) \
	(td)->td_pcb->pcb_hw.apcb_flags &= ~ALPHA_PCB_FLAGS_FEN

/*
 * Save the floating point state in the pcb. Use this to get read-only
 * access to the floating point state. If write is true, the current
 * fp process is cleared so that fp state can safely be modified. The
 * process will automatically reload the changed state by generating a 
 * FEN trap.
 */
void
alpha_fpstate_save(struct thread *td, int write)
{
	register_t s;

	s = intr_disable();
	if (td != NULL && td == PCPU_GET(fpcurthread)) {
		/*
		 * If curthread != fpcurthread, then we need to enable FEN 
		 * so that we can dump the fp state.
		 */
		alpha_pal_wrfen(1);

		/*
		 * Save the state in the pcb.
		 */
		savefpstate(&td->td_pcb->pcb_fp);

		if (write) {
			/*
			 * If fpcurthread == curthread, just ask the
			 * PALcode to disable FEN, otherwise we must
			 * clear the FEN bit in fpcurthread's pcb.
			 */
			if (PCPU_GET(fpcurthread) == curthread)
				alpha_pal_wrfen(0);
			else
				CLEAR_FEN(PCPU_GET(fpcurthread));
			PCPU_SET(fpcurthread, NULL);
		} else {
			/*
			 * Make sure that we leave FEN enabled if
			 * curthread == fpcurthread. We must have at most
			 * one process with FEN enabled. Note that FEN 
			 * must already be set in fpcurthread's pcb.
			 */
			if (curthread != PCPU_GET(fpcurthread))
				alpha_pal_wrfen(0);
		}
	}
	intr_restore(s);
}

/*
 * Relinquish ownership of the FP state. This is called instead of
 * alpha_save_fpstate() if the entire FP state is being changed
 * (e.g. on sigreturn).
 */
void
alpha_fpstate_drop(struct thread *td)
{
	register_t s;

	s = intr_disable();
	if (td == PCPU_GET(fpcurthread)) {
		if (td == curthread) {
			/*
			 * Disable FEN via the PALcode. This will
			 * clear the bit in the pcb as well.
			 */
			alpha_pal_wrfen(0);
		} else {
			/*
			 * Clear the FEN bit of the pcb.
			 */
			CLEAR_FEN(td);
		}
		PCPU_SET(fpcurthread, NULL);
	}
	intr_restore(s);
}

/*
 * Switch the current owner of the fp state to p, reloading the state
 * from the pcb.
 */
void
alpha_fpstate_switch(struct thread *td)
{
	register_t s;

	/*
	 * Enable FEN so that we can access the fp registers.
	 */
	s = intr_disable();
	alpha_pal_wrfen(1);
	if (PCPU_GET(fpcurthread)) {
		/*
		 * Dump the old fp state if its valid.
		 */
		savefpstate(&PCPU_GET(fpcurthread)->td_pcb->pcb_fp);
		CLEAR_FEN(PCPU_GET(fpcurthread));
	}

	/*
	 * Remember the new FP owner and reload its state.
	 */
	PCPU_SET(fpcurthread, td);
	restorefpstate(&PCPU_GET(fpcurthread)->td_pcb->pcb_fp);

	/*
	 * If the new owner is curthread, leave FEN enabled, otherwise
	 * mark its PCB so that it gets FEN when we context switch to
	 * it later.
	 */
	if (td != curthread) {
		alpha_pal_wrfen(0);
		SET_FEN(td);
	}

	td->td_md.md_flags |= MDTD_FPUSED;
	intr_restore(s);
}

/*
 * Initialise a struct pcpu.
 */
void
cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t sz)
{

	pcpu->pc_idlepcbphys = vtophys((vm_offset_t) &pcpu->pc_idlepcb);
	pcpu->pc_idlepcb.apcb_ksp = (u_int64_t)
		((caddr_t) pcpu + sz - sizeof(struct trapframe));
	pcpu->pc_idlepcb.apcb_ptbr = thread0.td_pcb->pcb_hw.apcb_ptbr;
	pcpu->pc_current_asngen = 1;
}

void
spinlock_enter(void)
{
	struct thread *td;

	td = curthread;
	if (td->td_md.md_spinlock_count == 0)
		td->td_md.md_saved_ipl = intr_disable();
	td->td_md.md_spinlock_count++;
	critical_enter();
}

void
spinlock_exit(void)
{
	struct thread *td;

	td = curthread;
	critical_exit();
	td->td_md.md_spinlock_count--;
	if (td->td_md.md_spinlock_count == 0)
		intr_restore(td->td_md.md_saved_ipl);
}
