// Start additional processor running bootstrap code at addr. // See Appendix B of MultiProcessor Specification. void lapic_startap(uint8_t apicid, uint32_t addr) { int i; uint16_t *wrv; // "The BSP must initialize CMOS shutdown code to 0AH // and the warm reset vector (DWORD based at 40:67) to point at // the AP startup code prior to the [universal startup algorithm]." outb(IO_RTC, 0xF); // offset 0xF is shutdown code outb(IO_RTC+1, 0x0A); wrv = (uint16_t *)RELOC(0x40<<4 | 0x67); // Warm reset vector wrv[0] = 0; wrv[1] = addr >> 4; // "Universal startup algorithm." // Send INIT (level-triggered) interrupt to reset other CPU. lapic_write(ICRHI, apicid<<ID_SHIFT); lapic_write(ICRLO, DLMODE_INIT | LEVEL | ASSERT); microdelay(200); lapic_write(ICRLO, DLMODE_INIT | LEVEL); microdelay(100); // should be 10ms, but too slow in Bochs! // Send startup IPI (twice!) to enter bootstrap code. // Regular hardware is supposed to only accept a STARTUP // when it is in the halted state due to an INIT. So the second // should be ignored, but it is part of the official Intel algorithm. // Bochs complains about the second one. Too bad for Bochs. for(i = 0; i < 2; i++){ lapic_write(ICRHI, apicid<<24); lapic_write(ICRLO, DLMODE_STARTUP | (addr>>12)); microdelay(200); } }
/* Send IPI to dedicated CPU * Success: 0 * Failure: < 0 */ int lapic_ipi(uint32_t cp_id, uint32_t ino) { lapic_write(ICRHI, cp_id << ID_SHIFT); lapic_write(ICRLO, DLMODE_FIXED | DEASSERT | ino); return lapic_icr_wait(); }
static void init_timer(void) { /* Set the apic timer to no interrupts and periodic mode */ lapic_write(LAPIC_LVTT, (1 << 17)|(1<< 16)|(0 << 12)|(0 << 0)); /* Set the divider to 1, no divider */ lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1); /* Set the initial counter to 0xffffffff */ lapic_write(LAPIC_TMICT, 0xffffffff); }
void init_timer(void) { /* Set the apic timer to no interrupts and periodic mode */ lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)); /* Set the divider to 1, no divider */ lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1); /* Set the initial counter to 0xffffffff */ lapic_write(LAPIC_TMICT, 0xffffffff); /* Set FSB frequency to a reasonable value */ set_timer_fsb(); }
static void apic_setup(void) { /* Set the IOAPIC ID to the static value used in the MP/ACPI tables. */ ioapic_write(0x00, IOAPIC_ID); /* NMIs are delivered direct to the BSP. */ lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF); lapic_write(APIC_LVT0, (APIC_MODE_EXTINT << 8) | APIC_LVT_MASKED); lapic_write(APIC_LVT1, APIC_MODE_NMI << 8); /* 8259A ExtInts are delivered through IOAPIC pin 0 (Virtual Wire Mode). */ ioapic_write(0x10, APIC_DM_EXTINT); ioapic_write(0x11, SET_APIC_ID(LAPIC_ID(0))); }
/* Broadcast IPI to CPUs * Success: 0 * Failure: < 0 */ int lapic_broadcast(int self, uint32_t ino) { uint32_t flag = self ? DEST_ALLINCL : DEST_ALLEXCL; flag |= ino == T_NMI ? DLMODE_NMI : 0; lapic_write(ICRLO, flag | DEASSERT | DLMODE_FIXED | ino); return lapic_icr_wait(); }
void lapic_configure(void) { unsigned i, physid = lapic_getcurrent(); struct lapic_desc *d = NULL; for (i = 0; i < lapics_no; i++) { if (lapics[i].physid == physid) d = lapics + i; } if (d == NULL) { printf("Warning: Current CPU not in Platform Tables!\n"); /* Try to continue, ignore the NMI configuration */ } else { lapic_write(L_LVT_LINT(0), d->lint[0]); lapic_write(L_LVT_LINT(1), d->lint[1]); } /* Enable LAPIC */ lapic_write(L_MISC, lapic_read(L_MISC) | 0x100); }
static void boot_cpu(unsigned int cpu) { unsigned int icr2 = SET_APIC_DEST_FIELD(LAPIC_ID(cpu)); /* Initialise shared variables. */ ap_cpuid = cpu; ap_callin = 0; wmb(); /* Wake up the secondary processor: INIT-SIPI-SIPI... */ lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_INIT); lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12)); lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12)); lapic_wait_ready(); /* * Wait for the secondary processor to complete initialisation. * Do not touch shared resources meanwhile. */ while ( !ap_callin ) cpu_relax(); /* Take the secondary processor offline. */ lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_INIT); lapic_wait_ready(); }
static void apic_setup(void) { /* * This would the The Right Thing To Do (tm), if only qemu negotiated * with Xen where the IO-APIC actually sits (which is currently hard * coded in Xen and can't be controlled externally). Uncomment this code * once that changed. ioapic_base_address |= (pci_readb(PCI_ISA_DEVFN, 0x80) & 0x3f) << 10; */ ioapic_version = ioapic_read(0x01) & 0xff; /* Set the IOAPIC ID to the static value used in the MP/ACPI tables. */ ioapic_write(0x00, IOAPIC_ID); /* NMIs are delivered direct to the BSP. */ lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF); lapic_write(APIC_LVT0, (APIC_MODE_EXTINT << 8) | APIC_LVT_MASKED); lapic_write(APIC_LVT1, APIC_MODE_NMI << 8); /* 8259A ExtInts are delivered through IOAPIC pin 0 (Virtual Wire Mode). */ ioapic_write(0x10, APIC_DM_EXTINT); ioapic_write(0x11, SET_APIC_ID(LAPIC_ID(0))); }
static int start_aps(int ap_count, atomic_t *num_aps) { int sipi_vector; /* Max location is 4KiB below 1MiB */ const int max_vector_loc = ((1 << 20) - (1 << 12)) >> 12; if (ap_count == 0) return 0; /* The vector is sent as a 4k aligned address in one byte */ sipi_vector = AP_DEFAULT_BASE >> 12; if (sipi_vector > max_vector_loc) { printf("SIPI vector too large! 0x%08x\n", sipi_vector); return -1; } debug("Attempting to start %d APs\n", ap_count); if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { debug("Waiting for ICR not to be busy..."); if (apic_wait_timeout(1000, 50)) { debug("timed out. Aborting.\n"); return -1; } else { debug("done.\n"); } } /* Send INIT IPI to all but self */ lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_INIT); debug("Waiting for 10ms after sending INIT.\n"); mdelay(10); /* Send 1st SIPI */ if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { debug("Waiting for ICR not to be busy..."); if (apic_wait_timeout(1000, 50)) { debug("timed out. Aborting.\n"); return -1; } else { debug("done.\n"); } } lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_STARTUP | sipi_vector); debug("Waiting for 1st SIPI to complete..."); if (apic_wait_timeout(10000, 50)) { debug("timed out.\n"); return -1; } else { debug("done.\n"); } /* Wait for CPUs to check in up to 200 us */ wait_for_aps(num_aps, ap_count, 200, 15); /* Send 2nd SIPI */ if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { debug("Waiting for ICR not to be busy..."); if (apic_wait_timeout(1000, 50)) { debug("timed out. Aborting.\n"); return -1; } else { debug("done.\n"); } } lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_STARTUP | sipi_vector); debug("Waiting for 2nd SIPI to complete..."); if (apic_wait_timeout(10000, 50)) { debug("timed out.\n"); return -1; } else { debug("done.\n"); } /* Wait for CPUs to check in */ if (wait_for_aps(num_aps, ap_count, 10000, 50)) { debug("Not all APs checked in: %d/%d.\n", atomic_read(num_aps), ap_count); return -1; } return 0; }
static void lapic_eoi(void){ lapic_write(LAPIC_EOI, 0); return; }
static uint32_t lapic_errstatus(void){ lapic_write(LAPIC_ESR, 0); return lapic_read(LAPIC_ESR); }
static int lapic_start_cpu(unsigned long apicid) { int timeout; unsigned long send_status, accept_status; int j, maxlvt; /* * Starting actual IPI sequence... */ printk(BIOS_SPEW, "Asserting INIT.\n"); /* * Turn INIT on target chip */ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid)); /* * Send IPI */ lapic_write_around(LAPIC_ICR, LAPIC_INT_LEVELTRIG | LAPIC_INT_ASSERT | LAPIC_DM_INIT); printk(BIOS_SPEW, "Waiting for send to finish...\n"); timeout = 0; do { printk(BIOS_SPEW, "+"); udelay(100); send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); if (timeout >= 1000) { printk(BIOS_ERR, "CPU %ld: First APIC write timed out. " "Disabling\n", apicid); // too bad. printk(BIOS_ERR, "ESR is 0x%lx\n", lapic_read(LAPIC_ESR)); if (lapic_read(LAPIC_ESR)) { printk(BIOS_ERR, "Try to reset ESR\n"); lapic_write_around(LAPIC_ESR, 0); printk(BIOS_ERR, "ESR is 0x%lx\n", lapic_read(LAPIC_ESR)); } return 0; } #if !IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX) \ && !IS_ENABLED(CONFIG_CPU_INTEL_MODEL_206AX) \ && !IS_ENABLED(CONFIG_CPU_INTEL_MODEL_2065X) mdelay(10); #endif printk(BIOS_SPEW, "Deasserting INIT.\n"); /* Target chip */ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid)); /* Send IPI */ lapic_write_around(LAPIC_ICR, LAPIC_INT_LEVELTRIG | LAPIC_DM_INIT); printk(BIOS_SPEW, "Waiting for send to finish...\n"); timeout = 0; do { printk(BIOS_SPEW, "+"); udelay(100); send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); if (timeout >= 1000) { printk(BIOS_ERR, "CPU %ld: Second APIC write timed out. " "Disabling\n", apicid); // too bad. return 0; } /* * Run STARTUP IPI loop. */ printk(BIOS_SPEW, "#startup loops: %d.\n", CONFIG_NUM_IPI_STARTS); maxlvt = 4; for (j = 1; j <= CONFIG_NUM_IPI_STARTS; j++) { printk(BIOS_SPEW, "Sending STARTUP #%d to %lu.\n", j, apicid); lapic_read_around(LAPIC_SPIV); lapic_write(LAPIC_ESR, 0); lapic_read(LAPIC_ESR); printk(BIOS_SPEW, "After apic_write.\n"); /* * STARTUP IPI */ /* Target chip */ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid)); /* Boot on the stack */ /* Kick the second */ lapic_write_around(LAPIC_ICR, LAPIC_DM_STARTUP | (AP_SIPI_VECTOR >> 12)); /* * Give the other CPU some time to accept the IPI. */ udelay(300); printk(BIOS_SPEW, "Startup point 1.\n"); printk(BIOS_SPEW, "Waiting for send to finish...\n"); timeout = 0; do { printk(BIOS_SPEW, "+"); udelay(100); send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); /* * Give the other CPU some time to accept the IPI. */ udelay(200); /* * Due to the Pentium erratum 3AP. */ if (maxlvt > 3) { lapic_read_around(LAPIC_SPIV); lapic_write(LAPIC_ESR, 0); } accept_status = (lapic_read(LAPIC_ESR) & 0xEF); if (send_status || accept_status) break; } printk(BIOS_SPEW, "After Startup.\n"); if (send_status) printk(BIOS_WARNING, "APIC never delivered???\n"); if (accept_status) printk(BIOS_WARNING, "APIC delivery error (%lx).\n", accept_status); if (send_status || accept_status) return 0; return 1; }
void lapic_init(int c) { uint32_t edx; if(!lapic) return; /* Check if APIC is supported */ cpuid(0x01, 0, 0, 0, &edx); if (!(edx & 0x200)) return; if (c == mp_bcpu()){ pte_t *pte; pte = pgdir_walk(boot_pgdir, (void *)lapic, 1); *pte = (physaddr_t)lapic | PTE_W | PTE_P | PTE_PCD; /* Must be Strong Uncacheable */ } // Enable local APIC; set spurious interrupt vector. lapic_write(SVR, ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS)); // The timer repeatedly counts down at bus frequency // from lapic[TICR] and then issues an interrupt. lapic_write(TDCR, X1); lapic_write(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER)); lapic_write(TICR, 10000000); /* Linux uses if () way -_-, but I'd like IOAPIC to distribute ExtINT */ if (0) {//(c == mp_bcpu()) { lapic_write(LINT0, DLMODE_EXTINT); lapic_write(LINT1, DLMODE_NMI); } else { lapic_write(LINT0, MASKED); lapic_write(LINT1, MASKED); } // Disable performance counter overflow interrupts // on machines that provide that interrupt entry. if(((lapic[VER]>>16) & 0xFF) >= 4) lapic_write(PCINT, MASKED); // Map error interrupt to IRQ_ERROR. lapic_write(ERROR, IRQ_OFFSET+IRQ_ERROR); // Clear error status register (requires back-to-back writes). lapic_write(ESR, 0); lapic_write(ESR, 0); // Ack any outstanding interrupts. lapic_write(EOI, 0); // Send an Init Level De-Assert to synchronise arbitration ID's. lapic_write(ICRHI, 0); lapic_write(ICRLO, BCAST | DLMODE_INIT | LEVEL); while(lapic[ICRLO] & DELIVS) ; // Enable interrupts on the APIC (but not on the processor). lapic_write(TPR, 0); /* 0: Handle all interrupts * 15: Interrupts inhibited * P. Vol.3 9-39 */ //cprintf("CPU %x: lapic_int() success\n", c); }
// Acknowledge interrupt. void lapic_eoi(void) { if(lapic) lapic_write(EOI, 0); }
int lapic_clock_setup(struct time_dev_conf *conf) { static int initialized = 0; uint32_t ticks, cpubusfreq, counter; uint8_t tmp; if (initialized) { return ENOERR; } initialized = 1; /* * Map APIC timer to an interrupt, and by that enable it in * one-shot mode. */ lapic_write(LAPIC_LVT_TR, 32); /* Set up divide value to 16 */ lapic_write(LAPIC_TIMER_DCR, 0x03); /* * Initialize PIT Ch 2 in one-shot mode. * Waiting 1/100 sec. */ outb((inb(0x61) & 0xFD) | 1, 0x61); outb(0xB2, 0x43); /* 1193180/100 Hz = 11931 = 2e9bh */ outb(0x9B, 0x42); /* LSB */ inb(0x60); /* short delay */ outb(0x2E, 0x42); /* MSB */ /* Reset PIT one-shot counter (start counting) */ tmp = inb(0x61) & 0xFE; outb((uint8_t) tmp, 0x61); /* Gate low */ outb((uint8_t) tmp | 1, 0x61); /* Gate high */ /* Reset APIC timer (set counter to -1) */ lapic_write(LAPIC_TIMER_ICR, 0xFFFFFFFF); /* Now wait until PIT counter reaches zero */ while(!(inb(0x61) & 0x20)); /* Stop APIC timer */ lapic_write(LAPIC_LVT_TR, 0x10000); /* Now do the math... */ ticks = (0xFFFFFFFF - lapic_read(LAPIC_TIMER_CCR)) + 1; cpubusfreq = ticks * 16 * 100; counter = cpubusfreq / LAPIC_HZ / 16; /* Set APIC timer counter initializer */ lapic_write(LAPIC_TIMER_ICR, counter < 16 ? 16 : counter); /* Finally re-enable timer in periodic mode. */ lapic_write(LAPIC_LVT_TR, 32 | 0x20000); #if 0 /* * Setting divide value register again not needed by the manuals * although I have found buggy hardware that required it */ lapic_write(LAPIC_TIMER_DCR, 0x03); #endif return ENOERR; }