akaros/kern/arch/x86/apic9.c
<<
>>
Prefs
   1#include <slab.h>
   2#include <kmalloc.h>
   3#include <kref.h>
   4#include <string.h>
   5#include <stdio.h>
   6#include <assert.h>
   7#include <error.h>
   8#include <cpio.h>
   9#include <pmap.h>
  10#include <smp.h>
  11#include <net/ip.h>
  12#include <arch/io.h>
  13#include <trap.h>
  14
  15static char *apicregnames[] = {
  16        [MSR_LAPIC_ID] "Identification",
  17        [MSR_LAPIC_VERSION] "Version",
  18        [MSR_LAPIC_TPR] "Task Priority",
  19//      [Ap] "Arbitration Priority",
  20        [MSR_LAPIC_PPR] "Processor Priority",
  21        [MSR_LAPIC_EOI] "EOI",
  22        [MSR_LAPIC_LDR] "Logical Destination",
  23        [MSR_LAPIC_SPURIOUS] "Spurious Interrupt Vector",
  24        [MSR_LAPIC_ISR_START] "Interrupt Status (8)",
  25        [MSR_LAPIC_TMR_START] "Trigger Mode (8)",
  26        [MSR_LAPIC_IRR_START] "Interrupt Request (8)",
  27        [MSR_LAPIC_ESR] "Error Status",
  28        [MSR_LAPIC_ICR] "Interrupt Command",
  29        [MSR_LAPIC_INITIAL_COUNT] "Timer Initial Count",
  30        [MSR_LAPIC_CURRENT_COUNT] "Timer Current Count",
  31        [MSR_LAPIC_DIVIDE_CONFIG_REG] "Timer Divide Configuration",
  32
  33        [MSR_LAPIC_LVT_TIMER] "Timer",
  34        [MSR_LAPIC_LVT_LINT0] "Local Interrupt 0",
  35        [MSR_LAPIC_LVT_LINT1] "Local Interrupt 1",
  36        [MSR_LAPIC_LVT_ERROR_REG] "Error",
  37        [MSR_LAPIC_LVT_PERFMON] "Performance Counter",
  38        [MSR_LAPIC_LVT_THERMAL] "Thermal Sensor",
  39};
  40
  41enum {                          /* Siv */
  42        Swen = 0x00000100,      /* Software Enable */
  43        Fdis = 0x00000200,      /* Focus Disable */
  44};
  45
  46enum {                          /* Iclo */
  47        Lassert = 0x00004000,   /* Assert level */
  48
  49        DSnone = 0x00000000,    /* Use Destination Field */
  50        DSself = 0x00040000,    /* Self is only destination */
  51        DSallinc = 0x00080000,  /* All including self */
  52        DSallexc = 0x000c0000,  /* All Excluding self */
  53};
  54
  55enum {                          /* Tlvt */
  56        Periodic = 0x00020000,  /* Periodic Timer Mode */
  57};
  58
  59enum {                          /* Tdc */
  60        DivX2 = 0x00000000,     /* Divide by 2 */
  61        DivX4 = 0x00000001,     /* Divide by 4 */
  62        DivX8 = 0x00000002,     /* Divide by 8 */
  63        DivX16 = 0x00000003,    /* Divide by 16 */
  64        DivX32 = 0x00000008,    /* Divide by 32 */
  65        DivX64 = 0x00000009,    /* Divide by 64 */
  66        DivX128 = 0x0000000a,   /* Divide by 128 */
  67        DivX1 = 0x0000000b,     /* Divide by 1 */
  68};
  69
  70static uintptr_t apicbase;
  71static int apmachno = 1;
  72static uint32_t apicr310;
  73
  74struct apic xlapic[Napic];
  75
  76static void __apic_ir_dump(uint64_t r);
  77
  78static void __apic_ir_dump(uint64_t r)
  79{
  80        int i;
  81        uint32_t val;
  82
  83        if (r != MSR_LAPIC_ISR_START && r != MSR_LAPIC_IRR_START &&
  84            r != MSR_LAPIC_TMR_START)
  85                panic("Invalid register dump offset!");
  86
  87        for (i = 7; i >= 0; i--) {
  88                val = apicrget(r+i);
  89                if (val) {
  90                        printk("Register at range (%d,%d]: 0x%08x\n",
  91                               ((i + 1) * 32), i * 32, val);
  92                }
  93        }
  94}
  95
  96void apic_isr_dump(void)
  97{
  98        printk("ISR DUMP\n");
  99        __apic_ir_dump(MSR_LAPIC_ISR_START);
 100}
 101
 102void apic_irr_dump(void)
 103{
 104        printk("IRR DUMP\n");
 105        __apic_ir_dump(MSR_LAPIC_IRR_START);
 106}
 107
 108uint32_t apicrget(uint64_t r)
 109{
 110        uint32_t val;
 111
 112        if (r >= MSR_LAPIC_END)
 113                panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
 114        if (r != MSR_LAPIC_SPURIOUS && r != MSR_LAPIC_DIVIDE_CONFIG_REG)
 115                printd("%s: Reading from register 0x%llx\n",
 116                       __func__, r);
 117
 118        val = read_msr(r);
 119        printd("apicrget: %s returns %p\n", apicregnames[r], val);
 120        if (r == MSR_LAPIC_ID) {
 121                printd("APIC ID: 0x%lx\n", val);
 122                printd("APIC LOGICAL ID: 0x%lx\n",
 123                       apicrget(MSR_LAPIC_LDR));
 124        }
 125        return val;
 126}
 127
 128void apicrput(uint64_t r, uint32_t data)
 129{
 130        uint64_t temp_data = 0;
 131
 132        if (r >= MSR_LAPIC_END)
 133                panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
 134        if (r != MSR_LAPIC_INITIAL_COUNT && r != MSR_LAPIC_LVT_TIMER &&
 135            r != MSR_LAPIC_DIVIDE_CONFIG_REG && r != MSR_LAPIC_EOI)
 136                printd("%s: Writing to register 0x%llx, value 0x%lx\n",
 137                       __func__, r, data);
 138        if (r == MSR_LAPIC_ID)
 139                panic("ILLEGAL WRITE TO ID");
 140        printd("apicrput: %s = %p\n", apicregnames[r], data);
 141
 142        temp_data |= data;
 143
 144        write_msr(r, temp_data);
 145}
 146
 147void apicsendipi(uint64_t data)
 148{
 149        printd("SENDING IPI: 0x%016lx\n", data);
 150        write_msr(MSR_LAPIC_ICR, data);
 151}
 152
 153void apicinit(int apicno, uintptr_t pa, int isbp)
 154{
 155        struct apic *apic;
 156        uint64_t msr_val;
 157
 158        /*
 159         * Mark the APIC useable if it has a good ID
 160         * and the registers can be mapped.
 161         * The APIC Extended Broadcast and ID bits in the HyperTransport
 162         * Transaction Control register determine whether 4 or 8 bits
 163         * are used for the APIC ID. There is also xAPIC and x2APIC
 164         * to be dealt with sometime.
 165         */
 166        printd("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
 167        if (apicno >= Napic) {
 168                printd("apicinit%d: out of range\n", apicno);
 169                return;
 170        }
 171        if ((apic = &xlapic[apicno])->useable) {
 172                printd("apicinit%d: already initialised\n", apicno);
 173                return;
 174        }
 175        assert(pa == LAPIC_PBASE);
 176        apic->useable = 1;
 177
 178        /* plan 9 used to set up a mapping btw apic and pcpui like so:
 179                pcpui->apicno = apicno; // acpino is the hw_coreid
 180                apic->machno = apmachno++; // machno is the os_coreid
 181         * akaros does its own remapping of hw <-> os coreid during smp_boot */
 182
 183        //X2APIC INIT
 184        msr_val = read_msr(IA32_APIC_BASE);
 185        write_msr(IA32_APIC_BASE, msr_val | (3<<10));
 186}
 187
 188static char *apicdump0(char *start, char *end, struct apic *apic, int i)
 189{
 190        if (!apic->useable || apic->addr != 0)
 191                return start;
 192        start = seprintf(start, end,
 193                         "apic%d: oscore %d lint0 %#8.8p lint1 %#8.8p\n", i,
 194                         get_os_coreid(i), apic->lvt[0], apic->lvt[1]);
 195        start = seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
 196                         apicrget(MSR_LAPIC_LVT_THERMAL),
 197                         apicrget(MSR_LAPIC_LVT_PERFMON),
 198                         apicrget(MSR_LAPIC_LVT_ERROR_REG));
 199        start = seprintf(start, end,
 200                         " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
 201                         apicrget(MSR_LAPIC_LVT_TIMER),
 202                         apicrget(MSR_LAPIC_LVT_LINT0),
 203                         apicrget(MSR_LAPIC_LVT_LINT1),
 204                         apicrget(MSR_LAPIC_SPURIOUS));
 205        return start;
 206}
 207
 208char *apicdump(char *start, char *end)
 209{
 210        int i;
 211
 212        if (!2)
 213                return start;
 214
 215        start = seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase,
 216                         apmachno);
 217        for (i = 0; i < Napic; i++)
 218                start = apicdump0(start, end, xlapic + i, i);
 219        for (i = 0; i < Napic; i++)
 220                start = apicdump0(start, end, xioapic + i, i);
 221        return start;
 222}
 223
 224void handle_lapic_error(struct hw_trapframe *hw_tf, void *data)
 225{
 226        uint32_t err;
 227
 228        apicrput(MSR_LAPIC_ESR, 0);
 229        err = apicrget(MSR_LAPIC_ESR);
 230        /* i get a shitload of these on my nehalem, many with err == 0 */
 231        printd("LAPIC error vector, got 0x%08x\n", err);
 232}
 233
 234int apiconline(void)
 235{
 236        struct apic *apic;
 237        uint64_t tsc;
 238        uint32_t dfr, ver;
 239        int apicno, nlvt;
 240        uint64_t msr_val;
 241
 242        //X2APIC INIT
 243        msr_val = read_msr(IA32_APIC_BASE);
 244        write_msr(IA32_APIC_BASE, msr_val | (3<<10));
 245
 246        apicno = lapic_get_id();
 247        if (apicno >= Napic) {
 248                printk("Bad apicno %d on HW core %d!!\n", apicno, hw_core_id());
 249                return 0;
 250        }
 251        apic = &xlapic[apicno];
 252        /* The addr check tells us if it is an IOAPIC or not... */
 253        if (!apic->useable || apic->addr) {
 254                printk("Unsuitable apicno %d on HW core %d!!\n", apicno,
 255                       hw_core_id());
 256                return 0;
 257        }
 258        /* Things that can only be done when on the processor owning the APIC,
 259         * apicinit above runs on the bootstrap processor. */
 260        ver = apicrget(MSR_LAPIC_VERSION);
 261        nlvt = ((ver >> 16) & 0xff) + 1;
 262        if (nlvt > ARRAY_SIZE(apic->lvt)) {
 263                printk("apiconline%d: nlvt %d > max (%d)\n",
 264                           apicno, nlvt, ARRAY_SIZE(apic->lvt));
 265                nlvt = ARRAY_SIZE(apic->lvt);
 266        }
 267        apic->nlvt = nlvt;
 268        apic->ver = ver & 0xff;
 269
 270        /* These don't really matter in Physical mode; set the defaults anyway.
 271         * If we have problems with logical IPIs on AMD, check this out: */
 272        //if (memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
 273        //      dfr = 0xf0000000;
 274        //else
 275        //      dfr = 0xffffffff;
 276        //apicrput(Df, dfr);
 277        //apicrput(MSR_LAPIC_LDR, 0x00000000);
 278
 279        /* Disable interrupts until ready by setting the Task Priority register
 280         * to 0xff. */
 281        apicrput(MSR_LAPIC_TPR, 0xff);
 282
 283        /* Software-enable the APIC in the Spurious Interrupt Vector register
 284         * and set the vector number. The vector number must have bits 3-0 0x0f
 285         * unless the Extended Spurious Vector Enable bit is set in the
 286         * HyperTransport Transaction Control register. */
 287        apicrput(MSR_LAPIC_SPURIOUS, Swen | IdtLAPIC_SPURIOUS);
 288
 289        /* Acknowledge any outstanding interrupts. */
 290        apicrput(MSR_LAPIC_EOI, 0);
 291
 292        /* Mask interrupts on Performance Counter overflow and Thermal Sensor if
 293         * implemented, and on Lintr0 (Legacy INTR), Lintr1 (Legacy NMI), and
 294         * the Timer.  Clear any Error Status (write followed by read) and
 295         * enable the Error interrupt. */
 296        switch (apic->nlvt) {
 297        case 6:
 298                apicrput(MSR_LAPIC_LVT_THERMAL, Im);
 299                /* fall-through */
 300        case 5:
 301                apicrput(MSR_LAPIC_LVT_PERFMON, Im);
 302                /* fall-through */
 303        default:
 304                break;
 305        }
 306        /* lvt[0] and [1] were set to 0 in the BSS */
 307        apicrput(MSR_LAPIC_LVT_LINT1, apic->lvt[1] | Im | IdtLAPIC_LINT1);
 308        apicrput(MSR_LAPIC_LVT_LINT0, apic->lvt[0] | Im | IdtLAPIC_LINT0);
 309        apicrput(MSR_LAPIC_LVT_TIMER, Im);
 310
 311        apicrput(MSR_LAPIC_ESR, 0);
 312        apicrget(MSR_LAPIC_ESR);
 313        apicrput(MSR_LAPIC_LVT_ERROR_REG, IdtLAPIC_ERROR | Im);
 314
 315        /* Not sure we need this from plan 9, Akaros never did:
 316         *
 317         * Issue an INIT Level De-Assert to synchronise arbitration ID's.
 318         * (Necessary in this implementation? - not if Pentium 4 or Xeon (APIC
 319         * Version >= 0x14), or AMD). */
 320        //apicrput(Ichi, 0);
 321        //apicrput(Iclo, DSallinc | Lassert | MTir);
 322        //while (apicrget(Iclo) & Ds)
 323        //      cpu_relax();
 324
 325        /* this is to enable the APIC interrupts.  we did a SW lapic_enable()
 326         * earlier.  if we ever have issues where the lapic seems offline, check
 327         * here. */
 328        apicrput(MSR_LAPIC_TPR, 0);
 329        return 1;
 330}
 331