Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / arch / x86_64 / kernel / lapic.c
diff --git a/kitten/arch/x86_64/kernel/lapic.c b/kitten/arch/x86_64/kernel/lapic.c
new file mode 100644 (file)
index 0000000..f10c372
--- /dev/null
@@ -0,0 +1,472 @@
+#include <lwk/kernel.h>
+#include <lwk/init.h>
+#include <lwk/resource.h>
+#include <lwk/cpuinfo.h>
+#include <lwk/smp.h>
+#include <lwk/delay.h>
+#include <arch/page.h>
+#include <arch/pgtable.h>
+#include <arch/fixmap.h>
+#include <arch/apicdef.h>
+#include <arch/apic.h>
+#include <arch/idt_vectors.h>
+#include <arch/tsc.h>
+
+/**
+ * Physical address of the local APIC memory mapping.
+ * If the system BIOS provided an MP configuration table, this is set in
+ * arch/x86_64/kernel/mpparse.c to the value parsed from the table.
+ * Otherwise, the default address is used.
+ */
+unsigned long lapic_phys_addr = APIC_DEFAULT_PHYS_BASE;
+
+/**
+ * Resource entry for the local APIC memory mapping.
+ */
+static struct resource lapic_resource = {
+       .name  = "Local APIC",
+       .flags = IORESOURCE_MEM | IORESOURCE_BUSY,
+       /* .start and .end filled in later based on detected address */
+};
+
+/**
+ * Creates a kernel mapping for the local APIC.
+ *
+ * The hardware/platform/BIOS maps each CPU's local APIC at the same location
+ * in physical memory. This function uses the 'fixmap' to map the local APIC
+ * into the kernel's virtual memory space at a fixed virtual address that is
+ * known at compile time. Since the local APIC's virtual address is known
+ * at compile time, local APIC registers can be accessed directly, without
+ * any pointer dereferencing.
+ */
+void __init
+lapic_map(void)
+{
+       if (!cpu_has_apic)
+               panic("No local APIC.");
+
+       /* Reserve physical memory used by the local APIC */
+       lapic_resource.start = lapic_phys_addr;
+       lapic_resource.end   = lapic_phys_addr + 4096 - 1;
+       request_resource(&iomem_resource, &lapic_resource);
+
+       /* Map local APIC into the kernel */ 
+       set_fixmap_nocache(FIX_APIC_BASE, lapic_phys_addr);
+
+       printk(KERN_DEBUG "Local APIC mapped to virtual address 0x%016lx\n",
+                         fix_to_virt(FIX_APIC_BASE));
+}
+
+/**
+ * Initializes the calling CPU's local APIC.
+ */
+void __init
+lapic_init(void)
+{
+       uint32_t val;
+
+       /*
+        * Initialize Destination Format Register.
+        * When using logical destination mode, we want to use the flat model.
+        */
+       apic_write(APIC_DFR, APIC_DFR_FLAT);
+
+       /*
+        * Initialize the Logical Destination Register.
+        * The LWK never uses logical destination mode, so just set it to the
+        * APIC's physical ID to avoid possible confusion.
+        */
+       val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
+       val |= SET_APIC_LOGICAL_ID( GET_APIC_ID(apic_read(APIC_ID)) );
+       apic_write(APIC_LDR, val);
+
+       /*
+        * Initialize the Task Priority Register.
+        * We set this to accept all (0) and never touch it again.
+        */
+       val = apic_read(APIC_TASKPRI) & ~APIC_TPRI_MASK;
+       apic_write(APIC_TASKPRI, val);
+
+       /*
+        * Intialize the Spurious-Interrupt Vector Register.
+        * This also enables the local APIC.
+        */
+       val = apic_read(APIC_SPIV) & ~APIC_VECTOR_MASK;
+       val |= (APIC_SPIV_APIC_ENABLED | APIC_SPURIOUS_VECTOR);
+       apic_write(APIC_SPIV, val);
+
+       /* Setup LVT[0] = APIC Timer Interrupt */
+       apic_write(APIC_LVTT, 0
+                    | APIC_DM_FIXED       /* route to fixed IDT vector */
+                    | APIC_TIMER_VECTOR   /* IDT vector to route to */
+                    | APIC_LVT_MASKED     /* initially disable */
+       );
+
+       /* Setup LVT[1] = Thermal Sensor Interrupt */
+       apic_write(APIC_LVTTHMR, 0
+                    | APIC_DM_FIXED       /* route to fixed IDT vector */     
+                    | APIC_THERMAL_VECTOR /* IDT vector to route to */
+       );
+
+       /* Setup LVT[2] = Performance Counter Interrupt */
+       apic_write(APIC_LVTPC, 0
+                    | APIC_DM_NMI         /* treat as non-maskable interrupt */
+                                          /* NMIs are routed to IDT vector 2 */
+                    | APIC_LVT_MASKED     /* initially disable */
+       );
+
+       /* Setup LVT[3] = Local Interrupt Pin 0 */
+       apic_write(APIC_LVT0, 0
+                    | APIC_DM_EXTINT      /* hooked up to old 8259A PIC   */
+                                          /* IDT vector provided by 8259A */
+                    | APIC_LVT_MASKED     /* disable */
+       );
+
+       /* Setup LVT[4] = Local Interrupt Pin 1 */
+       apic_write(APIC_LVT1, 0
+                    | APIC_DM_NMI         /* treat as non-maskable interrupt */
+                                          /* NMIs are routed to IDT vector 2 */
+                    | ((this_cpu != 0)
+                        ? APIC_LVT_MASKED /* mask on all but bootstrap CPU */
+                        : 0)              /* bootstrap CPU (0) receives NMIs */
+       );
+
+       /* Setup LVT[5] = Internal APIC Error Detector Interrupt */
+       apic_write(APIC_LVTERR, 0
+                    | APIC_DM_FIXED       /* route to fixed IDT vector */
+                    | APIC_ERROR_VECTOR   /* IDT vector to route to */
+       );
+       apic_write(APIC_ESR, 0); /* spec says to clear after enabling LVTERR */
+}
+
+void 
+lapic_set_timer(uint32_t count)
+{
+       uint32_t lvt;
+
+       /* Setup Divide Count Register to use the bus frequency directly. */
+       apic_write(APIC_TDCR, APIC_TDR_DIV_1);
+
+       /* Program the initial count register */
+       apic_write(APIC_TMICT, count);
+
+       /* Enable the local APIC timer */
+       lvt = apic_read(APIC_LVTT);
+       lvt &= ~APIC_LVT_MASKED;
+       lvt |= APIC_LVT_TIMER_PERIODIC;
+       apic_write(APIC_LVTT, lvt);
+}
+
+void
+lapic_stop_timer(void)
+{
+       uint32_t lvt;
+
+       /* Set the initial count to 0 */
+       apic_write(APIC_TMICT, 0);
+
+       /* Enable the local APIC timer */
+       lvt = apic_read(APIC_LVTT);
+       lvt |= APIC_LVT_MASKED;
+       apic_write(APIC_LVTT, lvt);
+}
+
+/**
+ * Detects the local APIC reference bus clock. The only sure-fire way to do
+ * this is to depend on some other absolute timing source. This function uses
+ * the CPU's cycle counter and the previously detected CPU clock frequency.
+ *
+ * NOTE: This assumes that the CPU's clock frequency has already been detected.
+ *       (i.e., cpu_info[cpu_id()].arch.tsc_khz has been initialized.
+ */
+unsigned int __init
+lapic_calibrate_timer(void)
+{
+       const unsigned int tick_count = 100000000;
+       cycles_t tsc_start, tsc_now;
+       uint32_t apic_start, apic_now;
+       unsigned int apic_Hz;
+
+       /* Start the APIC counter running for calibration */
+       lapic_set_timer(4000000000);
+
+       apic_start = apic_read(APIC_TMCCT);
+       tsc_start  = get_cycles_sync();
+
+       /* Spin until enough ticks for a meaningful result have elapsed */
+       do {
+               apic_now = apic_read(APIC_TMCCT);
+               tsc_now  = get_cycles_sync();
+       } while ( ((tsc_now    - tsc_start) < tick_count) &&
+                 ((apic_start - apic_now)  < tick_count) );
+
+       apic_Hz = (apic_start - apic_now) * 1000L *
+                 cpu_info[this_cpu].arch.tsc_khz / (tsc_now - tsc_start);
+
+       lapic_stop_timer();
+
+       return (apic_Hz / 1000);
+}
+
+static uint32_t
+lapic_wait4_icr_idle(void)
+{
+       uint32_t send_status;
+       int timeout;
+
+       /* Wait up to 100 milliseconds */
+       timeout = 0;
+       do {
+               send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+               if (!send_status)
+                       break;
+               udelay(100);
+       } while (timeout++ < 1000);
+
+       return send_status;
+}
+
+/**
+ * Returns the number of entries in the Local Vector Table minus one.
+ * 
+ * This should return 5 or higher on all x86_64 CPUs.
+ * 6 is returned if the APIC Thermal Interrupt is supported, 5 otherwise.
+ */
+static uint32_t
+lapic_get_maxlvt(void)
+{
+       return GET_APIC_MAXLVT(apic_read(APIC_LVR));
+}
+
+/**
+ * Sends an INIT inter-processor interrupt.
+ * This is used during bootstrap to wakeup the AP CPUs.
+ */
+void __init
+lapic_send_init_ipi(unsigned int cpu)
+{
+       uint32_t status;
+       unsigned int apic_id = cpu_info[cpu].arch.apic_id;
+
+       /* Turn on INIT at target CPU */
+       apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
+       apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT
+                               | APIC_DM_INIT);
+       status = lapic_wait4_icr_idle();
+       if (status)
+               panic("INIT IPI ERROR: failed to assert INIT. (%x)", status);
+       mdelay(10);
+
+       /* Turn off INIT at target CPU */
+       apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
+       apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
+       status = lapic_wait4_icr_idle();
+       if (status)
+               panic("INIT IPI ERROR: failed to deassert INIT. (%x)", status);
+}
+
+/**
+ * Send a STARTUP inter-processor interrupt.
+ * This is used during bootstrap to wakeup the AP CPUs.
+ */
+void __init
+lapic_send_startup_ipi(
+       unsigned int    cpu,            /* Logical CPU ID */
+       unsigned long   start_rip       /* Physical addr  */
+)
+{
+       uint32_t status;
+       unsigned int maxlvt  = lapic_get_maxlvt();
+       unsigned int apic_id = cpu_info[cpu].arch.apic_id;
+
+       /* Clear errors */
+       apic_write(APIC_ESR, 0);
+       apic_read(APIC_ESR);
+
+       /* Set target CPU */
+       apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
+
+       /* Send Startup IPI to target CPU */
+       apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
+       apic_write(APIC_ICR, APIC_DM_STARTUP | (start_rip >> 12));
+       udelay(300);  /* Give AP CPU some time to accept the IPI */
+       status = lapic_wait4_icr_idle();
+       if (status)
+               panic("STARTUP IPI ERROR: failed to send. (%x)", status);
+       udelay(300);  /* Give AP CPU some time to accept the IPI */
+
+       /* Fixup for Pentium erratum 3AP, clear errors */
+       if (maxlvt > 3)
+               apic_write(APIC_ESR, 0);
+
+       /* Verify that IPI was accepted */
+       status = (apic_read(APIC_ESR) & 0xEF);
+       if (status)
+               panic("STARTUP IPI ERROR: failed to accept. (%x)", status);
+}
+
+/**
+ * Sends an inter-processor interrupt (IPI) to the specified CPU.
+ * Note that the IPI has not necessarily been delivered when this function
+ * returns.
+ */
+void
+lapic_send_ipi(
+       unsigned int    cpu,            /* Logical CPU ID */
+       unsigned int    vector          /* Interrupt vector to send */
+)
+{
+       uint32_t status;
+       unsigned int apic_id;
+
+       /* Wait for idle */
+       status = lapic_wait4_icr_idle();
+       if (status)
+               panic("lapic_wait4_icr_idle() timed out. (%x)", status);
+
+       /* Set target CPU */
+       apic_id = cpu_info[cpu].arch.apic_id;
+       apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
+
+       /* Send the IPI */
+       if (unlikely(vector == NMI_VECTOR))
+               apic_write(APIC_ICR, APIC_DEST_PHYSICAL|APIC_DM_NMI);
+       else
+               apic_write(APIC_ICR, APIC_DEST_PHYSICAL|APIC_DM_FIXED|vector);
+}
+
+/**
+ * Converts an entry in a local APIC's Local Vector Table to a
+ * human-readable string.
+ */
+static char *
+lvt_stringify(uint32_t entry, char *buf)
+{
+       uint32_t delivery_mode = GET_APIC_DELIVERY_MODE(entry);
+
+       if (delivery_mode == APIC_MODE_FIXED) {
+               sprintf(buf, "FIXED -> IDT_VECTOR %d",
+                       entry & APIC_VECTOR_MASK
+               );
+       } else if (delivery_mode == APIC_MODE_NMI) {
+               sprintf(buf, "NMI   -> IDT VECTOR 2"); 
+       } else if (delivery_mode == APIC_MODE_EXTINT) {
+               sprintf(buf, "ExtINT, hooked to old 8259A PIC");
+       } else {
+               sprintf(buf, "UNKNOWN");
+       }
+
+       if (entry & APIC_LVT_MASKED)
+               strcat(buf, ", MASKED");
+
+       return buf;
+}
+
+/**
+ * Prints various local APIC registers of interest to the console.
+ */
+void
+lapic_dump(void)
+{
+       char buf[128];
+
+       printk(KERN_DEBUG "LOCAL APIC DUMP (LOGICAL CPU #%d):\n", this_cpu);
+
+       /*
+        * Lead off with the important stuff...
+        */
+       printk(KERN_DEBUG
+               "  ID:  0x%08x (id=%d)\n",
+               apic_read(APIC_ID),
+               GET_APIC_ID(apic_read(APIC_ID))
+       );
+       printk(KERN_DEBUG
+               "  VER: 0x%08x (version=0x%x, max_lvt=%d)\n",
+               apic_read(APIC_LVR),
+               GET_APIC_VERSION(apic_read(APIC_LVR)),
+               GET_APIC_MAXLVT(apic_read(APIC_LVR))
+       );
+       printk(KERN_DEBUG
+               "  ESR: 0x%08x (Error Status Reg, non-zero is bad)\n",
+               apic_read(APIC_ESR)
+       );
+       printk(KERN_DEBUG
+               "  SVR: 0x%08x (Spurious vector=%d, %s)\n",
+               apic_read(APIC_SPIV),
+               apic_read(APIC_SPIV) & APIC_VECTOR_MASK,
+               (apic_read(APIC_SPIV) & APIC_SPIV_APIC_ENABLED)
+                       ? "APIC IS ENABLED"
+                       : "APIC IS DISABLED"
+       );
+
+       /*
+        * Local Vector Table
+        */
+       printk(KERN_DEBUG "  Local Vector Table Entries:\n");
+       printk(KERN_DEBUG "      LVT[0] Timer:     0x%08x (%s)\n",
+               apic_read(APIC_LVTT),
+               lvt_stringify(apic_read(APIC_LVTT), buf)
+       );
+       printk(KERN_DEBUG "      LVT[1] Thermal:   0x%08x (%s)\n",
+               apic_read(APIC_LVTTHMR),
+               lvt_stringify(apic_read(APIC_LVTTHMR), buf)
+       );
+       printk(KERN_DEBUG "      LVT[2] Perf Cnt:  0x%08x (%s)\n",
+               apic_read(APIC_LVTPC),
+               lvt_stringify(apic_read(APIC_LVTPC), buf)
+       );
+       printk(KERN_DEBUG "      LVT[3] LINT0 Pin: 0x%08x (%s)\n",
+               apic_read(APIC_LVT0),
+               lvt_stringify(apic_read(APIC_LVT0), buf)
+       );
+       printk(KERN_DEBUG "      LVT[4] LINT1 Pin: 0x%08x (%s)\n",
+               apic_read(APIC_LVT1),
+               lvt_stringify(apic_read(APIC_LVT1), buf)
+       );
+       printk(KERN_DEBUG "      LVT[5] Error:     0x%08x (%s)\n",
+               apic_read(APIC_LVTERR),
+               lvt_stringify(apic_read(APIC_LVTERR), buf)
+       );
+
+       /*
+        * APIC timer configuration registers
+        */
+       printk(KERN_DEBUG "  Local APIC Timer:\n");
+       printk(KERN_DEBUG "      DCR (Divide Config Reg): 0x%08x\n",
+               apic_read(APIC_TDCR)
+       );
+       printk(KERN_DEBUG "      ICT (Initial Count Reg): 0x%08x\n",
+               apic_read(APIC_TMICT)
+       );
+       printk(KERN_DEBUG "      CCT (Current Count Reg): 0x%08x\n",
+               apic_read(APIC_TMCCT)
+       );
+
+       /*
+        * Logical APIC addressing mode registers
+        */
+       printk(KERN_DEBUG "  Logical Addressing Mode Information:\n");
+       printk(KERN_DEBUG "      LDR (Logical Dest Reg):  0x%08x (id=%d)\n",
+               apic_read(APIC_LDR),
+               GET_APIC_LOGICAL_ID(apic_read(APIC_LDR))
+       );
+       printk(KERN_DEBUG "      DFR (Dest Format Reg):   0x%08x (%s)\n",
+               apic_read(APIC_DFR),
+               (apic_read(APIC_DFR) == APIC_DFR_FLAT) ? "FLAT" : "CLUSTER"
+       );
+
+       /*
+        * Task/processor/arbitration priority registers
+        */
+       printk(KERN_DEBUG "  Task/Processor/Arbitration Priorities:\n");
+       printk(KERN_DEBUG "      TPR (Task Priority Reg):        0x%08x\n",
+               apic_read(APIC_TASKPRI)
+       );
+       printk(KERN_DEBUG "      PPR (Processor Priority Reg):   0x%08x\n",
+               apic_read(APIC_PROCPRI)
+       );
+       printk(KERN_DEBUG "      APR (Arbitration Priority Reg): 0x%08x\n",
+               apic_read(APIC_ARBPRI)
+       );
+}
+