NetBSD-5.0.2/sys/arch/x86/x86/mpbios.c
/* $NetBSD: mpbios.c,v 1.47 2008/08/26 12:04:18 cegger 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mpbios.c,v 1.47 2008/08/26 12:04:18 cegger Exp $");
#include "acpi.h"
#include "lapic.h"
#include "ioapic.h"
#include "opt_acpi.h"
#include "opt_mpbios.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <sys/reboot.h>
#include <uvm/uvm_extern.h>
#include <machine/specialreg.h>
#include <machine/cpuvar.h>
#include <machine/mpbiosvar.h>
#include <machine/pio.h>
#include <machine/i82093reg.h>
#include <machine/i82093var.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#include <dev/isa/isareg.h>
#ifdef X86_MPBIOS_SUPPORT_EISA
#include <dev/eisa/eisavar.h> /* for ELCR* def'ns */
#endif
#if NACPI > 0
extern int mpacpi_ncpu;
extern int mpacpi_nioapic;
#endif
int mpbios_ncpu;
int mpbios_nioapic;
#include "pci.h"
#if NPCI > 0
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#endif
#include "locators.h"
static struct mpbios_ioapic default_ioapic = {
2,0,1,IOAPICENTRY_FLAG_EN,(uint32_t)IOAPIC_BASE_DEFAULT
};
/* descriptions of MP basetable entries */
struct mpbios_baseentry {
uint8_t type;
uint8_t length;
uint16_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_cpuprint(void *, const char *);
int mp_ioapicprint(void *, const char *);
static const void *mpbios_search(struct device *, paddr_t, int,
struct mp_map *);
static inline int mpbios_cksum(const void *,int);
static void mp_cfg_special_intr(const struct mpbios_int *, uint32_t *);
static void mp_print_special_intr (int intr);
static void mp_cfg_pci_intr(const struct mpbios_int *, uint32_t *);
static void mp_print_pci_intr (int intr);
#ifdef X86_MPBIOS_SUPPORT_EISA
static void mp_print_eisa_intr (int intr);
static void mp_cfg_eisa_intr(const struct mpbios_int *, uint32_t *);
#endif
static void mp_cfg_isa_intr(const struct mpbios_int *, uint32_t *);
static void mp_print_isa_intr(int intr);
static void mpbios_cpus(struct device *);
static void mpbios_cpu(const uint8_t *, struct device *);
static void mpbios_bus(const uint8_t *, struct device *);
static void mpbios_ioapic(const uint8_t *, struct device *);
static void mpbios_int(const uint8_t *, int, struct mp_intr_map *);
static const void *mpbios_map(paddr_t, int, struct mp_map *);
static 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;
int mpbios_scanned;
int
mp_cpuprint(void *aux, const char *pnp)
{
struct cpu_attach_args *caa = aux;
if (pnp)
aprint_normal("cpu at %s", pnp);
printf(" apid %d", caa->cpu_number);
return (UNCONF);
}
int
mp_ioapicprint(void *aux, const char *pnp)
{
struct apic_attach_args *aaa = aux;
if (pnp)
aprint_normal("ioapic at %s", pnp);
printf(" apid %d", aaa->apic_id);
return (UNCONF);
}
/*
* Map a chunk of memory read-only and return an appropraitely
* const'ed pointer.
*/
static const void *
mpbios_map(paddr_t pa, int len, struct mp_map *handle)
{
paddr_t pgpa = x86_trunc_page(pa);
paddr_t endpa = x86_round_page(pa + len);
vaddr_t va = uvm_km_alloc(kernel_map, endpa - pgpa, 0, UVM_KMF_VAONLY);
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, VM_PROT_READ);
va += PAGE_SIZE;
pgpa += PAGE_SIZE;
} while (pgpa < endpa);
pmap_update(pmap_kernel());
return (const void *)retva;
}
inline static void
mpbios_unmap(struct mp_map *handle)
{
pmap_kremove(handle->baseva, handle->vsize);
pmap_update(pmap_kernel());
uvm_km_free(kernel_map, handle->baseva, handle->vsize, UVM_KMF_VAONLY);
}
/*
* 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 uint8_t *mpbios_page;
int scan_loc;
struct mp_map t;
/* If MP is disabled, don't use MPBIOS or the ioapics. */
if ((boothowto & RB_MD1) != 0)
return 0;
/* see if EBDA exists */
mpbios_page = mpbios_map (0, PAGE_SIZE, &t);
ebda = *(const uint16_t *) (&mpbios_page[0x40e]);
ebda <<= 4;
memtop = *(const uint16_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)
aprint_verbose_dev(self, "MP floating pointer found in %s at 0x%lx\n",
loc_where[scan_loc], mp_fp_map.pa);
if (mp_fps->pap == 0) {
if (mp_fps->mpfb1 == 0) {
aprint_error_dev(self, "MP fps invalid: "
"no default config and no configuration table\n");
goto err;
}
aprint_normal_dev(self, "MP default configuration %d\n",
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)
aprint_verbose_dev(self, "MP config table at 0x%lx, %d bytes long\n",
cthpa, cthlen);
if (mp_cth->signature != MP_CT_SIG) {
aprint_error_dev(self, "MP signature mismatch (%x vs %x)\n",
MP_CT_SIG, mp_cth->signature);
goto err;
}
if (mpbios_cksum(mp_cth, cthlen)) {
aprint_error_dev(self, "MP Configuration Table checksum mismatch\n");
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.
*/
inline static 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 uint8_t *base = mpbios_map (start, count, &t);
if (mp_verbose)
aprint_verbose_dev(self, "scanning 0x%lx to 0x%lx for MP signature\n",
start, start+count-sizeof(*m));
for (i = 0; i <= end; i += 4) {
m = (const 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"},
};
static struct mp_bus extint_bus = {
"ExtINT",
-1,
mp_print_special_intr,
mp_cfg_special_intr,
NULL, 0, 0, NULL, 0
};
static struct mp_bus smi_bus = {
"SMI",
-1,
mp_print_special_intr,
mp_cfg_special_intr,
NULL, 0, 0, NULL, 0
};
static struct mp_bus nmi_bus = {
"NMI",
-1,
mp_print_special_intr,
mp_cfg_special_intr,
NULL, 0, 0, NULL, 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_nbus
* mp_napics
* nintrs
*/
void
mpbios_scan(struct device *self, int *ncpup, int *napic)
{
const uint8_t *position, *end;
int count;
int type;
int intr_cnt, cur_intr;
paddr_t lapic_base;
const struct mpbios_int *iep;
struct mpbios_int ie;
aprint_normal_dev(self, "Intel MP Specification ");
switch (mp_fps->spec_rev) {
case 1:
printf("(Version 1.1)");
break;
case 4:
printf("(Version 1.4)");
break;
default:
printf("(unrecognized rev %d)", mp_fps->spec_rev);
}
/*
* looks like we've got a MP system. start setting up
* infrastructure..
* XXX is this the right place??
*/
#if NACPI > 0
if (mpacpi_ncpu == 0) {
#endif
lapic_base = LAPIC_BASE;
if (mp_cth != NULL)
lapic_base = (paddr_t)mp_cth->apic_address;
#if NLAPIC > 0
lapic_boot_init(lapic_base);
#endif
#if NACPI > 0
}
#endif
/* check for use of 'default' configuration */
if (mp_fps->mpfb1 != 0) {
aprint_normal("\n");
aprint_normal_dev(self, "MP default configuration %d\n",
mp_fps->mpfb1);
#if NACPI > 0
if (mpacpi_ncpu == 0)
#endif
mpbios_cpus(self);
#if NACPI > 0
if (mpacpi_nioapic == 0)
#endif
mpbios_ioapic((uint8_t *)&default_ioapic, self);
/* XXX */
aprint_verbose_dev(self, "WARNING: interrupts not configured\n");
/*
* XXX rpaulo: I have a machine that can boot, so I
* commented this (for now).
*/
#if 0
panic("lazy bum");
return;
#endif
} else {
/*
* should not happen; mp_probe returns 0 in this case,
* but..
*/
if (mp_cth == NULL)
panic ("mpbios_scan: no config (can't happen?)");
printf(" (%8.8s %12.12s)\n",
mp_cth->oem_id, mp_cth->product_id);
/*
* Walk the table once, counting items
*/
position = (const uint8_t *)(mp_cth);
end = position + mp_cth->base_len;
position += sizeof(*mp_cth);
count = mp_cth->entry_count;
intr_cnt = 0;
while ((count--) && (position < end)) {
type = *position;
if (type >= MPS_MCT_NTYPES) {
aprint_error_dev(self, "unknown entry type %x"
" in MP config table\n",
type);
break;
}
mp_conf[type].count++;
if (type == MPS_MCT_BUS) {
const struct mpbios_bus *bp =
(const struct mpbios_bus *)position;
if (bp->bus_id >= mp_nbus)
mp_nbus = 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) {
iep = (const struct mpbios_int *)position;
if (iep->dst_apic_id == MPS_ALL_APICS)
intr_cnt +=
mp_conf[MPS_MCT_IOAPIC].count;
else
intr_cnt++;
} else if (type == MPS_MCT_LINT)
intr_cnt++;
position += mp_conf[type].length;
}
mp_busses = malloc(sizeof(struct mp_bus)*mp_nbus,
M_DEVBUF, M_NOWAIT | M_ZERO);
mp_intrs = malloc(sizeof(struct mp_intr_map)*intr_cnt,
M_DEVBUF, M_NOWAIT | M_ZERO);
mp_nintr = intr_cnt;
/* re-walk the table, recording info of interest */
position = (const uint8_t *) mp_cth + sizeof(*mp_cth);
count = mp_cth->entry_count;
cur_intr = 0;
while ((count--) && (position < end)) {
switch (type = *position) {
case MPS_MCT_CPU:
#if NACPI > 0
/* ACPI has done this for us */
if (mpacpi_ncpu)
break;
#endif
mpbios_cpu(position, self);
break;
case MPS_MCT_BUS:
mpbios_bus(position, self);
break;
case MPS_MCT_IOAPIC:
#if NACPI > 0
/* ACPI has done this for us */
if (mpacpi_nioapic)
break;
#endif
mpbios_ioapic(position, self);
break;
case MPS_MCT_IOINT:
iep = (const struct mpbios_int *)position;
ie = *iep;
if (iep->dst_apic_id == MPS_ALL_APICS) {
#if NIOAPIC > 0
struct ioapic_softc *sc;
for (sc = ioapics ; sc != NULL;
sc = sc->sc_next) {
ie.dst_apic_id = sc->sc_apicid;
mpbios_int((char *)&ie, type,
&mp_intrs[cur_intr++]);
}
#endif
} else {
mpbios_int(position, type,
&mp_intrs[cur_intr++]);
}
break;
case MPS_MCT_LINT:
mpbios_int(position, type,
&mp_intrs[cur_intr]);
cur_intr++;
break;
default:
aprint_error_dev(self, "unknown entry type %x in MP config table\n",
type);
/* NOTREACHED */
return;
}
position += mp_conf[type].length;
}
if (mp_verbose && mp_cth->ext_len)
aprint_verbose_dev(self, "MP WARNING: %d bytes of extended entries not examined\n",
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);
}
mpbios_scanned = 1;
*ncpup = mpbios_ncpu;
*napic = mpbios_nioapic;
}
static void
mpbios_cpu(const uint8_t *ent, struct device *self)
{
const struct mpbios_proc *entry = (const struct mpbios_proc *)ent;
struct cpu_attach_args caa;
int locs[CPUBUSCF_NLOCS];
/* XXX move this into the CPU attachment goo. */
/* check for usability */
if (!(entry->cpu_flags & PROCENTRY_FLAG_EN))
return;
mpbios_ncpu++;
/* check for BSP flag */
if (entry->cpu_flags & PROCENTRY_FLAG_BP)
caa.cpu_role = CPU_ROLE_BP;
else
caa.cpu_role = CPU_ROLE_AP;
caa.cpu_number = entry->apic_id;
caa.cpu_func = &mp_cpu_funcs;
locs[CPUBUSCF_APID] = caa.cpu_number;
config_found_sm_loc(self, "cpubus", locs, &caa, mp_cpuprint,
config_stdsubmatch);
}
static void
mpbios_cpus(struct device *self)
{
struct mpbios_proc pe;
/* use default addresses */
pe.apic_id = lapic_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((uint8_t *)&pe, self);
pe.apic_id = 1 - lapic_cpu_number();
pe.cpu_flags = PROCENTRY_FLAG_EN;
mpbios_cpu((uint8_t *)&pe, self);
}
/*
* 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.
*/
static void
mp_cfg_special_intr(const struct mpbios_int *entry, uint32_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;
}
}
/* XXX too much duplicated code here. */
static void
mp_cfg_pci_intr(const struct mpbios_int *entry, uint32_t *redir)
{
int mpspo = entry->int_flags & 0x03; /* XXX magic */
int mpstrig = (entry->int_flags >> 2) & 0x03; /* XXX magic */
*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_FIXED<<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);
}
}
#ifdef X86_MPBIOS_SUPPORT_EISA
static void
mp_cfg_eisa_intr(const struct mpbios_int *entry, uint32_t *redir)
{
int mpspo = entry->int_flags & 0x03; /* XXX magic */
int mpstrig = (entry->int_flags >> 2) & 0x03; /* XXX magic */
*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_FIXED<<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);
}
}
#endif
static void
mp_cfg_isa_intr(const struct mpbios_int *entry, uint32_t *redir)
{
int mpspo = entry->int_flags & 0x03; /* XXX magic */
int mpstrig = (entry->int_flags >> 2) & 0x03; /* XXX magic */
*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_FIXED<<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);
}
}
static void
mp_print_special_intr(int intr)
{
}
static void
mp_print_pci_intr(int intr)
{
printf(" device %d INT_%c", (intr>>2)&0x1f, 'A' + (intr & 0x3));
}
static void
mp_print_isa_intr(int intr)
{
printf(" irq %d", intr);
}
#ifdef X86_MPBIOS_SUPPORT_EISA
static void
mp_print_eisa_intr(int intr)
{
printf(" EISA irq %d", intr);
}
#endif
#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)))
static void
mpbios_bus(const uint8_t *ent, struct device *self)
{
const struct mpbios_bus *entry = (const struct mpbios_bus *)ent;
int bus_id = entry->bus_id;
aprint_verbose("mpbios: bus %d is type %6.6s\n", bus_id,
entry->bus_type);
#ifdef DIAGNOSTIC
/*
* This "should not happen" unless the table changes out
* from underneath us
*/
if (bus_id >= mp_nbus) {
panic("mpbios: bus number %d out of range?? (type %6.6s)\n",
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;
#ifdef X86_MPBIOS_SUPPORT_EISA
} 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 != -1)
aprint_error("oops: multiple isa busses?\n");
else
mp_eisa_bus = bus_id;
#endif
} else if (memcmp(entry->bus_type, "ISA ", 6) == 0) {
mp_busses[bus_id].mb_name = "isa";
mp_busses[bus_id].mb_idx = 0; /* XXX */
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 != -1)
printf("oops: multiple isa busses?\n");
else
mp_isa_bus = bus_id;
} else {
aprint_error_dev(self, "unsupported bus type %6.6s\n",
entry->bus_type);
}
}
static void
mpbios_ioapic(const uint8_t *ent, struct device *self)
{
const struct mpbios_ioapic *entry = (const struct mpbios_ioapic *)ent;
/* XXX let flags checking happen in ioapic driver.. */
if (!(entry->apic_flags & IOAPICENTRY_FLAG_EN))
return;
mpbios_nioapic++;
#if NIOAPIC > 0
{
int locs[IOAPICBUSCF_NLOCS];
struct apic_attach_args aaa;
aaa.apic_id = entry->apic_id;
aaa.apic_version = entry->apic_version;
aaa.apic_address = (paddr_t)entry->apic_address;
aaa.apic_vecbase = -1;
aaa.flags = (mp_fps->mpfb2 & 0x80) ? IOAPIC_PICMODE : IOAPIC_VWIRE;
locs[IOAPICBUSCF_APID] = aaa.apic_id;
config_found_sm_loc(self, "ioapicbus", locs, &aaa, mp_ioapicprint,
config_stdsubmatch);
}
#endif
}
static const char inttype_fmt[] = "\177\020"
"f\0\2type\0" "=\1NMI\0" "=\2SMI\0" "=\3ExtINT\0";
static const char flagtype_fmt[] = "\177\020"
"f\0\2pol\0" "=\1Act Hi\0" "=\3Act Lo\0"
"f\2\2trig\0" "=\1Edge\0" "=\3Level\0";
static void
mpbios_int(const uint8_t *ent, int enttype, struct mp_intr_map *mpi)
{
const struct mpbios_int *entry = (const struct mpbios_int *)ent;
struct ioapic_softc *sc = NULL;
struct pic *sc2;
struct mp_intr_map *altmpi;
struct mp_bus *mpb;
uint32_t id = entry->dst_apic_id;
uint32_t pin = entry->dst_apic_int;
uint32_t bus = entry->src_bus_id;
uint32_t dev = entry->src_bus_irq;
uint32_t type = entry->int_type;
uint32_t flags = entry->int_flags;
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->next = mpb->mb_intrs;
mpb->mb_intrs = mpi;
mpi->bus = mpb;
mpi->bus_pin = dev;
mpi->global_int = -1;
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;
}
(*mpb->mb_intr_cfg)(entry, &mpi->redir);
if (enttype == MPS_MCT_IOINT) {
#if NIOAPIC > 0
sc = ioapic_find(id);
#else
sc = NULL;
#endif
if (sc == NULL) {
printf("mpbios: can't find ioapic %d\n", id);
return;
}
/*
* 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 = intr_findpic(pin);
if (sc2 && sc2->pic_ioapic != sc) {
printf("mpbios: bad pin %d for apic %d\n",
pin, id);
return;
}
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 = (struct pic *)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",
device_xname(sc->sc_dev), 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;
}
}
mpi->ioapic_ih = APIC_INT_VIA_APIC |
((id<<APIC_INT_APIC_SHIFT) | ((pin<<APIC_INT_PIN_SHIFT)));
if (mp_verbose) {
char buf[256];
printf("%s: int%d attached to %s",
sc ? device_xname(sc->sc_dev) : "local apic",
pin, mpb->mb_name);
if (mpb->mb_idx != -1)
printf("%d", mpb->mb_idx);
(*(mpb->mb_intr_print))(dev);
printf(" (type %s",
bitmask_snprintf(type, inttype_fmt, buf, sizeof(buf)));
printf(" flags %s)\n",
bitmask_snprintf(flags, flagtype_fmt, buf, sizeof(buf)));
}
}
#if NPCI > 0
int
mpbios_pci_attach_hook(struct device *parent, struct device *self,
struct pcibus_attach_args *pba)
{
struct mp_bus *mpb;
if (mpbios_scanned == 0)
return ENOENT;
if (pba->pba_bus >= mp_isa_bus) {
intr_add_pcibus(pba);
return 0;
}
mpb = &mp_busses[pba->pba_bus];
if (mpb->mb_name != NULL) {
if (strcmp(mpb->mb_name, "pci"))
return EINVAL;
} else
mpb->mb_name = "pci";
if (mp_verbose)
printf("\n%s: added to list as bus %d", device_xname(parent),
pba->pba_bus);
mpb->mb_configured = 1;
mpb->mb_pci_bridge_tag = pba->pba_bridgetag;
mpb->mb_pci_chipset_tag = pba->pba_pc;
return 0;
}
int
mpbios_scan_pci(struct device *self, struct pcibus_attach_args *pba,
cfprint_t print)
{
int i;
struct mp_bus *mpb;
struct pci_attach_args;
for (i = 0; i < mp_nbus; i++) {
mpb = &mp_busses[i];
if (mpb->mb_name == NULL)
continue;
if (!strcmp(mpb->mb_name, "pci") && mpb->mb_configured == 0) {
pba->pba_bus = i;
config_found_ia(self, "pcibus", pba, print);
}
}
return 0;
}
#endif