blob: d03d0d637c323b22a7aaf7f9b1fa77fa0da9fd3d [file] [log] [blame] [edit]
#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>
#include <trap.h>
enum { /* Local APIC registers */
Id = 0x0020, /* Identification */
Ver = 0x0030, /* Version */
Tp = 0x0080, /* Task Priority */
Ap = 0x0090, /* Arbitration Priority */
Pp = 0x00a0, /* Processor Priority */
Eoi = 0x00b0, /* EOI */
Ld = 0x00d0, /* Logical Destination */
Df = 0x00e0, /* Destination Format */
Siv = 0x00f0, /* Spurious Interrupt Vector */
Is = 0x0100, /* Interrupt Status (8) */
Tm = 0x0180, /* Trigger Mode (8) */
Ir = 0x0200, /* Interrupt Request (8) */
Es = 0x0280, /* Error Status */
Iclo = 0x0300, /* Interrupt Command */
Ichi = 0x0310, /* Interrupt Command [63:32] */
Lvt0 = 0x0320, /* Local Vector Table 0 */
Lvt5 = 0x0330, /* Local Vector Table 5 */
Lvt4 = 0x0340, /* Local Vector Table 4 */
Lvt1 = 0x0350, /* Local Vector Table 1 */
Lvt2 = 0x0360, /* Local Vector Table 2 */
Lvt3 = 0x0370, /* Local Vector Table 3 */
Tic = 0x0380, /* Timer Initial Count */
Tcc = 0x0390, /* Timer Current Count */
Tdc = 0x03e0, /* Timer Divide Configuration */
Tlvt = Lvt0, /* Timer */
Lint0 = Lvt1, /* Local Interrupt 0 */
Lint1 = Lvt2, /* Local Interrupt 1 */
Elvt = Lvt3, /* Error */
Pclvt = Lvt4, /* Performance Counter */
Tslvt = Lvt5, /* Thermal Sensor */
};
static char *apicregnames[] = {
[Id] "Identification",
[Ver] "Version",
[Tp] "Task Priority",
[Ap] "Arbitration Priority",
[Pp] "Processor Priority",
[Eoi] "EOI",
[Ld] "Logical Destination",
[Df] "Destination Format",
[Siv] "Spurious Interrupt Vector",
[Is] "Interrupt Status (8)",
[Tm] "Trigger Mode (8)",
[Ir] "Interrupt Request (8)",
[Es] "Error Status",
[Iclo] "Interrupt Command",
[Ichi] "Interrupt Command [63:32]",
[Lvt0] "Local Vector Table 0",
[Lvt5] "Local Vector Table 5",
[Lvt4] "Local Vector Table 4",
[Lvt1] "Local Vector Table 1",
[Lvt2] "Local Vector Table 2",
[Lvt3] "Local Vector Table 3",
[Tic] "Timer Initial Count",
[Tcc] "Timer Current Count",
[Tdc] "Timer Divide Configuration",
[Tlvt] "Timer",
[Lint0] "Local Interrupt 0",
[Lint1] "Local Interrupt 1",
[Elvt] "Error",
[Pclvt] "Performance Counter",
[Tslvt] "Thermal Sensor",
};
enum { /* Siv */
Swen = 0x00000100, /* Software Enable */
Fdis = 0x00000200, /* Focus Disable */
};
enum { /* Iclo */
Lassert = 0x00004000, /* Assert level */
DSnone = 0x00000000, /* Use Destination Field */
DSself = 0x00040000, /* Self is only destination */
DSallinc = 0x00080000, /* All including self */
DSallexc = 0x000c0000, /* All Excluding self */
};
enum { /* Tlvt */
Periodic = 0x00020000, /* Periodic Timer Mode */
};
enum { /* Tdc */
DivX2 = 0x00000000, /* Divide by 2 */
DivX4 = 0x00000001, /* Divide by 4 */
DivX8 = 0x00000002, /* Divide by 8 */
DivX16 = 0x00000003, /* Divide by 16 */
DivX32 = 0x00000008, /* Divide by 32 */
DivX64 = 0x00000009, /* Divide by 64 */
DivX128 = 0x0000000a, /* Divide by 128 */
DivX1 = 0x0000000b, /* Divide by 1 */
};
static uintptr_t apicbase;
static int apmachno = 1;
struct apic xlapic[Napic];
static uint32_t apicrget(int r)
{
uint32_t val;
if (!apicbase)
panic("apicrget: no apic");
val = read_mmreg32(apicbase + r);
printd("apicrget: %s returns %p\n", apicregnames[r], val);
return val;
}
static void apicrput(int r, uint32_t data)
{
if (!apicbase)
panic("apicrput: no apic");
printd("apicrput: %s = %p\n", apicregnames[r], data);
write_mmreg32(apicbase + r, data);
}
void apicinit(int apicno, uintptr_t pa, int isbp)
{
struct apic *apic;
/*
* Mark the APIC useable if it has a good ID
* and the registers can be mapped.
* The APIC Extended Broadcast and ID bits in the HyperTransport
* Transaction Control register determine whether 4 or 8 bits
* are used for the APIC ID. There is also xAPIC and x2APIC
* to be dealt with sometime.
*/
printd("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
if (apicno >= Napic) {
printd("apicinit%d: out of range\n", apicno);
return;
}
if ((apic = &xlapic[apicno])->useable) {
printd("apicinit%d: already initialised\n", apicno);
return;
}
assert(pa == LAPIC_PBASE);
apicbase = LAPIC_BASE; /* was the plan to just clobber the global? */
apic->useable = 1;
/* plan 9 used to set up a mapping btw apic and pcpui like so:
pcpui->apicno = apicno; // acpino is the hw_coreid
apic->machno = apmachno++; // machno is the os_coreid
* akaros does its own remapping of hw <-> os coreid during smp_boot */
}
static char *apicdump0(char *start, char *end, struct apic *apic, int i)
{
if (!apic->useable || apic->addr != 0)
return start;
start =
seprintf(start, end, "apic%d: oscore %d lint0 %#8.8p lint1 %#8.8p\n", i,
get_os_coreid(i), apic->lvt[0], apic->lvt[1]);
start =
seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
start =
seprintf(start, end,
" tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
apicrget(Tlvt), apicrget(Lint0), apicrget(Lint1),
apicrget(Siv));
return start;
}
char *apicdump(char *start, char *end)
{
int i;
if (!2)
return start;
start =
seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
for (i = 0; i < Napic; i++)
start = apicdump0(start, end, xlapic + i, i);
for (i = 0; i < Napic; i++)
start = apicdump0(start, end, xioapic + i, i);
return start;
}
void handle_lapic_error(struct hw_trapframe *hw_tf, void *data)
{
uint32_t err;
apicrput(Es, 0);
err = apicrget(Es);
/* i get a shitload of these on my nehalem, many with err == 0 */
printd("LAPIC error vector, got 0x%08x\n", err);
}
int apiconline(void)
{
struct apic *apic;
uint64_t tsc;
uint32_t dfr, ver;
int apicno, nlvt;
if (!apicbase) {
printk("No apicbase on HW core %d!!\n", hw_core_id());
return 0;
}
if ((apicno = ((apicrget(Id) >> 24) & 0xff)) >= Napic) {
printk("Bad apicno %d on HW core %d!!\n", apicno, hw_core_id());
return 0;
}
apic = &xlapic[apicno];
/* The addr check tells us if it is an IOAPIC or not... */
if (!apic->useable || apic->addr) {
printk("Unsuitable apicno %d on HW core %d!!\n", apicno, hw_core_id());
return 0;
}
/* Things that can only be done when on the processor owning the APIC,
* apicinit above runs on the bootstrap processor. */
ver = apicrget(Ver);
nlvt = ((ver >> 16) & 0xff) + 1;
if (nlvt > ARRAY_SIZE(apic->lvt)) {
printk("apiconline%d: nlvt %d > max (%d)\n",
apicno, nlvt, ARRAY_SIZE(apic->lvt));
nlvt = ARRAY_SIZE(apic->lvt);
}
apic->nlvt = nlvt;
apic->ver = ver & 0xff;
/* These don't really matter in Physical mode; set the defaults anyway. If
* we have problems with logical IPIs on AMD, check this out: */
//if (memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
// dfr = 0xf0000000;
//else
dfr = 0xffffffff;
apicrput(Df, dfr);
apicrput(Ld, 0x00000000);
/* Disable interrupts until ready by setting the Task Priority register to
* 0xff. */
apicrput(Tp, 0xff);
/* Software-enable the APIC in the Spurious Interrupt Vector register and
* set the vector number. The vector number must have bits 3-0 0x0f unless
* the Extended Spurious Vector Enable bit is set in the HyperTransport
* Transaction Control register. */
apicrput(Siv, Swen | IdtLAPIC_SPURIOUS);
/* Acknowledge any outstanding interrupts. */
apicrput(Eoi, 0);
/* Mask interrupts on Performance Counter overflow and Thermal Sensor if
* implemented, and on Lintr0 (Legacy INTR), Lintr1 (Legacy NMI), and the
* Timer. Clear any Error Status (write followed by read) and enable the
* Error interrupt. */
switch (apic->nlvt) {
case 6:
apicrput(Tslvt, Im);
/* fall-through */
case 5:
apicrput(Pclvt, Im);
/* fall-through */
default:
break;
}
/* lvt[0] and [1] were set to 0 in the BSS */
apicrput(Lint1, apic->lvt[1] | Im | IdtLAPIC_LINT1);
apicrput(Lint0, apic->lvt[0] | Im | IdtLAPIC_LINT0);
apicrput(Tlvt, Im);
apicrput(Es, 0);
apicrget(Es);
apicrput(Elvt, IdtLAPIC_ERROR | Im);
/* Not sure we need this from plan 9, Akaros never did:
*
* Issue an INIT Level De-Assert to synchronise arbitration ID's.
* (Necessary in this implementation? - not if Pentium 4 or Xeon (APIC
* Version >= 0x14), or AMD). */
//apicrput(Ichi, 0);
//apicrput(Iclo, DSallinc | Lassert | MTir);
//while (apicrget(Iclo) & Ds)
// cpu_relax();
/* this is to enable the APIC interrupts. we did a SW lapic_enable()
* earlier. if we ever have issues where the lapic seems offline, check
* here. */
apicrput(Tp, 0);
return 1;
}