More ioapic support, will it ever end.
authorRonald G. Minnich <rminnich@google.com>
Fri, 28 Feb 2014 23:36:46 +0000 (15:36 -0800)
committerRonald G. Minnich <rminnich@google.com>
Fri, 28 Feb 2014 23:36:46 +0000 (15:36 -0800)
Signed-off-by: Ronald G. Minnich <rminnich@google.com>
Makefile
kern/arch/x86/Kbuild
kern/arch/x86/apic.h
kern/arch/x86/io.h [new file with mode: 0644]
kern/arch/x86/ioapic.c [new file with mode: 0644]
kern/arch/x86/ioapic.h
kern/arch/x86/mpacpi.c [new file with mode: 0644]
kern/drivers/dev/acpi.c
scripts/spatch/typedef.cocci

index d182d39..65a2c3f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -316,6 +316,7 @@ CFLAGS_KERNEL += -fno-stack-protector
 CFLAGS_KERNEL += -Wall -Wno-format -Wno-unused
 CFLAGS_KERNEL += -DROS_KERNEL 
 CFLAGS_KERNEL += -include include/generated/autoconf.h -include include/common.h
+CFLAGS_KERNEL += -fplan9-extensions
 ifeq ($(CONFIG_64BIT),y)
 CFLAGS_KERNEL += -m64 -g
 else
index 20e2829..b97549a 100644 (file)
@@ -6,8 +6,10 @@ obj-y                                          += entry$(BITS).o
 #obj-y                                         += emulate.o
 obj-y                                          += frontend.o
 obj-y                                          += init.o
+obj-y                                          += ioapic.o
 obj-y                                          += kclock.o
 obj-y                                          += kdebug.o
+obj-y                                          += mpacpi.o
 obj-y                                          += page_alloc.o
 obj-y                                          += pci.o
 obj-y                                          += perfmon.o
index c7473a3..8533d80 100644 (file)
@@ -307,4 +307,106 @@ static inline void __send_nmi(uint8_t hw_coreid)
        msr_val = msr_val & ~MSR_APIC_BASE_ADDRESS | 0xfaa00000;
        write_msr(IA32_APIC_BASE, msr_val);
 */
+
+
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+/*
+ * There are 2 flavours of APIC, Local APIC and IOAPIC,
+ * Each I/O APIC has a unique physical address,
+ * Local APICs are all at the same physical address as they can only be
+ * accessed by the local CPU.  APIC ids are unique to the
+ * APIC type, so an IOAPIC and APIC both with id 0 is ok.
+ */
+
+struct ioapic {
+       // include hell. spinlock_t lock;                                       /* IOAPIC: register access */
+       uint32_t*       addr;                           /* IOAPIC: register base */
+       int     nrdt;                           /* IOAPIC: size of RDT */
+       int     gsib;                           /* IOAPIC: global RDT index */
+};
+
+struct lapic {
+       int     machno;                         /* APIC */
+
+       uint32_t        lvt[6];
+       int     nlvt;
+       int     ver;
+
+       int64_t hz;                             /* APIC Timer frequency */
+       int64_t max;
+       int64_t min;
+       int64_t div;
+};
+
+struct Apic {
+       int     useable;                        /* en */
+       struct ioapic;
+       struct lapic;
+};
+
+enum {
+       Nbus            = 256,
+       Napic           = 254,                  /* xAPIC architectural limit */
+       Nrdt            = 64,
+};
+
+/*
+ * Common bits for
+ *     IOAPIC Redirection Table Entry (RDT);
+ *     APIC Local Vector Table Entry (LVT);
+ *     APIC Interrupt Command Register (ICR).
+ * [10:8] Message Type
+ * [11] Destination Mode (RW)
+ * [12] Delivery Status (RO)
+ * [13] Interrupt Input Pin Polarity (RW)
+ * [14] Remote IRR (RO)
+ * [15] Trigger Mode (RW)
+ * [16] Interrupt Mask
+ */
+enum {
+       MTf             = 0x00000000,           /* Fixed */
+       MTlp            = 0x00000100,           /* Lowest Priority */
+       MTsmi           = 0x00000200,           /* SMI */
+       MTrr            = 0x00000300,           /* Remote Read */
+       MTnmi           = 0x00000400,           /* NMI */
+       MTir            = 0x00000500,           /* INIT/RESET */
+       MTsipi          = 0x00000600,           /* Startup IPI */
+       MTei            = 0x00000700,           /* ExtINT */
+
+       Pm              = 0x00000000,           /* Physical Mode */
+       Lm              = 0x00000800,           /* Logical Mode */
+
+       Ds              = 0x00001000,           /* Delivery Status */
+       IPhigh          = 0x00000000,           /* IIPP High */
+       IPlow           = 0x00002000,           /* IIPP Low */
+       Rirr            = 0x00004000,           /* Remote IRR */
+       TMedge          = 0x00000000,           /* Trigger Mode Edge */
+       TMlevel         = 0x00008000,           /* Trigger Mode Level */
+       Im              = 0x00010000,           /* Interrupt Mask */
+};
+
+extern struct apic     xlapic[Napic];
+extern struct apic     xioapic[Napic];
+
+#define l16get(p)      (((p)[1]<<8)|(p)[0])
+#define        l32get(p)       (((uint32_t)l16get(p+2)<<16)|l16get(p))
+#define        l64get(p)       (((uint64_t)l32get(p+4)<<32)|l32get(p))
+
+extern void apicdump(void);
+extern void apictimerenab(void);
+extern void ioapicdump(void);
+
+/*
+extern int pcimsienable(Pcidev*, uint64_t);
+extern int pcimsimask(Pcidev*, int);
+*/
+
 #endif /* ROS_KERN_APIC_H */
diff --git a/kern/arch/x86/io.h b/kern/arch/x86/io.h
new file mode 100644 (file)
index 0000000..5e79be2
--- /dev/null
@@ -0,0 +1,324 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+enum {
+       VectorNMI       = 2,            /* non-maskable interrupt */
+       VectorBPT       = 3,            /* breakpoint */
+       VectorUD        = 6,            /* invalid opcode exception */
+       VectorCNA       = 7,            /* coprocessor not available */
+       Vector2F        = 8,            /* double fault */
+       VectorCSO       = 9,            /* coprocessor segment overrun */
+       VectorPF        = 14,           /* page fault */
+       Vector15        = 15,           /* reserved */
+       VectorCERR      = 16,           /* coprocessor error */
+
+       VectorPIC       = 32,           /* external i8259 interrupts */
+       IrqCLOCK        = 0,
+       IrqKBD          = 1,
+       IrqUART1        = 3,
+       IrqUART0        = 4,
+       IrqPCMCIA       = 5,
+       IrqFLOPPY       = 6,
+       IrqLPT          = 7,
+       IrqIRQ7         = 7,
+       IrqAUX          = 12,           /* PS/2 port */
+       IrqIRQ13        = 13,           /* coprocessor on 386 */
+       IrqATA0         = 14,
+       IrqATA1         = 15,
+       MaxIrqPIC       = 15,
+
+       VectorLAPIC     = VectorPIC+16, /* local APIC interrupts */
+       IrqLINT0        = VectorLAPIC+0,
+       IrqLINT1        = VectorLAPIC+1,
+       IrqTIMER        = VectorLAPIC+2,
+       IrqERROR        = VectorLAPIC+3,
+       IrqPCINT        = VectorLAPIC+4,
+       IrqSPURIOUS     = VectorLAPIC+15,
+       MaxIrqLAPIC     = VectorLAPIC+15,
+
+       VectorSYSCALL   = 64,
+
+       VectorAPIC      = 65,           /* external APIC interrupts */
+       MaxVectorAPIC   = 255,
+};
+
+enum {
+       IdtPIC          = 32,                   /* external i8259 interrupts */
+
+       IdtLINT0        = 48,                   /* local APIC interrupts */
+       IdtLINT1        = 49,
+       IdtTIMER        = 50,
+       IdtERROR        = 51,
+       IdtPCINT        = 52,
+
+       IdtIPI          = 62,
+       IdtSPURIOUS     = 63,
+
+       IdtSYSCALL      = 64,
+
+       IdtIOAPIC       = 65,                   /* external APIC interrupts */
+
+       IdtMAX          = 255,
+};
+
+typedef struct Vkey {
+       int     tbdf;                   /* pci: ioapic or msi sources */
+       int     irq;                    /* 8259-emulating sources */
+} Vkey;
+
+typedef struct Vctl {
+       Vctl*   next;                   /* handlers on this vector */
+
+       int     isintr;                 /* interrupt or fault/trap */
+
+       Vkey;                           /* source-specific key; tbdf for pci */
+       void    (*f)(Ureg*, void*);     /* handler to call */
+       void*   a;                      /* argument to call it with */
+       char    name[KNAMELEN];         /* of driver */
+       char    *type;
+
+       int     (*isr)(int);            /* get isr bit for this irq */
+       int     (*eoi)(int);            /* eoi */
+       int     (*mask)(Vkey*, int);    /* interrupt enable returns masked vector */
+       int     vno;
+} Vctl;
+
+typedef struct ACVctl {
+       char*   (*f)(Ureg*,void*);
+       void*   a;
+       int     vno;
+       char    name[KNAMELEN];         /* of driver */
+} ACVctl;
+
+enum {
+       BusCBUS         = 0,            /* Corollary CBUS */
+       BusCBUSII,                      /* Corollary CBUS II */
+       BusEISA,                        /* Extended ISA */
+       BusFUTURE,                      /* IEEE Futurebus */
+       BusINTERN,                      /* Internal bus */
+       BusISA,                         /* Industry Standard Architecture */
+       BusMBI,                         /* Multibus I */
+       BusMBII,                        /* Multibus II */
+       BusMCA,                         /* Micro Channel Architecture */
+       BusMPI,                         /* MPI */
+       BusMPSA,                        /* MPSA */
+       BusNUBUS,                       /* Apple Macintosh NuBus */
+       BusPCI,                         /* Peripheral Component Interconnect */
+       BusPCMCIA,                      /* PC Memory Card International Association */
+       BusTC,                          /* DEC TurboChannel */
+       BusVL,                          /* VESA Local bus */
+       BusVME,                         /* VMEbus */
+       BusXPRESS,                      /* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)   (((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)   (((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)   (((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)  ((tbdf)>>24)
+#define BUSBDF(tbdf)   ((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN     (-1)
+
+enum {
+       MaxEISA         = 16,
+       CfgEISA         = 0xC80,
+};
+
+/*
+ * PCI support code.
+ */
+enum {                                 /* type 0 and type 1 pre-defined header */
+       PciVID          = 0x00,         /* vendor ID */
+       PciDID          = 0x02,         /* device ID */
+       PciPCR          = 0x04,         /* command */
+       PciPSR          = 0x06,         /* status */
+       PciRID          = 0x08,         /* revision ID */
+       PciCCRp         = 0x09,         /* programming interface class code */
+       PciCCRu         = 0x0A,         /* sub-class code */
+       PciCCRb         = 0x0B,         /* base class code */
+       PciCLS          = 0x0C,         /* cache line size */
+       PciLTR          = 0x0D,         /* latency timer */
+       PciHDT          = 0x0E,         /* header type */
+       PciBST          = 0x0F,         /* BIST */
+
+       PciBAR0         = 0x10,         /* base address */
+       PciBAR1         = 0x14,
+
+       PciCP           = 0x34,         /* capabilities pointer */
+
+       PciINTL         = 0x3C,         /* interrupt line */
+       PciINTP         = 0x3D,         /* interrupt pin */
+};
+
+/* ccrb (base class code) values; controller types */
+enum {
+       Pcibcpci1       = 0,            /* pci 1.0; no class codes defined */
+       Pcibcstore      = 1,            /* mass storage */
+       Pcibcnet        = 2,            /* network */
+       Pcibcdisp       = 3,            /* display */
+       Pcibcmmedia     = 4,            /* multimedia */
+       Pcibcmem        = 5,            /* memory */
+       Pcibcbridge     = 6,            /* bridge */
+       Pcibccomm       = 7,            /* simple comms (e.g., serial) */
+       Pcibcbasesys    = 8,            /* base system */
+       Pcibcinput      = 9,            /* input */
+       Pcibcdock       = 0xa,          /* docking stations */
+       Pcibcproc       = 0xb,          /* processors */
+       Pcibcserial     = 0xc,          /* serial bus (e.g., USB) */
+       Pcibcwireless   = 0xd,          /* wireless */
+       Pcibcintell     = 0xe,          /* intelligent i/o */
+       Pcibcsatcom     = 0xf,          /* satellite comms */
+       Pcibccrypto     = 0x10,         /* encryption/decryption */
+       Pcibcdacq       = 0x11,         /* data acquisition & signal proc. */
+};
+
+/* ccru (sub-class code) values; common cases only */
+enum {
+       /* mass storage */
+       Pciscscsi       = 0,            /* SCSI */
+       Pciscide        = 1,            /* IDE (ATA) */
+       Pciscsata       = 6,            /* SATA */
+
+       /* network */
+       Pciscether      = 0,            /* Ethernet */
+
+       /* display */
+       Pciscvga        = 0,            /* VGA */
+       Pciscxga        = 1,            /* XGA */
+       Pcisc3d         = 2,            /* 3D */
+
+       /* bridges */
+       Pcischostpci    = 0,            /* host/pci */
+       Pciscpcicpci    = 1,            /* pci/pci */
+
+       /* simple comms */
+       Pciscserial     = 0,            /* 16450, etc. */
+       Pciscmultiser   = 1,            /* multiport serial */
+
+       /* serial bus */
+       Pciscusb        = 3,            /* USB */
+};
+
+enum {                                 /* type 0 pre-defined header */
+       PciCIS          = 0x28,         /* cardbus CIS pointer */
+       PciSVID         = 0x2C,         /* subsystem vendor ID */
+       PciSID          = 0x2E,         /* cardbus CIS pointer */
+       PciEBAR0        = 0x30,         /* expansion ROM base address */
+       PciMGNT         = 0x3E,         /* burst period length */
+       PciMLT          = 0x3F,         /* maximum latency between bursts */
+};
+
+enum {                                 /* type 1 pre-defined header */
+       PciPBN          = 0x18,         /* primary bus number */
+       PciSBN          = 0x19,         /* secondary bus number */
+       PciUBN          = 0x1A,         /* subordinate bus number */
+       PciSLTR         = 0x1B,         /* secondary latency timer */
+       PciIBR          = 0x1C,         /* I/O base */
+       PciILR          = 0x1D,         /* I/O limit */
+       PciSPSR         = 0x1E,         /* secondary status */
+       PciMBR          = 0x20,         /* memory base */
+       PciMLR          = 0x22,         /* memory limit */
+       PciPMBR         = 0x24,         /* prefetchable memory base */
+       PciPMLR         = 0x26,         /* prefetchable memory limit */
+       PciPUBR         = 0x28,         /* prefetchable base upper 32 bits */
+       PciPULR         = 0x2C,         /* prefetchable limit upper 32 bits */
+       PciIUBR         = 0x30,         /* I/O base upper 16 bits */
+       PciIULR         = 0x32,         /* I/O limit upper 16 bits */
+       PciEBAR1        = 0x28,         /* expansion ROM base address */
+       PciBCR          = 0x3E,         /* bridge control register */
+};
+
+enum {                                 /* type 2 pre-defined header */
+       PciCBExCA       = 0x10,
+       PciCBSPSR       = 0x16,
+       PciCBPBN        = 0x18,         /* primary bus number */
+       PciCBSBN        = 0x19,         /* secondary bus number */
+       PciCBUBN        = 0x1A,         /* subordinate bus number */
+       PciCBSLTR       = 0x1B,         /* secondary latency timer */
+       PciCBMBR0       = 0x1C,
+       PciCBMLR0       = 0x20,
+       PciCBMBR1       = 0x24,
+       PciCBMLR1       = 0x28,
+       PciCBIBR0       = 0x2C,         /* I/O base */
+       PciCBILR0       = 0x30,         /* I/O limit */
+       PciCBIBR1       = 0x34,         /* I/O base */
+       PciCBILR1       = 0x38,         /* I/O limit */
+       PciCBSVID       = 0x40,         /* subsystem vendor ID */
+       PciCBSID        = 0x42,         /* subsystem ID */
+       PciCBLMBAR      = 0x44,         /* legacy mode base address */
+};
+
+/* capabilities */
+enum {
+       PciCapPMG       = 0x01,         /* power management */
+       PciCapAGP       = 0x02,
+       PciCapVPD       = 0x03,         /* vital product data */
+       PciCapSID       = 0x04,         /* slot id */
+       PciCapMSI       = 0x05,
+       PciCapCHS       = 0x06,         /* compact pci hot swap */
+       PciCapPCIX      = 0x07,
+       PciCapHTC       = 0x08,         /* hypertransport irq conf */
+       PciCapVND       = 0x09,         /* vendor specific information */
+       PciCapPCIe      = 0x10,
+       PciCapMSIX      = 0x11,
+       PciCapSATA      = 0x12,
+       PciCapHSW       = 0x0c,         /* hot swap */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+       Pcidev* dev;
+       int     siz;
+       int     bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+       int     tbdf;                   /* type+bus+device+function */
+       ushort  vid;                    /* vendor ID */
+       ushort  did;                    /* device ID */
+
+       ushort  pcr;
+
+       uchar   rid;
+       uchar   ccrp;
+       uchar   ccru;
+       uchar   ccrb;
+       uchar   cls;
+       uchar   ltr;
+
+       struct {
+               ulong   bar;            /* base address */
+               int     size;
+       } mem[6];
+
+       struct {
+               ulong   bar;
+               int     size;
+       } rom;
+       uchar   intl;                   /* interrupt line */
+
+       Pcidev* list;
+       Pcidev* link;                   /* next device on this bno */
+
+       Pcidev* bridge;                 /* down a bus */
+       struct {
+               ulong   bar;
+               int     size;
+       } ioa, mema;
+};
+
+#define PCIWINDOW      0
+#define PCIWADDR(va)   (PADDR(va)+PCIWINDOW)
+#define ISAWINDOW      0
+#define ISAWADDR(va)   (PADDR(va)+ISAWINDOW)
+
+#pragma        varargck        type    "T"     int
diff --git a/kern/arch/x86/ioapic.c b/kern/arch/x86/ioapic.c
new file mode 100644 (file)
index 0000000..a71b1e2
--- /dev/null
@@ -0,0 +1,497 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include <vfs.h>
+#include <kfs.h>
+#include <slab.h>
+#include <kmalloc.h>
+#include <kref.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <error.h>
+#include <cpio.h>
+#include <pmap.h>
+#include <smp.h>
+#include <ip.h>
+#include <arch/io.h>
+
+struct Rbus {
+       struct Rbus     *next;
+       int     devno;
+       struct Rdt      *rdt;
+};
+
+struct Rdt {
+       struct apic     *apic;
+       int     intin;
+       uint32_t        lo;
+
+       int     ref;                            /* could map to multiple busses */
+       int     enabled;                                /* times enabled */
+};
+
+enum {                                         /* IOAPIC registers */
+       Ioregsel        = 0x00,                 /* indirect register address */
+       Iowin           = 0x04,                 /* indirect register data */
+       Ioipa           = 0x08,                 /* IRQ Pin Assertion */
+       Ioeoi           = 0x10,                 /* EOI */
+
+       Ioapicid        = 0x00,                 /* Identification */
+       Ioapicver       = 0x01,                 /* Version */
+       Ioapicarb       = 0x02,                 /* Arbitration */
+       Ioabcfg         = 0x03,                 /* Boot Coniguration */
+       Ioredtbl        = 0x10,                 /* Redirection Table */
+};
+
+static struct Rdt rdtarray[Nrdt];
+static int nrdtarray;
+static struct Rbus* rdtbus[Nbus];
+static struct Rdt* rdtvecno[IdtMAX+1];
+
+static spinlock_t idtnolock;
+static int idtno = IdtIOAPIC;
+
+struct apic    xioapic[Napic];
+
+static void
+rtblget(struct apic* apic, int sel, uint32_t* hi, uint32_t* lo)
+{
+       sel = Ioredtbl + 2*sel;
+
+       *(apic->addr+Ioregsel) = sel+1;
+       *hi = *(apic->addr+Iowin);
+       *(apic->addr+Ioregsel) = sel;
+       *lo = *(apic->addr+Iowin);
+}
+
+static void
+rtblput(struct apic* apic, int sel, uint32_t hi, uint32_t lo)
+{
+       sel = Ioredtbl + 2*sel;
+
+       *(apic->addr+Ioregsel) = sel+1;
+       *(apic->addr+Iowin) = hi;
+       *(apic->addr+Ioregsel) = sel;
+       *(apic->addr+Iowin) = lo;
+}
+
+struct Rdt*
+rdtlookup(struct apic *apic, int intin)
+{
+       int i;
+       struct Rdt *r;
+
+       for(i = 0; i < nrdtarray; i++){
+               r = rdtarray + i;
+               if(apic == r->apic && intin == r->intin)
+                       return r;
+       }
+       return NULL;
+}
+
+void
+ioapicintrinit(int busno, int apicno, int intin, int devno, uint32_t lo)
+{
+       struct Rbus *rbus;
+       struct Rdt *rdt;
+       struct apic *apic;
+
+       if(busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt)
+               return;
+       apic = &xioapic[apicno];
+       if(!apic->useable || intin >= apic->nrdt)
+               return;
+
+       rdt = rdtlookup(apic, intin);
+       if(rdt == NULL){
+               rdt = &rdtarray[nrdtarray++];
+               rdt->apic = apic;
+               rdt->intin = intin;
+               rdt->lo = lo;
+       }else{
+               if(lo != rdt->lo){
+                       printd("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
+                               busno, apicno, intin, devno, lo, rdt->lo);
+                       return;
+               }
+               printk("dup rdt %d %d %d %d %.8ux\n", busno, apicno, intin, devno, lo);
+       }
+       rdt->ref++;
+       rbus = kzmalloc(sizeof *rbus, 0);
+       rbus->rdt = rdt;
+       rbus->devno = devno;
+       rbus->next = rdtbus[busno];
+       rdtbus[busno] = rbus;
+}
+
+void
+ioapicinit(int id, int ibase, uintptr_t_t pa)
+{
+       struct apic *apic;
+       static int base;
+
+       /*
+        * Mark the IOAPIC useable if it has a good ID
+        * and the registers can be mapped.
+        */
+       if(id >= Napic)
+               return;
+
+       apic = &xioapic[id];
+       if(apic->useable || (apic->addr = vmap(pa, 1024)) == NULL)
+               return;
+       apic->useable = 1;
+       apic->paddr = pa;
+
+       /*
+        * Initialise the I/O APIC.
+        * The MultiProcessor Specification says it is the
+        * responsibility of the O/S to set the APIC ID.
+        */
+       spin_lock(&apic->lock);
+       *(apic->addr+Ioregsel) = Ioapicver;
+       apic->nrdt = ((*(apic->addr+Iowin)>>16) & 0xff) + 1;
+       if(ibase != -1)
+               apic->ibase = ibase;
+       else{
+               apic->ibase = base;
+               base += apic->nrdt;
+       }
+       *(apic->addr+Ioregsel) = Ioapicid;
+       *(apic->addr+Iowin) = id<<24;
+       spin_unlock(&apic->lock);
+}
+
+void
+ioapicdump(void)
+{
+       int i, n;
+       struct Rbus *rbus;
+       struct Rdt *rdt;
+       struct apic *apic;
+       uint32_t hi, lo;
+
+       if(!2)
+               return;
+       for(i = 0; i < Napic; i++){
+               apic = &xioapic[i];
+               if(!apic->useable || apic->addr == 0)
+                       continue;
+               printd("ioapic %d addr %#p nrdt %d ibase %d\n",
+                       i, apic->addr, apic->nrdt, apic->ibase);
+               for(n = 0; n < apic->nrdt; n++){
+                       spin_lock(&apic->lock);
+                       rtblget(apic, n, &hi, &lo);
+                       spin_unlock(&apic->lock);
+                       printd(" rdt %2.2d %#8.8ux %#8.8ux\n", n, hi, lo);
+               }
+       }
+       for(i = 0; i < Nbus; i++){
+               if((rbus = rdtbus[i]) == NULL)
+                       continue;
+               printd("iointr bus %d:\n", i);
+               for(; rbus != NULL; rbus = rbus->next){
+                       rdt = rbus->rdt;
+                       printd(" apic %ld devno %#ux (%d %d) intin %d lo %#ux ref %d\n",
+                               rdt->apic-xioapic, rbus->devno, rbus->devno>>2,
+                               rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
+               }
+       }
+}
+
+void
+ioapiconline(void)
+{
+       int i;
+       struct apic *apic;
+
+       for(apic = xioapic; apic < &xioapic[Napic]; apic++){
+               if(!apic->useable || apic->addr == NULL)
+                       continue;
+               for(i = 0; i < apic->nrdt; i++){
+                       spin_lock(&apic->lock);
+                       rtblput(apic, i, 0, Im);
+                       spin_unlock(&apic->lock);
+               }
+       }
+       ioapicdump();
+}
+
+static int dfpolicy = 0;
+
+static void
+ioapicintrdd(uint32_t* hi, uint32_t* lo)
+{
+       int i;
+       static int df;
+       static spinlock_t dflock;
+
+       /*
+        * Set delivery mode (lo) and destination field (hi),
+        * according to interrupt routing policy.
+        */
+       /*
+        * The bulk of this code was written ~1995, when there was
+        * one architecture and one generation of hardware, the number
+        * of CPUs was up to 4(8) and the choices for interrupt routing
+        * were physical, or flat logical (optionally with lowest
+        * priority interrupt). Logical mode hasn't scaled well with
+        * the increasing number of packages/cores/threads, so the
+        * fall-back is to physical mode, which works across all processor
+        * generations, both AMD and Intel, using the APIC and xAPIC.
+        *
+        * Interrupt routing policy can be set here.
+        */
+       switch(dfpolicy){
+       default:                                /* noise core 0 */
+               *hi = sys->machptr[0]->apicno<<24;
+               break;
+       case 1:                                 /* round-robin */
+               /*
+                * Assign each interrupt to a different CPU on a round-robin
+                * Some idea of the packages/cores/thread topology would be
+                * useful here, e.g. to not assign interrupts to more than one
+                * thread in a core. But, as usual, Intel make that an onerous
+                * task.
+                */
+               spin_lock(&(&dflock)->lock);
+               for(;;){
+                       i = df++;
+                       if(df >= sys->nmach+1)
+                               df = 0;
+                       if(sys->machptr[i] == NULL || !sys->machptr[i]->online)
+                               continue;
+                       i = sys->machptr[i]->apicno;
+                       if(xlapic[i].useable && xlapic[i].addr == 0)
+                               break;
+               }
+               spin_unlock(&(&dflock)->lock);
+
+               *hi = i<<24;
+               break;
+       }
+       *lo |= Pm|MTf;
+}
+
+int
+nextvec(void)
+{
+       unsigned int vecno;
+
+       spin_lock(&(&idtnolock)->lock);
+       vecno = idtno;
+       idtno = (idtno+8) % IdtMAX;
+       if(idtno < IdtIOAPIC)
+               idtno += IdtIOAPIC;
+       spin_unlock(&(&idtnolock)->lock);
+
+       return vecno;
+}
+
+static int
+msimask(Vkey *v, int mask)
+{
+       Pcidev *p;
+
+       p = pcimatchtbdf(v->tbdf);
+       if(p == NULL)
+               return -1;
+       return pcimsimask(p, mask);
+}
+
+static int
+intrenablemsi(Vctl* v, Pcidev *p)
+{
+       unsigned int vno, lo, hi;
+       uint64_t msivec;
+
+       vno = nextvec();
+
+       lo = IPlow | TMedge | vno;
+       ioapicintrdd(&hi, &lo);
+
+       if(lo & Lm)
+               lo |= MTlp;
+
+       msivec = (uint64_t)hi<<32 | lo;
+       if(pcimsienable(p, msivec) == -1)
+               return -1;
+       v->isr = apicisr;
+       v->eoi = apiceoi;
+       v->vno = vno;
+       v->type = "msi";
+       v->mask = msimask;
+
+       printk("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno);
+       return vno;
+}
+
+int
+disablemsi(Vctl*, Pcidev *p)
+{
+       if(p == NULL)
+               return -1;
+       return pcimsimask(p, 1);
+}
+
+int
+ioapicintrenable(Vctl* v)
+{
+       struct Rbus *rbus;
+       struct Rdt *rdt;
+       uint32_t hi, lo;
+       int busno, devno, vecno;
+
+       /*
+        * Bridge between old and unspecified new scheme,
+        * the work in progress...
+        */
+       if(v->tbdf == BUSUNKNOWN){
+               if(v->irq >= IrqLINT0 && v->irq <= MaxIrqLAPIC){
+                       if(v->irq != IrqSPURIOUS)
+                               v->isr = apiceoi;
+                       v->type = "lapic";
+                       return v->irq;
+               }
+               else{
+                       /*
+                        * Legacy ISA.
+                        * Make a busno and devno using the
+                        * ISA bus number and the irq.
+                        */
+                       extern int mpisabusno;
+
+                       if(mpisabusno == -1)
+                               panic("no ISA bus allocated");
+                       busno = mpisabusno;
+                       devno = v->irq<<2;
+               }
+       }
+       else if(BUSTYPE(v->tbdf) == BusPCI){
+               /*
+                * PCI.
+                * Make a devno from BUSDNO(tbdf) and pcidev->intp.
+                */
+               Pcidev *pcidev;
+
+               busno = BUSBNO(v->tbdf);
+               if((pcidev = pcimatchtbdf(v->tbdf)) == NULL)
+                       panic("no PCI dev for tbdf %#8.8ux", v->tbdf);
+               if((vecno = intrenablemsi(v, pcidev)) != -1)
+                       return vecno;
+               disablemsi(v, pcidev);
+               if((devno = pcicfgr8(pcidev, PciINTP)) == 0)
+                       panic("no INTP for tbdf %#8.8ux", v->tbdf);
+               devno = BUSDNO(v->tbdf)<<2|(devno-1);
+               printk("ioapicintrenable: tbdf %#8.8ux busno %d devno %d\n",
+                       v->tbdf, busno, devno);
+       }
+       else{
+               SET(busno, devno);
+               panic("unknown tbdf %#8.8ux", v->tbdf);
+       }
+
+       rdt = NULL;
+       for(rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next)
+               if(rbus->devno == devno){
+                       rdt = rbus->rdt;
+                       break;
+               }
+       if(rdt == NULL){
+               extern int mpisabusno;
+
+               /*
+                * First crack in the smooth exterior of the new code:
+                * some BIOS make an MPS table where the PCI devices are
+                * just defaulted to ISA.
+                * Rewrite this to be cleaner.
+                */
+               if((busno = mpisabusno) == -1)
+                       return -1;
+               devno = v->irq<<2;
+               for(rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next)
+                       if(rbus->devno == devno){
+                               rdt = rbus->rdt;
+                               break;
+                       }
+               printk("isa: tbdf %#8.8ux busno %d devno %d %#p\n",
+                       v->tbdf, busno, devno, rdt);
+       }
+       if(rdt == NULL)
+               return -1;
+
+       /*
+        * Second crack:
+        * what to do about devices that intrenable/intrdisable frequently?
+        * 1) there is no ioapicdisable yet;
+        * 2) it would be good to reuse freed vectors.
+        * Oh bugger.
+        */
+       /*
+        * This is a low-frequency event so just lock
+        * the whole IOAPIC to initialise the RDT entry
+        * rather than putting a Lock in each entry.
+        */
+       spin_lock(&rdt->apic->lock);
+       printk("%T: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno, rdt->intin, devno);
+       if((rdt->lo & 0xff) == 0){
+               vecno = nextvec();
+               rdt->lo |= vecno;
+               rdtvecno[vecno] = rdt;
+       }else
+               printk("%T: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
+
+       rdt->enabled++;
+       lo = (rdt->lo & ~Im);
+       ioapicintrdd(&hi, &lo);
+       rtblput(rdt->apic, rdt->intin, hi, lo);
+       vecno = lo & 0xff;
+       spin_unlock(&rdt->apic->lock);
+
+       printk("busno %d devno %d hi %#8.8ux lo %#8.8ux vecno %d\n",
+               busno, devno, hi, lo, vecno);
+       v->isr = apicisr;
+       v->eoi = apiceoi;
+       v->vno = vecno;
+       v->type = "ioapic";
+
+       return vecno;
+}
+
+int
+ioapicintrdisable(int vecno)
+{
+       struct Rdt *rdt;
+
+       /*
+        * FOV. Oh dear. This isn't very good.
+        * Fortunately rdtvecno[vecno] is static
+        * once assigned.
+        * Must do better.
+        *
+        * What about any pending interrupts?
+        */
+       if(vecno < 0 || vecno > MaxVectorAPIC){
+               panic("ioapicintrdisable: vecno %d out of range", vecno);
+               return -1;
+       }
+       if((rdt = rdtvecno[vecno]) == NULL){
+               panic("ioapicintrdisable: vecno %d has no rdt", vecno);
+               return -1;
+       }
+
+       spin_lock(&rdt->apic->lock);
+       rdt->enabled--;
+       if(rdt->enabled == 0)
+               rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
+       spin_unlock(&rdt->apic->lock);
+
+       return 0;
+}
index d6f493a..87733ac 100644 (file)
@@ -1,13 +1,90 @@
-/*
- * Copyright (c) 2009 The Regents of the University of California
- * See LICENSE for details.
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
  */
 
 #ifndef ROS_KERN_IOAPIC_H
 #define ROS_KERN_IOAPIC_H
+struct bus {
+       uint8_t type;
+       uint8_t busno;
+       uint8_t po;
+       uint8_t el;
+
+       struct aintr*   aintr;                  /* interrupts tied to this bus */
+       struct bus*     next;
+};
+
+struct aintr {
+       // no idea yet. PCMPintr* intr;
+       struct apic*    apic;
+       struct aintr*   next;
+};
+
+struct apic {
+       int     useable;
+       int     type;
+       int     apicno;
+       uint32_t*       addr;                   /* register base address */
+       uint32_t        paddr;
+       int     flags;                  /* PcmpBP|PcmpEN */
+
+       //spinlock_t lock;              /* I/O APIC: register access */
+       int     mre;                    /* I/O APIC: maximum redirection entry */
+
+       int     lintr[2];               /* Local APIC */
+       int     machno;
+
+       int     online;
+};
+
+enum {
+       MaxAPICNO       = 254,          /* 255 is physical broadcast */
+};
+
+enum {                                 /* I/O APIC registers */
+       IoapicID        = 0x00,         /* ID */
+       IoapicVER       = 0x01,         /* version */
+       IoapicARB       = 0x02,         /* arbitration ID */
+       IoapicRDT       = 0x10,         /* redirection table */
+};
+
+/*
+ * Common bits for
+ *     I/O APIC Redirection Table Entry;
+ *     Local APIC Local Interrupt Vector Table;
+ *     Local APIC Inter-Processor Interrupt;
+ *     Local APIC Timer Vector Table.
+ */
+enum {
+       ApicFIXED       = 0x00000000,   /* [10:8] Delivery Mode */
+       ApicLOWEST      = 0x00000100,   /* Lowest priority */
+       ApicSMI         = 0x00000200,   /* System Management Interrupt */
+       ApicRR          = 0x00000300,   /* Remote Read */
+       ApicNMI         = 0x00000400,
+       ApicINIT        = 0x00000500,   /* INIT/RESET */
+       ApicSTARTUP     = 0x00000600,   /* Startup IPI */
+       ApicExtINT      = 0x00000700,
+
+       ApicPHYSICAL    = 0x00000000,   /* [11] Destination Mode (RW) */
+       ApicLOGICAL     = 0x00000800,
+
+       ApicDELIVS      = 0x00001000,   /* [12] Delivery Status (RO) */
+       ApicHIGH        = 0x00000000,   /* [13] Interrupt Input Pin Polarity (RW) */
+       ApicLOW         = 0x00002000,
+       ApicRemoteIRR   = 0x00004000,   /* [14] Remote IRR (RO) */
+       ApicEDGE        = 0x00000000,   /* [15] Trigger Mode (RW) */
+       ApicLEVEL       = 0x00008000,
+       ApicIMASK       = 0x00010000,   /* [16] Interrupt Mask */
+       IOAPIC_PBASE    = 0xfec00000, /* default *physical* address */
+};
 
-/* Physical address of the IOAPIC, can be changed.  Currently, it's mapped at
- * the VADDR IOAPIC_BASE */
-#define IOAPIC_PBASE                           0xfec00000 /* default *physical* address */
+extern void ioapicinit(struct apic*, int);
+extern void ioapicrdtr(struct apic*, int unused_int, int*, int*);
+extern void ioapicrdtw(struct apic*, int unused_int, int, int);
 
 #endif /* ROS_KERN_IOAPIC_H */
diff --git a/kern/arch/x86/mpacpi.c b/kern/arch/x86/mpacpi.c
new file mode 100644 (file)
index 0000000..d02562c
--- /dev/null
@@ -0,0 +1,70 @@
+#include <vfs.h>
+#include <kfs.h>
+#include <slab.h>
+#include <kmalloc.h>
+#include <kref.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <error.h>
+#include <cpio.h>
+#include <pmap.h>
+#include <smp.h>
+#include <ip.h>
+#include <acpi.h>
+#include <arch/ioapic.h>
+
+extern struct Madt     *apics;
+
+int
+mpacpi(int ncleft)
+{
+       char *already;
+       int np, bp;
+       struct apic *apic;
+       struct Apicst *st;
+
+       if (apics == NULL)
+               return ncleft;
+
+       printd("APIC lapic paddr %#.8llux, flags %#.8ux\n",
+               apics->lapicpa, apics->pcat);
+       np = 0;
+       for(st = apics->st; st != NULL; st = st->next){
+               already = "";
+               switch(st->type){
+               case ASlapic:
+                       /* this table is supposed to have all of them if it exists */
+                       if(st->lapic.id > MaxAPICNO)
+                               break;
+                       apic = xlapic + st->lapic.id;
+                       bp = (np++ == 0);
+                       if(apic->useable){
+                               already = "(mp)";
+                       }
+                       else if(ncleft != 0){
+                               ncleft--;
+                               apicinit(st->lapic.id, apics->lapicpa, bp);
+                       } else
+                               already = "(off)";
+
+                       printd("apic proc %d/%d apicid %d %s\n", np-1, apic->machno, st->lapic.id, already);
+                       break;
+               case ASioapic:
+                       if(st->ioapic.id > Napic)
+                               break;
+                       apic = xioapic + st->ioapic.id;
+                       if(apic->useable){
+                               apic->ibase = st->ioapic.ibase; /* gnarly */
+                               already = "(mp)";
+                               goto pr1;
+                       }
+                       ioapicinit(st->ioapic.id, st->ioapic.ibase, st->ioapic.addr);
+               pr1:
+                       printd("ioapic %d ", st->ioapic.id);
+                       printd("addr %p base %d %s\n", apic->paddr, apic->ibase, already);
+                       break;
+               }
+       }
+       return ncleft;
+}
index bac174a..d54bba8 100644 (file)
@@ -116,6 +116,7 @@ acpiregid(char *s)
        return -1;
 }
 
+#if 0
 static uint64_t
 l64get(uint8_t* p)
 {
@@ -127,7 +128,7 @@ l64get(uint8_t* p)
         */
        return (((uint64_t)l32get(p+4)<<32)|l32get(p));
 }
-
+#endif
 static uint8_t
 mget8(uintptr_t p, void*unused)
 {
index ff5a585..3dca855 100644 (file)
@@ -476,4 +476,33 @@ typedef SDreq;
 -SDreq
 +struct sdreq
 
+@@
+typedef Bus;
+@@
+-Bus
++struct bus
+
+@@
+typedef Aintr;
+@@
+-Aintr
++struct aintr
+
+@@
+typedef Apic;
+@@
+-Apic
++struct apic
+
+@@
+typedef Ioapic;
+@@
+-Ioapic
++struct ioapic
+
+@@
+typedef Lapic;
+@@
+-Lapic
++struct lapic