code:
/*	$OpenBSD: mainbus.c,v 1.52 2015/07/23 06:21:37 reyk Exp $	*/
/*	$NetBSD: mainbus.c,v 1.21 1997/06/06 23:14:20 thorpej Exp $	*/

/*
 * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Christopher G. Demetriou
 *	for the NetBSD Project.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <machine/bus.h>

#include <dev/isa/isavar.h>
#include <dev/eisa/eisavar.h>
#include <dev/pci/pcivar.h>

#include <dev/isa/isareg.h>		/* for ISA_HOLE_VADDR */
#include <i386/isa/isa_machdep.h>

#include "pci.h"
#include "eisa.h"
#include "isa.h"
#include "apm.h"
#include "bios.h"
#include "acpi.h"
#include "ipmi.h"
#include "esm.h"
#include "vesabios.h"
#include "amdmsr.h"
#include "pvbus.h"

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

#if NBIOS > 0
#include <machine/biosvar.h>
#endif

#include <dev/acpi/acpivar.h>

#if NIPMI > 0
#include <dev/ipmivar.h>
#endif

#if NPVBUS > 0
#include <dev/pv/pvvar.h>
#endif

#if NAMDMSR > 0
#include <machine/amdmsr.h>
#endif

#if NESM > 0
#include <arch/i386/i386/esmvar.h>
#endif

#if NVESABIOS > 0
#include <dev/vesa/vesabiosvar.h>
#endif

int	mainbus_match(struct device *, void *, void *);
void	mainbus_attach(struct device *, struct device *, void *);

struct cfattach mainbus_ca = {
	sizeof(struct device), mainbus_match, mainbus_attach, NULL,
	config_activate_children
};

struct cfdriver mainbus_cd = {
	NULL, "mainbus", DV_DULL
};

int	mainbus_print(void *, const char *);

union mainbus_attach_args {
	const char *mba_busname;		/* first elem of all */
	struct pcibus_attach_args mba_pba;
	struct eisabus_attach_args mba_eba;
	struct isabus_attach_args mba_iba;
#if NBIOS > 0
	struct bios_attach_args mba_bios;
#endif
	struct cpu_attach_args mba_caa;
	struct apic_attach_args	aaa_caa;
#if NIPMI > 0
	struct ipmi_attach_args mba_iaa;
#endif
#if NESM > 0
	struct esm_attach_args mba_eaa;
#endif
#if NPVBUS > 0
	struct pvbus_attach_args mba_pvba;
#endif
};

/*
 * This is set when the ISA bus is attached.  If it's not set by the
 * time it's checked below, then mainbus attempts to attach an ISA.
 */
int     isa_has_been_seen;

/*
 * Probe for the mainbus; always succeeds.
 */
int
mainbus_match(struct device *parent, void *match, void *aux)
{
	return (1);
}

/*
 * Attach the mainbus.
 */
void
mainbus_attach(struct device *parent, struct device *self, void *aux)
{
	union mainbus_attach_args	mba;
	extern void			(*setperf_setup)(struct cpu_info *);
	extern void			(*cpusensors_setup)(struct cpu_info *);

	printf("\n");

#if NBIOS > 0
	{
		mba.mba_bios.ba_name = "bios";
		mba.mba_bios.ba_iot = I386_BUS_SPACE_IO;
		mba.mba_bios.ba_memt = I386_BUS_SPACE_MEM;
		config_found(self, &mba.mba_bios, mainbus_print);
	}
#endif

#if NIPMI > 0
	{
		memset(&mba.mba_iaa, 0, sizeof(mba.mba_iaa));
		mba.mba_iaa.iaa_name = "ipmi";
		mba.mba_iaa.iaa_iot  = I386_BUS_SPACE_IO;
		mba.mba_iaa.iaa_memt = I386_BUS_SPACE_MEM;
		if (ipmi_probe(&mba.mba_iaa))
			config_found(self, &mba.mba_iaa, mainbus_print);
	}
#endif

	if ((cpu_info_primary.ci_flags & CPUF_PRESENT) == 0) {
		struct cpu_attach_args caa;

		memset(&caa, 0, sizeof(caa));
		caa.caa_name = "cpu";
		caa.cpu_number = 0;
		caa.cpu_role = CPU_ROLE_SP;
		caa.cpu_func = 0;
		caa.cpu_signature = cpu_id;
		caa.feature_flags = cpu_feature;

		config_found(self, &caa, mainbus_print);
	}
#if NAMDMSR > 0
	if (amdmsr_probe()) {
		mba.mba_busname = "amdmsr";
		config_found(self, &mba.mba_busname, mainbus_print);
	}
#endif

#if NACPI > 0
	if (!acpi_hasprocfvs)
#endif
	{
		if (setperf_setup != NULL)
			setperf_setup(&cpu_info_primary);
	}

#ifdef MULTIPROCESSOR
	mp_setperf_init();
#endif

	if (cpusensors_setup != NULL)
		cpusensors_setup(&cpu_info_primary);

#if NVESABIOS > 0
	if (vbeprobe())	{
		mba.mba_busname = "vesabios";
		config_found(self, &mba.mba_busname, NULL);
	}
#endif

#if NESM > 0
	{
		memset(&mba.mba_eaa, 0, sizeof(mba.mba_eaa));
		mba.mba_eaa.eaa_name = "esm";
		mba.mba_eaa.eaa_iot  = I386_BUS_SPACE_IO;
		mba.mba_eaa.eaa_memt = I386_BUS_SPACE_MEM;
		if (esm_probe(&mba.mba_eaa))
			config_found(self, &mba.mba_eaa, mainbus_print);
	}
#endif

#if NPVBUS > 0
	/* Probe first to hide the "not configured" message */
	if (pvbus_probe()) {
		mba.mba_pvba.pvba_busname = "pvbus";
		config_found(self, &mba.mba_pvba.pvba_busname, mainbus_print);
	}
#endif

	/*
	 * XXX Note also that the presence of a PCI bus should
	 * XXX _always_ be checked, and if present the bus should be
	 * XXX 'found'.  However, because of the structure of the code,
	 * XXX that's not currently possible.
	 */
#if NPCI > 0
	if (pci_mode_detect() != 0) {
		pci_init_extents();

		bzero(&mba.mba_pba, sizeof(mba.mba_pba));
		mba.mba_pba.pba_busname = "pci";
		mba.mba_pba.pba_iot = I386_BUS_SPACE_IO;
		mba.mba_pba.pba_memt = I386_BUS_SPACE_MEM;
		mba.mba_pba.pba_dmat = &pci_bus_dma_tag;
		mba.mba_pba.pba_ioex = pciio_ex;
		mba.mba_pba.pba_memex = pcimem_ex;
		mba.mba_pba.pba_busex = pcibus_ex;
		mba.mba_pba.pba_domain = pci_ndomains++;
		mba.mba_pba.pba_bus = 0;
		config_found(self, &mba.mba_pba, mainbus_print);
#if NACPI > 0
		acpi_pciroots_attach(self, &mba.mba_pba, mainbus_print);
#endif
	}
#endif

	if (!bcmp(ISA_HOLE_VADDR(EISA_ID_PADDR), EISA_ID, EISA_ID_LEN)) {
		mba.mba_eba.eba_busname = "eisa";
		mba.mba_eba.eba_iot = I386_BUS_SPACE_IO;
		mba.mba_eba.eba_memt = I386_BUS_SPACE_MEM;
#if NEISA > 0
		mba.mba_eba.eba_dmat = &eisa_bus_dma_tag;
#endif
		config_found(self, &mba.mba_eba, mainbus_print);
	}

	if (isa_has_been_seen == 0) {
		mba.mba_iba.iba_busname = "isa";
		mba.mba_iba.iba_iot = I386_BUS_SPACE_IO;
		mba.mba_iba.iba_memt = I386_BUS_SPACE_MEM;
#if NISADMA > 0
		mba.mba_iba.iba_dmat = &isa_bus_dma_tag;
#endif
		config_found(self, &mba.mba_iba, mainbus_print);
	}
}

int
mainbus_print(void *aux, const char *pnp)
{
	union mainbus_attach_args	*mba = aux;

	if (pnp)
		printf("%s at %s", mba->mba_busname, pnp);
	if (!strcmp(mba->mba_busname, "pci"))
		printf(" bus %d", mba->mba_pba.pba_bus);

	return (UNCONF);
}

license: unknown


code:
/*	$NetBSD: mem.c,v 1.31 1996/05/03 19:42:19 christos Exp $	*/
/*	$OpenBSD: mem.c,v 1.45 2015/06/22 18:57:26 kettenis Exp $ */
/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1982, 1986, 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * 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.
 *
 *	@(#)mem.c	8.3 (Berkeley) 1/12/94
 */

/*
 * Memory special file
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/ioccom.h>
#include <sys/malloc.h>
#include <sys/memrange.h>
#include <sys/fcntl.h>

#include <machine/cpu.h>
#include <machine/conf.h>

#include <uvm/uvm_extern.h>

extern char *vmmap;            /* poor name! */
caddr_t zeropage;

/* open counter for aperture */
#ifdef APERTURE
static int ap_open_count = 0;
extern int allowaperture;

#define VGA_START 0xA0000
#define BIOS_END  0xFFFFF
#endif

#ifdef MTRR
struct mem_range_softc mem_range_softc;
static int mem_ioctl(dev_t, u_long, caddr_t, int, struct proc *);
#endif

/*ARGSUSED*/
int
mmopen(dev_t dev, int flag, int mode, struct proc *p)
{

	switch (minor(dev)) {
	case 0:
	case 1:
	case 2:
	case 12:
		break;
#ifdef APERTURE
	case 4:
	        if (suser(p, 0) != 0 || !allowaperture)
			return (EPERM);

		/* authorize only one simultaneous open() unless
		 * allowaperture=3 */
		if (ap_open_count > 0 && allowaperture < 3)
			return(EPERM);
		ap_open_count++;
		break;
#endif
	default:
		return (ENXIO);
	}
	return (0);
}

/*ARGSUSED*/
int
mmclose(dev_t dev, int flag, int mode, struct proc *p)
{
#ifdef APERTURE
	if (minor(dev) == 4)
		ap_open_count = 0;
#endif
	return (0);
}

/*ARGSUSED*/
int
mmrw(dev_t dev, struct uio *uio, int flags)
{
	vaddr_t o, v;
	size_t c;
	struct iovec *iov;
	int error = 0;
	static int physlock;

	if (minor(dev) == 0) {
		/* lock against other uses of shared vmmap */
		while (physlock > 0) {
			physlock++;
			error = tsleep((caddr_t)&physlock, PZERO | PCATCH,
			    "mmrw", 0);
			if (error)
				return (error);
		}
		physlock = 1;
	}
	while (uio->uio_resid > 0 && error == 0) {
		iov = uio->uio_iov;
		if (iov->iov_len == 0) {
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if (uio->uio_iovcnt < 0)
				panic("mmrw");
			continue;
		}
		switch (minor(dev)) {

/* minor device 0 is physical memory */
		case 0:
			v = uio->uio_offset;
			pmap_enter(pmap_kernel(), (vaddr_t)vmmap,
			    trunc_page(v), uio->uio_rw == UIO_READ ?
			    PROT_READ : PROT_WRITE, PMAP_WIRED);
			pmap_update(pmap_kernel());
			o = uio->uio_offset & PGOFSET;
			c = ulmin(uio->uio_resid, NBPG - o);
			error = uiomove((caddr_t)vmmap + o, c, uio);
			pmap_remove(pmap_kernel(), (vaddr_t)vmmap,
			    (vaddr_t)vmmap + NBPG);
			pmap_update(pmap_kernel());
			continue;

/* minor device 1 is kernel memory */
		case 1:
			v = uio->uio_offset;
			c = ulmin(iov->iov_len, MAXPHYS);
			if (!uvm_kernacc((caddr_t)v, c,
			    uio->uio_rw == UIO_READ ? B_READ : B_WRITE))
				return (EFAULT);
			error = uiomove((caddr_t)v, c, uio);
			continue;

/* minor device 2 is EOF/RATHOLE */
		case 2:
			if (uio->uio_rw == UIO_WRITE)
				uio->uio_resid = 0;
			return (0);

/* minor device 12 (/dev/zero) is source of nulls on read, rathole on write */
		case 12:
			if (uio->uio_rw == UIO_WRITE) {
				c = iov->iov_len;
				break;
			}
			if (zeropage == NULL) {
				zeropage = malloc(PAGE_SIZE, M_TEMP,
				    M_WAITOK|M_ZERO);
			}
			c = ulmin(iov->iov_len, PAGE_SIZE);
			error = uiomove(zeropage, c, uio);
			continue;

		default:
			return (ENXIO);
		}
		iov->iov_base = (char *)iov->iov_base + c;
		iov->iov_len -= c;
		uio->uio_offset += c;
		uio->uio_resid -= c;
	}
	if (minor(dev) == 0) {
		if (physlock > 1)
			wakeup((caddr_t)&physlock);
		physlock = 0;
	}
	return (error);
}

paddr_t
mmmmap(dev_t dev, off_t off, int prot)
{
	struct proc *p = curproc;	/* XXX */

	switch (minor(dev)) {
/* minor device 0 is physical memory */
	case 0:
		if ((u_int)off > ptoa(physmem) && suser(p, 0) != 0)
			return -1;
		return off;

#ifdef APERTURE
/* minor device 4 is aperture driver */
	case 4:
		/* Check if a write combining mapping is requested. */
		if (off >= MEMRANGE_WC_RANGE)
			off = (off - MEMRANGE_WC_RANGE) | PMAP_WC;

		switch (allowaperture) {
		case 1:
			/* Allow mapping of the VGA framebuffer & BIOS only */
			if ((off >= VGA_START && off <= BIOS_END) ||
			    (unsigned)off > (unsigned)ptoa(physmem))
				return off;
			else
				return -1;
		case 2:
		case 3:
			/* Allow mapping of the whole 1st megabyte
			   for x86emu */
			if (off <= BIOS_END ||
			    (unsigned)off > (unsigned)ptoa(physmem))
				return off;
			else
				return -1;
		default:
			return -1;
		}

#endif
	default:
		return -1;
	}
}

int
mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
#ifdef MTRR
	switch (minor(dev)) {
	case 0:
	case 4:
		return mem_ioctl(dev, cmd, data, flags, p);
	}
#endif
	return (ENODEV);
}

#ifdef MTRR
/*
 * Operations for changing memory attributes.
 *
 * This is basically just an ioctl shim for mem_range_attr_get
 * and mem_range_attr_set.
 */
static int
mem_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	int nd, error = 0;
	struct mem_range_op *mo = (struct mem_range_op *)data;
	struct mem_range_desc *md;

	/* is this for us? */
	if ((cmd != MEMRANGE_GET) &&
	    (cmd != MEMRANGE_SET))
		return (ENOTTY);

	/* any chance we can handle this? */
	if (mem_range_softc.mr_op == NULL)
		return (EOPNOTSUPP);

	/* do we have any descriptors? */
	if (mem_range_softc.mr_ndesc == 0)
		return (ENXIO);

	switch (cmd) {
	case MEMRANGE_GET:
		nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
		if (nd > 0) {
			md = (struct mem_range_desc *)
				malloc(nd * sizeof(struct mem_range_desc),
				       M_MEMDESC, M_WAITOK);
			error = mem_range_attr_get(md, &nd);
			if (!error)
				error = copyout(md, mo->mo_desc,
					nd * sizeof(struct mem_range_desc));
			free(md, M_MEMDESC, 0);
		} else {
			nd = mem_range_softc.mr_ndesc;
		}
		mo->mo_arg[0] = nd;
		break;

	case MEMRANGE_SET:
		md = malloc(sizeof(struct mem_range_desc), M_MEMDESC, M_WAITOK);
		error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
		/* clamp description string */
		md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
		if (error == 0)
			error = mem_range_attr_set(md, &mo->mo_arg[0]);
		free(md, M_MEMDESC, 0);
		break;
	}
	return (error);
}

/*
 * Implementation-neutral, kernel-callable functions for manipulating
 * memory range attributes.
 */
int
mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
{
	/* can we handle this? */
	if (mem_range_softc.mr_op == NULL)
		return (EOPNOTSUPP);

	if (*arg == 0) {
		*arg = mem_range_softc.mr_ndesc;
	} else {
		bcopy(mem_range_softc.mr_desc, mrd, (*arg) * sizeof(struct mem_range_desc));
	}
	return (0);
}

int
mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
{
	/* can we handle this? */
	if (mem_range_softc.mr_op == NULL)
		return (EOPNOTSUPP);

	return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
}

#endif /* MTRR */


code:
/* $OpenBSD: mp_setperf.c,v 1.6 2014/09/12 09:52:45 kettenis Exp $ */
/*
 * Copyright (c) 2007 Gordon Willem Klok <gwk@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>

#include <machine/cpu.h>
#include <machine/intr.h>

struct mutex setperf_mp_mutex = MUTEX_INITIALIZER(IPL_HIGH);

/* underlying setperf mechanism e.g. k8_powernow_setperf() */
void (*ul_setperf)(int);

/* protected by setperf_mp_mutex */
volatile int mp_perflevel;

void mp_setperf(int);

void
mp_setperf(int level)
{
	mtx_enter(&setperf_mp_mutex);
	mp_perflevel = level;

	ul_setperf(mp_perflevel);
	i386_broadcast_ipi(I386_IPI_SETPERF);
	mtx_leave(&setperf_mp_mutex);
}

void
i386_setperf_ipi(struct cpu_info *ci)
{
	ul_setperf(mp_perflevel);
}

void
mp_setperf_init(void)
{
	if (!cpu_setperf)
		return;

	ul_setperf = cpu_setperf;
	cpu_setperf = mp_setperf;
	mtx_init(&setperf_mp_mutex, IPL_HIGH);
}


code:
/*	$OpenBSD: mpbios.c,v 1.38 2015/06/07 12:16:27 jsg Exp $	*/
/*	$NetBSD: mpbios.c,v 1.2 2002/10/01 12:56:57 fvdl 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.
 */
/*
 * Derived from FreeBSD's mp_machdep.c
 */
/*
 * Copyright (c) 1996, by Steve Passe
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 */

/*
 * The Intel MP-stuff is just one way of x86 SMP systems
 * so only Intel MP specific stuff is here.
 */

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

#include <uvm/uvm_extern.h>

#include <machine/specialreg.h>
#include <machine/cputypes.h>
#include <machine/cpuvar.h>
#include <machine/bus.h>
#include <machine/biosvar.h>
#include <machine/mpbiosreg.h>
#include <machine/mpbiosvar.h>

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

#include <dev/isa/isareg.h>
#include <dev/pci/pcivar.h>

#include <dev/eisa/eisavar.h>	/* for ELCR* def'ns */

#include "pci.h"

#include "apm.h"
#include "acpi.h"
#if NAPM > 0 && NACPI > 0
extern int haveacpibutusingapm;
#endif

static struct mpbios_ioapic default_ioapic = {
    2, 0, 1, IOAPICENTRY_FLAG_EN, (caddr_t)IOAPIC_BASE_DEFAULT
};

/* descriptions of MP basetable entries */
struct mpbios_baseentry {
	u_int8_t  	type;
	u_int8_t  	length;
	u_int16_t	count;
	const char    	*name;
};

static const char *loc_where[] = {
	"extended bios data area",
	"last page of base memory",
	"bios"
};

struct mp_map
{
	vaddr_t 	baseva;
	int	 	vsize;
	paddr_t 	pa;
	paddr_t 	pg;
	int		psize;
};

int	mp_print(void *, const char *);
int	mp_match(struct device *, void *, void *);
int	mpbios_cpu_start(struct cpu_info *);
const void *mpbios_search(struct device *, paddr_t, int, struct mp_map *);
static __inline int mpbios_cksum(const void *, int);

void	mp_cfg_special_intr(const struct mpbios_int *, u_int32_t *);
void	mp_print_special_intr(int);

void	mp_cfg_pci_intr(const struct mpbios_int *, u_int32_t *);
void	mp_print_pci_intr(int);

void	mp_cfg_eisa_intr(const struct mpbios_int *, u_int32_t *);
void	mp_print_eisa_intr(int);

void	mp_cfg_isa_intr(const struct mpbios_int *, u_int32_t *);
void	mp_print_isa_intr(int);

void	mpbios_cpu(const u_int8_t *, struct device *);
void	mpbios_bus(const u_int8_t *, struct device *);
void	mpbios_ioapic(const u_int8_t *, struct device *);
int	mpbios_int(const u_int8_t *, struct mp_intr_map *);

const void *mpbios_map(paddr_t, int, struct mp_map *);
void	mpbios_unmap(struct mp_map *);

/*
 * globals to help us bounce our way through parsing the config table.
 */

static struct mp_map mp_cfg_table_map;
static struct mp_map mp_fp_map;
const struct mpbios_cth	*mp_cth;
const struct mpbios_fps	*mp_fps;

#ifdef MPVERBOSE
int mp_verbose = 1;
#else
int mp_verbose = 0;
#endif

int	mpbios_match(struct device *, void *, void *);
void	mpbios_attach(struct device *, struct device *, void *);

struct cfattach mpbios_ca = {
	sizeof(struct device), mpbios_match, mpbios_attach
};

struct cfdriver mpbios_cd = {
	NULL, "mpbios", DV_DULL
};

int
mpbios_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct bios_attach_args *bia = aux;

	if (strcmp(bia->ba_name, cf->cf_driver->cd_name) == 0)
		return (1);
	return (0);
}

void
mpbios_attach(struct device *parent, struct device *self, void *aux)
{
	mpbios_scan(self);
}

int
mp_print(void *aux, const char *pnp)
{
	struct cpu_attach_args *caa = aux;

	if (pnp)
		printf("%s at %s:", caa->caa_name, pnp);
	return (UNCONF);
}

int
mp_match(struct device *parent, void *cfv, void *aux)
{
	struct cfdata *cf = cfv;
	struct cpu_attach_args *caa = aux;

	if (strcmp(caa->caa_name, cf->cf_driver->cd_name))
		return 0;

	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
}

/*
 * Map a chunk of memory read-only and return an appropriately
 * const'ed pointer.
 */
const void *
mpbios_map(paddr_t pa, int len, struct mp_map *handle)
{
	paddr_t pgpa = trunc_page(pa);
	paddr_t endpa = round_page(pa + len);
	vaddr_t va = uvm_km_valloc(kernel_map, endpa - pgpa);
	vaddr_t retva = va + (pa & PGOFSET);

	handle->pa = pa;
	handle->pg = pgpa;
	handle->psize = len;
	handle->baseva = va;
	handle->vsize = endpa - pgpa;

	do {
		pmap_kenter_pa(va, pgpa, PROT_READ);
		va += PAGE_SIZE;
		pgpa += PAGE_SIZE;
	} while (pgpa < endpa);

	return ((const void *)retva);
}

void
mpbios_unmap(struct mp_map *handle)
{
	pmap_kremove(handle->baseva, handle->vsize);
	uvm_km_free(kernel_map, handle->baseva, handle->vsize);
}

/*
 * Look for an Intel MP spec table, indicating SMP capable hardware.
 */
int
mpbios_probe(struct device *self)
{
	paddr_t  	ebda, memtop;

	paddr_t		cthpa;
	int		cthlen;
	const u_int8_t	*mpbios_page;
	int 		scan_loc;

	struct		mp_map t;

#if NAPM > 0 && NACPI > 0
	/*
	 * If we have acpi but chose to use apm, then we really should
	 * not go use mpbios.  Systems with usable acpi typically have
	 * unuseable mpbios
	 */
	if (haveacpibutusingapm)
		return (0);
#endif

	/*
	 * Skip probe if someone else (e.g. acpi) already provided the
	 * necessary details.
	 */
	if (mp_busses)
		return (0);

	/* see if EBDA exists */

	mpbios_page = mpbios_map(0, PAGE_SIZE, &t);

	/* XXX Ugly magic constants below. */
	ebda = *(const u_int16_t *)(&mpbios_page[0x40e]);
	ebda <<= 4;

	memtop = *(const u_int16_t *)(&mpbios_page[0x413]);
	memtop <<= 10;

	mpbios_page = NULL;
	mpbios_unmap(&t);

	scan_loc = 0;

	if (ebda && ebda < IOM_BEGIN ) {
		mp_fps = mpbios_search(self, ebda, 1024, &mp_fp_map);
		if (mp_fps != NULL)
			goto found;
	}

	scan_loc = 1;

	if (memtop && memtop <= IOM_BEGIN ) {
		mp_fps = mpbios_search(self, memtop - 1024, 1024, &mp_fp_map);
		if (mp_fps != NULL)
			goto found;
	}

	scan_loc = 2;

	mp_fps = mpbios_search(self, BIOS_BASE, BIOS_COUNT, &mp_fp_map);
	if (mp_fps != NULL)
		goto found;

	/* nothing found */
	return (0);

 found:
	if (mp_verbose)
		printf("%s: MP floating pointer found in %s at 0x%lx\n",
		    self->dv_xname, loc_where[scan_loc], mp_fp_map.pa);

	if (mp_fps->pap == 0) {
		if (mp_fps->mpfb1 == 0) {
			printf("%s: MP fps invalid: "
			    "no default config and no configuration table\n",
			    self->dv_xname);

			goto err;
		}
		printf("%s: MP default configuration %d\n",
		    self->dv_xname, mp_fps->mpfb1);
		return (10);
	}

	cthpa = mp_fps->pap;

	mp_cth = mpbios_map(cthpa, sizeof (*mp_cth), &mp_cfg_table_map);
	cthlen = mp_cth->base_len;
	mpbios_unmap(&mp_cfg_table_map);

	mp_cth = mpbios_map(cthpa, cthlen, &mp_cfg_table_map);

	if (mp_verbose)
		printf("%s: MP config table at 0x%lx, %d bytes long\n",
		    self->dv_xname, cthpa, cthlen);

	if (mp_cth->signature != MP_CT_SIG) {
		printf("%s: MP signature mismatch (%x vs %x)\n",
		    self->dv_xname,
		    MP_CT_SIG, mp_cth->signature);
		goto err;
	}

	if (mpbios_cksum(mp_cth, cthlen)) {
		printf ("%s: MP Configuration Table checksum mismatch\n",
		    self->dv_xname);
		goto err;
	}
	return (10);

 err:
	if (mp_fps) {
		mp_fps = NULL;
		mpbios_unmap(&mp_fp_map);
	}
	if (mp_cth) {
		mp_cth = NULL;
		mpbios_unmap(&mp_cfg_table_map);
	}
	return (0);
}

/*
 * Simple byte checksum used on config tables.
 */

static __inline int
mpbios_cksum(const void *start, int len)
{
	unsigned char res=0;
	const char *p = start;
	const char *end = p + len;

	while (p < end)
		res += *p++;

	return res;
}

/*
 * Look for the MP floating pointer signature in the given physical
 * address range.
 *
 * We map the memory, scan through it, and unmap it.
 * If we find it, remap the floating pointer structure and return it.
 */

const void *
mpbios_search(struct device *self, paddr_t start, int count, struct mp_map *map)
{
	struct mp_map t;

	int i, len;
	const struct mpbios_fps *m;
	int end = count - sizeof(*m);
	const u_int8_t *base = mpbios_map(start, count, &t);

	if (mp_verbose)
		printf("%s: scanning 0x%lx to 0x%lx for MP signature\n",
		    self->dv_xname, start, start + count - sizeof(*m));

	for (i = 0; i <= end; i += 4) {
		m = (struct mpbios_fps *)&base[i];

		if ((m->signature == MP_FP_SIG) &&
		    ((len = m->length << 4) != 0) &&
		    mpbios_cksum(m, (m->length << 4)) == 0) {
			mpbios_unmap(&t);

			return (mpbios_map(start + i, len, map));
		}
	}
	mpbios_unmap(&t);

	return (0);
}

/*
 * MP configuration table parsing.
 */

static struct mpbios_baseentry mp_conf[] =
{
	{0, 20, 0, "cpu"},
	{1, 8, 0, "bus"},
	{2, 8, 0, "ioapic"},
	{3, 8, 0, "ioint"},
	{4, 8, 0, "lint"},
};

struct mp_bus *mp_busses;
int mp_nbusses;
struct mp_intr_map *mp_intrs;
int mp_nintrs;

struct mp_bus *mp_isa_bus;
struct mp_bus *mp_eisa_bus;

static struct mp_bus extint_bus = {
	"ExtINT",
	-1,
	mp_print_special_intr,
	mp_cfg_special_intr,
	0
};
static struct mp_bus smi_bus = {
	"SMI",
	-1,
	mp_print_special_intr,
	mp_cfg_special_intr,
	0
};
static struct mp_bus nmi_bus = {
	"NMI",
	-1,
	mp_print_special_intr,
	mp_cfg_special_intr,
	0
};

/*
 * 1st pass on BIOS's Intel MP specification table.
 *
 * initializes:
 *	mp_ncpus = 1
 *
 * determines:
 *	cpu_apic_address (common to all CPUs)
 *	ioapic_address[N]
 *	mp_naps
 *	mp_nbusses
 *	mp_napics
 *	nintrs
 */
void
mpbios_scan(struct device *self)
{
	const u_int8_t 	*position, *end;
	int		count;
	int		type;
	int		intr_cnt;
	paddr_t		lapic_base;

	printf(": Intel MP Specification 1.%d\n", mp_fps->spec_rev);

	/*
	 * looks like we've got a MP system.  start setting up
	 * infrastructure..
	 * XXX is this the right place??
	 */

	lapic_base = LAPIC_BASE;
	if (mp_cth != NULL)
		lapic_base = (paddr_t)mp_cth->apic_address;

	lapic_boot_init(lapic_base);

	/* check for use of 'default' configuration */
	if (mp_fps->mpfb1 != 0) {
		struct mpbios_proc pe;

		printf("%s: MP default configuration %d\n",
		    self->dv_xname, mp_fps->mpfb1);

		/* use default addresses */
		pe.apic_id = cpu_number();
		pe.cpu_flags = PROCENTRY_FLAG_EN|PROCENTRY_FLAG_BP;
		pe.cpu_signature = cpu_info_primary.ci_signature;
		pe.feature_flags = cpu_info_primary.ci_feature_flags;

		mpbios_cpu((u_int8_t *)&pe, self);

		pe.apic_id = 1 - cpu_number();
		pe.cpu_flags = PROCENTRY_FLAG_EN;

		mpbios_cpu((u_int8_t *)&pe, self);

		mpbios_ioapic((u_int8_t *)&default_ioapic, self);

		/* XXX */
		printf("%s: WARNING: interrupts not configured\n",
		    self->dv_xname);
		panic("lazy bum");
		return;
	} else {
		/*
		 * should not happen; mp_probe returns 0 in this case,
		 * but..
		 */
		if (mp_cth == NULL)
			panic("mpbios_scan: no config (can't happen?)");

		/*
		 * Walk the table once, counting items
		 */
		for (count = mp_cth->entry_count,
		    position = (const u_int8_t *)mp_cth + sizeof(*mp_cth),
		    end = position + mp_cth->base_len;
		    count-- && position < end;
		    position += mp_conf[type].length) {

			type = *position;
			if (type >= MPS_MCT_NTYPES) {
				printf("%s: unknown entry type %x"
				    " in MP config table\n",
				    self->dv_xname, type);
				end = position;
				break;
			}
			mp_conf[type].count++;
		}

		/*
		 * Walk the table twice, counting int and bus entries
		 */
		for (count = mp_cth->entry_count,
		    intr_cnt = 15,	/* presume all isa irqs missing */
		    position = (const u_int8_t *)mp_cth + sizeof(*mp_cth);
		    count-- && position < end;
		    position += mp_conf[type].length) {
			type = *position;
			if (type == MPS_MCT_BUS) {
				const struct mpbios_bus *bp =
				    (const struct mpbios_bus *)position;
				if (bp->bus_id >= mp_nbusses)
					mp_nbusses = bp->bus_id + 1;
			}

			/*
			 * Count actual interrupt instances.
			 * dst_apic_id of MPS_ALL_APICS means "wired to all
			 * apics of this type".
			 */
			if ((type == MPS_MCT_IOINT) ||
			    (type == MPS_MCT_LINT)) {
				const struct mpbios_int *ie =
				    (const struct mpbios_int *)position;
				if (ie->dst_apic_id != MPS_ALL_APICS)
					intr_cnt++;
				else if (type == MPS_MCT_IOINT)
					intr_cnt +=
					    mp_conf[MPS_MCT_IOAPIC].count;
				else
					intr_cnt += mp_conf[MPS_MCT_CPU].count;
			}
		}

		mp_busses = mallocarray(mp_nbusses, sizeof(struct mp_bus),
		    M_DEVBUF, M_NOWAIT|M_ZERO);
		mp_intrs = mallocarray(intr_cnt, sizeof(struct mp_intr_map),
		    M_DEVBUF, M_NOWAIT);

		/* re-walk the table, recording info of interest */
		position = (const u_int8_t *)mp_cth + sizeof(*mp_cth);
		count = mp_cth->entry_count;
		mp_nintrs = 0;

		while ((count--) && (position < end)) {
			switch (type = *(u_char *)position) {
			case MPS_MCT_CPU:
				mpbios_cpu(position, self);
				break;
			case MPS_MCT_BUS:
				mpbios_bus(position, self);
				break;
			case MPS_MCT_IOAPIC:
				mpbios_ioapic(position, self);
				break;
			case MPS_MCT_IOINT:
			case MPS_MCT_LINT:
				if (mpbios_int(position,
				    &mp_intrs[mp_nintrs]) == 0)
					mp_nintrs++;
				break;
			default:
				printf("%s: unknown entry type %x "
				    "in MP config table\n",
				    self->dv_xname, type);
				/* NOTREACHED */
				return;
			}

			position += mp_conf[type].length;
		}
		if (mp_verbose && mp_cth->ext_len)
			printf("%s: MP WARNING: %d "
			    "bytes of extended entries not examined\n",
			    self->dv_xname, mp_cth->ext_len);
	}

	/* Clean up. */
	mp_fps = NULL;
	mpbios_unmap(&mp_fp_map);
	if (mp_cth != NULL) {
		mp_cth = NULL;
		mpbios_unmap(&mp_cfg_table_map);
	}

#if NPCI > 0
	if (pci_mode_detect() != 0)
		mpbios_intr_fixup();
#endif
}

int
mpbios_invent(int irq, int type, int bus)
{
	struct mp_intr_map *mip;
	struct mpbios_int e;

	e.type = MPS_MCT_IOINT;
	e.int_type = MPS_INTTYPE_INT;
	switch (type) {
	case IST_EDGE:
		e.int_flags = MPS_INT(MPS_INTPO_ACTHI, MPS_INTTR_EDGE);
		break;

	case IST_LEVEL:
		e.int_flags = MPS_INT(MPS_INTPO_ACTLO, MPS_INTTR_LEVEL);
		break;

	case IST_NONE:
	case IST_PULSE:
		e.int_flags = MPS_INT(MPS_INTPO_DEF, MPS_INTTR_DEF);
		break;
	}
	e.src_bus_id = bus;
	e.src_bus_irq = irq;
	e.dst_apic_id = mp_busses[bus].mb_intrs->ioapic->sc_apicid;
	e.dst_apic_int = irq;

	if (mpbios_int((const u_int8_t *)&e, &mp_intrs[mp_nintrs]) == 0) {
		mip = &mp_intrs[mp_nintrs++];
		return (mip->ioapic_ih | irq);
	}

	return irq;
}

void
mpbios_cpu(const u_int8_t *ent, struct device *self)
{
	const struct mpbios_proc *entry = (const struct mpbios_proc *)ent;
	struct device *mainbus = self->dv_parent->dv_parent;
	struct cpu_attach_args caa;

	/* XXX move this into the CPU attachment goo. */
	/* check for usability */
	if (!(entry->cpu_flags & PROCENTRY_FLAG_EN))
		return;

	/* check for BSP flag */
	if (entry->cpu_flags & PROCENTRY_FLAG_BP)
		caa.cpu_role = CPU_ROLE_BP;
	else {
		caa.cpu_role = CPU_ROLE_AP;
		ncpusfound++;
	}

	caa.caa_name = "cpu";
	caa.cpu_number = entry->apic_id;
#ifdef MULTIPROCESSOR
	caa.cpu_func = &mp_cpu_funcs;
#endif
#if 1 /* XXX Will be removed when the real stuff is probed */
	caa.cpu_signature = entry->cpu_signature;

	/*
	 * XXX this is truncated to just contain the low-order 16 bits
	 * of the flags on at least some MP bioses
	 */
	caa.feature_flags = entry->feature_flags;

	/*
	 * XXX some MP bioses don't specify a valid CPU signature; use
	 * the result of the 'cpuid' instruction for the processor
	 * we're running on
	 */
	if ((caa.cpu_signature & 0x00000fff) == 0) {
		caa.cpu_signature = cpu_id;
		caa.feature_flags = cpu_feature;
	}
#endif

	config_found_sm(mainbus, &caa, mp_print, mp_match);
}

/*
 * The following functions conspire to compute base ioapic redirection
 * table entry for a given interrupt line.
 *
 * Fill in: trigger mode, polarity, and possibly delivery mode.
 */
void
mp_cfg_special_intr(const struct mpbios_int *entry, u_int32_t *redir)
{

	/*
	 * All of these require edge triggered, zero vector,
	 * appropriate delivery mode.
	 * see page 13 of the 82093AA datasheet.
	 */
	*redir &= ~IOAPIC_REDLO_DEL_MASK;
	*redir &= ~IOAPIC_REDLO_VECTOR_MASK;
	*redir &= ~IOAPIC_REDLO_LEVEL;

	switch (entry->int_type) {
	case MPS_INTTYPE_NMI:
		*redir |= (IOAPIC_REDLO_DEL_NMI<<IOAPIC_REDLO_DEL_SHIFT);
		break;

	case MPS_INTTYPE_SMI:
		*redir |= (IOAPIC_REDLO_DEL_SMI<<IOAPIC_REDLO_DEL_SHIFT);
		break;
	case MPS_INTTYPE_ExtINT:
		/*
		 * We are using the ioapic in "native" mode.
		 * This indicates where the 8259 is wired to the ioapic
		 * and/or local apic..
		 */
		*redir |= (IOAPIC_REDLO_DEL_EXTINT<<IOAPIC_REDLO_DEL_SHIFT);
		*redir |= (IOAPIC_REDLO_MASK);
		break;
	default:
		panic("unknown MPS interrupt type %d", entry->int_type);
	}
}

/* XXX too much duplicated code here. */

void
mp_cfg_pci_intr(const struct mpbios_int *entry, u_int32_t *redir)
{
	int mpspo = (entry->int_flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
	int mpstrig = (entry->int_flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;

	*redir &= ~IOAPIC_REDLO_DEL_MASK;
	switch (mpspo) {
	case MPS_INTPO_ACTHI:
		*redir &= ~IOAPIC_REDLO_ACTLO;
		break;
	case MPS_INTPO_DEF:
	case MPS_INTPO_ACTLO:
		*redir |= IOAPIC_REDLO_ACTLO;
		break;
	default:
		panic("unknown MPS interrupt polarity %d", mpspo);
	}

	if (entry->int_type != MPS_INTTYPE_INT) {
		mp_cfg_special_intr(entry, redir);
		return;
	}
	*redir |= (IOAPIC_REDLO_DEL_LOPRI<<IOAPIC_REDLO_DEL_SHIFT);

	switch (mpstrig) {
	case MPS_INTTR_DEF:
	case MPS_INTTR_LEVEL:
		*redir |= IOAPIC_REDLO_LEVEL;
		break;
	case MPS_INTTR_EDGE:
		*redir &= ~IOAPIC_REDLO_LEVEL;
		break;
	default:
		panic("unknown MPS interrupt trigger %d", mpstrig);
	}
}

void
mp_cfg_eisa_intr(const struct mpbios_int *entry, u_int32_t *redir)
{
	int mpspo = (entry->int_flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
	int mpstrig = (entry->int_flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;

	*redir &= ~IOAPIC_REDLO_DEL_MASK;
	switch (mpspo) {
	case MPS_INTPO_DEF:
	case MPS_INTPO_ACTHI:
		*redir &= ~IOAPIC_REDLO_ACTLO;
		break;
	case MPS_INTPO_ACTLO:
		*redir |= IOAPIC_REDLO_ACTLO;
		break;
	default:
		panic("unknown MPS interrupt polarity %d", mpspo);
	}

	if (entry->int_type != MPS_INTTYPE_INT) {
		mp_cfg_special_intr(entry, redir);
		return;
	}
	*redir |= (IOAPIC_REDLO_DEL_LOPRI<<IOAPIC_REDLO_DEL_SHIFT);

	switch (mpstrig) {
	case MPS_INTTR_LEVEL:
		*redir |= IOAPIC_REDLO_LEVEL;
		break;
	case MPS_INTTR_EDGE:
		*redir &= ~IOAPIC_REDLO_LEVEL;
		break;
	case MPS_INTTR_DEF:
		/*
		 * Set "default" setting based on ELCR value snagged
		 * earlier.
		 */
		if (mp_busses[entry->src_bus_id].mb_data &
		    (1<<entry->src_bus_irq)) {
			*redir |= IOAPIC_REDLO_LEVEL;
		} else {
			*redir &= ~IOAPIC_REDLO_LEVEL;
		}
		break;
	default:
		panic("unknown MPS interrupt trigger %d", mpstrig);
	}
}

void
mp_cfg_isa_intr(const struct mpbios_int *entry, u_int32_t *redir)
{
	int mpspo = (entry->int_flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
	int mpstrig = (entry->int_flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;

	*redir &= ~IOAPIC_REDLO_DEL_MASK;
	switch (mpspo) {
	case MPS_INTPO_DEF:
	case MPS_INTPO_ACTHI:
		*redir &= ~IOAPIC_REDLO_ACTLO;
		break;
	case MPS_INTPO_ACTLO:
		*redir |= IOAPIC_REDLO_ACTLO;
		break;
	default:
		panic("unknown MPS interrupt polarity %d", mpspo);
	}

	if (entry->int_type != MPS_INTTYPE_INT) {
		mp_cfg_special_intr(entry, redir);
		return;
	}
	*redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);

	switch (mpstrig) {
	case MPS_INTTR_LEVEL:
		*redir |= IOAPIC_REDLO_LEVEL;
		break;
	case MPS_INTTR_DEF:
	case MPS_INTTR_EDGE:
		*redir &= ~IOAPIC_REDLO_LEVEL;
		break;
	default:
		panic("unknown MPS interrupt trigger %d", mpstrig);
	}
}

void
mp_print_special_intr(int intr)
{
}

void
mp_print_pci_intr(int intr)
{
	printf(" device %d INT_%c", (intr >> 2) & 0x1f, 'A' + (intr & 0x3));
}

void
mp_print_isa_intr(int intr)
{
	printf(" irq %d", intr);
}

void
mp_print_eisa_intr(int intr)
{
	printf(" EISA irq %d", intr);
}

#define TAB_UNIT	4
#define TAB_ROUND(a)	_TAB_ROUND(a, TAB_UNIT)

#define _TAB_ROUND(a,u)	(((a) + (u - 1)) & ~(u - 1))
#define EXTEND_TAB(a,u)	(!(_TAB_ROUND(a, u) == _TAB_ROUND((a + 1), u)))

void
mpbios_bus(const u_int8_t *ent, struct device *self)
{
	const struct mpbios_bus *entry = (const struct mpbios_bus *)ent;
	int bus_id = entry->bus_id;

	printf("%s: bus %d is type %6.6s\n", self->dv_xname,
	    bus_id, entry->bus_type);

#ifdef DIAGNOSTIC
	/*
	 * This "should not happen" unless the table changes out
	 * from underneath us
	 */
	if (bus_id >= mp_nbusses) {
		panic("%s: bus number %d out of range?? (type %6.6s)",
		    self->dv_xname, bus_id, entry->bus_type);
	}
#endif

	mp_busses[bus_id].mb_intrs = NULL;

	if (memcmp(entry->bus_type, "PCI   ", 6) == 0) {
		mp_busses[bus_id].mb_name = "pci";
		mp_busses[bus_id].mb_idx = bus_id;
		mp_busses[bus_id].mb_intr_print = mp_print_pci_intr;
		mp_busses[bus_id].mb_intr_cfg = mp_cfg_pci_intr;
	} else if (memcmp(entry->bus_type, "EISA  ", 6) == 0) {
		mp_busses[bus_id].mb_name = "eisa";
		mp_busses[bus_id].mb_idx = bus_id;
		mp_busses[bus_id].mb_intr_print = mp_print_eisa_intr;
		mp_busses[bus_id].mb_intr_cfg = mp_cfg_eisa_intr;

		mp_busses[bus_id].mb_data = inb(ELCR0) | (inb(ELCR1) << 8);

		if (mp_eisa_bus)
			printf("%s: multiple eisa busses?\n",
			    self->dv_xname);
		else
			mp_eisa_bus = &mp_busses[bus_id];
	} else if (memcmp(entry->bus_type, "ISA   ", 6) == 0) {
		mp_busses[bus_id].mb_name = "isa";
		mp_busses[bus_id].mb_idx = bus_id;
		mp_busses[bus_id].mb_intr_print = mp_print_isa_intr;
		mp_busses[bus_id].mb_intr_cfg = mp_cfg_isa_intr;
		if (mp_isa_bus)
			printf("%s: multiple isa busses?\n",
			    self->dv_xname);
		else
			mp_isa_bus = &mp_busses[bus_id];
	} else {
		printf("%s: unsupported bus type %6.6s\n", self->dv_xname,
		    entry->bus_type);
	}
}

void
mpbios_ioapic(const u_int8_t *ent, struct device *self)
{
	const struct mpbios_ioapic *entry = (const struct mpbios_ioapic *)ent;
	struct device *mainbus = self->dv_parent->dv_parent;
	struct apic_attach_args aaa;

	/* XXX let flags checking happen in ioapic driver.. */
	if (!(entry->apic_flags & IOAPICENTRY_FLAG_EN))
		return;

	aaa.aaa_name = "ioapic";
	aaa.apic_id = entry->apic_id;
	aaa.apic_version = entry->apic_version;
	aaa.apic_address = (u_int32_t)entry->apic_address;
	aaa.apic_vecbase = -1;
	aaa.flags = (mp_fps->mpfb2 & 0x80) ? IOAPIC_PICMODE : IOAPIC_VWIRE;

	config_found_sm(mainbus, &aaa, mp_print, mp_match);
}

int
mpbios_int(const u_int8_t *ent, struct mp_intr_map *mpi)
{
	const struct mpbios_int *entry = (const struct mpbios_int *)ent;
	struct mpbios_int rw_entry = *entry;
	struct ioapic_softc *sc = NULL, *sc2;

	struct mp_intr_map *altmpi;
	struct mp_bus *mpb;

	u_int32_t id = entry->dst_apic_id;
	u_int32_t pin = entry->dst_apic_int;
	u_int32_t bus = entry->src_bus_id;
	u_int32_t dev = entry->src_bus_irq;
	u_int32_t type = entry->int_type;
	u_int32_t flags = entry->int_flags;

	rw_entry.dst_apic_id = id;

	switch (type) {
	case MPS_INTTYPE_INT:
		mpb = &(mp_busses[bus]);
		break;
	case MPS_INTTYPE_ExtINT:
		mpb = &extint_bus;
		break;
	case MPS_INTTYPE_SMI:
		mpb = &smi_bus;
		break;
	case MPS_INTTYPE_NMI:
		mpb = &nmi_bus;
		break;
	default:
		panic("unknown MPS interrupt type %d", entry->int_type);
	}
	mpi->bus = mpb;
	mpi->bus_pin = dev;

	mpi->ioapic_ih = APIC_INT_VIA_APIC |
	    ((id << APIC_INT_APIC_SHIFT) | ((pin << APIC_INT_PIN_SHIFT)));

	mpi->type = type;
	mpi->flags = flags;
	mpi->redir = 0;
	if (mpb->mb_intr_cfg == NULL) {
		printf("mpbios: can't find bus %d for apic %d pin %d\n",
		    bus, id, pin);
		return (1);
	}

	(*mpb->mb_intr_cfg)(&rw_entry, &mpi->redir);

	if (entry->type == MPS_MCT_IOINT) {
		sc = ioapic_find(id);
		if (sc == NULL) {
			printf("mpbios: can't find ioapic %d\n", id);
			return (1);
		}

		/*
		 * XXX workaround for broken BIOSs that put the ACPI
		 * global interrupt number in the entry, not the pin
		 * number.
		 */
		if (pin >= sc->sc_apic_sz) {
			sc2 = ioapic_find_bybase(pin);
			if (sc2 != sc) {
				printf("mpbios: bad pin %d for apic %d\n",
				    pin, id);
				return (1);
			}
			printf("mpbios: WARNING: pin %d for apic %d too high; "
			       "assuming ACPI global int value\n", pin, id);
			pin -= sc->sc_apic_vecbase;
		}

		mpi->ioapic = sc;
		mpi->ioapic_pin = pin;

		altmpi = sc->sc_pins[pin].ip_map;

		if (altmpi != NULL) {
			if ((altmpi->type != type) ||
			    (altmpi->flags != flags)) {
				printf(
				    "%s: conflicting map entries for pin %d\n",
				    sc->sc_pic.pic_dev.dv_xname, pin);
			}
		} else {
			sc->sc_pins[pin].ip_map = mpi;
		}
	} else {
		if (pin >= 2)
			printf("pin %d of local apic doesn't exist!\n", pin);
		else {
			mpi->ioapic = NULL;
			mpi->ioapic_pin = pin;
			mpi->cpu_id = id;
		}
	}

	if (mp_verbose) {
		printf("%s: int%d attached to %s",
		    sc ? sc->sc_pic.pic_dev.dv_xname : "local apic", pin,
		    mpb->mb_name);
		if (mpb->mb_idx != -1)
			printf("%d", mpb->mb_idx);

		(*(mpb->mb_intr_print))(dev);

		printf(" (type 0x%x flags 0x%x)\n", type, flags);
	}

	mpi->next = mpb->mb_intrs;
	mpb->mb_intrs = mpi;

	return (0);
}


code:
/*	$OpenBSD: mpbios_intr_fixup.c,v 1.6 2015/08/10 12:12:42 deraadt Exp $	*/

/*
 * Copyright (c) 2006 Mark Kettenis
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

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

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

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

void mpbios_pin_fixup(int, int, int, int);
const struct mpbios_icu_table *mpbios_icu_lookup(pcireg_t);

void via8237_mpbios_fixup(pci_chipset_tag_t, pcitag_t);
void nforce4_mpbios_fixup(pci_chipset_tag_t, pcitag_t);
void mcp04_mpbios_fixup(pci_chipset_tag_t, pcitag_t);

const struct mpbios_icu_table {
	pci_vendor_id_t mpit_vendor;
	pci_product_id_t mpit_product;
	void (*mpit_mpbios_fixup)(pci_chipset_tag_t, pcitag_t);
} mpbios_icu_table[] = {
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8237_ISA,
	  via8237_mpbios_fixup },
	{ PCI_VENDOR_NVIDIA,	PCI_PRODUCT_NVIDIA_NFORCE4_ISA1,
	  nforce4_mpbios_fixup },
	{ PCI_VENDOR_NVIDIA,	PCI_PRODUCT_NVIDIA_NFORCE4_ISA2,
	  nforce4_mpbios_fixup },
	{ PCI_VENDOR_NVIDIA,	PCI_PRODUCT_NVIDIA_MCP04_ISA,
	  mcp04_mpbios_fixup },
	{ 0,			0,
	  NULL},
};

const struct mpbios_icu_table *
mpbios_icu_lookup(pcireg_t id)
{
	const struct mpbios_icu_table *mpit;

	for (mpit = mpbios_icu_table; mpit->mpit_mpbios_fixup != NULL; mpit++)
		if (PCI_VENDOR(id) == mpit->mpit_vendor &&
		    PCI_PRODUCT(id) == mpit->mpit_product)
			return (mpit);

	return (NULL);
}

/*
 * NVIDIA nForce4 PCI-ISA bridge.
 */

#define NFORCE4_PNPIRQ1	0x7c
#define NFORCE4_PNPIRQ2	0x80
#define  NFORCE4_USB2_SHIFT	12
#define  NFORCE4_USB2_MASK	(0xf << NFORCE4_USB2_SHIFT)
#define  NFORCE4_SATA1_SHIFT	28
#define  NFORCE4_SATA1_MASK	(0xf << NFORCE4_SATA1_SHIFT)
#define  NFORCE4_SATA2_SHIFT	24
#define  NFORCE4_SATA2_MASK	(0xf << NFORCE4_SATA2_SHIFT)
#define NFORCE4_PNPIRQ3	0x84
#define  NFORCE4_USB1_SHIFT	0
#define  NFORCE4_USB1_MASK	(0xf << NFORCE4_USB1_SHIFT)
#define  NFORCE4_LAN_SHIFT	8
#define  NFORCE4_LAN_MASK	(0xf << NFORCE4_LAN_SHIFT)

void
nforce4_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
{
	pcireg_t reg;
	int bus, pin;

	pci_decompose_tag (pc, tag, &bus, NULL, NULL);

	reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ2);
	pin = (reg & NFORCE4_USB2_MASK) >> NFORCE4_USB2_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 2, PCI_INTERRUPT_PIN_B, pin);
	pin = (reg & NFORCE4_SATA1_MASK) >> NFORCE4_SATA1_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 7, PCI_INTERRUPT_PIN_A, pin);
	pin = (reg & NFORCE4_SATA2_MASK) >> NFORCE4_SATA2_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 8, PCI_INTERRUPT_PIN_A, pin);

	reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ3);
	pin = (reg & NFORCE4_USB1_MASK) >> NFORCE4_USB1_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 2, PCI_INTERRUPT_PIN_A, pin);
	pin = (reg & NFORCE4_LAN_MASK) >> NFORCE4_LAN_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 10, PCI_INTERRUPT_PIN_A, pin);
}

/*
 * NVIDIA MCP04 PCI-ISA bridge.
 */

void
mcp04_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
{
	pcireg_t reg;
	int bus, pin;

	pci_decompose_tag (pc, tag, &bus, NULL, NULL);

	reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ2);
	pin = (reg & NFORCE4_SATA1_MASK) >> NFORCE4_SATA1_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 16, PCI_INTERRUPT_PIN_A, pin);
	pin = (reg & NFORCE4_SATA2_MASK) >> NFORCE4_SATA2_SHIFT;
	if (pin != 0)
		mpbios_pin_fixup(bus, 17, PCI_INTERRUPT_PIN_A, pin);
}

/*
 * VIA VT8237 PCI-ISA bridge.
 */

void
via8237_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
{
	int bus;

	pci_decompose_tag (pc, tag, &bus, NULL, NULL);

	/* SATA is hardwired to APIC pin 20. */
	mpbios_pin_fixup(bus, 15, 2, 20);
}

void
mpbios_pin_fixup(int bus, int dev, int rawpin, int pin)
{
	struct mp_bus *mpb = &mp_busses[bus];
	struct mp_intr_map *mip;

	for (mip = mpb->mb_intrs; mip != NULL; mip = mip->next) {
		if (mip->bus_pin == ((dev << 2) | (rawpin - 1)) &&
		    mip->ioapic_pin != pin) {

			if (mp_verbose) {

				printf("%s: int%d attached to %s",
				    mip->ioapic->sc_pic.pic_name,
				    pin, mpb->mb_name);

				if (mpb->mb_idx != -1)
					printf("%d", mpb->mb_idx);

				(*(mpb->mb_intr_print))(mip->bus_pin);

				printf(" (fixup)\n");
			}

			mip->ioapic_pin = pin;
			mip->ioapic_ih &= ~APIC_INT_PIN_MASK;
			mip->ioapic_ih |= (pin << APIC_INT_PIN_SHIFT);
			if (mip->ioapic->sc_pins[pin].ip_map == NULL)
				mip->ioapic->sc_pins[pin].ip_map = mip;
		}
	}
}

void
mpbios_intr_fixup(void)
{
	const struct mpbios_icu_table *mpit = NULL;
	pci_chipset_tag_t pc = NULL;
	pcitag_t icutag;
	int device, maxdevs = pci_bus_maxdevs(pc, 0);

	/* Search configuration space for a known interrupt router. */
	for (device = 0; device < maxdevs; device++) {
		const struct pci_quirkdata *qd;
		int function, nfuncs;
		pcireg_t icuid;
		pcireg_t bhlcr;

		icutag = pci_make_tag(pc, 0, device, 0);
		icuid = pci_conf_read(pc, icutag, PCI_ID_REG);

		/* Invalid vendor ID value? */
		if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
			continue;

		qd = pci_lookup_quirkdata(PCI_VENDOR(icuid),
	       	    PCI_PRODUCT(icuid));

		bhlcr = pci_conf_read(pc, icutag, PCI_BHLC_REG);
		if (PCI_HDRTYPE_MULTIFN(bhlcr) || (qd != NULL &&
		    (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
			nfuncs = 8;
		else
			nfuncs = 1;

		for (function = 0; function < nfuncs; function++) {
			icutag = pci_make_tag(pc, 0, device, function);
			icuid = pci_conf_read(pc, icutag, PCI_ID_REG);

			/* Invalid vendor ID value? */
			if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
				continue;

			if ((mpit = mpbios_icu_lookup(icuid)))
				break;
		}

		if (mpit != NULL)
			break;
	}

	if (mpit)
		mpit->mpit_mpbios_fixup(pc, icutag);
}


code:
/*	$OpenBSD: mptramp.s,v 1.17 2015/04/26 09:48:29 kettenis 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.
 */
/*
 * MP startup ...
 * the stuff from cpu_spinup_trampoline to mp_startup
 * is copied into the first 640 KB
 *
 * We startup the processors now when the kthreads become ready.
 * The steps are:
 *	1) Get the processors running kernel-code from a special
 *	   page-table and stack page, do chip identification.
 *	2) halt the processors waiting for them to be enabled
 *	   by a idle-thread
 */

#include "assym.h"
#include <machine/param.h>
#include <machine/asm.h>
#include <machine/specialreg.h>
#include <machine/segments.h>
#include <machine/gdt.h>
#include <machine/mpbiosvar.h>
#include <machine/i82489reg.h>

#define GDTE(a,b)	.byte	0xff,0xff,0x0,0x0,0x0,a,b,0x0
#define _RELOC(x)	((x) - KERNBASE)
#define RELOC(x)	_RELOC(_C_LABEL(x))

#define _TRMP_LABEL(a)  a = . - _C_LABEL(cpu_spinup_trampoline) + MP_TRAMPOLINE
#define _TRMP_OFFSET(a)  a = . - _C_LABEL(cpu_spinup_trampoline)

/*
 * Debug code to stop aux. processors in various stages based on the
 * value in cpu_trace.
 *
 * %edi points at cpu_trace;  cpu_trace[0] is the "hold point";
 * cpu_trace[1] is the point which the cpu has reached.
 * cpu_trace[2] is the last value stored by HALTT.
 */

#ifdef MPDEBUG
#define HALT(x)	1: movl (%edi),%ebx;cmpl $ x,%ebx ; jle 1b ; movl $x,4(%edi)
#define HALTT(x,y)	movl y,8(%edi); HALT(x)
#else
#define HALT(x)
#define HALTT(x,y)
#endif

	.globl	_C_LABEL(cpu),_C_LABEL(cpu_id),_C_LABEL(cpu_vendor)
	.globl	_C_LABEL(cpuid_level),_C_LABEL(cpu_feature)

	.global _C_LABEL(cpu_spinup_trampoline)
	.global _C_LABEL(cpu_spinup_trampoline_end)
	.global _C_LABEL(cpu_hatch)
	.global _C_LABEL(mp_pdirpa)
	.global _C_LABEL(gdt), _C_LABEL(local_apic)

	.text
	.align 4,0x0
	.code16
_C_LABEL(cpu_spinup_trampoline):
	cli
	movw	%cs, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss
	data32 addr32 lgdt	(gdt_desc)	# load flat descriptor table
	movl	%cr0, %eax	# get cr0
	orl	$0x1, %eax	# enable protected mode
	movl	%eax, %cr0	# doit
	ljmpl	$0x8, $mp_startup

_TRMP_LABEL(mp_startup)
	.code32

	movl	$0x10, %eax	# data segment
	movw	%ax, %ds
	movw	%ax, %ss
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movl	$(MP_TRAMPOLINE+NBPG-16),%esp	# bootstrap stack end,
						# with scratch space..

#ifdef MPDEBUG
	leal	RELOC(cpu_trace),%edi
#endif

	HALT(0x1)
	/* First, reset the PSL. */
	pushl	$PSL_MBO
	popfl

	movl	RELOC(mp_pdirpa),%ecx
	HALTT(0x5,%ecx)

	/* Load base of page directory and enable mapping. */
	movl	%ecx,%cr3		# load ptd addr into mmu
#ifndef SMALL_KERNEL
	movl	$_C_LABEL(pmap_pte_set_pae),%eax
	cmpl	RELOC(_C_LABEL(pmap_pte_set_p)),%eax
	jne	nopae

	movl	%cr4,%eax
	orl	$CR4_PAE,%eax
	movl	%eax, %cr4

	movl	$MSR_EFER,%ecx
	rdmsr
	orl	$EFER_NXE, %eax
	wrmsr

nopae:
#endif
	movl	%cr0,%eax		# get control word
					# enable paging & NPX emulation
	orl	$(CR0_PE|CR0_PG|CR0_NE|CR0_TS|CR0_EM|CR0_MP|CR0_WP),%eax
	movl	%eax,%cr0		# and let's page NOW!

#ifdef MPDEBUG
	leal	_C_LABEL(cpu_trace),%edi
#endif
	HALT(0x6)

# ok, we're now running with paging enabled and sharing page tables with cpu0.
# figure out which processor we really are, what stack we should be on, etc.

	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

	HALTT(0x7, %ecx)

# %ecx points at our cpu_info structure..

	movw	$(MAXGDTSIZ-1), 6(%esp)		# prepare segment descriptor
	movl	CPU_INFO_GDT(%ecx), %eax	# for real gdt
	movl	%eax, 8(%esp)
	HALTT(0x8, %eax)
	lgdt	6(%esp)
	HALT(0x9)
	jmp	1f
	nop
1:
	HALT(0xa)
	movl	$GSEL(GDATA_SEL, SEL_KPL),%eax	#switch to new segment
	HALTT(0x10, %eax)
	movw	%ax,%ds
	HALT(0x11)
	movw	%ax,%es
	HALT(0x12)
	movw	%ax,%ss
	HALT(0x13)
	pushl	$GSEL(GCODE_SEL, SEL_KPL)
	pushl	$mp_cont
	HALT(0x14)
	lret
	.align 4,0x0
_TRMP_LABEL(gdt_table)
	.word	0x0,0x0,0x0,0x0			# null GDTE
	 GDTE(0x9f,0xcf)			# Kernel text
	 GDTE(0x93,0xcf)			# Kernel data
_TRMP_OFFSET(gdt_desc)
	.word	0x17				# limit 3 entries
	.long	gdt_table			# where is gdt

_C_LABEL(cpu_spinup_trampoline_end):	#end of code copied to MP_TRAMPOLINE
mp_cont:

	movl	CPU_INFO_IDLE_PCB(%ecx),%esi

# %esi now points at our PCB.

	HALTT(0x19, %esi)

	movl	PCB_ESP(%esi),%esp
	movl	PCB_EBP(%esi),%ebp

	HALT(0x20)
	/* Switch address space. */
	movl	PCB_CR3(%esi),%eax
	HALTT(0x22, %eax)
	movl	%eax,%cr3
	HALT(0x25)
	/* Load segment registers. */
	movl	$GSEL(GCPU_SEL, SEL_KPL),%eax
	HALTT(0x26,%eax)
	movl	%eax,%fs
	xorl	%eax,%eax
	HALTT(0x27,%eax)
	movl	%eax,%gs
	movl	PCB_CR0(%esi),%eax
	HALTT(0x28,%eax)
	movl	%eax,%cr0
	HALTT(0x30,%ecx)
	pushl	%ecx
	call	_C_LABEL(cpu_hatch)
	/* NOTREACHED */

	.data
_C_LABEL(mp_pdirpa):
	.long	0
#ifdef MPDEBUG
	.global _C_LABEL(cpu_trace)
_C_LABEL(cpu_trace):
	.long	0x40
	.long	0xff
	.long	0xff
#endif


code:
/* $OpenBSD: mtrr.c,v 1.13 2013/12/19 21:30:02 deraadt Exp $ */
/*-
 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
 * 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.
 *
 */

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

#include <machine/specialreg.h>

extern struct mem_range_ops mrops;
extern struct mem_range_ops k6_mrops;

void
mem_range_attach(void)
{
	int family, model, step;

	family = (cpu_id >> 8) & 0xf;
	model  = (cpu_id >> 4) & 0xf;
	step   = (cpu_id >> 0) & 0xf;

	if (strcmp(cpu_vendor, "AuthenticAMD") == 0 &&
	    family == 0x5 &&
	    (model > 0x8 ||
	     (model == 0x8 &&
	      step > 0x7))) {
		mem_range_softc.mr_op = &k6_mrops;

		/* Try for i686 MTRRs */
	} else if (((strcmp(cpu_vendor, "GenuineIntel") == 0) ||
		    (strcmp(cpu_vendor, "CentaurHauls") == 0) ||
		    (strcmp(cpu_vendor, "AuthenticAMD") == 0)) &&
		    (family == 0x6 || family == 0xf) &&
		    (cpu_feature & CPUID_MTRR)) {
		mem_range_softc.mr_op = &mrops;

	}
	/* Initialise memory range handling */
	if (mem_range_softc.mr_op != NULL)
		mem_range_softc.mr_op->init(&mem_range_softc);
}


code:
/*	$OpenBSD: mutex.S,v 1.9 2013/07/10 21:31:11 kettenis Exp $	*/

/*
 * Copyright (c) 2004 Artur Grabowski <art@openbsd.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. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "assym.h"
/*
 * Yeah, we don't really need to implement mtx_init here, but let's keep
 * all the functions in the same place.
 */
ENTRY(__mtx_init)
	pushl	%ebp
	movl	%esp, %ebp
	movl	8(%esp), %eax
	movl	12(%esp), %edx
	movl	%edx, MTX_WANTIPL(%eax)
	xorl	%edx, %edx
	movl	%edx, MTX_OLDIPL(%eax)
	movl	%edx, MTX_LOCK(%eax)
	movl	%edx, MTX_OWNER(%eax)
	leave
	ret

#define SOFF	8

ENTRY(mtx_enter)
	pushl	%ebp
	movl	%esp, %ebp
1:	movl	SOFF(%ebp), %ecx
	movl	MTX_WANTIPL(%ecx), %eax
	movl	CPL, %edx		# oipl = cpl;
	cmpl	%edx, %eax		# if (cpl < mtx->mtx_wantipl)
	jle	2f
	movl	%eax, CPL		#	cpl = mtx->mtx_wantipl;
2:	/*
	 * %edx now contains the oldipl.
	 * %ecx contains the mtx.
	 */
	movl	$1, %eax
	xchgl	%eax, MTX_LOCK(%ecx)	# test_and_set(mtx->mtx_lock)
	testl	%eax, %eax		# if (already held)
	jnz	3f
	movl	CPUVAR(SELF), %eax
#ifdef DIAGNOSTIC
	incl	CPU_INFO_MUTEX_LEVEL(%eax)
#endif
	movl	%eax, MTX_OWNER(%ecx)
	movl	%edx, MTX_OLDIPL(%ecx)
	leave
	ret

	/* We failed to obtain the lock. splx, spin and retry. */
3:	pushl	%edx
	call	_C_LABEL(splx)
	movl	%ebp, %esp
	movl	SOFF(%ebp), %ecx		# %ecx clobbered
4:
#ifdef DIAGNOSTIC
	movl	CPUVAR(SELF), %edx
	cmpl	MTX_OWNER(%ecx), %edx
	je	5f
#endif
	movl	MTX_LOCK(%ecx), %eax
	testl	%eax, %eax
	jz	1b
	jmp	4b
#ifdef DIAGNOSTIC
5:	pushl	$6f
	call	_C_LABEL(panic)
6:	.asciz	"mtx_enter: locking against myself"
#endif

ENTRY(mtx_enter_try)
	pushl	%ebp
	movl	%esp, %ebp
1:	movl	SOFF(%ebp), %ecx
	movl	MTX_WANTIPL(%ecx), %eax
	movl	CPL, %edx		# oipl = cpl;
	cmpl	%edx, %eax		# if (cpl < mtx->mtx_wantipl)
	jle	2f
	movl	%eax, CPL		#	cpl = mtx->mtx_wantipl;
2:	/*
	 * %edx now contains the oldipl.
	 * %ecx contains the mtx.
	 */
	movl	$1, %eax
	xchgl	%eax, MTX_LOCK(%ecx)	# test_and_set(mtx->mtx_lock)
	testl	%eax, %eax		# if (already held)
	jnz	3f
	movl	CPUVAR(SELF), %eax
#ifdef DIAGNOSTIC
	incl	CPU_INFO_MUTEX_LEVEL(%eax)
#endif
	movl	%eax, MTX_OWNER(%ecx)
	movl	%edx, MTX_OLDIPL(%ecx)
	movl	$1, %eax
	leave
	ret

	/* We failed to obtain the lock. splx and return zero. */
3:	pushl	%edx
	call	_C_LABEL(splx)
	movl	%ebp, %esp
	movl	SOFF(%ebp), %ecx		# %ecx clobbered
#ifdef DIAGNOSTIC
	movl	CPUVAR(SELF), %edx
	cmpl	MTX_OWNER(%ecx), %edx
	je	4f
#endif
	xorl	%eax, %eax
	leave
	ret

#ifdef DIAGNOSTIC
4:	pushl	$5f
	call	_C_LABEL(panic)
5:	.asciz	"mtx_enter_try: locking against myself"
#endif

ENTRY(mtx_leave)
	pushl	%ebp
	movl	%esp, %ebp
	movl	SOFF(%ebp), %ecx
#ifdef DIAGNOSTIC
	movl	CPUVAR(SELF), %eax
	cmpl	%eax, MTX_OWNER(%ecx)
	jne	1f
	decl	CPU_INFO_MUTEX_LEVEL(%eax)
#endif
	xorl	%eax, %eax
	movl	%eax, MTX_OWNER(%ecx)
	pushl	MTX_OLDIPL(%ecx)
	movl	%eax, MTX_OLDIPL(%ecx)
	movl	%eax, MTX_LOCK(%ecx)
	call	_C_LABEL(splx)
	leave
	ret

#ifdef DIAGNOSTIC
1:	pushl	$2f
	call	_C_LABEL(panic)
2:	.asciz	"mtx_leave: lock not held"
#endif


code:
/*	$OpenBSD: nvram.c,v 1.4 2015/02/10 21:56:09 miod Exp $ */

/*
 * Copyright (c) 2004 Joshua Stein <jcs@openbsd.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 ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <sys/conf.h>

#include <dev/ic/mc146818reg.h>
#include <i386/isa/nvram.h>

/* checksum is calculated over bytes 2 to 31 and stored in byte 32 */
#define NVRAM_CSUM_START	(MC_NVRAM_START + 2)
#define NVRAM_CSUM_END		(MC_NVRAM_START + 31)
#define NVRAM_CSUM_LOC		(MC_NVRAM_START + 32)

#define NVRAM_SIZE		(128 - MC_NVRAM_START)

/* #define NVRAM_DEBUG 1 */

void nvramattach(int);

int nvramopen(dev_t dev, int flag, int mode, struct proc *p);
int nvramclose(dev_t dev, int flag, int mode, struct proc *p);
int nvramread(dev_t dev, struct uio *uio, int flags);

int nvram_csum_valid(void);
int nvram_get_byte(int byteno);

static int nvram_initialized;

void
nvramattach(int num)
{
	if (num > 1)
		return;

	if (nvram_initialized || nvram_csum_valid()) {
#ifdef NVRAM_DEBUG
		printf("nvram: initialized\n");
#endif
		nvram_initialized = 1;
	} else
		printf("nvram: invalid checksum\n");
}

int
nvramopen(dev_t dev, int flag, int mode, struct proc *p)
{
	/* TODO: re-calc checksum on every open? */

	if ((minor(dev) != 0) || (!nvram_initialized))
		return (ENXIO);

	if ((flag & FWRITE))
		return (EPERM);

	return (0);
}

int
nvramclose(dev_t dev, int flag, int mode, struct proc *p)
{
	return (0);
}

int
nvramread(dev_t dev, struct uio *uio, int flags)
{
	u_char buf[NVRAM_SIZE];
	u_int pos = uio->uio_offset;
	u_char *tmp;
	int count = min(sizeof(buf), uio->uio_resid);
	int ret;

	if (!nvram_initialized)
		return (ENXIO);

	if (uio->uio_resid == 0)
		return (0);

#ifdef NVRAM_DEBUG
	printf("attempting to read %d bytes at offset %d\n", count, pos);
#endif

	for (tmp = buf; count-- > 0 && pos < NVRAM_SIZE; ++pos, ++tmp)
		*tmp = nvram_get_byte(pos);

#ifdef NVRAM_DEBUG
	printf("nvramread read %d bytes (%s)\n", (tmp - buf), tmp);
#endif

	ret = uiomovei((caddr_t)buf, (tmp - buf), uio);

	uio->uio_offset += uio->uio_resid;

	return (ret);
}

int
nvram_get_byte(int byteno)
{
	if (!nvram_initialized)
		return (ENXIO);

	return (mc146818_read(NULL, byteno + MC_NVRAM_START) & 0xff);
}

int
nvram_csum_valid()
{
	u_short csum = 0;
	u_short csumexpect;
	int nreg;

	for (nreg = NVRAM_CSUM_START; nreg <= NVRAM_CSUM_END; nreg++)
		csum += mc146818_read(NULL, nreg);

	csumexpect = mc146818_read(NULL, NVRAM_CSUM_LOC) << 8 |
	    mc146818_read(NULL, NVRAM_CSUM_LOC + 1);

#ifdef NVRAM_DEBUG
	printf("nvram: checksum is %x, expecting %x\n", (csum & 0xffff),
		csumexpect);
#endif

	return ((csum & 0xffff) == csumexpect);
}


code:
/*	$OpenBSD: p4tcc.c,v 1.19 2014/09/14 14:17:23 jsg Exp $ */
/*
 * Copyright (c) 2003 Ted Unangst
 * 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.
 */
/*
 * Restrict power consumption by using thermal control circuit.
 * This operates independently of speedstep.
 * Found on Pentium 4 and later models (feature TM).
 *
 * References:
 * Intel Developer's manual v.3 #245472-012
 *
 * On some models, the cpu can hang if it's running at a slow speed.
 * Workarounds included below.
 */

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

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

static struct {
	u_short level;
	u_short reg;
} tcc[] = {
	{ 88, 0 },
	{ 75, 7 },
	{ 63, 6 },
	{ 50, 5 },
	{ 38, 4 },
	{ 25, 3 },
	{ 13, 2 },
	{ 0, 1 }
};

#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0])

extern int setperf_prio;
int p4tcc_level;

int p4tcc_cpuspeed(int *);

void
p4tcc_init(int family, int step)
{
	if (setperf_prio > 1)
		return;

	switch (family) {
	case 0xf:	/* Pentium 4 */
		switch (step) {
		case 0x22:	/* errata O50 P44 and Z21 */
		case 0x24:
		case 0x25:
		case 0x27:
		case 0x29:
			/* hang with 12.5 */
			tcc[TCC_LEVELS - 1].reg = 2;
			break;
		case 0x07:	/* errata N44 and P18 */
		case 0x0a:
		case 0x12:
		case 0x13:
			/* hang at 12.5 and 25 */
			tcc[TCC_LEVELS - 1].reg = 3;
			tcc[TCC_LEVELS - 2].reg = 3;
			break;
		}
		break;
	}

	p4tcc_level = tcc[0].level;
	cpu_setperf = p4tcc_setperf;
	cpu_cpuspeed = p4tcc_cpuspeed;
	setperf_prio = 1;
}

int
p4tcc_cpuspeed(int *speed)
{
	*speed = cpuspeed * (p4tcc_level + 12) / 100;

	return 0;
}

void
p4tcc_setperf(int level)
{
	int i;
	uint64_t msreg, vet;

	for (i = 0; i < TCC_LEVELS; i++) {
		if (level >= tcc[i].level)
			break;
	}
	if (i == TCC_LEVELS)
		i = TCC_LEVELS - 1;

	msreg = rdmsr(MSR_THERM_CONTROL);
	msreg &= ~0x1e; /* bit 0 reserved */
	if (tcc[i].reg != 0) /* enable it */
		msreg |= tcc[i].reg << 1 | 1 << 4;
	wrmsr(MSR_THERM_CONTROL, msreg);
	vet = rdmsr(MSR_THERM_CONTROL);

	if ((vet & 0x1e) != (msreg & 0x1e))
		printf("p4_tcc: cpu did not honor request\n");
	else
		p4tcc_level = tcc[i].level;
}


code:
/*	$OpenBSD: pctr.c,v 1.27 2014/03/29 18:09:29 guenther Exp $	*/

/*
 * Pentium performance counter driver for OpenBSD.
 * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
 *
 * Modification and redistribution in source and binary forms is
 * permitted provided that due credit is given to the author and the
 * OpenBSD project by leaving this copyright notice intact.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioccom.h>
#include <sys/systm.h>

#include <machine/cputypes.h>
#include <machine/psl.h>
#include <machine/pctr.h>
#include <machine/cpu.h>
#include <machine/specialreg.h>

#define PCTR_AMD_NUM	PCTR_NUM
#define PCTR_INTEL_NUM	2		/* Intel supports only 2 counters */

#define usetsc		(cpu_feature & CPUID_TSC)
#define usep5ctr	(pctr_isintel && (((cpu_id >> 8) & 15) == 5) && \
				(((cpu_id >> 4) & 15) > 0))
#define usepctr		((pctr_isamd || pctr_isintel) && \
			    ((cpu_id >> 8) & 15) >= 6)

int			pctr_isamd;
int			pctr_isintel;

static int		p5ctrsel(int fflag, u_int cmd, u_int fn);
static int		pctrsel(int fflag, u_int cmd, u_int fn);
static void		pctrrd(struct pctrst *);
static void		pctrrd(struct pctrst *);

static void
p5ctrrd(struct pctrst *st)
{
	u_int msr11;

	msr11 = rdmsr(P5MSR_CTRSEL);
	st->pctr_fn[0] = msr11 & 0xffff;
	st->pctr_fn[1] = msr11 >> 16;
	__asm volatile("cli");
	st->pctr_tsc = rdtsc();
	st->pctr_hwc[0] = rdmsr(P5MSR_CTR0);
	st->pctr_hwc[1] = rdmsr(P5MSR_CTR1);
	__asm volatile("sti");
}

static void
pctrrd(struct pctrst *st)
{
	int i, num, reg;

	num = pctr_isamd ? PCTR_AMD_NUM : PCTR_INTEL_NUM;
	reg = pctr_isamd ? MSR_K7_EVNTSEL0 : P6MSR_CTRSEL0;
	for (i = 0; i < num; i++)
			st->pctr_fn[i] = rdmsr(reg + i);
	__asm volatile("cli");
	st->pctr_tsc = rdtsc();
	for (i = 0; i < num; i++)
		st->pctr_hwc[i] = rdpmc(i);
	__asm volatile("sti");
}

void
pctrattach(int num)
{

	if (num > 1)
		return;

	pctr_isamd = (strcmp(cpu_vendor, "AuthenticAMD") == 0);
	if (!pctr_isamd)
		pctr_isintel = (strcmp(cpu_vendor, "GenuineIntel") == 0);

	if (usepctr) {
		/* Enable RDTSC and RDPMC instructions from user-level. */
		__asm volatile ("movl %%cr4,%%eax\n"
				  "\tandl %0,%%eax\n"
				  "\torl %1,%%eax\n"
				  "\tmovl %%eax,%%cr4"
				  :: "i" (~CR4_TSD), "i" (CR4_PCE) : "eax");
	} else if (usetsc) {
		/* Enable RDTSC instruction from user-level. */
		__asm volatile ("movl %%cr4,%%eax\n"
				  "\tandl %0,%%eax\n"
				  "\tmovl %%eax,%%cr4"
				  :: "i" (~CR4_TSD) : "eax");
	}
}

int
pctropen(dev_t dev, int oflags, int devtype, struct proc *p)
{

	if (minor(dev))
		return (ENXIO);
	return (0);
}

int
pctrclose(dev_t dev, int oflags, int devtype, struct proc *p)
{

	return (0);
}

int
p5ctrsel(int fflag, u_int cmd, u_int fn)
{
	pctrval msr11;
	int msr, shift;

	cmd -= PCIOCS0;
	if (cmd > 1)
		return (EINVAL);
	msr = P5MSR_CTR0 + cmd;
	shift = cmd ? 0x10 : 0;

	if (!(fflag & FWRITE))
		return (EPERM);
	if (fn >= 0x200)
		return (EINVAL);

	msr11 = rdmsr(P5MSR_CTRSEL);
	msr11 &= ~(0x1ffLL << shift);
	msr11 |= fn << shift;
	wrmsr(P5MSR_CTRSEL, msr11);
	wrmsr(msr, 0);

	return (0);
}

int
pctrsel(int fflag, u_int cmd, u_int fn)
{
	int msrsel, msrval;

	cmd -= PCIOCS0;
	if (pctr_isamd) {
		if (cmd > PCTR_AMD_NUM-1)
			return (EINVAL);
		msrsel = MSR_K7_EVNTSEL0 + cmd;
		msrval = MSR_K7_PERFCTR0 + cmd;
	} else {
		if (cmd > PCTR_INTEL_NUM-1)
			return (EINVAL);
		msrsel = P6MSR_CTRSEL0 + cmd;
		msrval = P6MSR_CTR0 + cmd;
	}

	if (!(fflag & FWRITE))
		return (EPERM);
	if (fn & 0x380000)
		return (EINVAL);

	wrmsr(msrval, 0);
	wrmsr(msrsel, fn);
	wrmsr(msrval, 0);

	return (0);
}

int
pctrioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
{
	switch (cmd) {
	case PCIOCRD:
	{
		struct pctrst *st = (struct pctrst *)data;

		if (usepctr)
			pctrrd(st);
		else if (usep5ctr)
			p5ctrrd(st);
		else {
			bzero(st, sizeof(*st));
			if (usetsc)
				st->pctr_tsc = rdtsc();
		}
		return (0);
	}
	case PCIOCS0:
	case PCIOCS1:
	case PCIOCS2:
	case PCIOCS3:
		if (usepctr)
			return (pctrsel(fflag, cmd, *(u_int *)data));
		if (usep5ctr)
			return (p5ctrsel(fflag, cmd, *(u_int *)data));
		return (ENODEV);
	default:
		return (EINVAL);
	}
}