code:
/* $OpenBSD: i686_mem.c,v 1.17 2014/12/09 06:58:28 doug Exp $ */
/*
 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
 * 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.
 *
 * $FreeBSD: src/sys/i386/i386/i686_mem.c,v 1.8 1999/10/12 22:53:05 green Exp $
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/memrange.h>

#include <machine/cpufunc.h>
#include <machine/intr.h>
#include <machine/specialreg.h>

/*
 * This code implements a set of MSRs known as MTRR which define caching
 * modes/behavior for various memory ranges.
 */

char *mem_owner_bios = "BIOS";

#define MR_FIXMTRR	(1<<0)

#define mrwithin(mr, a) \
    (((a) >= (mr)->mr_base) && ((a) < ((mr)->mr_base + (mr)->mr_len)))
#define mroverlap(mra, mrb) \
    (mrwithin(mra, mrb->mr_base) || mrwithin(mrb, mra->mr_base))

#define mrvalid(base, len) 						\
    ((!(base & ((1 << 12) - 1))) && 	/* base is multiple of 4k */	\
     ((len) >= (1 << 12)) && 		/* length is >= 4k */		\
     powerof2((len)) && 		/* ... and power of two */	\
     !((base) & ((len) - 1)))		/* range is not discontiuous */

#define mrcopyflags(curr, new) (((curr) & ~MDF_ATTRMASK) | \
	((new) & MDF_ATTRMASK))

#define FIXTOP	((MTRR_N64K * 0x10000) + (MTRR_N16K * 0x4000) + \
	(MTRR_N4K * 0x1000))

void	mrinit(struct mem_range_softc *sc);
int	mrset(struct mem_range_softc *sc,
	    struct mem_range_desc *mrd, int *arg);
void	mrinit_cpu(struct mem_range_softc *sc);
void	mrreload_cpu(struct mem_range_softc *sc);

struct mem_range_ops mrops = {
	mrinit,
	mrset,
	mrinit_cpu,
	mrreload_cpu
};

u_int64_t	mtrrcap, mtrrdef;
u_int64_t	mtrrmask = 0x0000000ffffff000ULL;

struct mem_range_desc	*mem_range_match(struct mem_range_softc *sc,
			     struct mem_range_desc *mrd);
void			 mrfetch(struct mem_range_softc *sc);
int			 mtrrtype(u_int64_t flags);
int			 mrt2mtrr(u_int64_t flags);
int			 mtrr2mrt(int val);
int			 mtrrconflict(u_int64_t flag1, u_int64_t flag2);
void			 mrstore(struct mem_range_softc *sc);
void			 mrstoreone(struct mem_range_softc *sc);
struct mem_range_desc	*mtrrfixsearch(struct mem_range_softc *sc,
			     u_int64_t addr);
int			 mrsetlow(struct mem_range_softc *sc,
			     struct mem_range_desc *mrd, int *arg);
int			 mrsetvariable(struct mem_range_softc *sc,
			     struct mem_range_desc *mrd, int *arg);

/* MTRR type to memory range type conversion */
int mtrrtomrt[] = {
	MDF_UNCACHEABLE,
	MDF_WRITECOMBINE,
	MDF_UNKNOWN,
	MDF_UNKNOWN,
	MDF_WRITETHROUGH,
	MDF_WRITEPROTECT,
	MDF_WRITEBACK
};

int
mtrr2mrt(int val)
{
	if (val < 0 || val >= nitems(mtrrtomrt))
		return MDF_UNKNOWN;
	return mtrrtomrt[val];
}

/*
 * MTRR conflicts. Writeback and uncachable may overlap.
 */
int
mtrrconflict(u_int64_t flag1, u_int64_t flag2)
{
	flag1 &= MDF_ATTRMASK;
	flag2 &= MDF_ATTRMASK;
	if (flag1 == flag2 ||
	    (flag1 == MDF_WRITEBACK && flag2 == MDF_UNCACHEABLE) ||
	    (flag2 == MDF_WRITEBACK && flag1 == MDF_UNCACHEABLE))
		return 0;
	return 1;
}

/*
 * Look for an exactly-matching range.
 */
struct mem_range_desc *
mem_range_match(struct mem_range_softc *sc, struct mem_range_desc *mrd)
{
	struct mem_range_desc	*cand;
	int			 i;

	for (i = 0, cand = sc->mr_desc; i < sc->mr_ndesc; i++, cand++)
		if ((cand->mr_base == mrd->mr_base) &&
		    (cand->mr_len == mrd->mr_len))
			return(cand);
	return(NULL);
}

/*
 * Fetch the current mtrr settings from the current CPU (assumed to all
 * be in sync in the SMP case).  Note that if we are here, we assume
 * that MTRRs are enabled, and we may or may not have fixed MTRRs.
 */
void
mrfetch(struct mem_range_softc *sc)
{
	struct mem_range_desc	*mrd;
	u_int64_t		 msrv;
	int			 i, j, msr, mrt;

	mrd = sc->mr_desc;

	/* We should never be fetching MTRRs from an AP */
	KASSERT(CPU_IS_PRIMARY(curcpu()));

	/* Get fixed-range MTRRs, if the CPU supports them */
	if (sc->mr_cap & MR_FIXMTRR) {
		msr = MSR_MTRRfix64K_00000;
		for (i = 0; i < (MTRR_N64K / 8); i++, msr++) {
			msrv = rdmsr(msr);
			for (j = 0; j < 8; j++, mrd++) {
				mrt = mtrr2mrt(msrv & 0xff);
				if (mrt == MDF_UNKNOWN)
					mrt = MDF_UNCACHEABLE;
				mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
					mrt | MDF_ACTIVE;
				if (mrd->mr_owner[0] == 0)
					strlcpy(mrd->mr_owner, mem_owner_bios,
					    sizeof(mrd->mr_owner));
				msrv = msrv >> 8;
			}
		}

		msr = MSR_MTRRfix16K_80000;
		for (i = 0; i < (MTRR_N16K / 8); i++, msr++) {
			msrv = rdmsr(msr);
			for (j = 0; j < 8; j++, mrd++) {
				mrt = mtrr2mrt(msrv & 0xff);
				if (mrt == MDF_UNKNOWN)
					mrt = MDF_UNCACHEABLE;
				mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
					mrt | MDF_ACTIVE;
				if (mrd->mr_owner[0] == 0)
					strlcpy(mrd->mr_owner, mem_owner_bios,
					    sizeof(mrd->mr_owner));
				msrv = msrv >> 8;
			}
		}

		msr = MSR_MTRRfix4K_C0000;
		for (i = 0; i < (MTRR_N4K / 8); i++, msr++) {
			msrv = rdmsr(msr);
			for (j = 0; j < 8; j++, mrd++) {
				mrt = mtrr2mrt(msrv & 0xff);
				if (mrt == MDF_UNKNOWN)
					mrt = MDF_UNCACHEABLE;
				mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
					mrt | MDF_ACTIVE;
				if (mrd->mr_owner[0] == 0)
					strlcpy(mrd->mr_owner, mem_owner_bios,
					    sizeof(mrd->mr_owner));
				msrv = msrv >> 8;
			}
		}
	}

	/* Get remainder which must be variable MTRRs */
	msr = MSR_MTRRvarBase;
	for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) {
		msrv = rdmsr(msr);
		mrt = mtrr2mrt(msrv & 0xff);
		if (mrt == MDF_UNKNOWN)
			mrt = MDF_UNCACHEABLE;
		mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | mrt;
		mrd->mr_base = msrv & mtrrmask;
		msrv = rdmsr(msr + 1);
		mrd->mr_flags = (msrv & 0x800) ?
			(mrd->mr_flags | MDF_ACTIVE) :
			(mrd->mr_flags & ~MDF_ACTIVE);
		/* Compute the range from the mask. Ick. */
		mrd->mr_len = (~(msrv & mtrrmask) & mtrrmask) + 0x1000;
		if (!mrvalid(mrd->mr_base, mrd->mr_len))
			mrd->mr_flags |= MDF_BOGUS;
		/* If unclaimed and active, must be the BIOS */
		if ((mrd->mr_flags & MDF_ACTIVE) && (mrd->mr_owner[0] == 0))
			strlcpy(mrd->mr_owner, mem_owner_bios,
			    sizeof(mrd->mr_owner));
	}
}

/*
 * Return the MTRR memory type matching a region's flags
 */
int
mtrrtype(u_int64_t flags)
{
	int i;

	flags &= MDF_ATTRMASK;

	for (i = 0; i < nitems(mtrrtomrt); i++) {
		if (mtrrtomrt[i] == MDF_UNKNOWN)
			continue;
		if (flags == mtrrtomrt[i])
			return(i);
	}
	return MDF_UNCACHEABLE;
}

int
mrt2mtrr(u_int64_t flags)
{
	int val;

	val = mtrrtype(flags);

	return val & 0xff;
}

/*
 * Update running CPU(s) MTRRs to match the ranges in the descriptor
 * list.
 *
 * XXX Must be called with interrupts enabled.
 */
void
mrstore(struct mem_range_softc *sc)
{
	disable_intr();				/* disable interrupts */
#ifdef MULTIPROCESSOR
	i386_broadcast_ipi(I386_IPI_MTRR);
#endif
	mrstoreone(sc);
	enable_intr();
}

/*
 * Update the current CPU's MTRRs with those represented in the
 * descriptor list.  Note that we do this wholesale rather than
 * just stuffing one entry; this is simpler (but slower, of course).
 */
void
mrstoreone(struct mem_range_softc *sc)
{
	struct mem_range_desc	*mrd;
	u_int64_t		 msrv;
	int			 i, j, msr;
	u_int			 cr4save;

	mrd = sc->mr_desc;

	cr4save = rcr4();	/* save cr4 */
	if (cr4save & CR4_PGE)
		lcr4(cr4save & ~CR4_PGE);

	/* Flush caches, then disable caches, then disable MTRRs */
	wbinvd();
	lcr0((rcr0() & ~CR0_NW) | CR0_CD);
	wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) & ~0x800);

	/* Set fixed-range MTRRs */
	if (sc->mr_cap & MR_FIXMTRR) {
		msr = MSR_MTRRfix64K_00000;
		for (i = 0; i < (MTRR_N64K / 8); i++, msr++) {
			msrv = 0;
			for (j = 7; j >= 0; j--) {
				msrv = msrv << 8;
				msrv |= mrt2mtrr((mrd + j)->mr_flags);
			}
			wrmsr(msr, msrv);
			mrd += 8;
		}

		msr = MSR_MTRRfix16K_80000;
		for (i = 0, msrv = 0; i < (MTRR_N16K / 8); i++, msr++) {
			for (j = 7; j >= 0; j--) {
				msrv = msrv << 8;
				msrv |= mrt2mtrr((mrd + j)->mr_flags);
			}
			wrmsr(msr, msrv);
			mrd += 8;
		}

		msr = MSR_MTRRfix4K_C0000;
		for (i = 0, msrv = 0; i < (MTRR_N4K / 8); i++, msr++) {
			for (j = 7; j >= 0; j--) {
				msrv = msrv << 8;
				msrv |= mrt2mtrr((mrd + j)->mr_flags);
			}
			wrmsr(msr, msrv);
			mrd += 8;
		}
	}

	/* Set remainder which must be variable MTRRs */
	msr = MSR_MTRRvarBase;
	for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) {
		if (mrd->mr_flags & MDF_ACTIVE) {
			msrv = mrd->mr_base & mtrrmask;
			msrv |= mrt2mtrr(mrd->mr_flags);
		} else
			msrv = 0;

		wrmsr(msr, msrv);

		/* mask/active register */
		if (mrd->mr_flags & MDF_ACTIVE) {
			msrv = 0x800 | (~(mrd->mr_len - 1) & mtrrmask);
		} else
			msrv = 0;

		wrmsr(msr + 1, msrv);
	}

	/* Re-enable caches and MTRRs */
	wrmsr(MSR_MTRRdefType, mtrrdef | 0x800);
	lcr0(rcr0() & ~(CR0_CD | CR0_NW));
	lcr4(cr4save);
}

/*
 * Hunt for the fixed MTRR referencing (addr)
 */
struct mem_range_desc *
mtrrfixsearch(struct mem_range_softc *sc, u_int64_t addr)
{
	struct mem_range_desc *mrd;
	int			i;

	for (i = 0, mrd = sc->mr_desc; i < (MTRR_N64K + MTRR_N16K + MTRR_N4K); i++, mrd++)
		if ((addr >= mrd->mr_base) && (addr < (mrd->mr_base + mrd->mr_len)))
			return(mrd);
	return(NULL);
}

/*
 * Try to satisfy the given range request by manipulating the fixed MTRRs that
 * cover low memory.
 *
 * Note that we try to be generous here; we'll bloat the range out to the
 * next higher/lower boundary to avoid the consumer having to know too much
 * about the mechanisms here.
 */
int
mrsetlow(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
{
	struct mem_range_desc	*first_md, *last_md, *curr_md;

	/* range check */
	if (((first_md = mtrrfixsearch(sc, mrd->mr_base)) == NULL) ||
	    ((last_md = mtrrfixsearch(sc, mrd->mr_base + mrd->mr_len - 1)) == NULL))
		return(EINVAL);

	/* check we aren't doing something risky */
	if (!(mrd->mr_flags & MDF_FORCE))
		for (curr_md = first_md; curr_md <= last_md; curr_md++) {
			if ((curr_md->mr_flags & MDF_ATTRMASK) == MDF_UNKNOWN)
				return (EACCES);
		}

	/* set flags, clear set-by-firmware flag */
	for (curr_md = first_md; curr_md <= last_md; curr_md++) {
		curr_md->mr_flags = mrcopyflags(curr_md->mr_flags & ~MDF_FIRMWARE, mrd->mr_flags);
		bcopy(mrd->mr_owner, curr_md->mr_owner, sizeof(mrd->mr_owner));
	}

	return(0);
}

/*
 * Modify/add a variable MTRR to satisfy the request.
 */
int
mrsetvariable(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
{
	struct mem_range_desc	*curr_md, *free_md;
	int			 i;

	/*
	 * Scan the currently active variable descriptors, look for
	 * one we exactly match (straight takeover) and for possible
	 * accidental overlaps.
	 * Keep track of the first empty variable descriptor in case we
	 * can't perform a takeover.
	 */
	i = (sc->mr_cap & MR_FIXMTRR) ? MTRR_N64K + MTRR_N16K + MTRR_N4K : 0;
	curr_md = sc->mr_desc + i;
	free_md = NULL;
	for (; i < sc->mr_ndesc; i++, curr_md++) {
		if (curr_md->mr_flags & MDF_ACTIVE) {
			/* exact match? */
			if ((curr_md->mr_base == mrd->mr_base) &&
			    (curr_md->mr_len == mrd->mr_len)) {
				/* check we aren't doing something risky */
				if (!(mrd->mr_flags & MDF_FORCE) &&
				    ((curr_md->mr_flags & MDF_ATTRMASK)
				    == MDF_UNKNOWN))
					return (EACCES);
				/* Ok, just hijack this entry */
				free_md = curr_md;
				break;
			}
			/* non-exact overlap ? */
			if (mroverlap(curr_md, mrd)) {
				/* between conflicting region types? */
				if (mtrrconflict(curr_md->mr_flags,
						      mrd->mr_flags))
					return(EINVAL);
			}
		} else if (free_md == NULL) {
			free_md = curr_md;
		}
	}
	/* got somewhere to put it? */
	if (free_md == NULL)
		return(ENOSPC);

	/* Set up new descriptor */
	free_md->mr_base = mrd->mr_base;
	free_md->mr_len = mrd->mr_len;
	free_md->mr_flags = mrcopyflags(MDF_ACTIVE, mrd->mr_flags);
	bcopy(mrd->mr_owner, free_md->mr_owner, sizeof(mrd->mr_owner));
	return(0);
}

/*
 * Handle requests to set memory range attributes by manipulating MTRRs.
 */
int
mrset(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
{
	struct mem_range_desc	*targ;
	int			 error = 0;

	switch(*arg) {
	case MEMRANGE_SET_UPDATE:
		/* make sure that what's being asked for is possible */
		if (!mrvalid(mrd->mr_base, mrd->mr_len) ||
		    mtrrtype(mrd->mr_flags) == -1)
			return(EINVAL);

		/* are the "low memory" conditions applicable? */
		if ((sc->mr_cap & MR_FIXMTRR) &&
		    ((mrd->mr_base + mrd->mr_len) <= FIXTOP)) {
			if ((error = mrsetlow(sc, mrd, arg)) != 0)
				return(error);
		} else {
			/* it's time to play with variable MTRRs */
			if ((error = mrsetvariable(sc, mrd, arg)) != 0)
				return(error);
		}
		break;

	case MEMRANGE_SET_REMOVE:
		if ((targ = mem_range_match(sc, mrd)) == NULL)
			return(ENOENT);
		if (targ->mr_flags & MDF_FIXACTIVE)
			return(EPERM);
		targ->mr_flags &= ~MDF_ACTIVE;
		targ->mr_owner[0] = 0;
		break;

	default:
		return(EOPNOTSUPP);
	}

	/* update the hardware */
	mrstore(sc);
	return(0);
}

/*
 * Work out how many ranges we support, initialise storage for them,
 * fetch the initial settings.
 */
void
mrinit(struct mem_range_softc *sc)
{
	struct mem_range_desc	*mrd;
	uint32_t		 regs[4];
	int			 nmdesc = 0;
	int			 i;

	mtrrcap = rdmsr(MSR_MTRRcap);
	mtrrdef = rdmsr(MSR_MTRRdefType);

	/* For now, bail out if MTRRs are not enabled */
	if (!(mtrrdef & 0x800)) {
		printf("mtrr: CPU supports MTRRs but not enabled by BIOS\n");
		return;
	}
	nmdesc = mtrrcap & 0xff;
	printf("mtrr: Pentium Pro MTRR support, %d var ranges", nmdesc);

	/* If fixed MTRRs supported and enabled */
	if ((mtrrcap & 0x100) && (mtrrdef & 0x400)) {
		sc->mr_cap = MR_FIXMTRR;
		nmdesc += MTRR_N64K + MTRR_N16K + MTRR_N4K;
		printf(", %d fixed ranges", MTRR_N64K + MTRR_N16K + MTRR_N4K);
	}

	printf("\n");

	sc->mr_desc = mallocarray(nmdesc, sizeof(struct mem_range_desc),
	     M_MEMDESC, M_WAITOK|M_ZERO);
	sc->mr_ndesc = nmdesc;

	mrd = sc->mr_desc;

	/* Populate the fixed MTRR entries' base/length */
	if (sc->mr_cap & MR_FIXMTRR) {
		for (i = 0; i < MTRR_N64K; i++, mrd++) {
			mrd->mr_base = i * 0x10000;
			mrd->mr_len = 0x10000;
			mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
		}

		for (i = 0; i < MTRR_N16K; i++, mrd++) {
			mrd->mr_base = i * 0x4000 + 0x80000;
			mrd->mr_len = 0x4000;
			mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
		}

		for (i = 0; i < MTRR_N4K; i++, mrd++) {
			mrd->mr_base = i * 0x1000 + 0xc0000;
			mrd->mr_len = 0x1000;
			mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
		}
	}

	/*
	 * Fetch maximum physical address size supported by the
	 * processor as supported by CPUID leaf function 0x80000008.
	 * If CPUID does not support leaf function 0x80000008, use the
	 * default a 36-bit address size.
	 */
	CPUID(0x80000000, regs[0], regs[1], regs[2], regs[3]);
	if (regs[0] >= 0x80000008) {
		CPUID(0x80000008, regs[0], regs[1], regs[2], regs[3]);
		if (regs[0] & 0xff) {
			mtrrmask = (1ULL << (regs[0] & 0xff)) - 1;
			mtrrmask &= ~0x0000000000000fffULL;
		}
	}

	/*
	 * Get current settings, anything set now is considered to have
	 * been set by the firmware.
	 */
	mrfetch(sc);
	mrd = sc->mr_desc;
	for (i = 0; i < sc->mr_ndesc; i++, mrd++) {
		if (mrd->mr_flags & MDF_ACTIVE)
			mrd->mr_flags |= MDF_FIRMWARE;
	}
}

/*
 * Initialise MTRRs on a cpu from the software state.
 */
void
mrinit_cpu(struct mem_range_softc *sc)
{
	mrstoreone(sc); /* set MTRRs to match BSP */
}

void
mrreload_cpu(struct mem_range_softc *sc)
{
	disable_intr();
	mrstoreone(sc); /* set MTRRs to match BSP */
	enable_intr();
}


code:
/*	$OpenBSD: in_cksum.s,v 1.8 2008/06/26 05:42:10 ray Exp $	*/
/*	$NetBSD: in_cksum.S,v 1.2 2003/08/07 16:27:54 agc Exp $	*/

/*-
 * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum.
 *
 * 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 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) 1990 The Regents of the University of California.
 * 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. 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.
 */

#include <machine/asm.h>
#include "assym.h"

/* LINTSTUB: include <sys/types.h> */
/* LINTSTUB: include <machine/param.h> */
/* LINTSTUB: include <sys/mbuf.h> */
/* LINTSTUB: include <netinet/in.h> */

/*
 * Checksum routine for Internet Protocol family headers.
 *
 * in_cksum(m, len)
 *
 * Registers used:
 * %eax = sum
 * %ebx = m->m_data
 * %cl = rotation count to unswap
 * %edx = m->m_len
 * %ebp = m
 * %esi = len
 */

#define	SWAP \
	roll	$8, %eax	; \
	xorb	$8, %cl

#define	UNSWAP \
	roll	%cl, %eax

#define	MOP \
	adcl	$0, %eax

#define	ADVANCE(n) \
	leal	n(%ebx), %ebx	; \
	leal	-n(%edx), %edx	; \

#define	ADDBYTE \
	SWAP			; \
	addb	(%ebx), %ah

#define	ADDWORD \
	addw	(%ebx), %ax

#define	ADD(n) \
	addl	n(%ebx), %eax

#define	ADC(n) \
	adcl	n(%ebx), %eax

#define	REDUCE \
	movzwl	%ax, %edx	; \
	shrl	$16, %eax	; \
	addw	%dx, %ax	; \
	adcw	$0, %ax

/* LINTSTUB: Func: int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len) */
ENTRY(in4_cksum)
	pushl	%ebp
	pushl	%ebx
	pushl	%esi

	movl	16(%esp), %ebp
	movzbl	20(%esp), %eax		/* sum = nxt */
	movl	24(%esp), %edx		/* %edx = off */
	movl	28(%esp), %esi		/* %esi = len */
	testl	%eax, %eax
	jz	.Lmbuf_loop_0		/* skip if nxt == 0 */
	movl	M_DATA(%ebp), %ebx
	addl	%esi, %eax		/* sum += len */
	shll	$8, %eax		/* sum = htons(sum) */

	ADD(IP_SRC)			/* sum += ip->ip_src */
	ADC(IP_DST)			/* sum += ip->ip_dst */
	MOP
.Lmbuf_loop_0:
	testl	%ebp, %ebp
	jz	.Lout_of_mbufs

	movl	M_DATA(%ebp), %ebx	/* %ebx = m_data */
	movl	M_LEN(%ebp), %ecx	/* %ecx = m_len */
	movl	M_NEXT(%ebp), %ebp

	subl	%ecx, %edx		/* %edx = off - m_len */
	jnb	.Lmbuf_loop_0

	addl	%edx, %ebx		/* %ebx = m_data + off - m_len */
	negl	%edx			/* %edx = m_len - off */
	addl	%ecx, %ebx		/* %ebx = m_data + off */
	xorb	%cl, %cl

	/*
	 * The len == 0 case is handled really inefficiently, by going through
	 * the whole short_mbuf path once to get back to mbuf_loop_1 -- but
	 * this case never happens in practice, so it's sufficient that it
	 * doesn't explode.
	 */
	jmp	.Lin4_entry

/* LINTSTUB: Func: int in_cksum(struct mbuf *m, int len) */
ENTRY(in_cksum)
	pushl	%ebp
	pushl	%ebx
	pushl	%esi

	movl	16(%esp), %ebp
	movl	20(%esp), %esi
	xorl	%eax, %eax
	xorb	%cl, %cl

.Lmbuf_loop_1:
	testl	%esi, %esi
	jz	.Ldone

.Lmbuf_loop_2:
	testl	%ebp, %ebp
	jz	.Lout_of_mbufs

	movl	M_DATA(%ebp), %ebx
	movl	M_LEN(%ebp), %edx
	movl	M_NEXT(%ebp), %ebp

.Lin4_entry:
	cmpl	%esi, %edx
	jbe	1f
	movl	%esi, %edx

1:
	subl	%edx, %esi

	cmpl	$32, %edx
	jb	.Lshort_mbuf

	testb	$3, %bl
	jz	.Ldword_aligned

	testb	$1, %bl
	jz	.Lbyte_aligned

	ADDBYTE
	ADVANCE(1)
	MOP

	testb	$2, %bl
	jz	.Lword_aligned

.Lbyte_aligned:
	ADDWORD
	ADVANCE(2)
	MOP

.Lword_aligned:
.Ldword_aligned:
	testb	$4, %bl
	jnz	.Lqword_aligned

	ADD(0)
	ADVANCE(4)
	MOP

.Lqword_aligned:
	testb	$8, %bl
	jz	.Loword_aligned

	ADD(0)
	ADC(4)
	ADVANCE(8)
	MOP

.Loword_aligned:
	subl	$128, %edx
	jb	.Lfinished_128

.Lloop_128:
	ADD(12)
	ADC(0)
	ADC(4)
	ADC(8)
	ADC(28)
	ADC(16)
	ADC(20)
	ADC(24)
	ADC(44)
	ADC(32)
	ADC(36)
	ADC(40)
	ADC(60)
	ADC(48)
	ADC(52)
	ADC(56)
	ADC(76)
	ADC(64)
	ADC(68)
	ADC(72)
	ADC(92)
	ADC(80)
	ADC(84)
	ADC(88)
	ADC(108)
	ADC(96)
	ADC(100)
	ADC(104)
	ADC(124)
	ADC(112)
	ADC(116)
	ADC(120)
	leal	128(%ebx), %ebx
	MOP

	subl	$128, %edx
	jnb	.Lloop_128

.Lfinished_128:
	subl	$32-128, %edx
	jb	.Lfinished_32

.Lloop_32:
	ADD(12)
	ADC(0)
	ADC(4)
	ADC(8)
	ADC(28)
	ADC(16)
	ADC(20)
	ADC(24)
	leal	32(%ebx), %ebx
	MOP

	subl	$32, %edx
	jnb	.Lloop_32

.Lfinished_32:
.Lshort_mbuf:
	testb	$16, %dl
	jz	.Lfinished_16

	ADD(12)
	ADC(0)
	ADC(4)
	ADC(8)
	leal	16(%ebx), %ebx
	MOP

.Lfinished_16:
	testb	$8, %dl
	jz	.Lfinished_8

	ADD(0)
	ADC(4)
	leal	8(%ebx), %ebx
	MOP

.Lfinished_8:
	testb	$4, %dl
	jz	.Lfinished_4

	ADD(0)
	leal	4(%ebx), %ebx
	MOP

.Lfinished_4:
	testb	$3, %dl
	jz	.Lmbuf_loop_1

	testb	$2, %dl
	jz	.Lfinished_2

	ADDWORD
	leal	2(%ebx), %ebx
	MOP

	testb	$1, %dl
	jz	.Lfinished_1

.Lfinished_2:
	ADDBYTE
	MOP

.Lfinished_1:
.Lmbuf_done:
	testl	%esi, %esi
	jnz	.Lmbuf_loop_2

.Ldone:
	UNSWAP
	REDUCE
	notw	%ax

.Lreturn:
	popl	%esi
	popl	%ebx
	popl	%ebp
	ret

.Lout_of_mbufs:
	pushl	$1f
	call	_C_LABEL(printf)
	leal	4(%esp), %esp
	jmp	.Lreturn
1:
	.asciz	"cksum: out of data\n"

license: unknown


code:
/*	$OpenBSD: ioapic.c,v 1.36 2015/07/13 17:45:01 mikeb Exp $	*/
/* 	$NetBSD: ioapic.c,v 1.7 2003/07/14 22:32:40 lukem Exp $	*/

/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by RedBack Networks Inc.
 *
 * Author: Bill Sommerfeld
 *
 * 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 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) 1999 Stefan Grefen
 *
 * 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 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 AND 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/bus.h>
#include <machine/psl.h>

#include <uvm/uvm_extern.h>

#include <machine/i82093reg.h>
#include <machine/i82093var.h>

#include <machine/i82489reg.h>
#include <machine/i82489var.h>

#include <machine/pmap.h>

#include <machine/mpbiosvar.h>

#include "isa.h"

/*
 * XXX locking
 */

int     ioapic_match(struct device *, void *, void *);
void    ioapic_attach(struct device *, struct device *, void *);
int	ioapic_activate(struct device *, int);

/* XXX */
extern int bus_mem_add_mapping(bus_addr_t, bus_size_t, int,
    bus_space_handle_t *);

void ioapic_hwmask(struct pic *, int);
void ioapic_hwunmask(struct pic *, int);
void	apic_set_redir(struct ioapic_softc *, int);
void	apic_vectorset(struct ioapic_softc *, int, int, int);

void	apic_stray(int);

int ioapic_bsp_id = 0;
int ioapic_cold = 1;

struct ioapic_softc *ioapics;	 /* head of linked list */
int nioapics = 0;	   	 /* number attached */
static int ioapic_vecbase;

void ioapic_set_id(struct ioapic_softc *);

static __inline u_long
ioapic_lock(struct ioapic_softc *sc)
{
	u_long flags;

	flags = read_psl();
	disable_intr();
#ifdef MULTIPROCESSOR
	mtx_enter(&sc->sc_pic.pic_mutex);
#endif
	return flags;
}

static __inline void
ioapic_unlock(struct ioapic_softc *sc, u_long flags)
{
#ifdef MULTIPROCESSOR
	mtx_leave(&sc->sc_pic.pic_mutex);
#endif
	write_psl(flags);
}

/*
 * Register read/write routines.
 */
static __inline u_int32_t
ioapic_read_ul(struct ioapic_softc *sc,int regid)
{
	u_int32_t val;

	*(sc->sc_reg) = regid;
	val = *sc->sc_data;

	return (val);
}

static __inline void
ioapic_write_ul(struct ioapic_softc *sc,int regid, u_int32_t val)
{
	*(sc->sc_reg) = regid;
	*(sc->sc_data) = val;
}

static __inline u_int32_t
ioapic_read(struct ioapic_softc *sc, int regid)
{
	u_int32_t val;
	u_long flags;

	flags = ioapic_lock(sc);
	val = ioapic_read_ul(sc, regid);
	ioapic_unlock(sc, flags);
	return val;
}

static __inline void
ioapic_write(struct ioapic_softc *sc,int regid, int val)
{
	u_long flags;

	flags = ioapic_lock(sc);
	ioapic_write_ul(sc, regid, val);
	ioapic_unlock(sc, flags);
}

struct ioapic_softc *
ioapic_find(int apicid)
{
	struct ioapic_softc *sc;

	if (apicid == MPS_ALL_APICS) {	/* XXX mpbios-specific */
		/*
		 * XXX kludge for all-ioapics interrupt support
		 * on single ioapic systems
		 */
		if (nioapics <= 1)
			return (ioapics);
		panic("unsupported: all-ioapics interrupt with >1 ioapic");
	}

	for (sc = ioapics; sc != NULL; sc = sc->sc_next)
		if (sc->sc_apicid == apicid)
			return (sc);

	return (NULL);
}

/*
 * For the case the I/O APICs were configured using ACPI, there must
 * be an option to match global ACPI interrupts with APICs.
 */
struct ioapic_softc *
ioapic_find_bybase(int vec)
{
	struct ioapic_softc *sc;

	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
		if (vec >= sc->sc_apic_vecbase &&
		    vec < (sc->sc_apic_vecbase + sc->sc_apic_sz))
			return sc;
	}

	return NULL;
}

static __inline void
ioapic_add(struct ioapic_softc *sc)
{
	sc->sc_next = ioapics;
	ioapics = sc;
	nioapics++;
}

void
ioapic_print_redir(struct ioapic_softc *sc, char *why, int pin)
{
	u_int32_t redirlo = ioapic_read(sc, IOAPIC_REDLO(pin));
	u_int32_t redirhi = ioapic_read(sc, IOAPIC_REDHI(pin));

	apic_format_redir(sc->sc_pic.pic_name, why, pin, redirhi, redirlo);
}

struct cfattach ioapic_ca = {
	sizeof(struct ioapic_softc), ioapic_match, ioapic_attach, NULL,
	ioapic_activate
};

struct cfdriver ioapic_cd = {
	NULL, "ioapic", DV_DULL
};

int
ioapic_match(struct device *parent, void *v, void *aux)
{
	struct apic_attach_args *aaa = (struct apic_attach_args *)aux;
	struct cfdata *match = v;

	if (strcmp(aaa->aaa_name, match->cf_driver->cd_name) == 0)
		return (1);
	return (0);
}

/* Reprogram the APIC ID, and check that it actually got set. */
void
ioapic_set_id(struct ioapic_softc *sc)
{
	u_int8_t apic_id;

	ioapic_write(sc, IOAPIC_ID,
	    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
	    (sc->sc_apicid << IOAPIC_ID_SHIFT));

	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
	    IOAPIC_ID_SHIFT;

	if (apic_id != sc->sc_apicid)
		printf(", can't remap to apid %d\n", sc->sc_apicid);
	else
		printf(", remapped to apid %d\n", sc->sc_apicid);
}

/*
 * can't use bus_space_xxx as we don't have a bus handle ...
 */
void
ioapic_attach(struct device *parent, struct device *self, void *aux)
{
	struct ioapic_softc *sc = (struct ioapic_softc *)self;
	struct apic_attach_args  *aaa = (struct apic_attach_args *)aux;
	int apic_id;
	bus_space_handle_t bh;
	u_int32_t ver_sz;
	int i;

	sc->sc_flags = aaa->flags;
	sc->sc_apicid = aaa->apic_id;

	printf(": apid %d", aaa->apic_id);

	if (ioapic_find(aaa->apic_id) != NULL) {
		printf(", duplicate apic id (ignored)\n");
		return;
	}

	ioapic_add(sc);

	printf(" pa 0x%x", aaa->apic_address);

	if (bus_mem_add_mapping(aaa->apic_address, PAGE_SIZE, 0, &bh) != 0) {
		printf(", map failed\n");
		return;
	}
	sc->sc_reg = (volatile u_int32_t *)(bh + IOAPIC_REG);
	sc->sc_data = (volatile u_int32_t *)(bh + IOAPIC_DATA);

	ver_sz = ioapic_read(sc, IOAPIC_VER);
	sc->sc_apic_vers = (ver_sz & IOAPIC_VER_MASK) >> IOAPIC_VER_SHIFT;
	sc->sc_apic_sz = (ver_sz & IOAPIC_MAX_MASK) >> IOAPIC_MAX_SHIFT;
	sc->sc_apic_sz++;

	if (aaa->apic_vecbase != -1)
		sc->sc_apic_vecbase = aaa->apic_vecbase;
	else {
		/*
		 * XXX this assumes ordering of ioapics in the table.
		 * Only needed for broken BIOS workaround (see mpbios.c)
		 */
		sc->sc_apic_vecbase = ioapic_vecbase;
		ioapic_vecbase += sc->sc_apic_sz;
	}

	if (mp_verbose) {
		printf(", %s mode",
		    aaa->flags & IOAPIC_PICMODE ? "PIC" : "virtual wire");
	}

	printf(", version %x, %d pins\n", sc->sc_apic_vers, sc->sc_apic_sz);

	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
	    IOAPIC_ID_SHIFT;

	sc->sc_pins = mallocarray(sc->sc_apic_sz, sizeof(struct ioapic_pin),
	    M_DEVBUF, M_WAITOK);

	for (i = 0; i < sc->sc_apic_sz; i++) {
		sc->sc_pins[i].ip_handler = NULL;
		sc->sc_pins[i].ip_next = NULL;
		sc->sc_pins[i].ip_map = NULL;
		sc->sc_pins[i].ip_vector = 0;
		sc->sc_pins[i].ip_type = 0;
		sc->sc_pins[i].ip_minlevel = 0xff; /* XXX magic*/
		sc->sc_pins[i].ip_maxlevel = 0; /* XXX magic */
	}

	/*
	 * In case the APIC is not initialized to the correct ID
	 * do it now.
	 */
	if (apic_id != sc->sc_apicid) {
		printf("%s: misconfigured as apic %d",
		    sc->sc_pic.pic_name, apic_id);
		ioapic_set_id(sc);
	}
#if 0
	/* output of this was boring. */
	if (mp_verbose)
		for (i = 0; i < sc->sc_apic_sz; i++)
			ioapic_print_redir(sc, "boot", i);
#endif
}

int
ioapic_activate(struct device *self, int act)
{
	struct ioapic_softc *sc = (struct ioapic_softc *)self;

	switch (act) {
	case DVACT_RESUME:
		/* On resume, reset the APIC id, like we do on boot */
		ioapic_write(sc, IOAPIC_ID,
		    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
		    (sc->sc_apicid << IOAPIC_ID_SHIFT));
	}

	return (0);
}

/*
 * Interrupt mapping.
 *
 * Multiple handlers may exist for each pin, so there's an
 * intrhand chain for each pin.
 *
 * Ideally, each pin maps to a single vector at the priority of the
 * highest level interrupt for that pin.
 *
 * XXX in the event that there are more than 16 interrupt sources at a
 * single level, some doubling-up may be needed.  This is not yet
 * implemented.
 *
 * XXX we are wasting some space here because we only use a limited
 * range of the vectors here.  (0x30..0xef)
 */

struct intrhand *apic_intrhand[256];
int	apic_maxlevel[256];

/* XXX should check vs. softc max int number */
#define	LEGAL_IRQ(x)	((x) >= 0 && (x) < APIC_ICU_LEN && (x) != 2)

void
apic_set_redir(struct ioapic_softc *sc, int pin)
{
	u_int32_t redlo;
	u_int32_t redhi = 0;
	int delmode;

	struct ioapic_pin *pp;
	struct mp_intr_map *map;

	pp = &sc->sc_pins[pin];
	map = pp->ip_map;
	redlo = (map == NULL) ? IOAPIC_REDLO_MASK : map->redir;
	delmode = (redlo & IOAPIC_REDLO_DEL_MASK) >> IOAPIC_REDLO_DEL_SHIFT;

	/* XXX magic numbers */
	if ((delmode != 0) && (delmode != 1))
		;
	else if (pp->ip_handler == NULL) {
		redlo |= IOAPIC_REDLO_MASK;
	} else {
		redlo |= (pp->ip_vector & 0xff);
		redlo &= ~IOAPIC_REDLO_DEL_MASK;
		redlo |= (IOAPIC_REDLO_DEL_FIXED << IOAPIC_REDLO_DEL_SHIFT);
		redlo &= ~IOAPIC_REDLO_DSTMOD;

		/*
		 * Destination: BSP CPU
		 *
		 * XXX will want to distribute interrupts across cpu's
		 * eventually.  most likely, we'll want to vector each
		 * interrupt to a specific CPU and load-balance across
		 * cpu's.  but there's no point in doing that until after
		 * most interrupts run without the kernel lock.
		 */
		redhi |= (ioapic_bsp_id << IOAPIC_REDHI_DEST_SHIFT);

		/* XXX derive this bit from BIOS info */
		if (pp->ip_type == IST_LEVEL)
			redlo |= IOAPIC_REDLO_LEVEL;
		else
			redlo &= ~IOAPIC_REDLO_LEVEL;
		if (map != NULL && ((map->flags & 3) == MPS_INTPO_DEF)) {
			if (pp->ip_type == IST_LEVEL)
				redlo |= IOAPIC_REDLO_ACTLO;
			else
				redlo &= ~IOAPIC_REDLO_ACTLO;
		}
	}
	/* Do atomic write */
	ioapic_write(sc, IOAPIC_REDLO(pin), IOAPIC_REDLO_MASK);
	ioapic_write(sc, IOAPIC_REDHI(pin), redhi);
	ioapic_write(sc, IOAPIC_REDLO(pin), redlo);
	if (mp_verbose)
		ioapic_print_redir(sc, "int", pin);
}

/*
 * XXX To be really correct an NISA > 0 condition should check for these.
 * However, the i386 port pretty much assumes isa is there anyway.
 * For example, pci_intr_establish calls isa_intr_establish unconditionally.
 */
extern int fakeintr(void *); 	/* XXX headerify */
extern char *isa_intr_typename(int); 	/* XXX headerify */

/*
 * apic_vectorset: allocate a vector for the given pin, based on
 * the levels of the interrupts on that pin.
 *
 * XXX if the level of the pin changes while the pin is
 * masked, need to do something special to prevent pending
 * interrupts from being lost.
 * (the answer may be to hang the interrupt chain off of both vectors
 * until any interrupts from the old source have been handled.  the trouble
 * is that we don't have a global view of what interrupts are pending.
 *
 * Deferring for now since MP systems are more likely servers rather
 * than laptops or desktops, and thus will have relatively static
 * interrupt configuration.
 */

void
apic_vectorset(struct ioapic_softc *sc, int pin, int minlevel, int maxlevel)
{
	struct ioapic_pin *pp = &sc->sc_pins[pin];
	int nvector, ovector = pp->ip_vector;

	if (maxlevel == 0) {
		/* no vector needed. */
		pp->ip_minlevel = 0xff; /* XXX magic */
		pp->ip_maxlevel = 0; /* XXX magic */
		pp->ip_vector = 0;
	} else if (minlevel != pp->ip_minlevel) {
#ifdef MPVERBOSE
		if (minlevel != maxlevel)
			printf("%s: pin %d shares different IPL interrupts "
			    "(%x..%x)\n", sc->sc_pic.pic_name, pin,
			    minlevel, maxlevel);
#endif

		/*
		 * Allocate interrupt vector at the *lowest* priority level
		 * of any of the handlers invoked by this pin.
		 *
		 * The interrupt handler will raise ipl higher than this
		 * as appropriate.
		 */
		nvector = idt_vec_alloc(minlevel, minlevel+15);
		if (nvector == 0) {
			/*
			 * XXX XXX we should be able to deal here..
			 * need to double-up an existing vector
			 * and install a slightly different handler.
			 */
			panic("%s: can't alloc vector for pin %d at level %x",
			    sc->sc_pic.pic_name, pin, maxlevel);
		}

		idt_vec_set(nvector, apichandler[nvector & 0xf]);
		pp->ip_minlevel = minlevel;
		pp->ip_vector = nvector;
	}

	pp->ip_maxlevel = maxlevel;
	apic_maxlevel[pp->ip_vector] = maxlevel;
	apic_intrhand[pp->ip_vector] = pp->ip_handler;

	if (ovector && ovector != pp->ip_vector) {
		/*
		 * XXX should defer this until we're sure the old vector
		 * doesn't have a pending interrupt on any processor.
		 * do this by setting a counter equal to the number of CPU's,
		 * and firing off a low-priority broadcast IPI to all cpu's.
		 * each cpu then decrements the counter; when it
		 * goes to zero, free the vector..
		 * i.e., defer until all processors have run with a CPL
		 * less than the level of the interrupt..
		 *
		 * this is only an issue for dynamic interrupt configuration
		 * (e.g., cardbus or pcmcia).
		 */
		apic_intrhand[ovector] = NULL;
		idt_vec_free(ovector);
	}

	apic_set_redir(sc, pin);
}

/*
 * Throw the switch and enable interrupts..
 */

void
ioapic_enable(void)
{
	int p, maxlevel, minlevel;
	struct ioapic_softc *sc;
	struct intrhand *q;
	extern void intr_calculatemasks(void); /* XXX */

	intr_calculatemasks();	/* for softints, AST's */

	ioapic_cold = 0;

	if (ioapics == NULL)
		return;

#if 1 /* XXX Will probably get removed */
	lapic_set_softvectors();
	lapic_set_lvt();
#endif

	if (ioapics->sc_flags & IOAPIC_PICMODE) {
		printf("%s: writing to IMCR to disable pics\n",
		    ioapics->sc_pic.pic_name);
		outb(IMCR_ADDR, IMCR_REGISTER);
		outb(IMCR_DATA, IMCR_APIC);
	}

	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
		if (mp_verbose)
			printf("%s: enabling\n", sc->sc_pic.pic_name);

		for (p = 0; p < sc->sc_apic_sz; p++) {
			maxlevel = 0;	 /* magic */
			minlevel = 0xff; /* magic */

			for (q = sc->sc_pins[p].ip_handler; q != NULL;
			     q = q->ih_next) {
				if (q->ih_level > maxlevel)
					maxlevel = q->ih_level;
				if (q->ih_level < minlevel)
					minlevel = q->ih_level;
			}
			apic_vectorset(sc, p, minlevel, maxlevel);
		}
	}
}

void
ioapic_hwmask(struct pic *pic, int pin)
{
	u_int32_t redlo;
	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
	u_long flags;

	if (ioapic_cold)
		return;
	flags = ioapic_lock(sc);
	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
	redlo |= IOAPIC_REDLO_MASK;
	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
	ioapic_unlock(sc, flags);
}

void
ioapic_hwunmask(struct pic *pic, int pin)
{
	u_int32_t redlo;
	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
	u_long flags;

	if (ioapic_cold)
		return;
	flags = ioapic_lock(sc);
	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
	redlo &= ~IOAPIC_REDLO_MASK;
	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
	ioapic_unlock(sc, flags);
}

/*
 * Interrupt handler management with the apic is radically different from the
 * good old 8259.
 *
 * The APIC adds an additional level of indirection between interrupt
 * signals and interrupt vectors in the IDT.
 * It also encodes a priority into the high-order 4 bits of the IDT vector
 * number.
 *
 *
 * interrupt establishment:
 *	-> locate interrupt pin.
 *	-> locate or allocate vector for pin.
 *	-> locate or allocate handler chain for vector.
 *	-> chain interrupt into handler chain.
 * 	#ifdef notyet
 *	-> if level of handler chain increases, reallocate vector, move chain.
 *	#endif
 */

void *
apic_intr_establish(int irq, int type, int level, int (*ih_fun)(void *),
    void *ih_arg, const char *ih_what)
{
	unsigned int ioapic = APIC_IRQ_APIC(irq);
	unsigned int intr = APIC_IRQ_PIN(irq);
	struct ioapic_softc *sc = ioapic_find(ioapic);
	struct ioapic_pin *pin;
	struct intrhand **p, *q, *ih;
	extern int cold;
	int minlevel, maxlevel;
	extern void intr_calculatemasks(void); /* XXX */
	int flags;

	flags = level & IPL_MPSAFE;
	level &= ~IPL_MPSAFE;

	KASSERT(level <= IPL_TTY || flags & IPL_MPSAFE);

	if (sc == NULL)
		panic("apic_intr_establish: unknown ioapic %d", ioapic);

	if ((irq & APIC_INT_VIA_APIC) == 0)
		panic("apic_intr_establish of non-apic interrupt 0x%x", irq);

	if (intr >= sc->sc_apic_sz || type == IST_NONE)
		panic("apic_intr_establish: bogus intr or type");

	/* no point in sleeping unless someone can free memory. */
	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
	if (ih == NULL)
		panic("apic_intr_establish: can't malloc handler info");

	pin = &sc->sc_pins[intr];
	switch (pin->ip_type) {
	case IST_NONE:
		pin->ip_type = type;
		break;
	case IST_EDGE:
		intr_shared_edge = 1;
		/* FALLTHROUGH */
	case IST_LEVEL:
		if (type == pin->ip_type)
			break;
	case IST_PULSE:
		if (type != IST_NONE) {
			/*printf("%s: intr_establish: can't share %s with %s, irq %d\n",
			    ih_what, isa_intr_typename(pin->ip_type),
			    isa_intr_typename(type), intr);*/
			free(ih, M_DEVBUF, 0);
			return (NULL);
		}
		break;
	}

	if (!cold)
		ioapic_hwmask(&sc->sc_pic, intr);

	/*
	 * Figure out where to put the handler.
	 * This is O(N^2) to establish N interrupts, but we want to
	 * preserve the order, and N is generally small.
	 */
	maxlevel = level;
	minlevel = level;
	for (p = &pin->ip_handler; (q = *p) != NULL; p = &q->ih_next) {
		if (q->ih_level > maxlevel)
			maxlevel = q->ih_level;
		if (q->ih_level < minlevel)
			minlevel = q->ih_level;
	}

	ih->ih_fun = ih_fun;
	ih->ih_arg = ih_arg;
	ih->ih_next = NULL;
	ih->ih_level = level;
	ih->ih_flags = flags;
	ih->ih_irq = irq;
	evcount_attach(&ih->ih_count, ih_what, &pin->ip_vector);

	*p = ih;

	intr_calculatemasks();

	/*
	 * Fix up the vector for this pin.
	 * (if cold, defer this until most interrupts have been established,
	 * to avoid too much thrashing of the idt..)
	 */

	if (!ioapic_cold)
		apic_vectorset(sc, intr, minlevel, maxlevel);

	if (!cold)
		ioapic_hwunmask(&sc->sc_pic, intr);

	return (ih);
}

/*
 * apic disestablish:
 *	locate handler chain.
 * 	dechain intrhand from handler chain
 *	if chain empty {
 *		reprogram apic for "safe" vector.
 *		free vector (point at stray handler).
 *	}
 *	#ifdef notyet
 *	else {
 *		recompute level for current chain.
 *		if changed, reallocate vector, move chain.
 *	}
 *	#endif
 */

void
apic_intr_disestablish(void *arg)
{
	struct intrhand *ih = arg;
	int irq = ih->ih_irq;
	unsigned int ioapic = APIC_IRQ_APIC(irq);
	unsigned int intr = APIC_IRQ_PIN(irq);
	struct ioapic_softc *sc = ioapic_find(ioapic);
	struct ioapic_pin *pin;
	struct intrhand **p, *q;
	int minlevel, maxlevel;
	extern void intr_calculatemasks(void); /* XXX */

	if (sc == NULL)
		panic("apic_intr_disestablish: unknown ioapic %d", ioapic);
	pin = &sc->sc_pins[intr];

	if (intr >= sc->sc_apic_sz)
		panic("apic_intr_disestablish: bogus irq");

	/*
	 * Remove the handler from the chain.
	 * This is O(n^2), too.
	 */
	maxlevel = 0;
	minlevel = 0xff;
	for (p = &pin->ip_handler; (q = *p) != NULL && q != ih;
	     p = &q->ih_next) {
		if (q->ih_level > maxlevel)
			maxlevel = q->ih_level;
		if (q->ih_level < minlevel)
			minlevel = q->ih_level;
	}

	if (q)
		*p = q->ih_next;
	else
		panic("intr_disestablish: handler not registered");
	for (q = *p; q != NULL; q = q->ih_next) {
		if (q->ih_level > maxlevel)
			maxlevel = q->ih_level;
		if (q->ih_level < minlevel)
			minlevel = q->ih_level;
	}

	intr_calculatemasks();
	if (!ioapic_cold)
		apic_vectorset(sc, intr, minlevel, maxlevel);

	evcount_detach(&ih->ih_count);
	free(ih, M_DEVBUF, 0);
}

void
apic_stray(int irqnum)
{
	unsigned int apicid;
	struct ioapic_softc *sc;

	apicid = APIC_IRQ_APIC(irqnum);
	sc = ioapic_find(apicid);
	if (sc == NULL)
		return;
	printf("%s: stray interrupt %d\n", sc->sc_pic.pic_name, irqnum);
}

#ifdef DDB
void ioapic_dump(void);

void
ioapic_dump(void)
{
	struct ioapic_softc *sc;
	struct ioapic_pin *ip;
	int p;

	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
		for (p = 0; p < sc->sc_apic_sz; p++) {
			ip = &sc->sc_pins[p];
			if (ip->ip_type != IST_NONE)
				ioapic_print_redir(sc, "dump", p);
		}
	}
}
#endif


code:
/*	$OpenBSD: ipifuncs.c,v 1.27 2015/07/19 18:53:49 sf Exp $	*/
/* $NetBSD: ipifuncs.c,v 1.1.2.3 2000/06/26 02:04:06 sommerfeld Exp $ */

/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by RedBack Networks Inc.
 *
 * Author: Bill Sommerfeld
 *
 * 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 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.
 */

/*
 * Interprocessor interrupt handlers.
 */

#include "npx.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/memrange.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <machine/cpufunc.h>
#include <machine/cpuvar.h>
#include <machine/intr.h>
#include <machine/atomic.h>
#include <machine/i82093var.h>
#include <machine/db_machdep.h>
#include <machine/mplock.h>

void i386_ipi_nop(struct cpu_info *);
void i386_ipi_halt(struct cpu_info *);

#if NNPX > 0
void i386_ipi_synch_fpu(struct cpu_info *);
void i386_ipi_flush_fpu(struct cpu_info *);
#else
#define i386_ipi_synch_fpu NULL
#define i386_ipi_flush_fpu NULL
#endif

#ifdef MTRR
void i386_ipi_reload_mtrr(struct cpu_info *);
#else
#define i386_ipi_reload_mtrr 0
#endif

void (*ipifunc[I386_NIPI])(struct cpu_info *) =
{
	i386_ipi_halt,
	i386_ipi_nop,
	i386_ipi_flush_fpu,
	i386_ipi_synch_fpu,
	i386_ipi_reload_mtrr,
#if 0
	gdt_reload_cpu,
#else
	NULL,
#endif
#ifdef DDB
	i386_ipi_db,
#else
	NULL,
#endif
	i386_setperf_ipi,
};

void
i386_ipi_nop(struct cpu_info *ci)
{
}

void
i386_ipi_halt(struct cpu_info *ci)
{
	SCHED_ASSERT_UNLOCKED();
	KASSERT(!__mp_lock_held(&kernel_lock));

	npxsave_cpu(ci, 1);
	disable_intr();
	lapic_disable();
	wbinvd();
	ci->ci_flags &= ~CPUF_RUNNING;
	wbinvd();

	for(;;) {
		asm volatile("hlt");
	}
}

#if NNPX > 0
void
i386_ipi_flush_fpu(struct cpu_info *ci)
{
	if (ci->ci_fpsaveproc == ci->ci_fpcurproc)
		npxsave_cpu(ci, 0);
}

void
i386_ipi_synch_fpu(struct cpu_info *ci)
{
	if (ci->ci_fpsaveproc == ci->ci_fpcurproc)
		npxsave_cpu(ci, 1);
}
#endif

#ifdef MTRR
void
i386_ipi_reload_mtrr(struct cpu_info *ci)
{
	if (mem_range_softc.mr_op != NULL)
		mem_range_softc.mr_op->reload(&mem_range_softc);
}
#endif

void
i386_spurious(void)
{
	printf("spurious intr\n");
}

void
i386_send_ipi(struct cpu_info *ci, int ipimask)
{
	i386_atomic_setbits_l(&ci->ci_ipis, ipimask);

	/* Don't send IPI to cpu which isn't (yet) running. */
	if (!(ci->ci_flags & CPUF_RUNNING))
		return;

	i386_ipi(LAPIC_IPI_VECTOR, ci->ci_apicid, LAPIC_DLMODE_FIXED);

	return;
}

int
i386_fast_ipi(struct cpu_info *ci, int ipi)
{
	if (!(ci->ci_flags & CPUF_RUNNING))
		return (ENOENT);

	i386_ipi(ipi, ci->ci_apicid, LAPIC_DLMODE_FIXED);

	return 0;
}

void
i386_broadcast_ipi(int ipimask)
{
	struct cpu_info *ci, *self = curcpu();
	CPU_INFO_ITERATOR cii;
	int count = 0;

	CPU_INFO_FOREACH(cii, ci) {
		if (ci == self)
			continue;
		if ((ci->ci_flags & CPUF_RUNNING) == 0)
			continue;
		i386_atomic_setbits_l(&ci->ci_ipis, ipimask);
		count++;
	}
	if (!count)
		return;

	i386_ipi(LAPIC_IPI_VECTOR, LAPIC_DEST_ALLEXCL, LAPIC_DLMODE_FIXED);
}

void
i386_ipi_handler(void)
{
	extern struct evcount ipi_count;
	struct cpu_info *ci = curcpu();
	u_int32_t pending;
	int bit;

	pending = i386_atomic_testset_ul(&ci->ci_ipis, 0);

	for (bit = 0; bit < I386_NIPI && pending; bit++) {
		if (pending & (1<<bit)) {
			pending &= ~(1<<bit);
			(*ipifunc[bit])(ci);
			ipi_count.ec_count++;
		}
	}
}


code:
/*	$OpenBSD: k1x-pstate.c,v 1.7 2014/09/14 14:17:23 jsg Exp $ */
/*
 * Copyright (c) 2011 Bryan Steele <brynet@gmail.com>
 *
 * 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 ``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.
 */
/* AMD K10/K11 pstate driver */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/bus.h>

#include "acpicpu.h"

#if NACPICPU > 0
#include <dev/acpi/acpidev.h>
#include <dev/acpi/acpivar.h>
#endif

extern int setperf_prio;

#define MSR_K1X_LIMIT		0xc0010061
#define MSR_K1X_CONTROL		0xc0010062
#define MSR_K1X_STATUS		0xc0010063
#define MSR_K1X_CONFIG		0xc0010064

/* MSR_K1X_LIMIT */
#define K1X_PSTATE_MAX_VAL(x)	(((x) >> 4) & 0x7)
#define K1X_PSTATE_LIMIT(x)	(((x)) & 0x7)

/* MSR_K1X_CONFIG */
#define K1X_FID(x)		((x) & 0x3f)
#define K1X_DID(x)		(((x) >> 6) & 0x07)

/* Maximum pstates */
#define K1X_MAX_STATES		16

struct k1x_state {
	int freq;
	u_int8_t fid;
};

struct k1x_cpu_state {
	struct k1x_state state_table[K1X_MAX_STATES];
	u_int n_states;
};

struct k1x_cpu_state *k1x_current_state;

void k1x_transition(struct k1x_cpu_state *, int);

#if NACPICPU > 0
void k1x_acpi_init(struct k1x_cpu_state *);
void k1x_acpi_states(struct k1x_cpu_state *, struct acpicpu_pss *, int);
#endif

void
k1x_setperf(int level)
{
	u_int i = 0;
	struct k1x_cpu_state *cstate;

	cstate = k1x_current_state;

	i = ((level * cstate->n_states) + 1) / 101;
	if (i >= cstate->n_states)
		i = cstate->n_states - 1;

	k1x_transition(cstate, i);
}

void
k1x_transition(struct k1x_cpu_state *cstate, int level)
{
	u_int64_t msr;
	int i, cfid, fid = cstate->state_table[level].fid;

	msr = rdmsr(MSR_K1X_STATUS);
	cfid = K1X_FID(msr);

	if (fid == cfid)
		return;

	if (cfid != fid) {
		wrmsr(MSR_K1X_CONTROL, fid);
		for (i = 0; i < 100; i++) {
			msr = rdmsr(MSR_K1X_STATUS);
			if (K1X_FID(msr) == fid)
				break;
			DELAY(100);
		}
		cfid = K1X_FID(msr);
	}
	if (cfid == fid) {
		cpuspeed = cstate->state_table[level].freq;
#if 0
		(void)printf("Target: %d Current: %d Pstate: %d\n",
		    cstate->state_table[level].freq,
		    cpuspeed, cfid);
#endif
	}
}

#if NACPICPU > 0

void
k1x_acpi_states(struct k1x_cpu_state *cstate, struct acpicpu_pss *pss,
    int nstates)
{
	struct k1x_state state;
	int j, n;
	u_int32_t ctrl;

	for (n = 0; n < cstate->n_states; n++) {
		ctrl = pss[n].pss_ctrl;
		state.fid = K1X_FID(ctrl);
		state.freq = pss[n].pss_core_freq;
		j = n;
		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
			memcpy(&cstate->state_table[j],
			    &cstate->state_table[j - 1],
			    sizeof(struct k1x_state));
			--j;
		}
		memcpy(&cstate->state_table[j], &state,
		    sizeof(struct k1x_state));
	}
}

void
k1x_acpi_init(struct k1x_cpu_state *cstate)
{
	struct acpicpu_pss *pss;

	cstate->n_states = acpicpu_fetch_pss(&pss);
	if (cstate->n_states == 0)
		return;

	k1x_acpi_states(cstate, pss, cstate->n_states);

	return;
}

#endif /* NACPICPU */

void
k1x_init(struct cpu_info *ci)
{
	struct k1x_cpu_state *cstate;
	struct k1x_state *state;
	u_int i;

	if (setperf_prio > 1)
		return;

	cstate = malloc(sizeof(struct k1x_cpu_state), M_DEVBUF, M_NOWAIT);
	if (!cstate)
		return;

	cstate->n_states = 0;

#if NACPICPU > 0
	k1x_acpi_init(cstate);
#endif
	if (cstate->n_states) {
		printf("%s: %d MHz: speeds:",
		    ci->ci_dev.dv_xname, cpuspeed);
		for (i = cstate->n_states; i > 0; i--) {
			state = &cstate->state_table[i-1];
			printf(" %d", state->freq);
		}
		printf(" MHz\n");
		k1x_current_state = cstate;
		cpu_setperf = k1x_setperf;
		setperf_prio = 1;
		return;
	}
	free(cstate, M_DEVBUF, 0);
}


code:
/* $OpenBSD: k6_mem.c,v 1.11 2010/02/23 21:54:53 kettenis Exp $ */
/*-
 * Copyright (c) 1999 Brian Fundakowski Feldman
 * 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.
 *
 * $FreeBSD: src/sys/i386/i386/k6_mem.c,v 1.4 1999/09/05 15:45:57 green Exp $
 *
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
#include <sys/malloc.h>
#include <sys/memrange.h>

#include <machine/cpufunc.h>
#include <machine/specialreg.h>

/*
 * A K6-2 MTRR is defined as the highest 15 bits having the address, the next
 * 15 having the mask, the 1st bit being "write-combining" and the 0th bit
 * being "uncacheable".
 *
 *	Address		    Mask	WC  UC
 * | XXXXXXXXXXXXXXX | XXXXXXXXXXXXXXX | X | X |
 *
 * There are two of these in the 64-bit UWCCR.
 */
#define UWCCR 0xc0000085

#define k6_reg_get(reg, addr, mask, wc, uc)	do {			\
		addr = (reg) & 0xfffe0000;				\
		mask = ((reg) & 0x1fffc) >> 2;				\
		wc = ((reg) & 0x2) >> 1;				\
		uc = (reg) & 0x1;					\
	} while (0)

#define k6_reg_make(addr, mask, wc, uc) 				\
		((addr) | ((mask) << 2) | ((wc) << 1) | uc)

void k6_mrinit(struct mem_range_softc *sc);
int k6_mrset(struct mem_range_softc *, struct mem_range_desc *, int *);
void k6_mrinit_cpu(struct mem_range_softc *sc);
static __inline int k6_mrmake(struct mem_range_desc *, u_int32_t *);

struct mem_range_ops k6_mrops = {
	k6_mrinit,
	k6_mrset,
	k6_mrinit_cpu,
	NULL
};

static __inline int
k6_mrmake(struct mem_range_desc *desc, u_int32_t *mtrr)
{
	u_int32_t len = 0, wc, uc;
	int bit;

	if (desc->mr_base &~ 0xfffe0000)
		return EINVAL;
	if (desc->mr_len < 131072 || !powerof2(desc->mr_len))
		return EINVAL;
	if (desc->mr_flags &~ (MDF_WRITECOMBINE|MDF_UNCACHEABLE))
		return EOPNOTSUPP;

	for (bit = ffs(desc->mr_len >> 17) - 1; bit < 15; bit++)
		len |= 1 << bit;
	wc = (desc->mr_flags & MDF_WRITECOMBINE) ? 1 : 0;
	uc = (desc->mr_flags & MDF_UNCACHEABLE) ? 1 : 0;

	*mtrr = k6_reg_make(desc->mr_base, len, wc, uc);
	return 0;
}

void
k6_mrinit(struct mem_range_softc *sc)
{
	u_int64_t reg;
	u_int32_t addr, mask, wc, uc;
	int d;

	sc->mr_cap = 0;
	sc->mr_ndesc = 2; /* XXX (BFF) For now, we only have one msr for this */
	sc->mr_desc = malloc(sc->mr_ndesc * sizeof(struct mem_range_desc),
	    M_MEMDESC, M_NOWAIT|M_ZERO);
	if (sc->mr_desc == NULL)
		panic("k6_mrinit: malloc returns NULL");

	reg = rdmsr(UWCCR);
	for (d = 0; d < sc->mr_ndesc; d++) {
		u_int32_t one = (reg & (0xffffffff << (32 * d))) >> (32 * d);

		k6_reg_get(one, addr, mask, wc, uc);
		sc->mr_desc[d].mr_base = addr;
		sc->mr_desc[d].mr_len = ffs(mask) << 17;
		if (wc)
			sc->mr_desc[d].mr_flags |= MDF_WRITECOMBINE;
		if (uc)
			sc->mr_desc[d].mr_flags |= MDF_UNCACHEABLE;
	}

	printf("mtrr: K6-family MTRR support (%d registers)\n", sc->mr_ndesc);
}

int
k6_mrset(struct mem_range_softc *sc, struct mem_range_desc *desc, int *arg)
{
	u_int64_t reg;
	u_int32_t mtrr;
	int error, d;

	switch (*arg) {
	case MEMRANGE_SET_UPDATE:
		error = k6_mrmake(desc, &mtrr);
		if (error)
			return error;
		for (d = 0; d < sc->mr_ndesc; d++) {
			if (!sc->mr_desc[d].mr_len) {
				sc->mr_desc[d] = *desc;
				goto out;
			}
			if (sc->mr_desc[d].mr_base == desc->mr_base &&
			    sc->mr_desc[d].mr_len == desc->mr_len)
				return EEXIST;
		}

		return ENOSPC;
	case MEMRANGE_SET_REMOVE:
		mtrr = 0;
		for (d = 0; d < sc->mr_ndesc; d++)
			if (sc->mr_desc[d].mr_base == desc->mr_base &&
			    sc->mr_desc[d].mr_len == desc->mr_len) {
				bzero(&sc->mr_desc[d], sizeof(sc->mr_desc[d]));
				goto out;
			}

		return ENOENT;
	default:
		return EOPNOTSUPP;
	}

out:

	disable_intr();
	wbinvd();
	reg = rdmsr(UWCCR);
	reg &= ~(0xffffffff << (32 * d));
	reg |= mtrr << (32 * d);
	wrmsr(UWCCR, reg);
	wbinvd();
	enable_intr();

	return 0;
}

/*
 * Re-initialise the MTRRs on the BSP after suspend.
 */
void
k6_mrinit_cpu(struct mem_range_softc *sc)
{
	u_int64_t reg;
	u_int32_t mtrr;
	int d;

	for (d = 0; d < sc->mr_ndesc; d++) {
		k6_mrmake(&sc->mr_desc[d], &mtrr);

		disable_intr();
		wbinvd();
		reg = rdmsr(UWCCR);
		reg &= ~(0xffffffff << (32 * d));
		reg |= mtrr << (32 * d);
		wrmsr(UWCCR, reg);
		wbinvd();
		enable_intr();
	}
}


code:
/*	$OpenBSD: kgdb_machdep.c,v 1.13 2015/06/28 01:11:27 guenther Exp $	*/
/*	$NetBSD: kgdb_machdep.c,v 1.6 1998/08/13 21:36:03 thorpej Exp $	*/

/*-
 * Copyright (c) 1997 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.
 *
 * 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 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) 1996 Matthias Pfaller.
 * 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 ``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.
 */

#if defined(DDB)
#error "Can't build DDB and KGDB together."
#endif

/*
 * Machine-dependent functions for remote KGDB.  Originally written
 * for NetBSD/pc532 by Matthias Pfaller.  Modified for NetBSD/i386
 * by Jason R. Thorpe.
 */

#include <sys/param.h>
#include <sys/kgdb.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <machine/pte.h>
#include <machine/reg.h>
#include <machine/trap.h>

/*
 * Determine if the memory at va..(va+len) is valid.
 */
int
kgdb_acc(vaddr_t va, size_t len)
{
	vaddr_t last_va;

	last_va = va + len;
	va  &= ~PGOFSET;
	last_va &= ~PGOFSET;

	do {
		if ((pmap_pte_bits(va) & PG_V) == 0)
			return (0);
		va  += NBPG;
	} while (va < last_va);

	return (1);
}

/*
 * Translate a trap number into a unix compatible signal value.
 * (gdb only understands unix signal numbers).
 */
int
kgdb_signal(int type)
{
	switch (type) {
	case T_NMI:
		return (SIGINT);

	case T_ALIGNFLT:
		return (SIGILL);

	case T_BPTFLT:
	case T_TRCTRAP:
		return (SIGTRAP);

	case T_DOUBLEFLT:
		return (SIGEMT);

	case T_ARITHTRAP:
	case T_DIVIDE:
	case T_OFLOW:
	case T_DNA:
	case T_FPOPFLT:
		return (SIGFPE);

	case T_PRIVINFLT:
	case T_PROTFLT:
	case T_PAGEFLT:
	case T_TSSFLT:
	case T_SEGNPFLT:
	case T_STKFLT:
		return (SIGSEGV);

	case T_BOUND:
		return (SIGURG);

	default:
		return (SIGEMT);
	}
}

/*
 * Translate the values stored in the kernel regs struct to the format
 * understood by gdb.
 */
void
kgdb_getregs(db_regs_t *regs, kgdb_reg_t *gdb_regs)
{

	gdb_regs[ 0] = regs->tf_eax;
	gdb_regs[ 1] = regs->tf_ecx;
	gdb_regs[ 2] = regs->tf_edx;
	gdb_regs[ 3] = regs->tf_ebx;
	gdb_regs[ 5] = regs->tf_ebp;
	gdb_regs[ 6] = regs->tf_esi;
	gdb_regs[ 7] = regs->tf_edi;
	gdb_regs[ 8] = regs->tf_eip;
	gdb_regs[ 9] = regs->tf_eflags;
	gdb_regs[10] = regs->tf_cs;
	gdb_regs[12] = regs->tf_ds;
	gdb_regs[13] = regs->tf_es;
	gdb_regs[14] = regs->tf_fs;
	gdb_regs[15] = regs->tf_gs;

	if (KERNELMODE(regs->tf_cs, regs->tf_eflags)) {
		/*
		 * Kernel mode - esp and ss not saved.
		 */
		gdb_regs[ 4] = (kgdb_reg_t)&regs->tf_esp; /* kernel stack
							     pointer */
		__asm volatile("movw %%ss,%w0" : "=r" (gdb_regs[11]));
	}
}

/*
 * Reverse the above.
 */
void
kgdb_setregs(db_regs_t *regs, kgdb_reg_t *gdb_regs)
{

	regs->tf_eax    = gdb_regs[ 0];
	regs->tf_ecx    = gdb_regs[ 1];
	regs->tf_edx    = gdb_regs[ 2];
	regs->tf_ebx    = gdb_regs[ 3];
	regs->tf_ebp    = gdb_regs[ 5];
	regs->tf_esi    = gdb_regs[ 6];
	regs->tf_edi    = gdb_regs[ 7];
	regs->tf_eip    = gdb_regs[ 8];
	regs->tf_eflags = gdb_regs[ 9];
	regs->tf_cs     = gdb_regs[10];
	regs->tf_ds     = gdb_regs[12];
	regs->tf_es     = gdb_regs[13];

	if (KERNELMODE(regs->tf_cs, regs->tf_eflags) == 0) {
		/*
		 * Trapped in user mode - restore esp and ss.
		 */
		regs->tf_esp = gdb_regs[ 4];
		regs->tf_ss  = gdb_regs[11];
	}
}


code:
/* $OpenBSD: kvm86.c,v 1.10 2015/04/12 18:37:53 mlarkin Exp $ */
/* $NetBSD: kvm86.c,v 1.10 2005/12/26 19:23:59 perry Exp $ */
/*
 * Copyright (c) 2002
 * 	Matthias Drochner.  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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/malloc.h>
#include <sys/mutex.h>

#include <uvm/uvm_extern.h>

#include <machine/pcb.h>
#include <machine/pte.h>
#include <machine/pmap.h>
#include <machine/kvm86.h>
#include <machine/cpu.h>

/* assembler functions in kvm86call.s */
extern int kvm86_call(struct trapframe *);
extern void kvm86_ret(struct trapframe *, int);

#define PGTABLE_SIZE	((1024 + 64) * 1024 / PAGE_SIZE)

typedef uint32_t pt_entry_t;

struct kvm86_data {
	pt_entry_t pgtbl[PGTABLE_SIZE];

	struct segment_descriptor sd;

	struct pcb pcb;
	u_long iomap[0x10000/32];
};

void kvm86_map(struct kvm86_data *, paddr_t, uint32_t);
void kvm86_mapbios(struct kvm86_data *);
void kvm86_prepare(struct kvm86_data *vmd);
/*
 * global VM for BIOS calls
 */
struct kvm86_data *bioscallvmd;
/* page for trampoline and stack */
void *bioscallscratchpage;
/* where this page is mapped in the vm86 */
#define BIOSCALLSCRATCHPAGE_VMVA 0x1000
/* a virtual page to map in vm86 memory temporarily */
vaddr_t bioscalltmpva;

struct mutex kvm86_mp_mutex;

#define KVM86_IOPL3 /* not strictly necessary, saves a lot of traps */

void
kvm86_init()
{
	size_t vmdsize;
	char *buf;
	struct kvm86_data *vmd;
	struct pcb *pcb;
	paddr_t pa;
	int i;

	vmdsize = round_page(sizeof(struct kvm86_data)) + PAGE_SIZE;

	if ((buf = (char *)uvm_km_zalloc(kernel_map, vmdsize)) == NULL)
		return;

	/* first page is stack */
	vmd = (struct kvm86_data *)(buf + PAGE_SIZE);
	pcb = &vmd->pcb;

	/*
	 * derive pcb and TSS from proc0
	 * we want to access all IO ports, so we need a full-size
	 *  permission bitmap
	 * XXX do we really need the pcb or just the TSS?
	 */
	memcpy(pcb, &proc0.p_addr->u_pcb, sizeof(struct pcb));
	pcb->pcb_tss.tss_esp0 = (int)vmd;
	pcb->pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
	for (i = 0; i < sizeof(vmd->iomap) / 4; i++)
		vmd->iomap[i] = 0;
	pcb->pcb_tss.tss_ioopt =
	    ((caddr_t)vmd->iomap - (caddr_t)&pcb->pcb_tss) << 16;

	/* setup TSS descriptor (including our iomap) */
	setsegment(&vmd->sd, &pcb->pcb_tss,
	    sizeof(struct pcb) + sizeof(vmd->iomap) - 1,
	    SDT_SYS386TSS, SEL_KPL, 0, 0);

	/* prepare VM for BIOS calls */
	kvm86_mapbios(vmd);
	if ((bioscallscratchpage = (void *)uvm_km_alloc(kernel_map, PAGE_SIZE))
	    == 0)
		return;

	pmap_extract(pmap_kernel(), (vaddr_t)bioscallscratchpage, &pa);
	kvm86_map(vmd, pa, BIOSCALLSCRATCHPAGE_VMVA);
	bioscallvmd = vmd;
	bioscalltmpva = uvm_km_alloc(kernel_map, PAGE_SIZE);
	mtx_init(&kvm86_mp_mutex, IPL_IPI);
}

/*
 * XXX pass some stuff to the assembler code
 * XXX this should be done cleanly (in call argument to kvm86_call())
 */

volatile struct pcb *vm86pcb;
volatile int vm86tssd0, vm86tssd1;
volatile paddr_t vm86newptd;
volatile struct trapframe *vm86frame;
volatile pt_entry_t *vm86pgtableva;

void
kvm86_prepare(struct kvm86_data *vmd)
{
	vm86newptd = vtophys((vaddr_t)vmd) | PG_V | PG_RW | PG_U | PG_u;
	vm86pgtableva = vmd->pgtbl;
	vm86frame = (struct trapframe *)vmd - 1;
	vm86pcb = &vmd->pcb;
	vm86tssd0 = *(int*)&vmd->sd;
	vm86tssd1 = *((int*)&vmd->sd + 1);
}

void
kvm86_map(struct kvm86_data *vmd, paddr_t pa, uint32_t vmva)
{

	vmd->pgtbl[vmva >> 12] = pa | PG_V | PG_RW | PG_U | PG_u;
}

void
kvm86_mapbios(struct kvm86_data *vmd)
{
	paddr_t pa;

	/* map first physical page (vector table, BIOS data) */
	kvm86_map(vmd, 0, 0);

	/* map ISA hole */
	for (pa = 0xa0000; pa < 0x100000; pa += PAGE_SIZE)
		kvm86_map(vmd, pa, pa);
}

void *
kvm86_bios_addpage(uint32_t vmva)
{
	void *mem;
	paddr_t pa;

	if (bioscallvmd->pgtbl[vmva >> 12]) /* allocated? */
		return (NULL);

	if ((mem = (void *)uvm_km_alloc(kernel_map, PAGE_SIZE)) == NULL)
		return (NULL);

	pmap_extract(pmap_kernel(), (vaddr_t)mem, &pa);
	kvm86_map(bioscallvmd, pa, vmva);

	return (mem);
}

void
kvm86_bios_delpage(uint32_t vmva, void *kva)
{

	bioscallvmd->pgtbl[vmva >> 12] = 0;
	uvm_km_free(kernel_map, (vaddr_t)kva, PAGE_SIZE);
}

size_t
kvm86_bios_read(u_int32_t vmva, char *buf, size_t len)
{
	size_t todo, now;
	paddr_t vmpa;

	todo = len;
	while (todo > 0) {
		now = min(todo, PAGE_SIZE - (vmva & (PAGE_SIZE - 1)));

		if (!bioscallvmd->pgtbl[vmva >> 12])
			break;
		vmpa = bioscallvmd->pgtbl[vmva >> 12] & ~(PAGE_SIZE - 1);
		pmap_kenter_pa(bioscalltmpva, vmpa, PROT_READ);
		pmap_update(pmap_kernel());

		memcpy(buf, (void *)(bioscalltmpva + (vmva & (PAGE_SIZE - 1))),
		       now);
		buf += now;
		todo -= now;
		vmva += now;
	}
	return (len - todo);
}

int
kvm86_bioscall(int intno, struct trapframe *tf)
{
	static const unsigned char call[] = {
		0xfa, /* CLI */
		0xcd, /* INTxx */
		0,
		0xfb, /* STI */
		0xf4  /* HLT */
	};

	memcpy(bioscallscratchpage, call, sizeof(call));
	*((unsigned char *)bioscallscratchpage + 2) = intno;

	tf->tf_eip = BIOSCALLSCRATCHPAGE_VMVA;
	tf->tf_cs = 0;
	tf->tf_esp = BIOSCALLSCRATCHPAGE_VMVA + PAGE_SIZE - 2;
	tf->tf_ss = 0;
	tf->tf_eflags = PSL_USERSET | PSL_VM;
#ifdef KVM86_IOPL3
	tf->tf_eflags |= PSL_IOPL;
#endif
	tf->tf_ds = tf->tf_es = tf->tf_fs = tf->tf_gs = 0;

	kvm86_prepare(bioscallvmd); /* XXX */
	return (kvm86_call(tf));
}

int
kvm86_simplecall(int no, struct kvm86regs *regs)
{
	struct trapframe tf;
	int res;

	memset(&tf, 0, sizeof(struct trapframe));
	tf.tf_eax = regs->eax;
	tf.tf_ebx = regs->ebx;
	tf.tf_ecx = regs->ecx;
	tf.tf_edx = regs->edx;
	tf.tf_esi = regs->esi;
	tf.tf_edi = regs->edi;
	tf.tf_vm86_es = regs->es;

	mtx_enter(&kvm86_mp_mutex);
	res = kvm86_bioscall(no, &tf);
	mtx_leave(&kvm86_mp_mutex);

	regs->eax = tf.tf_eax;
	regs->ebx = tf.tf_ebx;
	regs->ecx = tf.tf_ecx;
	regs->edx = tf.tf_edx;
	regs->esi = tf.tf_esi;
	regs->edi = tf.tf_edi;
	regs->es = tf.tf_vm86_es;
	regs->eflags = tf.tf_eflags;

	return (res);
}

void
kvm86_gpfault(struct trapframe *tf)
{
	unsigned char *kva, insn, trapno;
	uint16_t *sp;

	kva = (unsigned char *)((tf->tf_cs << 4) + tf->tf_eip);
	insn = *kva;
#ifdef KVM86DEBUG
	printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n",
	       tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags);
#endif

	KASSERT(tf->tf_eflags & PSL_VM);

	switch (insn) {
	case 0xf4: /* HLT - normal exit */
		kvm86_ret(tf, 0);
		break;
	case 0xcd: /* INTxx */
		/* fake a return stack frame and call real mode handler */
		trapno = *(kva + 1);
		sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp);
		*(--sp) = tf->tf_eflags;
		*(--sp) = tf->tf_cs;
		*(--sp) = tf->tf_eip + 2;
		tf->tf_esp -= 6;
		tf->tf_cs = *(uint16_t *)(trapno * 4 + 2);
		tf->tf_eip = *(uint16_t *)(trapno * 4);
		break;
	case 0xcf: /* IRET */
		sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp);
		tf->tf_eip = *(sp++);
		tf->tf_cs = *(sp++);
		tf->tf_eflags = *(sp++);
		tf->tf_esp += 6;
		tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */
		break;
#ifndef KVM86_IOPL3 /* XXX check VME? */
	case 0xfa: /* CLI */
	case 0xfb: /* STI */
		/* XXX ignore for now */
		tf->tf_eip++;
		break;
	case 0x9c: /* PUSHF */
		sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp);
		*(--sp) = tf->tf_eflags;
		tf->tf_esp -= 2;
		tf->tf_eip++;
		break;
	case 0x9d: /* POPF */
		sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp);
		tf->tf_eflags = *(sp++);
		tf->tf_esp += 2;
		tf->tf_eip++;
		tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */
		break;
#endif
	default:
#ifdef KVM86DEBUG
		printf("kvm86_gpfault: unhandled\n");
#else
		printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n",
		       tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags);
#endif
		/*
		 * signal error to caller
		 */
		kvm86_ret(tf, -1);
		break;
	}
}


code:
/* $OpenBSD: kvm86call.S,v 1.7 2015/04/25 21:31:24 guenther Exp $ */
/* $NetBSD: kvm86call.S,v 1.7 2006/04/11 17:14:07 drochner Exp $ */

/*-
 * Copyright (c) 1998 Jonathan Lemon
 * All rights reserved.
 * Copyright (c) 2002
 *	Matthias Drochner. 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.
 *
 * $FreeBSD: src/sys/i386/i386/vm86bios.s,v 1.28 2001/12/11 23:33:40 jhb Exp $
 */

#include <machine/asm.h>
#include <machine/param.h>
#include "assym.h"

	.data
	.align 4

/* flag for trap() */
	.globl	kvm86_incall
kvm86_incall:		.long	0

/* XXX arguments for kvm86call() */
	.globl vm86pcb, vm86newptd, vm86frame, vm86pgtableva
	.globl	vm86tssd0, vm86tssd1

/* locals */
SCRARGFRAME:		.long	0
SCRSTACK:		.long	0
SCRTSS0:		.long	0
SCRTSS1:		.long	0

	.text

#if defined MULTIPROCESSOR
#include <machine/i82489reg.h>
#endif

/* int kvm86_call(struct trapframe *) */
ENTRY(kvm86_call)
	pushl	%ebp
	movl	%esp,%ebp		/* set up frame ptr */
	pushl	%esi
	pushl	%edi
	pushl	%ebx
	pushl	%fs
	pushl	%gs

	movl	8(%ebp),%eax
	movl	%eax,SCRARGFRAME	/* save argument pointer */

	movl	vm86frame,%edi		/* target frame location */
	movl    SCRARGFRAME,%esi	/* source (set on entry) */
	movl	$FRAMESIZE/4,%ecx	/* sizeof(struct trapframe)/4 */
	rep
	movsl				/* copy frame to new stack */

#ifdef MULTIPROCESSOR
	movl	_C_LABEL(local_apic)+LAPIC_ID,%eax
	shrl	$LAPIC_ID_SHIFT,%eax
	xorl	%ebx,%ebx
1:
	leal	0(,%ebx,4),%ecx
	incl	%ebx
	movl	_C_LABEL(cpu_info)(%ecx),%ecx
	movl	CPU_INFO_APICID(%ecx),%edx
	cmpl	%eax,%edx
	jne 1b
	movl	%ecx,%edx
#else
	leal	_C_LABEL(cpu_info_primary),%ecx
#endif

	movl	CPU_INFO_CURPCB(%ecx),%eax
	pushl	%eax			/* save curpcb */

	movl	vm86pcb,%eax
	movl	%eax,CPU_INFO_CURPCB(%ecx) /* set curpcb to vm86pcb */
	movl	CPU_INFO_CURPROC(%ecx),%ecx
	movl	P_MD_TSS_SEL(%ecx),%edi

#ifdef	MULTIPROCESSOR
	movl	CPU_INFO_GDT(%edx),%eax
#else
	movl	_C_LABEL(gdt),%eax
#endif
	andl	$~0x0200,4(%eax,%edi,1)	/* reset "task busy" */

	movl	0(%eax,%edi,1),%edx
	movl	%edx,SCRTSS0		/* save first word */
	movl	4(%eax,%edi,1),%edx
	movl	%edx,SCRTSS1		/* save second word */

	movl	vm86tssd0,%edx		/* vm86 tssd entry */
	movl	%edx,0(%eax,%edi,1)
	movl	vm86tssd1,%edx		/* vm86 tssd entry */
	movl	%edx,4(%eax,%edi,1)
	ltr	%di

	movl	%cr3,%eax
	pushl	%eax			/* save address space */
	movl	PTDpaddr,%ecx
	movl	%ecx,%ebx
	addl	$KERNBASE,%ebx		/* va of Idle PDP */
	movl	0(%ebx),%eax
	pushl	%eax			/* old pde */
	pushl	%ebx			/* keep address for reuse */

	movl	%esp,SCRSTACK		/* save current stack location */

	movl	vm86newptd,%eax		/* mapping for vm86 page table */
	movl	%eax,0(%ebx)		/* ... install as PDP entry 0 */

	movl	%ecx,%cr3		/* new page tables */
	movl	vm86frame,%esp		/* switch to new stack */

	movl	$1,kvm86_incall		/* set flag for trap() */

	/* INTRFASTEXIT */
	popl	%gs
	popl	%fs
	popl	%es
	popl	%ds
	popl	%edi
	popl	%esi
	popl	%ebp
	popl	%ebx
	popl	%edx
	popl	%ecx
	popl	%eax
	addl	$8,%esp
	iret

/* void kvm86_ret(struct trapframe *, int) */
ENTRY(kvm86_ret)
	pushl	%ebp
	movl	%esp,%ebp		/* set up frame ptr */

	movl	8(%ebp),%esi		/* source */
	movl	SCRARGFRAME,%edi	/* destination */
	movl	$FRAMESIZE/4,%ecx	/* size */
	rep
	movsl				/* copy frame to original frame */

	movl	SCRSTACK,%esp		/* back to old stack */
	popl	%ebx			/* saved va of Idle PDP */
	popl	%eax
	movl	%eax,0(%ebx)		/* restore old pde */
	popl	%eax
	movl	%eax,%cr3		/* install old page table */

	movl	$0,kvm86_incall		/* reset trapflag */

#ifdef MULTIPROCESSOR
	movl	_C_LABEL(local_apic)+LAPIC_ID,%eax
	shrl	$LAPIC_ID_SHIFT,%eax
	xorl	%ebx,%ebx
1:
	leal	0(,%ebx,4),%ecx
	incl	%ebx
	movl	_C_LABEL(cpu_info)(%ecx),%ecx
	movl	CPU_INFO_APICID(%ecx),%edx
	cmpl	%eax,%edx
	jne 1b
	movl	CPU_INFO_GDT(%ecx),%eax
#else
	leal	_C_LABEL(cpu_info_primary),%ecx
	movl	_C_LABEL(gdt),%eax
#endif
	movl	CPU_INFO_CURPROC(%ecx),%ecx
	movl	P_MD_TSS_SEL(%ecx),%edi
	movl	SCRTSS0, %edx
	movl	%edx, 0(%eax,%edi,1)	/* restore first word */
	movl	SCRTSS1, %edx
	movl	%edx, 4(%eax,%edi,1)	/* restore second word */
	ltr	%di

#ifdef MULTIPROCESSOR
	movl	_C_LABEL(local_apic)+LAPIC_ID,%eax
	shrl	$LAPIC_ID_SHIFT,%eax
	xorl	%ebx,%ebx
1:
	leal	0(,%ebx,4),%ecx
	incl	%ebx
	movl	_C_LABEL(cpu_info)(%ecx),%ecx
	movl	CPU_INFO_APICID(%ecx),%edx
	cmpl	%eax,%edx
	jne 1b
#else
	leal	_C_LABEL(cpu_info_primary),%ecx
#endif
	popl	%eax			/* restore curpcb */
	movl	%eax,CPU_INFO_CURPCB(%ecx)

	movl	12(%ebp), %eax		/* will be kvm86_call()'s retval */

	popl	%gs
	popl	%fs
	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret				/* back to kvm86_call()'s caller */


code:
/*	$OpenBSD: lapic.c,v 1.40 2015/07/18 19:21:03 sf Exp $	*/
/* $NetBSD: lapic.c,v 1.1.2.8 2000/02/23 06:10:50 sommerfeld Exp $ */

/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by RedBack Networks Inc.
 *
 * Author: Bill Sommerfeld
 *
 * 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 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/cpuvar.h>
#include <machine/pmap.h>
#include <machine/vmparam.h>
#include <machine/mpbiosvar.h>
#include <machine/pcb.h>
#include <machine/specialreg.h>
#include <machine/segments.h>

#include <machine/apicvar.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#include <machine/pctr.h>

#include <dev/ic/i8253reg.h>

struct evcount clk_count;
#ifdef MULTIPROCESSOR
struct evcount ipi_count;
#endif

void	lapic_delay(int);
static u_int32_t lapic_gettick(void);
void	lapic_clockintr(void *);
void	lapic_initclocks(void);
void	lapic_map(paddr_t);

void
lapic_map(paddr_t lapic_base)
{
	int s;
	vaddr_t va = (vaddr_t)&local_apic;

	disable_intr();
	s = lapic_tpr;

	/*
	 * Map local apic.  If we have a local apic, it's safe to assume
	 * we're on a 486 or better and can use invlpg and non-cacheable PTE's
	 *
	 * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
	 * the latter will attempt to invoke TLB shootdown code just as we
	 * might have changed the value of cpu_number()..
	 */

	pmap_pte_set(va, lapic_base, PG_RW | PG_V | PG_N);
	invlpg(va);

#ifdef MULTIPROCESSOR
	cpu_init_first();
#endif

	lapic_tpr = s;
	enable_intr();
}

/*
 * enable local apic
 */
void
lapic_enable(void)
{
	i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
}

void
lapic_disable(void)
{
	i82489_writereg(LAPIC_SVR, 0);
}

void
lapic_set_softvectors(void)
{
	idt_vec_set(LAPIC_SOFTCLOCK_VECTOR, Xintrsoftclock);
	idt_vec_set(LAPIC_SOFTNET_VECTOR, Xintrsoftnet);
	idt_vec_set(LAPIC_SOFTTTY_VECTOR, Xintrsofttty);
}

void
lapic_set_lvt(void)
{
	struct cpu_info *ci = curcpu();
	int i;
	struct mp_intr_map *mpi;

#ifdef MULTIPROCESSOR
	if (mp_verbose) {
		apic_format_redir(ci->ci_dev.dv_xname, "prelint", 0, 0,
		    i82489_readreg(LAPIC_LVINT0));
		apic_format_redir(ci->ci_dev.dv_xname, "prelint", 1, 0,
		    i82489_readreg(LAPIC_LVINT1));
	}
#endif

	if (strcmp(cpu_vendor, "AuthenticAMD") == 0) {
		/*
		 * Detect the presence of C1E capability mostly on latest
		 * dual-cores (or future) k8 family. This mis-feature renders
		 * the local APIC timer dead, so we disable it by reading
		 * the Interrupt Pending Message register and clearing both
		 * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
		 *
		 * Reference:
		 *   "BIOS and Kernel Developer's Guide for AMD NPT
		 *    Family 0Fh Processors"
		 *   #32559 revision 3.00
		 */
		if ((cpu_id & 0x00000f00) == 0x00000f00 &&
		    (cpu_id & 0x0fff0000) >= 0x00040000) {
			uint64_t msr;

			msr = rdmsr(MSR_INT_PEN_MSG);
			if (msr & (IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT)) {
				msr &= ~(IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT);
				wrmsr(MSR_INT_PEN_MSG, msr);
			}
		}
	}

	for (i = 0; i < mp_nintrs; i++) {
		mpi = &mp_intrs[i];
		if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
					    || mpi->cpu_id == ci->ci_apicid)) {
#ifdef DIAGNOSTIC
			if (mpi->ioapic_pin > 1)
				panic("lapic_set_lvt: bad pin value %d",
				    mpi->ioapic_pin);
#endif
			if (mpi->ioapic_pin == 0)
				i82489_writereg(LAPIC_LVINT0, mpi->redir);
			else
				i82489_writereg(LAPIC_LVINT1, mpi->redir);
		}
	}

#ifdef MULTIPROCESSOR
	if (mp_verbose) {
		apic_format_redir(ci->ci_dev.dv_xname, "timer", 0, 0,
		    i82489_readreg(LAPIC_LVTT));
		apic_format_redir(ci->ci_dev.dv_xname, "pcint", 0, 0,
		    i82489_readreg(LAPIC_PCINT));
		apic_format_redir(ci->ci_dev.dv_xname, "lint", 0, 0,
		    i82489_readreg(LAPIC_LVINT0));
		apic_format_redir(ci->ci_dev.dv_xname, "lint", 1, 0,
		    i82489_readreg(LAPIC_LVINT1));
		apic_format_redir(ci->ci_dev.dv_xname, "err", 0, 0,
		    i82489_readreg(LAPIC_LVERR));
	}
#endif
}

/*
 * Initialize fixed idt vectors for use by local apic.
 */
void
lapic_boot_init(paddr_t lapic_base)
{
	static int clk_irq = 0;
#ifdef MULTIPROCESSOR
	static int ipi_irq = 0;
#endif

	lapic_map(lapic_base);

#ifdef MULTIPROCESSOR
	idt_vec_set(LAPIC_IPI_VECTOR, Xintripi);
	idt_vec_set(LAPIC_IPI_INVLTLB, Xintripi_invltlb);
	idt_vec_set(LAPIC_IPI_INVLPG, Xintripi_invlpg);
	idt_vec_set(LAPIC_IPI_INVLRANGE, Xintripi_invlrange);
	idt_vec_set(LAPIC_IPI_RELOADCR3, Xintripi_reloadcr3);
#endif
	idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
	idt_vec_set(LAPIC_TIMER_VECTOR, Xintrltimer);

	evcount_attach(&clk_count, "clock", &clk_irq);
#ifdef MULTIPROCESSOR
	evcount_attach(&ipi_count, "ipi", &ipi_irq);
#endif
}

static __inline u_int32_t
lapic_gettick(void)
{
	return i82489_readreg(LAPIC_CCR_TIMER);
}

#include <sys/kernel.h>		/* for hz */

u_int32_t lapic_tval;

/*
 * this gets us up to a 4GHz busclock....
 */
u_int32_t lapic_per_second;
u_int32_t lapic_frac_usec_per_cycle;
u_int64_t lapic_frac_cycle_per_usec;
u_int32_t lapic_delaytab[26];

void
lapic_clockintr(void *arg)
{
	struct clockframe *frame = arg;

	hardclock(frame);

	clk_count.ec_count++;
}

void
lapic_startclock(void)
{
	/*
	 * Start local apic countdown timer running, in repeated mode.
	 *
	 * Mask the clock interrupt and set mode,
	 * then set divisor,
	 * then unmask and set the vector.
	 */
	i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
	i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
	i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
	i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
}

void
lapic_initclocks(void)
{
	lapic_startclock();

	i8254_inittimecounter_simple();
}

extern int gettick(void);	/* XXX put in header file */

static __inline void
wait_next_cycle(void)
{
	unsigned int tick, tlast;

	tlast = (1 << 16);	/* i8254 counter has 16 bits at most */
	for (;;) {
		tick = gettick();
		if (tick > tlast)
			return;
		tlast = tick;
	}
}

/*
 * Calibrate the local apic count-down timer (which is running at
 * bus-clock speed) vs. the i8254 counter/timer (which is running at
 * a fixed rate).
 *
 * The Intel MP spec says: "An MP operating system may use the IRQ8
 * real-time clock as a reference to determine the actual APIC timer clock
 * speed."
 *
 * We're actually using the IRQ0 timer.  Hmm.
 */
void
lapic_calibrate_timer(struct cpu_info *ci)
{
	unsigned int startapic, endapic;
	u_int64_t dtick, dapic, tmp;
	int i, ef = read_eflags();

	if (mp_verbose)
		printf("%s: calibrating local timer\n", ci->ci_dev.dv_xname);

	/*
	 * Configure timer to one-shot, interrupt masked,
	 * large positive number.
	 */
	i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_M);
	i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
	i82489_writereg(LAPIC_ICR_TIMER, 0x80000000);

	disable_intr();

	/* wait for current cycle to finish */
	wait_next_cycle();

	startapic = lapic_gettick();

	/* wait the next hz cycles */
	for (i = 0; i < hz; i++)
		wait_next_cycle();

	endapic = lapic_gettick();
	write_eflags(ef);

	dtick = hz * TIMER_DIV(hz);
	dapic = startapic-endapic;

	/*
	 * there are TIMER_FREQ ticks per second.
	 * in dtick ticks, there are dapic bus clocks.
	 */
	tmp = (TIMER_FREQ * dapic) / dtick;

	lapic_per_second = tmp;

	printf("%s: apic clock running at %lldMHz\n",
	    ci->ci_dev.dv_xname, tmp / (1000 * 1000));

	if (lapic_per_second != 0) {
		/*
		 * reprogram the apic timer to run in periodic mode.
		 * XXX need to program timer on other cpu's, too.
		 */
		lapic_tval = (lapic_per_second * 2) / hz;
		lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);

		i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM | LAPIC_LVTT_M |
		    LAPIC_TIMER_VECTOR);
		i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
		i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);

		/*
		 * Compute fixed-point ratios between cycles and
		 * microseconds to avoid having to do any division
		 * in lapic_delay.
		 */

		tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
		lapic_frac_usec_per_cycle = tmp;

		tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;

		lapic_frac_cycle_per_usec = tmp;

		/*
		 * Compute delay in cycles for likely short delays in usec.
		 */
		for (i = 0; i < 26; i++)
			lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
			    32;

		/*
		 * Now that the timer's calibrated, use the apic timer routines
		 * for all our timing needs..
		 */
		delay_func = lapic_delay;
		initclock_func = lapic_initclocks;
	}
}

/*
 * delay for N usec.
 */

void
lapic_delay(int usec)
{
	int32_t tick, otick;
	int64_t deltat;		/* XXX may want to be 64bit */

	otick = lapic_gettick();

	if (usec <= 0)
		return;
	if (usec <= 25)
		deltat = lapic_delaytab[usec];
	else
		deltat = (lapic_frac_cycle_per_usec * usec) >> 32;

	while (deltat > 0) {
		tick = lapic_gettick();
		if (tick > otick)
			deltat -= lapic_tval - (tick - otick);
		else
			deltat -= otick - tick;
		otick = tick;
	}
}

/*
 * XXX the following belong mostly or partly elsewhere..
 */

static __inline void i82489_icr_wait(void);

static __inline void
i82489_icr_wait(void)
{
#ifdef DIAGNOSTIC
	unsigned j = 100000;
#endif /* DIAGNOSTIC */

	while ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
		__asm volatile("pause": : :"memory");
#ifdef DIAGNOSTIC
		j--;
		if (j == 0)
			panic("i82489_icr_wait: busy");
#endif /* DIAGNOSTIC */
	}
}

#ifdef MULTIPROCESSOR
void
i386_ipi_init(int target)
{
	if ((target & LAPIC_DEST_MASK) == 0)
		i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);

	i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
	    LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );

	i82489_icr_wait();

	i8254_delay(10000);

	i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
	     LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);

	i82489_icr_wait();
}

void
i386_ipi(int vec, int target, int dl)
{
	int s;

	s = splhigh();

	i82489_icr_wait();

	if ((target & LAPIC_DEST_MASK) == 0)
		i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);

	i82489_writereg(LAPIC_ICRLO,
	    (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);

	i82489_icr_wait();

	splx(s);
}
#endif /* MULTIPROCESSOR */