// Start additional processor running entry code at addr. // See Appendix B of MultiProcessor Specification. void lapicstartap(uchar apicid, uint addr) { int i; ushort *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(CMOS_PORT, 0xF); // offset 0xF is shutdown code outb(CMOS_PORT+1, 0x0A); wrv = (ushort*)P2V((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. lapicw(ICRHI, apicid<<24); lapicw(ICRLO, INIT | LEVEL | ASSERT); microdelay(200); lapicw(ICRLO, INIT | LEVEL); microdelay(100); // should be 10ms, but too slow in Bochs! // Send startup IPI (twice!) to enter 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++){ lapicw(ICRHI, apicid<<24); lapicw(ICRLO, STARTUP | (addr>>12)); microdelay(200); } }
int lapic_ipi_issue_spec(int lapic_id, uint8_t vector) { if (lapicr(ICRLO) & DELIVS) return -1; lapicw(ICRHI, lapic_id << 24); lapicw(ICRLO, ASSERT | vector); while (lapicr(ICRLO) & DELIVS) ; return 0; }
// Acknowledge interrupt. void lapiceoi(void) { if(lapic) { lapicw(EOI, 0); } }
void lapic_timer_set(uint32_t freq) { if (freq == 0) { // Disable timer lapicw(TIMER, MASKED); } else { // The timer repeatedly counts down at bus frequency // from lapic[TICR] and then issues an interrupt. // If xv6 cared more about precise timekeeping, // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER)); lapicw(TICR, LAPIC_PERIODIC); } }
void lapicinit(void) { if(!lapic) return; // Enable local APIC; set spurious interrupt vector. lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS)); // The timer repeatedly counts down at bus frequency // from lapic[TICR] and then issues an interrupt. // If xv6 cared more about precise timekeeping, // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); lapicw(TICR, 10000000); // Disable logical interrupt lines. lapicw(LINT0, MASKED); lapicw(LINT1, MASKED); // Disable performance counter overflow interrupts // on machines that provide that interrupt entry. if(((lapic[VER]>>16) & 0xFF) >= 4) lapicw(PCINT, MASKED); // Map error interrupt to IRQ_ERROR. lapicw(ERROR, T_IRQ0 + IRQ_ERROR); // Clear error status register (requires back-to-back writes). lapicw(ESR, 0); lapicw(ESR, 0); // Ack any outstanding interrupts. lapicw(EOI, 0); // Send an Init Level De-Assert to synchronise arbitration ID's. lapicw(ICRHI, 0); lapicw(ICRLO, BCAST | INIT | LEVEL); while(lapic[ICRLO] & DELIVS) ; // Enable interrupts on the APIC (but not on the processor). lapicw(TPR, 0); }
int lapic_init_ap(void) { // Enable local APIC; set spurious interrupt vector. lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS)); // Disable timer lapicw(TIMER, MASKED); // Disable logical interrupt lines. lapicw(LINT0, MASKED); lapicw(LINT1, MASKED); /* Disable the TM interrupt */ if(((lapicr(VER) >> 16) & 0xFF) >= 5) lapicw(TMINT, MASKED); // Disable performance counter overflow interrupts // on machines that provide that interrupt entry. if(((lapicr(VER) >> 16) & 0xFF) >= 4) lapicw(PCINT, MASKED); // Map error interrupt to IRQ_ERROR. lapicw(ERROR, IRQ_OFFSET + IRQ_ERROR); // Clear error status register (requires back-to-back writes). lapicw(ESR, 0); lapicw(ESR, 0); // Ack any outstanding interrupts. lapicw(EOI, 0); // Send an Init Level De-Assert to synchronise arbitration ID's. lapicw(ICRHI, 0); lapicw(ICRLO, BCAST | INIT | LEVEL); while(lapicr(ICRLO) & DELIVS) ; // Enable interrupts on the APIC (but not on the processor). lapicw(TPR, 0); // Bochs doesn't support IMCR, so this doesn't run on Bochs. // But it would on real hardware. /* outb(0x22, 0x70); // Select IMCR */ /* outb(0x23, inb(0x23) | 1); // Mask external interrupts. */ return 0; }
// Acknowledge interrupt. void lapic_eoi_send(void) { lapicw(EOI, 0); }
// Acknowledge interrupt. void lapic_eoi(void) { if(lapic) lapicw(EOI, 0); }