code:
/*	$OpenBSD: amd64errata.c,v 1.6 2013/12/07 01:43:50 brad Exp $	*/
/*	$NetBSD: errata.c,v 1.6 2007/02/05 21:05:45 ad Exp $	*/

/*-
 * Copyright (c) 2007 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * 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.
 */

/*
 * Detect, report on, and work around known errata with AMD amd64 CPUs.
 *
 * This is generalised because there are quite a few problems that the
 * BIOS can patch via MSR, but it is not known if the OS can patch these
 * yet.  The list is expected to grow over time.
 *
 * The data here are from: Revision Guide for AMD Athlon 64 and
 * AMD Opteron Processors, Publication #25759, Revision: 3.69,
 * Issue Date: September 2006
 */

#include <sys/param.h>

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

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

typedef struct errata {
	u_short		e_num;
	u_short		e_reported;
	u_int		e_data1;
	const uint8_t	*e_set;
	int		(*e_act)(struct cpu_info *, struct errata *);
	uint64_t	e_data2;
} errata_t;

typedef enum cpurev {
	BH_E4, CH_CG, CH_D0, DH_CG, DH_D0, DH_E3, DH_E6, JH_E1,
	JH_E6, SH_B0, SH_B3, SH_C0, SH_CG, SH_D0, SH_E4, SH_E5,
	DR_BA, DR_B2, DR_B3, RB_C2, RB_C3, BL_C2, BL_C3, DA_C2,
	DA_C3, HY_D0, HY_D1, HY_D1_G34R1,  PH_E0, LN_B0,
	OINK
} cpurev_t;

static const u_int cpurevs[] = {
	BH_E4, 0x0020fb1, CH_CG, 0x0000f82, CH_CG, 0x0000fb2,
	CH_D0, 0x0010f80, CH_D0, 0x0010fb0, DH_CG, 0x0000fc0,
	DH_CG, 0x0000fe0, DH_CG, 0x0000ff0, DH_D0, 0x0010fc0,
	DH_D0, 0x0010ff0, DH_E3, 0x0020fc0, DH_E3, 0x0020ff0,
	DH_E6, 0x0020fc2, DH_E6, 0x0020ff2, JH_E1, 0x0020f10,
	JH_E6, 0x0020f12, JH_E6, 0x0020f32, SH_B0, 0x0000f40,
	SH_B3, 0x0000f51, SH_C0, 0x0000f48, SH_C0, 0x0000f58,
	SH_CG, 0x0000f4a, SH_CG, 0x0000f5a, SH_CG, 0x0000f7a,
	SH_D0, 0x0010f40, SH_D0, 0x0010f50, SH_D0, 0x0010f70,
	SH_E4, 0x0020f51, SH_E4, 0x0020f71, SH_E5, 0x0020f42,
	DR_BA, 0x0100f2a, DR_B2, 0x0100f22, DR_B3, 0x0100f23,
	RB_C2, 0x0100f42, RB_C3, 0x0100f43, BL_C2, 0x0100f52,
	BL_C3, 0x0100f53, DA_C2, 0x0100f62, DA_C3, 0x0100f63,
	HY_D0, 0x0100f80, HY_D1, 0x0100f81, HY_D1_G34R1, 0x0100f91,
	PH_E0, 0x0100fa0, LN_B0, 0x0300f10,
	OINK
};

static const uint8_t amd64_errata_set1[] = {
	SH_B3, SH_C0, SH_CG, DH_CG, CH_CG, OINK
};

static const uint8_t amd64_errata_set2[] = {
	SH_B3, SH_C0, SH_CG, DH_CG, CH_CG, SH_D0, DH_D0, CH_D0, OINK
};

static const uint8_t amd64_errata_set3[] = {
	JH_E1, DH_E3, OINK
};

#if 0
static const uint8_t amd64_errata_set4[] = {
	SH_C0, SH_CG, DH_CG, CH_CG, SH_D0, DH_D0, CH_D0, JH_E1,
	DH_E3, SH_E4, BH_E4, SH_E5, DH_E6, JH_E6, OINK
};
#endif

static const uint8_t amd64_errata_set5[] = {
	SH_B3, OINK
};

static const uint8_t amd64_errata_set6[] = {
	SH_C0, SH_CG, DH_CG, CH_CG, OINK
};

static const uint8_t amd64_errata_set7[] = {
	SH_C0, SH_CG, DH_CG, CH_CG, SH_D0, DH_D0, CH_D0, OINK
};

static const uint8_t amd64_errata_set8[] = {
	BH_E4, CH_CG, CH_CG, CH_D0, CH_D0, DH_CG, DH_CG, DH_CG,
	DH_D0, DH_D0, DH_E3, DH_E3, DH_E6, DH_E6, JH_E1, JH_E6,
	JH_E6, SH_B0, SH_B3, SH_C0, SH_C0, SH_CG, SH_CG, SH_CG,
	SH_D0, SH_D0, SH_D0, SH_E4, SH_E4, SH_E5, OINK
};

static const uint8_t amd64_errata_set9[] = {
	DR_BA, DR_B2, DR_B3, RB_C2, RB_C3, BL_C2, BL_C3, DA_C2,
	DA_C3, HY_D0, HY_D1, HY_D1_G34R1,  PH_E0, LN_B0, OINK
};

int amd64_errata_setmsr(struct cpu_info *, errata_t *);
int amd64_errata_testmsr(struct cpu_info *, errata_t *);

static errata_t errata[] = {
	/*
	 * 81: Cache Coherency Problem with Hardware Prefetching
	 * and Streaming Stores
	 */
	{
		81, 0, MSR_DC_CFG, amd64_errata_set5,
		amd64_errata_testmsr, DC_CFG_DIS_SMC_CHK_BUF
	},
	/*
	 * 86: DRAM Data Masking Feature Can Cause ECC Failures
	 */
	{
		86, 0, MSR_NB_CFG, amd64_errata_set1,
		amd64_errata_testmsr, NB_CFG_DISDATMSK
	},
	/*
	 * 89: Potential Deadlock With Locked Transactions
	 */
	{
		89, 0, MSR_NB_CFG, amd64_errata_set8,
		amd64_errata_testmsr, NB_CFG_DISIOREQLOCK
	},
	/*
	 * 94: Sequential Prefetch Feature May Cause Incorrect
	 * Processor Operation
	 */
	{
		94, 0, MSR_IC_CFG, amd64_errata_set1,
		amd64_errata_testmsr, IC_CFG_DIS_SEQ_PREFETCH
	},
	/*
	 * 97: 128-Bit Streaming Stores May Cause Coherency
	 * Failure
	 *
	 * XXX "This workaround must not be applied to processors
	 * prior to revision C0."  We don't apply it, but if it
	 * can't be applied, it shouldn't be reported.
	 */
	{
		97, 0, MSR_DC_CFG, amd64_errata_set6,
		amd64_errata_testmsr, DC_CFG_DIS_CNV_WC_SSO
	},
	/*
	 * 104: DRAM Data Masking Feature Causes ChipKill ECC
	 * Failures When Enabled With x8/x16 DRAM Devices
	 */
	{
		104, 0, MSR_NB_CFG, amd64_errata_set7,
		amd64_errata_testmsr, NB_CFG_DISDATMSK
	},
	/*
	 * 113: Enhanced Write-Combining Feature Causes System Hang
	 */
	{
		113, 0, MSR_BU_CFG, amd64_errata_set3,
		amd64_errata_setmsr, BU_CFG_WBENHWSBDIS
	},
#ifdef MULTIPROCESSOR
	/*
	 * 69: Multiprocessor Coherency Problem with Hardware
	 * Prefetch Mechanism
	 */
	{
		69, 0, MSR_BU_CFG, amd64_errata_set5,
		amd64_errata_setmsr, BU_CFG_WBPFSMCCHKDIS
	},
	/*
	 * 101: DRAM Scrubber May Cause Data Corruption When Using
	 * Node-Interleaved Memory
	 */
	{
		101, 0, 0, amd64_errata_set2,
		NULL, 0
	},
	/*
	 * 106: Potential Deadlock with Tightly Coupled Semaphores
	 * in an MP System
	 */
	{
		106, 0, MSR_LS_CFG, amd64_errata_set2,
		amd64_errata_testmsr, LS_CFG_DIS_LS2_SQUISH
	},
	/*
	 * 107: Possible Multiprocessor Coherency Problem with
	 * Setting Page Table A/D Bits
	 */
	{
		107, 0, MSR_BU_CFG, amd64_errata_set2,
		amd64_errata_testmsr, BU_CFG_THRL2IDXCMPDIS
	},
#if 0
	/*
	 * 122: TLB Flush Filter May Cause Coherency Problem in
	 * Multiprocessor Systems
	 */
	{
		122, 0, MSR_HWCR, amd64_errata_set4,
		amd64_errata_setmsr, HWCR_FFDIS
	},
#endif
#endif	/* MULTIPROCESSOR */
	/*
	 * 721: Processor May Incorrectly Update Stack Pointer
	 */
	{
		721, 0, MSR_DE_CFG, amd64_errata_set9,
		amd64_errata_setmsr, DE_CFG_721
	},
};

int
amd64_errata_testmsr(struct cpu_info *ci, errata_t *e)
{
	uint64_t val;

	(void)ci;

	val = rdmsr_locked(e->e_data1, OPTERON_MSR_PASSCODE);
	if ((val & e->e_data2) != 0)
		return 0;		/* not found */

	e->e_reported = 1;
	return 1;			/* found */
}

int
amd64_errata_setmsr(struct cpu_info *ci, errata_t *e)
{
	uint64_t val;

	(void)ci;

	val = rdmsr_locked(e->e_data1, OPTERON_MSR_PASSCODE);
	if ((val & e->e_data2) != 0)
		return 0;		/* not found */

	wrmsr_locked(e->e_data1, OPTERON_MSR_PASSCODE, val | e->e_data2);

#ifdef ERRATA_DEBUG
	printf("ERRATA: writing a fix\n");
	val = rdmsr_locked(e->e_data1, OPTERON_MSR_PASSCODE);
	if ((val & e->e_data2) != 0)
		printf("ERRATA: fix seems to have worked!\n");
#endif

	e->e_reported = 1;
	return 2;			/* found and fixed */
}

void
amd64_errata(struct cpu_info *ci)
{
	errata_t *e, *ex;
	cpurev_t rev;
	int i, j;
	int rc;
	int found = 0;
	int corrected = 0;
	u_int32_t regs[4];

	cpuid(0x80000001, regs);

	for (i = 0; ; i += 2) {
		if ((rev = cpurevs[i]) == OINK) {
#ifdef ERRATA_DEBUG
			printf("ERRATA: this CPU ok\n");
#endif
			return;
		}
		if (cpurevs[i + 1] == regs[0]) {
#ifdef ERRATA_DEBUG
			printf("ERRATA: this CPU has errata\n");
#endif
			break;
		}
	}

	ex = errata + sizeof(errata) / sizeof(errata[0]);

	/* Reset e_reporteds (for multiple CPUs) */
	for (e = errata; e < ex; e++)
		e->e_reported = 0;

	for (e = errata; e < ex; e++) {
		if (e->e_reported)
			continue;
		if (e->e_set != NULL) {
			for (j = 0; e->e_set[j] != OINK; j++)
				if (e->e_set[j] == rev)
					break;
			if (e->e_set[j] == OINK)
				continue;
		}

#ifdef ERRATA_DEBUG
		printf("%s: testing for erratum %d\n",
		    ci->ci_dev.dv_xname, e->e_num);
#endif

		/*
		 * If we have an action routine, call it, otherwise
		 * the default is that this erratum is present.
		 */
		rc = (e->e_act == NULL) ? 1 : (*e->e_act)(ci, e);

		if (rc == 0)			/* not found */
			continue;
		if (rc == 1)
			found++;
		if (rc == 2)
			corrected++;

		e->e_reported = rc;

#ifdef ERRATA_DEBUG
		printf("%s: erratum %d present%s\n",
		    ci->ci_dev.dv_xname, e->e_num,
		    (rc == 2) ? " and patched" : "");
#endif
	}

#define ERRATA_VERBOSE
#ifdef ERRATA_VERBOSE
	if (corrected) {
		int first = 1;

		/* Print out found and corrected */
		printf("%s: AMD %s", ci->ci_dev.dv_xname,
		    (corrected == 1) ? "erratum" : "errata");
		for (e = errata; e < ex; e++) {
			if (e->e_reported == 2) {
				if (! first)
					printf(",");
				printf(" %d", e->e_num);
				first = 0;
			}
		}
		printf(" detected and fixed\n");
	}
#endif

	if (found) {
		int first = 1;

		/* Print out found but not corrected */
		printf("%s: AMD %s", ci->ci_dev.dv_xname,
		    (found == 1) ? "erratum" : "errata");
		for (e = errata; e < ex; e++) {
			if (e->e_reported == 1) {
				if (! first)
					printf(",");
				printf(" %d", e->e_num);
				first = 0;
			}
		}
		printf(" present, BIOS upgrade may be required\n");
	}
}


code:
/*      $OpenBSD: amdmsr.c,v 1.9 2010/04/20 22:05:41 tedu Exp $	*/

/*
 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
 * All rights reserved.
 *
 * 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 MIND, 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.
 */

/*
 * Enable MSR access for AMD Geode LX Processors with graphics processor
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <machine/amdmsr.h>

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

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

#ifdef APERTURE
static int amdmsr_open_cnt;
extern int allowaperture;
#endif

#define GLX_CPU_GLD_MSR_CAP	0x00002000
#define GLX_CPU_DID		0x0864		/* CPU device ID */
#define GLX_GP_GLD_MSR_CAP	0xa0002000
#define GLX_GP_DID		0x03d4		/* GP device ID */

struct amdmsr_softc {
	struct device		sc_dev;
};

struct cfdriver amdmsr_cd = {
	NULL, "amdmsr", DV_DULL
};

int	amdmsr_match(struct device *, void *, void *);
void	amdmsr_attach(struct device *, struct device *, void *);

struct cfattach amdmsr_ca = {
	sizeof(struct amdmsr_softc), amdmsr_match, amdmsr_attach
};

int
amdmsr_probe(void)
{
#ifdef APERTURE
	u_int64_t gld_msr_cap;
	int family, model;

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

	/* Check for AMD Geode LX CPU */
	if (strcmp(cpu_vendor, "AuthenticAMD") == 0 && family == 0x5 &&
	    model == 0x0a) {
		/* Check for graphics processor presence */
		gld_msr_cap = rdmsr(GLX_CPU_GLD_MSR_CAP);
		if (((gld_msr_cap >> 8) & 0x0fff) == GLX_CPU_DID) {
			gld_msr_cap = rdmsr(GLX_GP_GLD_MSR_CAP);
			if (((gld_msr_cap >> 8) & 0x0fff) == GLX_GP_DID)
				return 1;
		}
	}
#endif
	return 0;
}

int
amdmsr_match(struct device *parent, void *match, void *aux)
{
	const char **busname = (const char **)aux;

	return (strcmp(*busname, amdmsr_cd.cd_name) == 0);
}

void
amdmsr_attach(struct device *parent, struct device *self, void *aux)
{
	printf("\n");
}

int
amdmsropen(dev_t dev, int flags, int devtype, struct proc *p)
{
#ifdef APERTURE
	if (amdmsr_cd.cd_ndevs == 0 || minor(dev) != 0)
		return ENXIO;

	if (suser(p, 0) != 0 || !allowaperture)
		return EPERM;
	/* allow only one simultaneous open() */
	if (amdmsr_open_cnt > 0)
		return EPERM;
	amdmsr_open_cnt++;
	return 0;
#else
	return ENXIO;
#endif
}

int
amdmsrclose(dev_t dev, int flags, int devtype, struct proc *p)
{
#ifdef APERTURE
	amdmsr_open_cnt--;
#endif
	return 0;
}

int
amdmsrioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct amdmsr_req *req = (struct amdmsr_req *)data;

	switch (cmd) {
	case RDMSR:
		req->val = rdmsr(req->addr);
		break;
	case WRMSR:
		wrmsr(req->addr, req->val);
		break;
	default:
		return EINVAL;
	}
	return 0;
}

license: unknown


code:
/*	$OpenBSD: apic.c,v 1.6 2009/08/13 13:24:48 kettenis Exp $	*/
/* $NetBSD: apic.c,v 1.1.2.2 2000/02/21 18:51:00 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/types.h>
#include <sys/systm.h>

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

void
apic_format_redir(char *where1, char *where2, int idx, u_int32_t redirhi,
    u_int32_t redirlo)
{
	printf("%s: %s%d 0x%x", where1, where2, idx, redirlo);

	if ((redirlo & LAPIC_DEST_MASK) == 0)
		printf(" 0x%x", redirhi);

	printf("\n");
}


code:
/* $OpenBSD: apicvec.s,v 1.31 2015/07/16 05:10:14 guenther Exp $ */
/* $NetBSD: apicvec.s,v 1.1.2.2 2000/02/21 21:54:01 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 <machine/i82093reg.h>
#include <machine/i82489reg.h>

#define XINTR(vec) Xintr##vec

	.globl  _C_LABEL(apic_stray)

#ifdef MULTIPROCESSOR
	.globl	XINTR(ipi)
XINTR(ipi):
	subl	$8,%esp			/* space for tf_{err,trapno} */
	INTRENTRY
	MAKE_FRAME
	pushl	CPL
	movl	_C_LABEL(lapic_ppr),%eax
	movl	%eax,CPL
	ioapic_asm_ack()
	sti			/* safe to take interrupts.. */
	call	_C_LABEL(i386_ipi_handler)
	cli
	popl	CPL
	INTRFASTEXIT

	.globl	XINTR(ipi_invltlb)
	.p2align 4,0x90
XINTR(ipi_invltlb):
	pushl	%eax
	pushl	%ds
	movl	$GSEL(GDATA_SEL, SEL_KPL), %eax
	movl	%eax, %ds

	ioapic_asm_ack()

	movl	%cr3, %eax
	movl	%eax, %cr3

	lock
	decl	tlb_shoot_wait

	popl	%ds
	popl	%eax
	iret

	.globl	XINTR(ipi_invlpg)
	.p2align 4,0x90
XINTR(ipi_invlpg):
	pushl	%eax
	pushl	%ds
	movl	$GSEL(GDATA_SEL, SEL_KPL), %eax
	movl	%eax, %ds

	ioapic_asm_ack()

	movl	tlb_shoot_addr1, %eax
	invlpg	(%eax)

	lock
	decl	tlb_shoot_wait

	popl	%ds
	popl	%eax
	iret

	.globl	XINTR(ipi_invlrange)
	.p2align 4,0x90
XINTR(ipi_invlrange):
	pushl	%eax
	pushl	%edx
	pushl	%ds
	movl	$GSEL(GDATA_SEL, SEL_KPL), %eax
	movl	%eax, %ds

	ioapic_asm_ack()

	movl	tlb_shoot_addr1, %eax
	movl	tlb_shoot_addr2, %edx
1:	invlpg	(%eax)
	addl	$PAGE_SIZE, %eax
	cmpl	%edx, %eax
	jb	1b

	lock
	decl	tlb_shoot_wait

	popl	%ds
	popl	%edx
	popl	%eax
	iret

	.globl	XINTR(ipi_reloadcr3)
	.p2align 4,0x90
XINTR(ipi_reloadcr3):
	pushl	%eax
	pushl	%ds
	movl	$GSEL(GDATA_SEL, SEL_KPL), %eax
	movl	%eax, %ds
	pushl	%fs
	movl	$GSEL(GCPU_SEL, SEL_KPL),%eax
	movw	%ax,%fs

	ioapic_asm_ack()

	movl	CPUVAR(CURPCB), %eax
	movl	PCB_PMAP(%eax), %eax
	movl	%eax, CPUVAR(CURPMAP)
	movl	PM_PDIRPA(%eax), %eax
	movl	%eax, %cr3

	lock
	decl	tlb_shoot_wait

	popl	%fs
	popl	%ds
	popl	%eax
	iret

#endif

	/*
	 * Interrupt from the local APIC timer.
	 */
	.globl	XINTR(ltimer)
XINTR(ltimer):
	subl	$8,%esp			/* space for tf_{err,trapno} */
	INTRENTRY
	MAKE_FRAME
	pushl	CPL
	movl	_C_LABEL(lapic_ppr),%eax
	movl	%eax,CPL
	ioapic_asm_ack()
	sti
	incl	CPUVAR(IDEPTH)
	movl	%esp,%eax
	pushl	%eax
	call	_C_LABEL(lapic_clockintr)
	addl	$4,%esp
	decl	CPUVAR(IDEPTH)
	jmp	_C_LABEL(Xdoreti)

	.globl	XINTR(softclock), XINTR(softnet), XINTR(softtty)
XINTR(softclock):
	subl	$8,%esp			/* space for tf_{err,trapno} */
	INTRENTRY
	MAKE_FRAME
	pushl	CPL
	movl	$IPL_SOFTCLOCK,CPL
	andl	$~(1<<SIR_CLOCK),CPUVAR(IPENDING)
	ioapic_asm_ack()
	sti
	incl	CPUVAR(IDEPTH)
	pushl	$I386_SOFTINTR_SOFTCLOCK
	call	_C_LABEL(softintr_dispatch)
	addl	$4,%esp
	decl	CPUVAR(IDEPTH)
	jmp	_C_LABEL(Xdoreti)

XINTR(softnet):
	subl	$8,%esp			/* space for tf_{err,trapno} */
	INTRENTRY
	MAKE_FRAME
	pushl	CPL
	movl	$IPL_SOFTNET,CPL
	andl	$~(1<<SIR_NET),CPUVAR(IPENDING)
	ioapic_asm_ack()
	sti
	incl	CPUVAR(IDEPTH)
	pushl	$I386_SOFTINTR_SOFTNET
	call	_C_LABEL(softintr_dispatch)
	addl	$4,%esp
	decl	CPUVAR(IDEPTH)
	jmp	_C_LABEL(Xdoreti)
#undef DONETISR

XINTR(softtty):
	subl	$8,%esp			/* space for tf_{err,trapno} */
	INTRENTRY
	MAKE_FRAME
	pushl	CPL
	movl	$IPL_SOFTTTY,CPL
	andl	$~(1<<SIR_TTY),CPUVAR(IPENDING)
	ioapic_asm_ack()
	sti
	incl	CPUVAR(IDEPTH)
	pushl	$I386_SOFTINTR_SOFTTTY
	call	_C_LABEL(softintr_dispatch)
	addl	$4,%esp
	decl	CPUVAR(IDEPTH)
	jmp	_C_LABEL(Xdoreti)

#if NIOAPIC > 0

#define voidop(num)

	/*
	 * I/O APIC interrupt.
	 * We sort out which one is which based on the value of
	 * the processor priority register.
	 *
	 * XXX use cmove when appropriate.
	 */

#define APICINTR(name, num, early_ack, late_ack, mask, unmask, level_mask) \
_C_LABEL(Xintr_##name##num):						\
	subl	$8,%esp			/* space for tf_{err,trapno} */	;\
	INTRENTRY							;\
	MAKE_FRAME							;\
	pushl	CPL							;\
	movl	_C_LABEL(lapic_ppr),%eax				;\
	orl	$num,%eax						;\
	movl	_C_LABEL(apic_maxlevel)(,%eax,4),%ebx			;\
	movl	%ebx,CPL						;\
	mask(num)			/* mask it in hardware */	;\
	early_ack(num)			/* and allow other intrs */	;\
	incl	_C_LABEL(uvmexp)+V_INTR	/* statistical info */		;\
	sti								;\
	movl	_C_LABEL(apic_intrhand)(,%eax,4),%ebx /* chain head */	;\
	testl	%ebx,%ebx						;\
	jz      _C_LABEL(Xstray_##name##num)				;\
	APIC_STRAY_INIT			/* nobody claimed it yet */	;\
7:	incl	CPUVAR(IDEPTH)						;\
	movl	%esp, %eax		/* save frame pointer in eax */	;\
	pushl	%ebx			/* arg 2: ih structure */	;\
	pushl	%eax			/* arg 1: frame pointer */	;\
	call	_C_LABEL(intr_handler)	/* call it */			;\
	addl	$8, %esp		/* toss args */			;\
	APIC_STRAY_INTEGRATE		/* maybe he claimed it */	;\
	orl	%eax,%eax		/* should it be counted? */	;\
	jz	4f							;\
	addl	$1,IH_COUNT(%ebx)	/* count the intrs */		;\
	adcl	$0,IH_COUNT+4(%ebx)					;\
	cmpl	$0,_C_LABEL(intr_shared_edge)				;\
	jne	4f			/* if no shared edges ... */	;\
	orl	%eax,%eax		/* ... 1 means stop trying */	;\
	js	4f							;\
1:	decl	CPUVAR(IDEPTH)						;\
	jmp	8f							;\
4:	decl	CPUVAR(IDEPTH)						;\
	movl	IH_NEXT(%ebx),%ebx	/* next handler in chain */	;\
	testl	%ebx,%ebx						;\
	jnz	7b							;\
	APIC_STRAY_TEST(name,num)	/* see if it's a stray */	;\
8:									 \
	unmask(num)			/* unmask it in hardware */	;\
	late_ack(num)							;\
	jmp	_C_LABEL(Xdoreti)					;\
_C_LABEL(Xstray_##name##num):					 \
	pushl	$num							;\
	call	_C_LABEL(apic_stray)					;\
	addl	$4,%esp							;\
	jmp	8b							;\

#if defined(DEBUG)
#define APIC_STRAY_INIT \
	xorl	%esi,%esi
#define	APIC_STRAY_INTEGRATE \
	orl	%eax,%esi
#define APIC_STRAY_TEST(name,num) \
	testl 	%esi,%esi						;\
	jz 	_C_LABEL(Xstray_##name##num)
#else /* !DEBUG */
#define APIC_STRAY_INIT
#define APIC_STRAY_INTEGRATE
#define APIC_STRAY_TEST(name,num)
#endif /* DEBUG */

APICINTR(ioapic,0, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,1, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,2, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,3, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,4, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,5, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,6, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,7, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,8, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,9, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,10, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,11, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,12, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,13, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,14, voidop, ioapic_asm_ack, voidop, voidop, voidop)
APICINTR(ioapic,15, voidop, ioapic_asm_ack, voidop, voidop, voidop)

	.globl	_C_LABEL(Xintr_ioapic0),_C_LABEL(Xintr_ioapic1)
	.globl	_C_LABEL(Xintr_ioapic2),_C_LABEL(Xintr_ioapic3)
	.globl	_C_LABEL(Xintr_ioapic4),_C_LABEL(Xintr_ioapic5)
	.globl	_C_LABEL(Xintr_ioapic6),_C_LABEL(Xintr_ioapic7)
	.globl	_C_LABEL(Xintr_ioapic8),_C_LABEL(Xintr_ioapic9)
	.globl	_C_LABEL(Xintr_ioapic10),_C_LABEL(Xintr_ioapic11)
	.globl	_C_LABEL(Xintr_ioapic12),_C_LABEL(Xintr_ioapic13)
	.globl	_C_LABEL(Xintr_ioapic14),_C_LABEL(Xintr_ioapic15)
	.globl _C_LABEL(apichandler)

_C_LABEL(apichandler):
	.long	_C_LABEL(Xintr_ioapic0),_C_LABEL(Xintr_ioapic1)
	.long	_C_LABEL(Xintr_ioapic2),_C_LABEL(Xintr_ioapic3)
	.long	_C_LABEL(Xintr_ioapic4),_C_LABEL(Xintr_ioapic5)
	.long	_C_LABEL(Xintr_ioapic6),_C_LABEL(Xintr_ioapic7)
	.long	_C_LABEL(Xintr_ioapic8),_C_LABEL(Xintr_ioapic9)
	.long	_C_LABEL(Xintr_ioapic10),_C_LABEL(Xintr_ioapic11)
	.long	_C_LABEL(Xintr_ioapic12),_C_LABEL(Xintr_ioapic13)
	.long	_C_LABEL(Xintr_ioapic14),_C_LABEL(Xintr_ioapic15)

#endif


code:
/*	$OpenBSD: apm.c,v 1.113 2015/02/07 01:19:40 deraadt Exp $	*/

/*-
 * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
 * Copyright (c) 1995 John T. Kohl.  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 names of the authors nor the names of contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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 MIND, 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 "apm.h"

#if NAPM > 1
#error only one APM device may be configured
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/rwlock.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/event.h>
#include <dev/rndvar.h>

#include <machine/conf.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/gdt.h>
#include <machine/psl.h>

#include <dev/isa/isareg.h>
#include <i386/isa/isa_machdep.h>
#include <i386/isa/nvram.h>
#include <dev/isa/isavar.h>
#include <dev/wscons/wsdisplayvar.h>

#include <machine/acpiapm.h>
#include <machine/biosvar.h>
#include <machine/apmvar.h>

#include "wsdisplay.h"

#if defined(APMDEBUG)
#define DPRINTF(x)	printf x
#else
#define	DPRINTF(x)	/**/
#endif

struct cfdriver apm_cd = {
	NULL, "apm", DV_DULL
};

struct apm_softc {
	struct device sc_dev;
	struct klist sc_note;
	int	sc_flags;
	int	batt_life;
	int	be_batt;
	struct proc *sc_thread;
	struct rwlock sc_lock;
};
#define	SCFLAG_OREAD	0x0000001
#define	SCFLAG_OWRITE	0x0000002
#define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)

int	apmprobe(struct device *, void *, void *);
void	apmattach(struct device *, struct device *, void *);

struct cfattach apm_ca = {
	sizeof(struct apm_softc), apmprobe, apmattach
};

void	filt_apmrdetach(struct knote *kn);
int	filt_apmread(struct knote *kn, long hint);

struct filterops apmread_filtops = {
	1, NULL, filt_apmrdetach, filt_apmread
};

/* battery percentage at where we get verbose in our warnings.  This
 * value can be changed using sysctl(8), value machdep.apmwarn.
 * Setting it to zero kills all warnings
 */
int	cpu_apmwarn = 10;

#define	APM_RESUME_HOLDOFF	3

/*
 * Flags to control kernel display
 *	SCFLAG_NOPRINT:		do not output APM power messages due to
 *				a power change event.
 *
 *	SCFLAG_PCTPRINT:	do not output APM power messages due to
 *				to a power change event unless the battery
 *				percentage changes.
 */
#define SCFLAG_NOPRINT	0x0008000
#define SCFLAG_PCTPRINT	0x0004000
#define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)

#define	APMUNIT(dev)	(minor(dev)&0xf0)
#define	APMDEV(dev)	(minor(dev)&0x0f)
#define APMDEV_NORMAL	0
#define APMDEV_CTL	8

int	apm_standbys;
int	apm_lidclose;
int	apm_userstandbys;
int	apm_suspends;
int	apm_resumes;
int	apm_battlow;
int	apm_evindex;
int	apm_error;
int	apm_op_inprog;

u_int	apm_flags;
u_char	apm_majver;
u_char	apm_minver;
int	apm_attached = 0;
static int	apm_slow_called = 0;

struct {
	u_int32_t entry;
	u_int16_t seg;
	u_int16_t pad;
} apm_ep;

struct apmregs {
	u_int32_t	ax;
	u_int32_t	bx;
	u_int32_t	cx;
	u_int32_t	dx;
};

int  apmcall(u_int, u_int, struct apmregs *);
int  apm_handle_event(struct apm_softc *, struct apmregs *);
void apm_set_ver(struct apm_softc *);
int  apm_periodic_check(struct apm_softc *);
void apm_thread_create(void *v);
void apm_thread(void *);
void apm_disconnect(struct apm_softc *);
void apm_perror(const char *, struct apmregs *);
void apm_powmgt_enable(int onoff);
void apm_powmgt_engage(int onoff, u_int devid);
/* void apm_devpowmgt_enable(int onoff, u_int devid); */
int  apm_record_event(struct apm_softc *sc, u_int type);
const char *apm_err_translate(int code);

#define	apm_get_powstat(r) apmcall(APM_POWER_STATUS, APM_DEV_ALLDEVS, r)
void	apm_suspend(int);
void	apm_resume(struct apm_softc *, struct apmregs *);
void	apm_cpu_slow(void);

static int __inline
apm_get_event(struct apmregs *r)
{
	int rv;

	bzero(r, sizeof(*r));
	rv = apmcall(APM_GET_PM_EVENT, 0, r);
	return rv;
}

const char *
apm_err_translate(int code)
{
	switch (code) {
	case APM_ERR_PM_DISABLED:
		return "power management disabled";
	case APM_ERR_REALALREADY:
		return "real mode interface already connected";
	case APM_ERR_NOTCONN:
		return "interface not connected";
	case APM_ERR_16ALREADY:
		return "16-bit interface already connected";
	case APM_ERR_16NOTSUPP:
		return "16-bit interface not supported";
	case APM_ERR_32ALREADY:
		return "32-bit interface already connected";
	case APM_ERR_32NOTSUPP:
		return "32-bit interface not supported";
	case APM_ERR_UNRECOG_DEV:
		return "unrecognized device ID";
	case APM_ERR_ERANGE:
		return "parameter out of range";
	case APM_ERR_NOTENGAGED:
		return "interface not engaged";
	case APM_ERR_UNABLE:
		return "unable to enter requested state";
	case APM_ERR_NOEVENTS:
		return "No pending events";
	case APM_ERR_NOT_PRESENT:
		return "No APM present";
	default:
		return "unknown error code?";
	}
}

int apmerrors = 0;

void
apm_perror(const char *str, struct apmregs *regs)
{
	printf("apm0: APM %s: %s (%d)\n", str,
	    apm_err_translate(APM_ERR_CODE(regs)),
	    APM_ERR_CODE(regs));
	delay(1000000);

	apmerrors++;
}

void
apm_suspend(int state)
{
	extern int perflevel;
	int s;

#if NWSDISPLAY > 0
	wsdisplay_suspend();
#endif /* NWSDISPLAY > 0 */
	config_suspend_all(DVACT_QUIESCE);
	bufq_quiesce();

	s = splhigh();
	disable_intr();
	config_suspend_all(DVACT_SUSPEND);
	suspend_randomness();

	/* XXX
	 * Flag to disk drivers that they should "power down" the disk
	 * when we get to DVACT_POWERDOWN.
	 */
	boothowto |= RB_POWERDOWN;
	config_suspend_all(DVACT_POWERDOWN);
	boothowto &= ~RB_POWERDOWN;

	/* Send machine to sleep */
	apm_set_powstate(APM_DEV_ALLDEVS, state);
	/* Wake up  */

	/* They say that some machines may require reinitializing the clocks */
	i8254_startclock();
	if (initclock_func == i8254_initclocks)
		rtcstart();		/* in i8254 mode, rtc is profclock */
	inittodr(time_second);

	config_suspend_all(DVACT_RESUME);
	enable_intr();
	splx(s);

	resume_randomness(NULL, 0);	/* force RNG upper level reseed */
	bufq_restart();

	config_suspend_all(DVACT_WAKEUP);

#if NWSDISPLAY > 0
	wsdisplay_resume();
#endif /* NWSDISPLAY > 0 */

	/* restore hw.setperf */
	if (cpu_setperf != NULL)
		cpu_setperf(perflevel);
}

void
apm_resume(struct apm_softc *sc, struct apmregs *regs)
{

	apm_resumes = APM_RESUME_HOLDOFF;

	/* lower bit in cx means pccard was powered down */

	apm_record_event(sc, regs->bx);
}

int
apm_record_event(struct apm_softc *sc, u_int type)
{
	if (!apm_error && (sc->sc_flags & SCFLAG_OPEN) == 0) {
		DPRINTF(("apm_record_event: no user waiting\n"));
		apm_error++;
		return 1;
	}

	apm_evindex++;
	KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex));
	return (0);
}

int
apm_handle_event(struct apm_softc *sc, struct apmregs *regs)
{
	struct apmregs nregs;
	int ret = 0;

	switch (regs->bx) {
	case APM_NOEVENT:
		ret++;
		break;

	case APM_USER_STANDBY_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("user wants STANDBY--fat chance\n"));
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("standby ourselves\n"));
			apm_userstandbys++;
		}
		break;
	case APM_STANDBY_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("standby requested\n"));
		if (apm_standbys || apm_suspends) {
			DPRINTF(("premature standby\n"));
			apm_error++;
			ret++;
		}
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("standby ourselves\n"));
			apm_standbys++;
		}
		break;
	case APM_USER_SUSPEND_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("user wants suspend--fat chance!\n"));
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_SUSPEND_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("suspend requested\n"));
		if (apm_standbys || apm_suspends) {
			DPRINTF(("premature suspend\n"));
			apm_error++;
			ret++;
		}
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_POWER_CHANGE:
		DPRINTF(("power status change\n"));
		apm_get_powstat(&nregs);
		apm_record_event(sc, regs->bx);
		break;
	case APM_NORMAL_RESUME:
		DPRINTF(("system resumed\n"));
		apm_resume(sc, regs);
		break;
	case APM_CRIT_RESUME:
		DPRINTF(("system resumed without us!\n"));
		apm_resume(sc, regs);
		break;
	case APM_SYS_STANDBY_RESUME:
		DPRINTF(("system standby resume\n"));
		apm_resume(sc, regs);
		break;
	case APM_UPDATE_TIME:
		DPRINTF(("update time, please\n"));
		inittodr(time_second);
		apm_record_event(sc, regs->bx);
		break;
	case APM_CRIT_SUSPEND_REQ:
		DPRINTF(("suspend required immediately\n"));
		apm_record_event(sc, regs->bx);
		apm_suspend(APM_SYS_SUSPEND);
		break;
	case APM_BATTERY_LOW:
		DPRINTF(("Battery low!\n"));
		apm_battlow++;
		apm_record_event(sc, regs->bx);
		break;
	case APM_CAPABILITY_CHANGE:
		DPRINTF(("capability change\n"));
		if (apm_minver < 2) {
			DPRINTF(("adult event\n"));
		} else {
			if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS,
			    &nregs) != 0) {
				apm_perror("get capabilities", &nregs);
			} else {
				apm_get_powstat(&nregs);
			}
		}
		break;
	default: {
#ifdef APMDEBUG
		char *p;
		switch (regs->bx >> 8) {
		case 0:	p = "reserved system";	break;
		case 1:	p = "reserved device";	break;
		case 2:	p = "OEM defined";	break;
		default:p = "reserved";		break;
		}
#endif
		DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx));
	    }
	}
	return ret;
}

int
apm_periodic_check(struct apm_softc *sc)
{
	struct apmregs regs;
	int ret = 0;

	if (apm_op_inprog)
		apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);

	while (1) {
		if (apm_get_event(&regs) != 0) {
			/* i think some bioses combine the error codes */
			if (!(APM_ERR_CODE(&regs) & APM_ERR_NOEVENTS))
				apm_perror("get event", &regs);
			break;
		}

		/* If the APM BIOS tells us to suspend, don't do it twice */
		if (regs.bx == APM_SUSPEND_REQ)
			apm_lidclose = 0;
		if (apm_handle_event(sc, &regs))
			break;
	}

	if (apm_error || APM_ERR_CODE(&regs) == APM_ERR_NOTCONN)
		ret = -1;

	if (apm_lidclose) {
		apm_lidclose = 0;
		/* Fake a suspend request */
		regs.bx = APM_SUSPEND_REQ;
		apm_handle_event(sc, &regs);
	}
	if (apm_suspends /*|| (apm_battlow && apm_userstandbys)*/) {
		apm_op_inprog = 0;
		apm_suspend(APM_SYS_SUSPEND);
	} else if (apm_standbys || apm_userstandbys) {
		apm_op_inprog = 0;
		apm_suspend(APM_SYS_STANDBY);
	}
	apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
	apm_error = 0;

	if (apm_resumes)
		apm_resumes--;
	return (ret);
}

void
apm_powmgt_enable(int onoff)
{
	struct apmregs regs;

	bzero(&regs, sizeof(regs));
	regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
	if (apmcall(APM_PWR_MGT_ENABLE,
	    (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), &regs) != 0)
		apm_perror("power management enable", &regs);
}

void
apm_powmgt_engage(int onoff, u_int dev)
{
	struct apmregs regs;

	if (apm_minver == 0)
		return;
	bzero(&regs, sizeof(regs));
	regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE;
	if (apmcall(APM_PWR_MGT_ENGAGE, dev, &regs) != 0)
		printf("apm0: APM engage (device %x): %s (%d)\n",
		    dev, apm_err_translate(APM_ERR_CODE(&regs)),
		    APM_ERR_CODE(&regs));
}

#ifdef notused
void
apm_devpowmgt_enable(int onoff, u_int dev)
{
	struct apmregs regs;

	if (apm_minver == 0)
		return;
	/* enable is auto BIOS management.
	 * disable is program control.
	 */
	bzero(&regs, sizeof(regs));
	regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
	if (apmcall(APM_DEVICE_MGMT_ENABLE, dev, &regs) != 0)
		printf("APM device engage (device %x): %s (%d)\n",
		    dev, apm_err_translate(APM_ERR_CODE(&regs)),
		    APM_ERR_CODE(&regs));
}
#endif

int
apm_set_powstate(u_int dev, u_int state)
{
	struct apmregs regs;

	if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF))
		return EINVAL;
	bzero(&regs, sizeof(regs));
	regs.cx = state;
	if (apmcall(APM_SET_PWR_STATE, dev, &regs) != 0) {
		apm_perror("set power state", &regs);
		if (APM_ERR_CODE(&regs) == APM_ERR_UNRECOG_DEV)
			return ENXIO;
		else
			return EIO;
	}
	return 0;
}

void
apm_cpu_slow(void)
{
	struct apmregs regs;
	static u_int64_t call_apm_slow = 0;

	if  (call_apm_slow != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
		/* Always call BIOS halt/idle stuff */
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_IDLE, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU slow", &regs);
#endif
		}
		apm_slow_called = 1;
		call_apm_slow = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
	}
}

void
apm_cpu_busy(void)
{
	struct apmregs regs;

	if (!apm_slow_called)
		return;

	if (apm_flags & APM_IDLE_SLOWS) {
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_BUSY, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU busy", &regs);
#endif
		}
		apm_slow_called = 0;
	}
}

void
apm_cpu_idle(void)
{
	struct apmregs regs;
	static u_int64_t call_apm_idle = 0;

	/*
	 * We call the bios APM_IDLE routine here only when we
	 * have been idle for some time - otherwise we just hlt.
	 */

	if  (call_apm_idle != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
		/* Always call BIOS halt/idle stuff */
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_IDLE, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU idle", &regs);
#endif
		}

		/* If BIOS did not halt, halt now! */
		if (apm_flags & APM_IDLE_SLOWS) {
			__asm volatile("sti;hlt");
		}
		call_apm_idle = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
	} else {
		__asm volatile("sti;hlt");
	}
}

void
apm_set_ver(struct apm_softc *self)
{
	struct apmregs regs;
	int rv = 0;

	bzero(&regs, sizeof(regs));
	regs.cx = APM_VERSION;

	if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 &&
	    (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs)) == 0) {
		apm_majver = APM_CONN_MAJOR(&regs);
		apm_minver = APM_CONN_MINOR(&regs);
	} else {
#ifdef APMDEBUG
		if (rv)
			apm_perror("set version 1.2", &regs);
#endif
		/* try downgrading to 1.1 */
		bzero(&regs, sizeof(regs));
		regs.cx = 0x0101;

		if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs) == 0) {
			apm_majver = 1;
			apm_minver = 1;
		} else {
#ifdef APMDEBUG
			apm_perror("set version 1.1", &regs);
#endif
			/* stay w/ flags then */
			apm_majver = APM_MAJOR(apm_flags);
			apm_minver = APM_MINOR(apm_flags);

			/* fix version for some endianess-challenged compaqs */
			if (!apm_majver) {
				apm_majver = 1;
				apm_minver = 0;
			}
		}
	}
	printf(": Power Management spec V%d.%d", apm_majver, apm_minver);
#ifdef DIAGNOSTIC
	if (apm_flags & APM_IDLE_SLOWS)
		printf(" (slowidle)");
	if (apm_flags & APM_BIOS_PM_DISABLED)
		printf(" (BIOS management disabled)");
	if (apm_flags & APM_BIOS_PM_DISENGAGED)
		printf(" (BIOS managing devices)");
#endif
	printf("\n");
}

void
apm_disconnect(struct apm_softc *sc)
{
	struct apmregs regs;

	bzero(&regs, sizeof(regs));
	if (apmcall(APM_SYSTEM_DEFAULTS,
	    (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), &regs))
		apm_perror("system defaults failed", &regs);

	if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, &regs))
		apm_perror("disconnect failed", &regs);
	else
		printf("%s: disconnected\n", sc->sc_dev.dv_xname);
	apm_flags |= APM_BIOS_PM_DISABLED;
}

int
apmprobe(struct device *parent, void *match, void *aux)
{
	struct bios_attach_args *ba = aux;
	bios_apminfo_t *ap = ba->ba_apmp;
	bus_space_handle_t ch, dh;

	if (apm_cd.cd_ndevs || strcmp(ba->ba_name, "apm") ||
	    !(ap->apm_detail & APM_32BIT_SUPPORTED))
		return 0;

	/* addresses check
	   since pc* console and vga* probes much later
	   we cannot check for video memory being mapped
	   for apm stuff w/ bus_space_map() */
	if (ap->apm_code_len == 0 ||
	    (ap->apm_code32_base < IOM_BEGIN &&
	     ap->apm_code32_base + ap->apm_code_len > IOM_BEGIN) ||
	    (ap->apm_code16_base < IOM_BEGIN &&
	     ap->apm_code16_base + ap->apm_code16_len > IOM_BEGIN) ||
	    (ap->apm_data_base < IOM_BEGIN &&
	     ap->apm_data_base + ap->apm_data_len > IOM_BEGIN))
		return 0;

	if (bus_space_map(ba->ba_memt, ap->apm_code32_base,
	    ap->apm_code_len, 1, &ch) != 0) {
		DPRINTF(("apm0: can't map code\n"));
		return 0;
	}
	bus_space_unmap(ba->ba_memt, ch, ap->apm_code_len);

	if (bus_space_map(ba->ba_memt, ap->apm_data_base,
	    ap->apm_data_len, 1, &dh) != 0) {
		DPRINTF(("apm0: can't map data\n"));
		return 0;
	}
	bus_space_unmap(ba->ba_memt, dh, ap->apm_data_len);
	return 1;
}

void
apmattach(struct device *parent, struct device *self, void *aux)
{
	struct bios_attach_args *ba = aux;
	bios_apminfo_t *ap = ba->ba_apmp;
	struct apm_softc *sc = (void *)self;
	struct apmregs regs;
	u_int cbase, clen, l;
	bus_space_handle_t ch16, ch32, dh;

	apm_flags = ap->apm_detail;
	/*
	 * set up GDT descriptors for APM
	 */
	if (apm_flags & APM_32BIT_SUPPORTED) {

		/* truncate segments' limits to a page */
		ap->apm_code_len -= (ap->apm_code32_base +
		    ap->apm_code_len + 1) & 0xfff;
		ap->apm_code16_len -= (ap->apm_code16_base +
		    ap->apm_code16_len + 1) & 0xfff;
		ap->apm_data_len -= (ap->apm_data_base +
		    ap->apm_data_len + 1) & 0xfff;

		/* adjust version */
		if ((sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK) &&
		    (apm_flags & APM_VERMASK) !=
		    (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK))
			apm_flags = (apm_flags & ~APM_VERMASK) |
			    (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK);
		if (sc->sc_dev.dv_cfdata->cf_flags & APM_NOCLI) {
			extern int apm_cli; /* from apmcall.S */
			apm_cli = 0;
		}
		if (sc->sc_dev.dv_cfdata->cf_flags & APM_BEBATT)
			sc->be_batt = 1;
		apm_ep.seg = GSEL(GAPM32CODE_SEL,SEL_KPL);
		apm_ep.entry = ap->apm_entry;
		cbase = min(ap->apm_code32_base, ap->apm_code16_base);
		clen = max(ap->apm_code32_base + ap->apm_code_len,
			   ap->apm_code16_base + ap->apm_code16_len) - cbase;
		if ((cbase <= ap->apm_data_base &&
		     cbase + clen >= ap->apm_data_base) ||
		    (ap->apm_data_base <= cbase &&
		     ap->apm_data_base + ap->apm_data_len >= cbase)) {
			l = max(ap->apm_data_base + ap->apm_data_len + 1,
				cbase + clen + 1) -
			    min(ap->apm_data_base, cbase);
			bus_space_map(ba->ba_memt,
				min(ap->apm_data_base, cbase),
				l, 1, &dh);
			ch16 = dh;
			if (ap->apm_data_base < cbase)
				ch16 += cbase - ap->apm_data_base;
			else
				dh += ap->apm_data_base - cbase;
		} else {

			bus_space_map(ba->ba_memt, cbase, clen + 1, 1, &ch16);
			bus_space_map(ba->ba_memt, ap->apm_data_base,
			    ap->apm_data_len + 1, 1, &dh);
		}
		ch32 = ch16;
		if (ap->apm_code16_base == cbase)
			ch32 += ap->apm_code32_base - cbase;
		else
			ch16 += ap->apm_code16_base - cbase;

		setgdt(GAPM32CODE_SEL, (void *)ch32, ap->apm_code_len,
		    SDT_MEMERA, SEL_KPL, 1, 0);
		setgdt(GAPM16CODE_SEL, (void *)ch16, ap->apm_code16_len,
		    SDT_MEMERA, SEL_KPL, 0, 0);
		setgdt(GAPMDATA_SEL, (void *)dh, ap->apm_data_len, SDT_MEMRWA,
		    SEL_KPL, 1, 0);
		DPRINTF((": flags %x code 32:%x/%lx[%x] 16:%x/%lx[%x] "
		    "data %x/%lx/%x ep %x (%x:%lx)\n%s", apm_flags,
		    ap->apm_code32_base, ch32, ap->apm_code_len,
		    ap->apm_code16_base, ch16, ap->apm_code16_len,
		    ap->apm_data_base, dh, ap->apm_data_len,
		    ap->apm_entry, apm_ep.seg, ap->apm_entry+ch32,
		    sc->sc_dev.dv_xname));

		apm_set_ver(sc);

		if (apm_flags & APM_BIOS_PM_DISABLED)
			apm_powmgt_enable(1);

		/* Engage cooperative power management on all devices (v1.1) */
		apm_powmgt_engage(1, APM_DEV_ALLDEVS);

		bzero(&regs, sizeof(regs));
		if (apm_get_powstat(&regs) != 0)
			apm_perror("get power status", &regs);
		apm_cpu_busy();

		rw_init(&sc->sc_lock, "apmlk");

		/*
		 * Do a check once, ignoring any errors. This avoids
		 * gratuitous APM disconnects on laptops where the first
		 * event in the queue (after a boot) is non-recognizable.
		 * The IBM ThinkPad 770Z is one of those.
		 */
		apm_periodic_check(sc);

		if (apm_periodic_check(sc) == -1) {
			apm_disconnect(sc);

			/* Failed, nuke APM idle loop */
			cpu_idle_enter_fcn = NULL;
			cpu_idle_cycle_fcn = NULL;
			cpu_idle_leave_fcn = NULL;
		} else {
			kthread_create_deferred(apm_thread_create, sc);

			/* Setup APM idle loop */
			if (apm_flags & APM_IDLE_SLOWS) {
				cpu_idle_enter_fcn = apm_cpu_slow;
				cpu_idle_cycle_fcn = NULL;
				cpu_idle_leave_fcn = apm_cpu_busy;
			} else {
				cpu_idle_enter_fcn = NULL;
				cpu_idle_cycle_fcn = apm_cpu_idle;
				cpu_idle_leave_fcn = NULL;
			}

			/* All is well, let the rest of the world know */
			acpiapm_open = apmopen;
			acpiapm_close = apmclose;
			acpiapm_ioctl = apmioctl;
			acpiapm_kqfilter = apmkqfilter;
			apm_attached = 1;
		}
	} else {
		setgdt(GAPM32CODE_SEL, NULL, 0, 0, 0, 0, 0);
		setgdt(GAPM16CODE_SEL, NULL, 0, 0, 0, 0, 0);
		setgdt(GAPMDATA_SEL, NULL, 0, 0, 0, 0, 0);
	}
}

void
apm_thread_create(void *v)
{
	struct apm_softc *sc = v;

#ifdef MULTIPROCESSOR
	if (ncpus > 1) {
		apm_disconnect(sc);

		/* Nuke APM idle loop */
		cpu_idle_enter_fcn = NULL;
		cpu_idle_cycle_fcn = NULL;
		cpu_idle_leave_fcn = NULL;

		return;
	}
#endif

	if (kthread_create(apm_thread, sc, &sc->sc_thread,
	    sc->sc_dev.dv_xname)) {
		apm_disconnect(sc);
		printf("%s: failed to create kernel thread, disabled",
		    sc->sc_dev.dv_xname);

		/* Nuke APM idle loop */
		cpu_idle_enter_fcn = NULL;
		cpu_idle_cycle_fcn = NULL;
		cpu_idle_leave_fcn = NULL;
	}
}

void
apm_thread(void *v)
{
	struct apm_softc *sc = v;

	for (;;) {
		rw_enter_write(&sc->sc_lock);
		(void) apm_periodic_check(sc);
		rw_exit_write(&sc->sc_lock);
		tsleep(&lbolt, PWAIT, "apmev", 0);
	}
}

int
apmopen(dev_t dev, int flag, int mode, struct proc *p)
{
	struct apm_softc *sc;
	int error = 0;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return ENXIO;

	if (apm_flags & APM_BIOS_PM_DISABLED)
		return ENXIO;

	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
	    APMDEV(dev), p->p_pid, flag, mode));

	rw_enter_write(&sc->sc_lock);
	switch (APMDEV(dev)) {
	case APMDEV_CTL:
		if (!(flag & FWRITE)) {
			error = EINVAL;
			break;
		}
		if (sc->sc_flags & SCFLAG_OWRITE) {
			error = EBUSY;
			break;
		}
		sc->sc_flags |= SCFLAG_OWRITE;
		break;
	case APMDEV_NORMAL:
		if (!(flag & FREAD) || (flag & FWRITE)) {
			error = EINVAL;
			break;
		}
		sc->sc_flags |= SCFLAG_OREAD;
		break;
	default:
		error = ENXIO;
		break;
	}
	rw_exit_write(&sc->sc_lock);
	return error;
}

int
apmclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct apm_softc *sc;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return ENXIO;

	DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));

	rw_enter_write(&sc->sc_lock);
	switch (APMDEV(dev)) {
	case APMDEV_CTL:
		sc->sc_flags &= ~SCFLAG_OWRITE;
		break;
	case APMDEV_NORMAL:
		sc->sc_flags &= ~SCFLAG_OREAD;
		break;
	}
	rw_exit_write(&sc->sc_lock);
	return 0;
}

int
apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct apm_softc *sc;
	struct apmregs regs;
	int error = 0;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return ENXIO;

	rw_enter_write(&sc->sc_lock);
	switch (cmd) {
		/* some ioctl names from linux */
	case APM_IOC_STANDBY:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_userstandbys++;
		break;
	case APM_IOC_SUSPEND:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_suspends++;
		break;
	case APM_IOC_PRN_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			int flag = *(int *)data;
			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
			switch (flag) {
			case APM_PRINT_ON:	/* enable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				break;
			case APM_PRINT_OFF: /* disable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_NOPRINT;
				break;
			case APM_PRINT_PCT: /* disable some printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_PCTPRINT;
				break;
			default:
				error = EINVAL;
				break;
			}
		}
		break;
	case APM_IOC_DEV_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			struct apm_ctl *actl = (struct apm_ctl *)data;

			bzero(&regs, sizeof(regs));
			if (!apmcall(APM_GET_POWER_STATE, actl->dev, &regs))
				printf("%s: dev %04x state %04x\n",
				    sc->sc_dev.dv_xname, dev, regs.cx);

			error = apm_set_powstate(actl->dev, actl->mode);
		}
		break;
	case APM_IOC_GETPOWER:
		if (apm_get_powstat(&regs) == 0) {
			struct apm_power_info *powerp =
			    (struct apm_power_info *)data;

			bzero(powerp, sizeof(*powerp));
			if (BATT_LIFE(&regs) != APM_BATT_LIFE_UNKNOWN)
				powerp->battery_life = BATT_LIFE(&regs);
			powerp->ac_state = AC_STATE(&regs);
			switch (apm_minver) {
			case 0:
				if (!(BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY))
					powerp->battery_state = BATT_STATE(&regs);
				break;
			case 1:
			default:
				if (BATT_FLAGS(&regs) & APM_BATT_FLAG_HIGH)
					powerp->battery_state = APM_BATT_HIGH;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_LOW)
					powerp->battery_state = APM_BATT_LOW;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CRITICAL)
					powerp->battery_state = APM_BATT_CRITICAL;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CHARGING)
					powerp->battery_state = APM_BATT_CHARGING;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY)
					powerp->battery_state = APM_BATTERY_ABSENT;
				else
					powerp->battery_state = APM_BATT_UNKNOWN;
				if (BATT_REM_VALID(&regs)) {
					powerp->minutes_left = BATT_REMAINING(&regs);
					if (sc->be_batt)
						powerp->minutes_left =
						    swap16(powerp->minutes_left);
				}
			}
		} else {
			apm_perror("ioctl get power status", &regs);
			error = EIO;
		}
		break;
	case APM_IOC_STANDBY_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		/* only fails if no one cares. apmd at least should */
		else if (apm_record_event(sc, APM_USER_STANDBY_REQ))
			error = EINVAL; /* ? */
		break;
	case APM_IOC_SUSPEND_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		/* only fails if no one cares. apmd at least should */
		else if (apm_record_event(sc, APM_USER_SUSPEND_REQ))
			error = EINVAL; /* ? */
		break;
	default:
		error = ENOTTY;
	}

	rw_exit_write(&sc->sc_lock);
	return error;
}

void
filt_apmrdetach(struct knote *kn)
{
	struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;

	rw_enter_write(&sc->sc_lock);
	SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
	rw_exit_write(&sc->sc_lock);
}

int
filt_apmread(struct knote *kn, long hint)
{
	/* XXX weird kqueue_scan() semantics */
	if (hint && !kn->kn_data)
		kn->kn_data = (int)hint;
	return (1);
}

int
apmkqfilter(dev_t dev, struct knote *kn)
{
	struct apm_softc *sc;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return ENXIO;

	switch (kn->kn_filter) {
	case EVFILT_READ:
		kn->kn_fop = &apmread_filtops;
		break;
	default:
		return (EINVAL);
	}

	kn->kn_hook = (caddr_t)sc;

	rw_enter_write(&sc->sc_lock);
	SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
	rw_exit_write(&sc->sc_lock);
	return (0);
}


code:
/*	$OpenBSD: apmcall.S,v 1.6 2013/11/28 19:30:46 brad Exp $	*/

/*
 * Copyright (c) 2000,2001 Michael Shalayeff
 * 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 OR HIS RELATIVES 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 MIND, 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>

#ifndef	APM_DISABLE_INTERRUPTS
#define	APM_DISABLE_INTERRUPTS	1
#endif /* APM_DISABLE_INTERRUPTS */

	.data
	.globl	_C_LABEL(apm_ep)
	.globl	_C_LABEL(apm_cli)
_C_LABEL(apm_cli):
	.long	APM_DISABLE_INTERRUPTS
/*
 * int apmcall(u_int f, int dev, struct apmregs *r)
 *
 */
	.text
ENTRY(apmcall)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ebx
	pushl	%esi
	pushl	%edi

	movl	16(%ebp), %ebx
	movl	8(%ebx), %ecx
	movl	12(%ebx), %edx
	movl	12(%ebp), %ebx

	pushfl

	cmpl	$0, _C_LABEL(apm_cli)
	je	1f
	cli
1:
#if defined(DEBUG) || defined(DIAGNOSTIC)
	pushl	%ds
	pushl	%es
	pushl	%fs
	pushl	%gs
	xorl	%eax, %eax
	movl	%eax, %ds
	movl	%eax, %es
	movl	%eax, %fs
	movl	%eax, %gs
#endif
	movl	8(%ebp), %eax

	clc
	lcall	*%cs:(_C_LABEL(apm_ep))
	pushl	%eax
	setc	%al
	movzbl	%al, %esi
	popl	%eax

#if defined(DEBUG) || defined(DIAGNOSTIC)
	popl	%gs
	popl	%fs
	popl	%es
	popl	%ds
#endif
	popfl

	movl	16(%ebp), %edi
	movl	%eax, 0(%edi)
	movl	%ebx, 4(%edi)
	movl	%ecx, 8(%edi)
	movl	%edx, 12(%edi)

	testl	$0xff00, %eax
	jnz	1f
	xorl	%esi, %esi
1:
	movl	%esi, %eax

	popl	%edi
	popl	%esi
	popl	%ebx
	popl	%ebp
	ret

	.end


code:
/*	$OpenBSD: autoconf.c,v 1.94 2015/04/24 19:53:43 mlarkin Exp $	*/
/*	$NetBSD: autoconf.c,v 1.20 1996/05/03 19:41:56 christos Exp $	*/

/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * 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.
 *
 *	@(#)autoconf.c	7.1 (Berkeley) 5/9/91
 */

/*
 * Setup the system to run on the current machine.
 *
 * cpu_configure() is called at boot time and initializes the vba
 * device tables and the memory controller monitoring.  Available
 * devices are determined (from possibilities mentioned in ioconf.c),
 * and the drivers are initialized.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/conf.h>
#include <sys/reboot.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/hibernate.h>

#include <net/if.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <uvm/uvm_extern.h>

#include <machine/pte.h>
#include <machine/cpu.h>
#include <machine/gdt.h>
#include <machine/biosvar.h>
#include <machine/kvm86.h>

#include <dev/cons.h>

#include "ioapic.h"

#include "acpi.h"

#if NIOAPIC > 0
#include <machine/i82093var.h>
#endif

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

#ifdef MULTIPROCESSOR
#include <machine/mpbiosvar.h>
#endif

/*
 * The following several variables are related to
 * the configuration process, and are used in initializing
 * the machine.
 */
extern dev_t bootdev;

/* Support for VIA C3 RNG */
extern struct timeout viac3_rnd_tmo;
extern int	viac3_rnd_present;
void		viac3_rnd(void *);

extern struct timeout rdrand_tmo;
extern int	has_rdrand;
void		rdrand(void *);

#ifdef CRYPTO
void		viac3_crypto_setup(void);
extern int	i386_has_xcrypt;
#endif

/*
 * Determine i/o configuration for a machine.
 */
void
cpu_configure(void)
{
	/*
	 * Note, on i386, configure is not running under splhigh unlike other
	 * architectures.  This fact is used by the pcmcia irq line probing.
	 */

	gdt_init();		/* XXX - pcibios uses gdt stuff */

	/* Set up proc0's TSS and LDT */
	i386_proc0_tss_ldt_init();

#ifdef KVM86
	kvm86_init();
#endif
	pmap_bootstrap_pae();

#if defined(MULTIPROCESSOR) || \
    (NACPI > 0 && !defined(SMALL_KERNEL))
	/* install the lowmem ptp after boot args for 1:1 mappings */
	pmap_prealloc_lowmem_ptp();
#endif

#ifdef MULTIPROCESSOR
	pmap_kenter_pa((vaddr_t)MP_TRAMPOLINE,          /* virtual */
	    (paddr_t)MP_TRAMPOLINE,                     /* physical */
	    PROT_READ | PROT_WRITE | PROT_EXEC);        /* protection */
#endif

#if NACPI > 0 && !defined(SMALL_KERNEL)
	pmap_kenter_pa((vaddr_t)ACPI_TRAMPOLINE,        /* virtual */
	    (paddr_t)ACPI_TRAMPOLINE,                   /* physical */
	    PROT_READ | PROT_WRITE | PROT_EXEC);        /* protection */
#endif

	if (config_rootfound("mainbus", NULL) == NULL)
		panic("cpu_configure: mainbus not configured");

#if NIOAPIC > 0
	ioapic_enable();
#endif

	proc0.p_addr->u_pcb.pcb_cr0 = rcr0();

#ifdef MULTIPROCESSOR
	/* propagate TSS and LDT configuration to the idle pcb's. */
	cpu_init_idle_pcbs();
#endif
	spl0();

	/*
	 * We can not know which is our root disk, defer
	 * until we can checksum blocks to figure it out.
	 */
	cold = 0;

	/*
	 * At this point the RNG is running, and if FSXR is set we can
	 * use it.  Here we setup a periodic timeout to collect the data.
	 */
	if (viac3_rnd_present) {
		timeout_set(&viac3_rnd_tmo, viac3_rnd, &viac3_rnd_tmo);
		viac3_rnd(&viac3_rnd_tmo);
	}
	if (has_rdrand) {
		timeout_set(&rdrand_tmo, rdrand, &rdrand_tmo);
		rdrand(&rdrand_tmo);
	}

#ifdef CRYPTO
	/*
	 * Also, if the chip has crypto available, enable it.
	 */
	if (i386_has_xcrypt)
		viac3_crypto_setup();
#endif
}

void
device_register(struct device *dev, void *aux)
{
}

/*
 * Now that we are fully operational, we can checksum the
 * disks, and using some heuristics, hopefully are able to
 * always determine the correct root disk.
 */
void
diskconf(void)
{
	int majdev, unit, part = 0;
	struct device *bootdv = NULL;
	dev_t tmpdev;
	char buf[128];
	extern bios_bootmac_t *bios_bootmac;

	dkcsumattach();

	if ((bootdev & B_MAGICMASK) == (u_int)B_DEVMAGIC) {
		majdev = B_TYPE(bootdev);
		unit = B_UNIT(bootdev);
		part = B_PARTITION(bootdev);
		snprintf(buf, sizeof buf, "%s%d%c", findblkname(majdev),
		    unit, part + 'a');
		bootdv = parsedisk(buf, strlen(buf), part, &tmpdev);
	}

	if (bios_bootmac) {
		struct ifnet *ifp;

		for (ifp = TAILQ_FIRST(&ifnet); ifp != NULL;
		    ifp = TAILQ_NEXT(ifp, if_list)) {
			if (ifp->if_type == IFT_ETHER &&
			    bcmp(bios_bootmac->mac,
			    ((struct arpcom *)ifp)->ac_enaddr,
			    ETHER_ADDR_LEN) == 0)
				break;
		}
		if (ifp) {
#if defined(NFSCLIENT)
			printf("PXE boot MAC address %s, interface %s\n",
			    ether_sprintf(bios_bootmac->mac), ifp->if_xname);
			bootdv = parsedisk(ifp->if_xname, strlen(ifp->if_xname),
			    0, &tmpdev);
			part = 0;
#endif
		} else
			printf("PXE boot MAC address %s, interface %s\n",
			    ether_sprintf(bios_bootmac->mac), "unknown");
	}

	setroot(bootdv, part, RB_USERREQ);
	dumpconf();

#ifdef HIBERNATE
	hibernate_resume();
#endif /* HIBERNATE */
}

struct nam2blk nam2blk[] = {
	{ "wd",		0 },
	{ "fd",		2 },
	{ "sd",		4 },
	{ "cd",		6 },
	{ "rd",		17 },
	{ "raid",	19 },
	{ "vnd",	14 },
	{ NULL,		-1 }
};


code:
/*	$OpenBSD: bios.c,v 1.111 2015/07/17 22:31:16 mlarkin Exp $	*/

/*
 * Copyright (c) 1997-2001 Michael Shalayeff
 * 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 OR HIS RELATIVES 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 MIND, 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.
 */

/* #define BIOS_DEBUG */

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

#include <uvm/uvm_extern.h>
#include <sys/sysctl.h>

#include <dev/cons.h>
#include <stand/boot/bootarg.h>

#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <machine/conf.h>
#include <machine/gdt.h>
#include <machine/pcb.h>
#include <machine/biosvar.h>
#include <machine/apmvar.h>
#include <machine/mpbiosvar.h>
#include <machine/smbiosvar.h>

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

#include <dev/pci/pcivar.h>

#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>

#include <dev/rndvar.h>

#include "apm.h"
#include "acpi.h"
#include "mpbios.h"
#include "pcibios.h"
#include "pci.h"

#include "com.h"
#if NCOM > 0
#include <sys/tty.h>
#include <dev/ic/comvar.h>
#include <dev/ic/comreg.h>
#endif

#include "softraid.h"
#if NSOFTRAID > 0
#include <dev/softraidvar.h>
#endif

struct bios_softc {
	struct	device sc_dev;
	vaddr_t bios32_service_va;
};

int biosprobe(struct device *, void *, void *);
void biosattach(struct device *, struct device *, void *);
int bios_print(void *, const char *);
char *fixstring(char *);

struct cfattach bios_ca = {
	sizeof(struct bios_softc), biosprobe, biosattach, NULL,
	config_activate_children
};

struct cfdriver bios_cd = {
	NULL, "bios", DV_DULL
};

extern dev_t bootdev;

#if NAPM > 0 || defined(DEBUG)
bios_apminfo_t *apm;
#endif
#if NPCI > 0
bios_pciinfo_t *bios_pciinfo;
#endif
bios_diskinfo_t *bios_diskinfo;
bios_memmap_t  *bios_memmap;
struct bios_softc *bios_softc;
u_int32_t	bios_cksumlen;
struct bios32_entry bios32_entry;
struct smbios_entry smbios_entry;
#ifdef MULTIPROCESSOR
void		*bios_smpinfo;
#endif
bios_bootmac_t	*bios_bootmac;
#ifdef DDB
extern int	db_console;
#endif

void		smbios_info(char*);

bios_diskinfo_t *bios_getdiskinfo(dev_t);

/*
 * used by hw_sysctl
 */
extern char *hw_vendor, *hw_prod, *hw_uuid, *hw_serial, *hw_ver;
const char *smbios_uninfo[] = {
	"System",
	"Not ",
	"To be",
	"SYS-"
};

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

#ifdef BIOS_DEBUG
	printf("%s%d: boot API ver %x, %x; args %p[%d]\n",
	    bia->ba_name, bios_cd.cd_ndevs,
	    bootapiver, BOOTARG_APIVER, bootargp, bootargc);
#endif
	/* only one */
	if (bios_cd.cd_ndevs || strcmp(bia->ba_name, bios_cd.cd_name))
		return 0;

	if (!(bootapiver & BAPIV_VECTOR) || bootargp == NULL)
		return 0;

	return 1;
}

void
biosattach(struct device *parent, struct device *self, void *aux)
{
	struct bios_softc *sc = (struct bios_softc *)self;
#if (NPCI > 0 && NPCIBIOS > 0) || NAPM > 0
	struct bios_attach_args *bia = aux;
#endif
	struct smbios_struct_bios *sb;
	struct smbtable bios;
	volatile u_int8_t *va;
	char scratch[64], *str;
	int flags, smbiosrev = 0, ncpu = 0;
#if NACPI > 0
	int usingacpi = 0;
#endif

	bios_softc = sc;
	/* remember flags */
	flags = sc->sc_dev.dv_cfdata->cf_flags;

	va = ISA_HOLE_VADDR(0xffff0);
	printf(": date %c%c/%c%c/%c%c",
	    va[5], va[6], va[8], va[9], va[11], va[12]);

	/*
	 * Determining whether BIOS32 extensions are available is
	 * done by searching for the BIOS32 service directory.
	 * This 16-byte structure can be found somewhere in the
	 * range 0E0000h - 0FFFFFh and must be 16-byte aligned.
	 *
	 *  _______________________________________________________
	 * | Offset | Bytes | Description                          |
	 * |-------------------------------------------------------|
	 * |    0   |   4   | ASCII signature string of "_32_".    |
	 * |    4   |   4   | 32-bit entry point.                  |
	 * |    8   |   1   | Revision Level. Typically 00h.       |
	 * |    9   |   1   | Header length in 16-byte units. So   |
	 * |        |       | would have the value of 01h.         |
	 * |    A   |   1   | Checksum. The sum of all bytes in    |
	 * |        |       | this header must be zero.            |
	 * |    B   |   5   | Reserved. Set to zero.               |
	 *  -------------------------------------------------------
	 *
	 * To find the service directory, we first search for the
	 * signature. If we find a match, we must also verify the
	 * checksum. This service directory may then be used to
	 * determine whether a PCI BIOS is present.
	 *
	 * For more information see the PCI BIOS Specification,
	 * Revision 2.1 (August 26, 1994).
	 */

	if (!(flags & BIOSF_BIOS32)) {
		for (va = ISA_HOLE_VADDR(BIOS32_START);
		    va < (u_int8_t *)ISA_HOLE_VADDR(BIOS32_END); va += 16) {
			bios32_header_t h = (bios32_header_t)va;
			u_int8_t cksum;
			int i;

			if (h->signature != BIOS32_SIGNATURE)
				continue;

			/* verify checksum */
			for (cksum = 0, i = h->length * 16; i--; cksum += va[i])
				;
			if (cksum != 0)
				continue;

			if (h->entry <= BIOS32_START || h->entry >= BIOS32_END)
				continue;

			bios32_entry.segment = GSEL(GCODE_SEL, SEL_KPL);
			bios32_entry.offset = (u_int32_t)ISA_HOLE_VADDR(h->entry);
			printf(", BIOS32 rev. %d @ 0x%x", h->rev, h->entry);
			break;
		}
	}

	/* see if we have SMBIOS extensions */
	if (!(flags & BIOSF_SMBIOS)) {
		for (va = ISA_HOLE_VADDR(SMBIOS_START);
		    va < (u_int8_t *)ISA_HOLE_VADDR(SMBIOS_END); va+= 16) {
			struct smbhdr *sh = (struct smbhdr *)va;
			u_int8_t chksum;
			vaddr_t eva;
			paddr_t pa, end;
			int i;

			if (sh->sig != SMBIOS_SIGNATURE)
				continue;
			i = sh->len;
			for (chksum = 0; i--; chksum += va[i])
				;
			if (chksum != 0)
				continue;
			va += 0x10;
			if (va[0] != '_' && va[1] != 'D' && va[2] != 'M' &&
			    va[3] != 'I' && va[4] != '_')
				continue;
			for (chksum = 0, i = 0xf; i--; chksum += va[i])
				;
			if (chksum != 0)
				continue;

			pa = trunc_page(sh->addr);
			end = round_page(sh->addr + sh->size);
			eva = uvm_km_valloc(kernel_map, end-pa);
			if (eva == 0)
				break;

			smbios_entry.addr = (u_int8_t *)(eva +
			    (sh->addr & PGOFSET));
			smbios_entry.len = sh->size;
			smbios_entry.mjr = sh->majrev;
			smbios_entry.min = sh->minrev;
			smbios_entry.count = sh->count;

			for (; pa < end; pa+= NBPG, eva+= NBPG)
				pmap_kenter_pa(eva, pa, PROT_READ);

			printf(", SMBIOS rev. %d.%d @ 0x%x (%hd entries)",
			    sh->majrev, sh->minrev, sh->addr, sh->count);
			/*
			 * Unbelievably the SMBIOS version number
			 * sequence is like 2.3 ... 2.33 ... 2.4 ... 2.5
			 */
			smbiosrev = sh->majrev * 100 + sh->minrev;
			if (sh->minrev < 10)
				smbiosrev = sh->majrev * 100 + sh->minrev * 10;

			bios.cookie = 0;
			if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
				sb = bios.tblhdr;
				printf("\n%s:", sc->sc_dev.dv_xname);

				if ((smbios_get_string(&bios, sb->vendor,
				    scratch, sizeof(scratch))) != NULL)
					printf(" vendor %s",
					    fixstring(scratch));
				if ((smbios_get_string(&bios, sb->version,
				    scratch, sizeof(scratch))) != NULL)
					printf(" version \"%s\"",
					    fixstring(scratch));
				if ((smbios_get_string(&bios, sb->release,
				    scratch, sizeof(scratch))) != NULL)
					printf(" date %s", fixstring(scratch));
			}
			smbios_info(sc->sc_dev.dv_xname);

			/* count cpus so that we can disable apm when cpu > 1 */
			bzero(&bios, sizeof(bios));
			while (smbios_find_table(SMBIOS_TYPE_PROCESSOR,&bios)) {
				struct smbios_cpu *cpu = bios.tblhdr;

				if (cpu->cpu_status & SMBIOS_CPUST_POPULATED) {
					/* SMBIOS 2.5 added multicore support */
					if (smbiosrev >= 250 &&
					    cpu->cpu_core_enabled)
						ncpu += cpu->cpu_core_enabled;
					else {
						ncpu++;
						if (cpu->cpu_id_edx & CPUID_HTT)
							ncpu++;
					}
				}
			}
			break;
		}
	}

	printf("\n");

	/* No SMBIOS extensions, go looking for Soekris comBIOS */
	if (!(flags & BIOSF_SMBIOS) && smbiosrev == 0) {
		const char *signature = "Soekris Engineering";

		for (va = ISA_HOLE_VADDR(SMBIOS_START);
		    va <= (u_int8_t *)ISA_HOLE_VADDR(SMBIOS_END -
		    (strlen(signature) - 1)); va++)
			if (!memcmp((u_int8_t *)va, signature,
			    strlen(signature))) {
				hw_vendor = malloc(strlen(signature) + 1,
				    M_DEVBUF, M_NOWAIT);
				if (hw_vendor)
					strlcpy(hw_vendor, signature,
					    strlen(signature) + 1);
				va += strlen(signature);
				break;
			}

		for (; hw_vendor &&
		    va <= (u_int8_t *)ISA_HOLE_VADDR(SMBIOS_END - 6); va++)
			/*
			 * Search for "net(4(5xx|801)|[56]501)" which matches
			 * the strings found in the comBIOS images
			 */
			if (!memcmp((u_int8_t *)va, "net45xx", 7) ||
			    !memcmp((u_int8_t *)va, "net4801", 7) ||
			    !memcmp((u_int8_t *)va, "net5501", 7) ||
			    !memcmp((u_int8_t *)va, "net6501", 7)) {
				hw_prod = malloc(8, M_DEVBUF, M_NOWAIT);
				if (hw_prod) {
					memcpy(hw_prod, (u_int8_t *)va, 7);
					hw_prod[7] = '\0';
				}
				break;
			}
	}

#if NACPI > 0
#if NPCI > 0
	if (smbiosrev >= 210 && pci_mode_detect() != 0)
#endif
	{
		struct bios_attach_args ba;

		memset(&ba, 0, sizeof(ba));
		ba.ba_name = "acpi";
		ba.ba_func = 0x00;		/* XXX ? */
		ba.ba_iot = I386_BUS_SPACE_IO;
		ba.ba_memt = I386_BUS_SPACE_MEM;
		if (config_found(self, &ba, bios_print)) {
			flags |= BIOSF_PCIBIOS;
			usingacpi = 1;
		}
	}
#endif

#if NAPM > 0
	if (usingacpi == 0 && apm && ncpu < 2 && smbiosrev < 240) {
		struct bios_attach_args ba;

#if defined(DEBUG) || defined(APMDEBUG)
		printf("apminfo: %x, code %x[%x]/%x[%x], data %x[%x], ept %x\n",
		    apm->apm_detail,
		    apm->apm_code32_base, apm->apm_code_len,
		    apm->apm_code16_base, apm->apm_code16_len,
		    apm->apm_data_base, apm->apm_data_len, apm->apm_entry);
#endif
		ba.ba_name = "apm";
		ba.ba_func = 0x15;
		ba.ba_memt = bia->ba_memt;
		ba.ba_iot = bia->ba_iot;
		ba.ba_apmp = apm;
		config_found(self, &ba, bios_print);
	}
#endif

#if NMPBIOS > 0
	if (mpbios_probe(self)) {
		struct bios_attach_args ba;

		memset(&ba, 0, sizeof(ba));
		ba.ba_name = "mpbios";
		ba.ba_iot = I386_BUS_SPACE_IO;
		ba.ba_memt = I386_BUS_SPACE_MEM;

		config_found(self, &ba, bios_print);
	}
#endif

#if NPCI > 0 && NPCIBIOS > 0
	if (!(flags & BIOSF_PCIBIOS)) {
		struct bios_attach_args ba;

		ba.ba_name = "pcibios";
		ba.ba_func = 0x1A;
		ba.ba_memt = bia->ba_memt;
		ba.ba_iot = bia->ba_iot;
		config_found(self, &ba, bios_print);
	}
#endif

	/*
	 * now that we gave 'em a chance to attach,
	 * scan and map all the proms we can find
	 */
	if (!(flags & BIOSF_PROMSCAN)) {
		volatile u_int8_t *eva;

		for (str = NULL, va = ISA_HOLE_VADDR(0xc0000),
		    eva = ISA_HOLE_VADDR(0xf0000);
		    va < eva; va += 512) {
			extern struct extent *iomem_ex;
			bios_romheader_t romh = (bios_romheader_t)va;
			u_int32_t off, len;
			u_int8_t cksum;
			int i;

			if (romh->signature != 0xaa55)
				continue;

			/*
			 * for this and the next check we probably want
			 * to reserve the page in the extent anyway
			 */
			if (!romh->len || romh->len == 0xff)
				continue;

			len = romh->len * 512;
			if (va + len > eva)
				continue;

			for (cksum = 0, i = len; i--; cksum += va[i])
				;
			off = 0xc0000 + (va - (u_int8_t *)
			    ISA_HOLE_VADDR(0xc0000));

			if (!str)
				printf("%s: ROM list:",
				    str = sc->sc_dev.dv_xname);
			printf(" 0x%05x/0x%x%s", off, len,
			    cksum? "!" : "");

			if ((i = extent_alloc_region(iomem_ex,
			    (paddr_t)off, len, EX_NOWAIT)))
				printf(":%d", i);

			va += len - 512;
		}
	}

	if (str)
		printf("\n");
}

void
bios_getopt()
{
	bootarg_t *q;
	bios_ddb_t *bios_ddb;
	bios_bootduid_t *bios_bootduid;
	bios_bootsr_t *bios_bootsr;

#ifdef BIOS_DEBUG
	printf("bootargv:");
#endif

	for(q = bootargp; q->ba_type != BOOTARG_END; q = q->ba_next) {
		q->ba_next = (bootarg_t *)((caddr_t)q + q->ba_size);
		switch (q->ba_type) {
		case BOOTARG_MEMMAP:
			bios_memmap = (bios_memmap_t *)q->ba_arg;
#ifdef BIOS_DEBUG
			printf(" memmap %p", bios_memmap);
#endif
			break;
		case BOOTARG_DISKINFO:
			bios_diskinfo = (bios_diskinfo_t *)q->ba_arg;
#ifdef BIOS_DEBUG
			printf(" diskinfo %p", bios_diskinfo);
#endif
			break;
#if NAPM > 0 || defined(DEBUG)
		case BOOTARG_APMINFO:
#ifdef BIOS_DEBUG
			printf(" apminfo %p", q->ba_arg);
#endif
			apm = (bios_apminfo_t *)q->ba_arg;
			break;
#endif
		case BOOTARG_CKSUMLEN:
			bios_cksumlen = *(u_int32_t *)q->ba_arg;
#ifdef BIOS_DEBUG
			printf(" cksumlen %d", bios_cksumlen);
#endif
			break;
#if NPCI > 0
		case BOOTARG_PCIINFO:
			bios_pciinfo = (bios_pciinfo_t *)q->ba_arg;
#ifdef BIOS_DEBUG
			printf(" pciinfo %p", bios_pciinfo);
#endif
			break;
#endif
		case BOOTARG_CONSDEV:
			if (q->ba_size >= sizeof(bios_consdev_t) +
			    offsetof(bootarg_t, ba_arg)) {
				bios_consdev_t *cdp =
				    (bios_consdev_t*)q->ba_arg;
#if NCOM > 0
				static const int ports[] =
				    { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
				int unit = minor(cdp->consdev);
				int consaddr = cdp->consaddr;
				if (consaddr == -1 && unit >=0 &&
				    unit < (sizeof(ports)/sizeof(ports[0])))
					consaddr = ports[unit];
				if (major(cdp->consdev) == 8 &&
				    consaddr != -1) {
					comconsunit = unit;
					comconsaddr = consaddr;
					comconsrate = cdp->conspeed;
					comconsiot = I386_BUS_SPACE_IO;

					/* Probe the serial port this time. */
					cninit();
				}
#endif
#ifdef BIOS_DEBUG
				printf(" console 0x%x:%d",
				    cdp->consdev, cdp->conspeed);
#endif
			}
			break;
#ifdef MULTIPROCESSOR
		case BOOTARG_SMPINFO:
			bios_smpinfo = q->ba_arg;
			printf(" smpinfo %p", bios_smpinfo);
			break;
#endif

		case BOOTARG_BOOTMAC:
			bios_bootmac = (bios_bootmac_t *)q->ba_arg;
			break;

		case BOOTARG_DDB:
			bios_ddb = (bios_ddb_t *)q->ba_arg;
#ifdef DDB
			db_console = bios_ddb->db_console;
#endif
			break;

		case BOOTARG_BOOTDUID:
			bios_bootduid = (bios_bootduid_t *)q->ba_arg;
			bcopy(bios_bootduid, bootduid, sizeof(bootduid));
			break;

		case BOOTARG_BOOTSR:
			bios_bootsr = (bios_bootsr_t *)q->ba_arg;
#if NSOFTRAID > 0
			bcopy(&bios_bootsr->uuid, &sr_bootuuid,
			    sizeof(sr_bootuuid));
			bcopy(&bios_bootsr->maskkey, &sr_bootkey,
			    sizeof(sr_bootkey));
#endif
			explicit_bzero(bios_bootsr, sizeof(bios_bootsr_t));
			break;

		default:
#ifdef BIOS_DEBUG
			printf(" unsupported arg (%d) %p", q->ba_type,
			    q->ba_arg);
#endif
			break;
		}
	}
	printf("\n");

}

int
bios_print(void *aux, const char *pnp)
{
	struct bios_attach_args *ba = aux;

	if (pnp)
		printf("%s at %s function 0x%x",
		    ba->ba_name, pnp, ba->ba_func);
	return (UNCONF);
}

int
bios32_service(u_int32_t service, bios32_entry_t e, bios32_entry_info_t ei)
{
	u_long pa, endpa;
	vaddr_t va, sva;
	u_int32_t base, count, off, ent;
	int slot;

	if (bios32_entry.offset == 0)
		return 0;

	base = 0;
	__asm volatile("lcall *(%4)"
	    : "+a" (service), "+b" (base), "=c" (count), "=d" (off)
	    : "D" (&bios32_entry)
	    : "%esi", "cc", "memory");

	if (service & 0xff)
		return 0;	/* not found */

	ent = base + off;
	if (ent <= BIOS32_START || ent >= BIOS32_END)
		return 0;

	endpa = round_page(BIOS32_END);

	sva = va = uvm_km_valloc(kernel_map, endpa);
	if (va == 0)
		return (0);

	/* Store bios32 service kva for cleanup later */
	bios_softc->bios32_service_va = sva;

	slot = gdt_get_slot();
	setgdt(slot, (caddr_t)va, BIOS32_END, SDT_MEMERA, SEL_KPL, 1, 0);

	for (pa = trunc_page(BIOS32_START),
	    va += trunc_page(BIOS32_START);
	    pa < endpa; pa += NBPG, va += NBPG) {
		pmap_enter(pmap_kernel(), va, pa,
		    PROT_READ | PROT_WRITE | PROT_EXEC,
		    PROT_READ | PROT_WRITE | PROT_EXEC | PMAP_WIRED);

		if (pa >= trunc_page(base)) {
			pmap_enter(pmap_kernel(), sva, pa,
			    PROT_READ | PROT_WRITE | PROT_EXEC,
			    PROT_READ | PROT_WRITE | PROT_EXEC | PMAP_WIRED);
			sva += NBPG;
		}
	}

	e->segment = GSEL(slot, SEL_KPL);
	e->offset = (vaddr_t)ent;

	ei->bei_base = base;
	ei->bei_size = count;
	ei->bei_entry = ent;

	return 1;
}

void
bios32_cleanup(void)
{
	u_long pa, size;
	vaddr_t va;

	size = round_page(BIOS32_END);

	for (va = bios_softc->bios32_service_va;
	    va < bios_softc->bios32_service_va + size;
	    va += NBPG) {
		if (pmap_extract(pmap_kernel(), va, &pa))
			pmap_remove(pmap_kernel(), va, va + PAGE_SIZE);
	}

	uvm_km_free(kernel_map, bios_softc->bios32_service_va, size);
}

int
biosopen(dev_t dev, int flag, int mode, struct proc *p)
{
	struct bios_softc *sc = bios_cd.cd_devs[0];

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

	(void)sc;

	return 0;
}

int
biosclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct bios_softc *sc = bios_cd.cd_devs[0];

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

	(void)sc;

	return 0;
}

int
biosioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct bios_softc *sc = bios_cd.cd_devs[0];

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

	switch (cmd) {
	default:
		return ENXIO;
	}

	(void)sc;

	return 0;
}

int
bios_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
    size_t newlen, struct proc *p)
{
	bios_diskinfo_t *pdi;
	int biosdev;

	/* all sysctl names at this level except diskinfo are terminal */
	if (namelen != 1 && name[0] != BIOS_DISKINFO)
		return (ENOTDIR);		/* overloaded */

	if (!(bootapiver & BAPIV_VECTOR))
		return EOPNOTSUPP;

	switch (name[0]) {
	case BIOS_DEV:
		if ((pdi = bios_getdiskinfo(bootdev)) == NULL)
			return ENXIO;
		biosdev = pdi->bios_number;
		return sysctl_rdint(oldp, oldlenp, newp, biosdev);
	case BIOS_DISKINFO:
		if (namelen != 2)
			return ENOTDIR;
		if ((pdi = bios_getdiskinfo(name[1])) == NULL)
			return ENXIO;
		return sysctl_rdstruct(oldp, oldlenp, newp, pdi, sizeof(*pdi));
	case BIOS_CKSUMLEN:
		return sysctl_rdint(oldp, oldlenp, newp, bios_cksumlen);
	default:
		return EOPNOTSUPP;
	}
	/* NOTREACHED */
}

bios_diskinfo_t *
bios_getdiskinfo(dev_t dev)
{
	bios_diskinfo_t *pdi;

	if (bios_diskinfo == NULL)
		return NULL;

	for (pdi = bios_diskinfo; pdi->bios_number != -1; pdi++) {
		if ((dev & B_MAGICMASK) == B_DEVMAGIC) { /* search by bootdev */
			if (pdi->bsd_dev == dev)
				break;
		} else {
			if (pdi->bios_number == dev)
				break;
		}
	}

	if (pdi->bios_number == -1)
		return NULL;
	else
		return pdi;
}

/*
 * smbios_find_table() takes a caller supplied smbios struct type and
 * a pointer to a handle (struct smbtable) returning one if the structure
 * is successfully located and zero otherwise. Callers should take care
 * to initialize the cookie field of the smbtable structure to zero before
 * the first invocation of this function.
 * Multiple tables of the same type can be located by repeatedly calling
 * smbios_find_table with the same arguments.
 */
int
smbios_find_table(u_int8_t type, struct smbtable *st)
{
	u_int8_t *va, *end;
	struct smbtblhdr *hdr;
	int ret = 0, tcount = 1;

	va = smbios_entry.addr;
	end = va + smbios_entry.len;

	/*
	 * The cookie field of the smtable structure is used to locate
	 * multiple instances of a table of an arbitrary type. Following the
	 * successful location of a table, the type is encoded as bits 0:7 of
	 * the cookie value, the offset in terms of the number of structures
	 * preceding that referenced by the handle is encoded in bits 15:31.
	 */
	if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
		if ((u_int8_t *)st->hdr >= va && (u_int8_t *)st->hdr < end) {
			hdr = st->hdr;
			if (hdr->type == type) {
				va = (u_int8_t *)hdr + hdr->size;
				for (; va + 1 < end; va++)
					if (*va == 0 && *(va + 1) == 0)
						break;
				va+= 2;
				tcount = st->cookie >> 16;
			}
		}
	}
	for (; va + sizeof(struct smbtblhdr) < end && tcount <=
	    smbios_entry.count; tcount++) {
		hdr = (struct smbtblhdr *)va;
		if (hdr->type == type) {
			ret = 1;
			st->hdr = hdr;
			st->tblhdr = va + sizeof(struct smbtblhdr);
			st->cookie = (tcount + 1) << 16 | type;
			break;
		}
		if (hdr->type == SMBIOS_TYPE_EOT)
			break;
		va+= hdr->size;
		for (; va + 1 < end; va++)
			if (*va == 0 && *(va + 1) == 0)
				break;
		va+=2;
	}

	return ret;
}

char *
smbios_get_string(struct smbtable *st, u_int8_t indx, char *dest, size_t len)
{
	u_int8_t *va, *end;
	char *ret = NULL;
	int i;

	va = (u_int8_t *)st->hdr + st->hdr->size;
	end = smbios_entry.addr + smbios_entry.len;
	for (i = 1; va < end && i < indx && *va; i++)
		while (*va++)
			;
	if (i == indx) {
		if (va + len < end) {
			ret = dest;
			bcopy(va, ret, len);
			ret[len - 1] = '\0';
		}
	}

	return ret;
}

char *
fixstring(char *s)
{
	char *p, *e;
	int i;

	for (i= 0; i < sizeof(smbios_uninfo)/sizeof(smbios_uninfo[0]); i++)
		if ((strncasecmp(s, smbios_uninfo[i], strlen(smbios_uninfo[i])))==0)
			return NULL;
	/*
	 * Remove leading and trailing whitespace
	 */
	for (p = s; *p == ' '; p++)
		;
	/*
	 * Special case entire string is whitespace
	 */
	if (p == s + strlen(s))
		return NULL;
	for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
		;
	if (p > s || e < s + strlen(s) - 1) {
		bcopy(p, s, e-p + 1);
		s[e - p + 1] = '\0';
	}

	return s;
}

void
smbios_info(char * str)
{
	char *sminfop, sminfo[64];
	struct smbtable stbl, btbl;
	struct smbios_sys *sys;
	struct smbios_board *board;
	int i, infolen, uuidf, havebb;
	char *p;

	if (smbios_entry.mjr < 2)
		return;
	/*
	 * According to the spec the system table among others is required,
	 * if it is not we do not bother with this smbios implementation.
	 */
	stbl.cookie = btbl.cookie = 0;
	if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
		return;
	havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);

	sys = (struct smbios_sys *)stbl.tblhdr;
	if (havebb)
		board = (struct smbios_board *)btbl.tblhdr;
	/*
	 * Some smbios implementations have no system vendor or product strings,
	 * some have very uninformative data which is harder to work around
	 * and we must rely upon various heuristics to detect this. In both
	 * cases we attempt to fall back on the base board information in the
	 * perhaps naive belief that motherboard vendors will supply this
	 * information.
	 */
	sminfop = NULL;
	if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
	    sizeof(sminfo))) != NULL)
		sminfop = fixstring(p);
	if (sminfop == NULL) {
		if (havebb) {
			if ((p = smbios_get_string(&btbl, board->vendor,
			    sminfo, sizeof(sminfo))) != NULL)
				sminfop = fixstring(p);
		}
	}
	if (sminfop) {
		infolen = strlen(sminfop) + 1;
		hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
		if (hw_vendor)
			strlcpy(hw_vendor, sminfop, infolen);
		sminfop = NULL;
	}
	if ((p = smbios_get_string(&stbl, sys->product, sminfo,
	    sizeof(sminfo))) != NULL)
		sminfop = fixstring(p);
	if (sminfop == NULL) {
		if (havebb) {
			if ((p = smbios_get_string(&btbl, board->product,
			    sminfo, sizeof(sminfo))) != NULL)
				sminfop = fixstring(p);
		}
	}
	if (sminfop) {
		infolen = strlen(sminfop) + 1;
		hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
		if (hw_prod)
			strlcpy(hw_prod, sminfop, infolen);
		sminfop = NULL;
	}
	if (hw_vendor != NULL && hw_prod != NULL)
		printf("\n%s: %s %s", str, hw_vendor, hw_prod);
	if ((p = smbios_get_string(&stbl, sys->version, sminfo,
	    sizeof(sminfo))) != NULL)
		sminfop = fixstring(p);
	if (sminfop) {
		infolen = strlen(sminfop) + 1;
		hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
		if (hw_ver)
			strlcpy(hw_ver, sminfop, infolen);
		sminfop = NULL;
	}
	if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
	    sizeof(sminfo))) != NULL)
		sminfop = fixstring(p);
	if (sminfop) {
		infolen = strlen(sminfop) + 1;
		for (i = 0; i < infolen - 1; i++)
			add_timer_randomness(sminfop[i]);
		hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
		if (hw_serial)
			strlcpy(hw_serial, sminfop, infolen);
	}
	if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
	    smbios_entry.min >= 1)) {
		/*
		 * If the uuid value is all 0xff the uuid is present but not
		 * set, if its all 0 then the uuid isn't present at all.
		 */
		uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
		for (i = 0; i < sizeof(sys->uuid); i++) {
			if (sys->uuid[i] != 0xff)
				uuidf &= ~SMBIOS_UUID_NSET;
			if (sys->uuid[i] != 0)
				uuidf &= ~SMBIOS_UUID_NPRESENT;
		}

		if (uuidf & SMBIOS_UUID_NPRESENT)
			hw_uuid = NULL;
		else if (uuidf & SMBIOS_UUID_NSET)
			hw_uuid = "Not Set";
		else {
			for (i = 0; i < sizeof(sys->uuid); i++)
				add_timer_randomness(sys->uuid[i]);
			hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
			    M_NOWAIT);
			if (hw_uuid) {
				snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
				    SMBIOS_UUID_REP,
				    sys->uuid[0], sys->uuid[1], sys->uuid[2],
				    sys->uuid[3], sys->uuid[4], sys->uuid[5],
				    sys->uuid[6], sys->uuid[7], sys->uuid[8],
				    sys->uuid[9], sys->uuid[10], sys->uuid[11],
				    sys->uuid[12], sys->uuid[13], sys->uuid[14],
				    sys->uuid[15]);
			}
		}
	}
}