1 // The local APIC manages internal (non-I/O) interrupts. 2 // See Chapter 8 & Appendix C of Intel processor manual volume 3. 3 4 #include "types.h" 5 #include "traps.h" 6 7 // Local APIC registers, divided by 4 for use as uint[] indices. 8 #define ID (0x0020/4) // ID 9 #define VER (0x0030/4) // Version 10 #define TPR (0x0080/4) // Task Priority 11 #define EOI (0x00B0/4) // EOI 12 #define SVR (0x00F0/4) // Spurious Interrupt Vector 13 #define ENABLE 0x00000100 // Unit Enable 14 #define ESR (0x0280/4) // Error Status 15 #define ICRLO (0x0300/4) // Interrupt Command 16 #define INIT 0x00000500 // INIT/RESET 17 #define STARTUP 0x00000600 // Startup IPI 18 #define DELIVS 0x00001000 // Delivery status 19 #define ASSERT 0x00004000 // Assert interrupt (vs deassert) 20 #define LEVEL 0x00008000 // Level triggered 21 #define BCAST 0x00080000 // Send to all APICs, including self. 22 #define ICRHI (0x0310/4) // Interrupt Command [63:32] 23 #define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) 24 #define X1 0x0000000B // divide counts by 1 25 #define PERIODIC 0x00020000 // Periodic 26 #define PCINT (0x0340/4) // Performance Counter LVT 27 #define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) 28 #define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) 29 #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) 30 #define MASKED 0x00010000 // Interrupt masked 31 #define TICR (0x0380/4) // Timer Initial Count 32 #define TCCR (0x0390/4) // Timer Current Count 33 #define TDCR (0x03E0/4) // Timer Divide Configuration 34 35 volatile uint *lapic; // Initialized in mp.c 36 37 //PAGEBREAK! 38 void 39 lapic_init(int c) 40 { 41 if(!lapic) 42 return; 43 44 // Enable local APIC; set spurious interrupt vector. 45 lapic[SVR] = ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS); 46 47 // The timer repeatedly counts down at bus frequency 48 // from lapic[TICR] and then issues an interrupt. 49 // If xv6 cared more about precise timekeeping, 50 // TICR would be calibrated using an external time source. 51 lapic[TDCR] = X1; 52 lapic[TIMER] = PERIODIC | (IRQ_OFFSET + IRQ_TIMER); 53 lapic[TICR] = 10000000; 54 55 // Disable logical interrupt lines. 56 lapic[LINT0] = MASKED; 57 lapic[LINT1] = MASKED; 58 59 // Disable performance counter overflow interrupts 60 // on machines that provide that interrupt entry. 61 if(((lapic[VER]>>16) & 0xFF) >= 4) 62 lapic[PCINT] = MASKED; 63 64 // Map error interrupt to IRQ_ERROR. 65 lapic[ERROR] = IRQ_OFFSET+IRQ_ERROR; 66 67 // Clear error status register (requires back-to-back writes). 68 lapic[ESR] = 0; 69 lapic[ESR] = 0; 70 71 // Ack any outstanding interrupts. 72 lapic[EOI] = 0; 73 74 // Send an Init Level De-Assert to synchronise arbitration ID's. 75 lapic[ICRHI] = 0; 76 lapic[ICRLO] = BCAST | INIT | LEVEL; 77 while(lapic[ICRLO] & DELIVS) 78 ; 79 80 // Enable interrupts on the APIC (but not on the processor). 81 lapic[TPR] = 0; 82 } 83 84 int 85 cpu(void) 86 { 87 if(lapic) 88 return lapic[ID]>>24; 89 return 0; 90 } 91 92 // Acknowledge interrupt. 93 void 94 lapic_eoi(void) 95 { 96 if(lapic) 97 lapic[EOI] = 0; 98 } 99 100 // Spin for a given number of microseconds. 101 // On real hardware would want to tune this dynamically. 102 static void 103 microdelay(int us) 104 { 105 volatile int j = 0; 106 107 while(us-- > 0) 108 for(j=0; j<10000; j++); 109 } 110 111 // Start additional processor running bootstrap code at addr. 112 // See Appendix B of MultiProcessor Specification. 113 void 114 lapic_startap(uchar apicid, uint addr) 115 { 116 int i; 117 volatile int j = 0; 118 119 // Send INIT interrupt to reset other CPU. 120 lapic[ICRHI] = apicid<<24; 121 lapic[ICRLO] = INIT | LEVEL; 122 microdelay(10); 123 124 // Send startup IPI (twice!) to enter bootstrap code. 125 // Regular hardware wants it twice, but Bochs complains. 126 // Too bad for Bochs. 127 for(i = 0; i < 2; i++){ 128 lapic[ICRHI] = apicid<<24; 129 lapic[ICRLO] = STARTUP | (addr>>12); 130 for(j=0; j<10000; j++); // 200us 131 } 132 } 133