blob: e5eb11efc0ec2145695504bcde90725ef7a5c2bc [file] [log] [blame]
#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 <net/ip.h>
#include <arch/io.h>
#include <trap.h>
static char *apicregnames[] = {
[MSR_LAPIC_ID] "Identification",
[MSR_LAPIC_VERSION] "Version",
[MSR_LAPIC_TPR] "Task Priority",
// [Ap] "Arbitration Priority",
[MSR_LAPIC_PPR] "Processor Priority",
[MSR_LAPIC_EOI] "EOI",
[MSR_LAPIC_LDR] "Logical Destination",
[MSR_LAPIC_SPURIOUS] "Spurious Interrupt Vector",
[MSR_LAPIC_ISR_START] "Interrupt Status (8)",
[MSR_LAPIC_TMR_START] "Trigger Mode (8)",
[MSR_LAPIC_IRR_START] "Interrupt Request (8)",
[MSR_LAPIC_ESR] "Error Status",
[MSR_LAPIC_ICR] "Interrupt Command",
[MSR_LAPIC_INITIAL_COUNT] "Timer Initial Count",
[MSR_LAPIC_CURRENT_COUNT] "Timer Current Count",
[MSR_LAPIC_DIVIDE_CONFIG_REG] "Timer Divide Configuration",
[MSR_LAPIC_LVT_TIMER] "Timer",
[MSR_LAPIC_LVT_LINT0] "Local Interrupt 0",
[MSR_LAPIC_LVT_LINT1] "Local Interrupt 1",
[MSR_LAPIC_LVT_ERROR_REG] "Error",
[MSR_LAPIC_LVT_PERFMON] "Performance Counter",
[MSR_LAPIC_LVT_THERMAL] "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;
static uint32_t apicr310;
struct apic xlapic[Napic];
static void __apic_ir_dump(uint64_t r);
static void __apic_ir_dump(uint64_t r)
{
int i;
uint32_t val;
if (r != MSR_LAPIC_ISR_START && r != MSR_LAPIC_IRR_START &&
r != MSR_LAPIC_TMR_START)
panic("Invalid register dump offset!");
for (i = 7; i >= 0; i--) {
val = apicrget(r+i);
if (val) {
printk("Register at range (%d,%d]: 0x%08x\n",
((i + 1) * 32), i * 32, val);
}
}
}
void apic_isr_dump(void)
{
printk("ISR DUMP\n");
__apic_ir_dump(MSR_LAPIC_ISR_START);
}
void apic_irr_dump(void)
{
printk("IRR DUMP\n");
__apic_ir_dump(MSR_LAPIC_IRR_START);
}
uint32_t apicrget(uint64_t r)
{
uint32_t val;
if (r >= MSR_LAPIC_END)
panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
if (r != MSR_LAPIC_SPURIOUS && r != MSR_LAPIC_DIVIDE_CONFIG_REG)
printd("%s: Reading from register 0x%llx\n",
__func__, r);
val = read_msr(r);
printd("apicrget: %s returns %p\n", apicregnames[r], val);
if (r == MSR_LAPIC_ID) {
printd("APIC ID: 0x%lx\n", val);
printd("APIC LOGICAL ID: 0x%lx\n",
apicrget(MSR_LAPIC_LDR));
}
return val;
}
void apicrput(uint64_t r, uint32_t data)
{
uint64_t temp_data = 0;
if (r >= MSR_LAPIC_END)
panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
if (r != MSR_LAPIC_INITIAL_COUNT && r != MSR_LAPIC_LVT_TIMER &&
r != MSR_LAPIC_DIVIDE_CONFIG_REG && r != MSR_LAPIC_EOI)
printd("%s: Writing to register 0x%llx, value 0x%lx\n",
__func__, r, data);
if (r == MSR_LAPIC_ID)
panic("ILLEGAL WRITE TO ID");
printd("apicrput: %s = %p\n", apicregnames[r], data);
temp_data |= data;
write_msr(r, temp_data);
}
void apicsendipi(uint64_t data)
{
printd("SENDING IPI: 0x%016lx\n", data);
write_msr(MSR_LAPIC_ICR, data);
}
void apicinit(int apicno, uintptr_t pa, int isbp)
{
struct apic *apic;
uint64_t msr_val;
/*
* 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);
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 */
//X2APIC INIT
msr_val = read_msr(IA32_APIC_BASE);
write_msr(IA32_APIC_BASE, msr_val | (3<<10));
}
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(MSR_LAPIC_LVT_THERMAL),
apicrget(MSR_LAPIC_LVT_PERFMON),
apicrget(MSR_LAPIC_LVT_ERROR_REG));
start = seprintf(start, end,
" tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
apicrget(MSR_LAPIC_LVT_TIMER),
apicrget(MSR_LAPIC_LVT_LINT0),
apicrget(MSR_LAPIC_LVT_LINT1),
apicrget(MSR_LAPIC_SPURIOUS));
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(MSR_LAPIC_ESR, 0);
err = apicrget(MSR_LAPIC_ESR);
/* 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;
uint64_t msr_val;
//X2APIC INIT
msr_val = read_msr(IA32_APIC_BASE);
write_msr(IA32_APIC_BASE, msr_val | (3<<10));
apicno = lapic_get_id();
if (apicno >= 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(MSR_LAPIC_VERSION);
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(MSR_LAPIC_LDR, 0x00000000);
/* Disable interrupts until ready by setting the Task Priority register
* to 0xff. */
apicrput(MSR_LAPIC_TPR, 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(MSR_LAPIC_SPURIOUS, Swen | IdtLAPIC_SPURIOUS);
/* Acknowledge any outstanding interrupts. */
apicrput(MSR_LAPIC_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(MSR_LAPIC_LVT_THERMAL, Im);
/* fall-through */
case 5:
apicrput(MSR_LAPIC_LVT_PERFMON, Im);
/* fall-through */
default:
break;
}
/* lvt[0] and [1] were set to 0 in the BSS */
apicrput(MSR_LAPIC_LVT_LINT1, apic->lvt[1] | Im | IdtLAPIC_LINT1);
apicrput(MSR_LAPIC_LVT_LINT0, apic->lvt[0] | Im | IdtLAPIC_LINT0);
apicrput(MSR_LAPIC_LVT_TIMER, Im);
apicrput(MSR_LAPIC_ESR, 0);
apicrget(MSR_LAPIC_ESR);
apicrput(MSR_LAPIC_LVT_ERROR_REG, 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(MSR_LAPIC_TPR, 0);
return 1;
}