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 "defs.h" 6 #include "traps.h" 7 #include "mmu.h" 8 #include "x86.h" 9 10 // Local APIC registers, divided by 4 for use as uint[] indices. 11 #define ID (0x0020/4) // ID 12 #define VER (0x0030/4) // Version 13 #define TPR (0x0080/4) // Task Priority 14 #define EOI (0x00B0/4) // EOI 15 #define SVR (0x00F0/4) // Spurious Interrupt Vector 16 #define ENABLE 0x00000100 // Unit Enable 17 #define ESR (0x0280/4) // Error Status 18 #define ICRLO (0x0300/4) // Interrupt Command 19 #define INIT 0x00000500 // INIT/RESET 20 #define STARTUP 0x00000600 // Startup IPI 21 #define DELIVS 0x00001000 // Delivery status 22 #define ASSERT 0x00004000 // Assert interrupt (vs deassert) 23 #define DEASSERT 0x00000000 24 #define LEVEL 0x00008000 // Level triggered 25 #define BCAST 0x00080000 // Send to all APICs, including self. 26 #define BUSY 0x00001000 27 #define FIXED 0x00000000 28 #define ICRHI (0x0310/4) // Interrupt Command [63:32] 29 #define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) 30 #define X1 0x0000000B // divide counts by 1 31 #define PERIODIC 0x00020000 // Periodic 32 #define PCINT (0x0340/4) // Performance Counter LVT 33 #define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) 34 #define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) 35 #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) 36 #define MASKED 0x00010000 // Interrupt masked 37 #define TICR (0x0380/4) // Timer Initial Count 38 #define TCCR (0x0390/4) // Timer Current Count 39 #define TDCR (0x03E0/4) // Timer Divide Configuration 40 41 volatile uint *lapic; // Initialized in mp.c 42 43 static void 44 lapicw(int index, int value) 45 { 46 lapic[index] = value; 47 lapic[ID]; // wait for write to finish, by reading 48 } 49 50 static uint 51 lapicr(uint off) 52 { 53 return lapic[off]; 54 } 55 56 static int 57 apic_icr_wait() 58 { 59 uint i = 100000; 60 while ((lapicr(ICRLO) & BUSY) != 0) { 61 nop_pause(); 62 i--; 63 if (i == 0) { 64 cprintf("apic_icr_wait: wedged?\n"); 65 return -1; 66 } 67 } 68 return 0; 69 } 70 71 //PAGEBREAK! 72 void 73 lapicinit(int c) 74 { 75 cprintf("lapicinit: %d 0x%x\n", c, lapic); 76 if(!lapic) 77 return; 78 79 // Enable local APIC; set spurious interrupt vector. 80 lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS)); 81 82 // The timer repeatedly counts down at bus frequency 83 // from lapic[TICR] and then issues an interrupt. 84 // If xv6 cared more about precise timekeeping, 85 // TICR would be calibrated using an external time source. 86 lapicw(TDCR, X1); 87 lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); 88 lapicw(TICR, 10000000); 89 90 // Disable logical interrupt lines. 91 lapicw(LINT0, MASKED); 92 lapicw(LINT1, MASKED); 93 94 // Disable performance counter overflow interrupts 95 // on machines that provide that interrupt entry. 96 if(((lapic[VER]>>16) & 0xFF) >= 4) 97 lapicw(PCINT, MASKED); 98 99 // Map error interrupt to IRQ_ERROR. 100 lapicw(ERROR, T_IRQ0 + IRQ_ERROR); 101 102 // Clear error status register (requires back-to-back writes). 103 lapicw(ESR, 0); 104 lapicw(ESR, 0); 105 106 // Ack any outstanding interrupts. 107 lapicw(EOI, 0); 108 109 // Send an Init Level De-Assert to synchronise arbitration ID's. 110 lapicw(ICRHI, 0); 111 lapicw(ICRLO, BCAST | INIT | LEVEL); 112 while(lapic[ICRLO] & DELIVS) 113 ; 114 115 // Enable interrupts on the APIC (but not on the processor). 116 lapicw(TPR, 0); 117 } 118 119 int 120 cpunum(void) 121 { 122 // Cannot call cpu when interrupts are enabled: 123 // result not guaranteed to last long enough to be used! 124 // Would prefer to panic but even printing is chancy here: 125 // almost everything, including cprintf and panic, calls cpu, 126 // often indirectly through acquire and release. 127 if(readeflags()&FL_IF){ 128 static int n; 129 if(n++ == 0) 130 cprintf("cpu called from %x with interrupts enabled\n", 131 __builtin_return_address(0)); 132 } 133 134 if(lapic) 135 return lapic[ID]>>24; 136 return 0; 137 } 138 139 // Acknowledge interrupt. 140 void 141 lapiceoi(void) 142 { 143 if(lapic) 144 lapicw(EOI, 0); 145 } 146 147 // Spin for a given number of microseconds. 148 // On real hardware would want to tune this dynamically. 149 void 150 microdelay(int us) 151 { 152 } 153 154 155 // Send IPI 156 void 157 lapic_ipi(int cpu, int ino) 158 { 159 lapicw(ICRHI, cpu << 24); 160 lapicw(ICRLO, FIXED | DEASSERT | ino); 161 if (apic_icr_wait() < 0) 162 panic("lapic_ipi: icr_wait failure"); 163 } 164 165 void 166 lapic_tlbflush(uint cpu) 167 { 168 lapic_ipi(cpu, T_TLBFLUSH); 169 } 170 171 #define IO_RTC 0x70 172 173 // Start additional processor running bootstrap code at addr. 174 // See Appendix B of MultiProcessor Specification. 175 void 176 lapicstartap(uchar apicid, uint addr) 177 { 178 int i; 179 ushort *wrv; 180 181 // "The BSP must initialize CMOS shutdown code to 0AH 182 // and the warm reset vector (DWORD based at 40:67) to point at 183 // the AP startup code prior to the [universal startup algorithm]." 184 outb(IO_RTC, 0xF); // offset 0xF is shutdown code 185 outb(IO_RTC+1, 0x0A); 186 wrv = (ushort*)(0x40<<4 | 0x67); // Warm reset vector 187 wrv[0] = 0; 188 wrv[1] = addr >> 4; 189 190 // "Universal startup algorithm." 191 // Send INIT (level-triggered) interrupt to reset other CPU. 192 lapicw(ICRHI, apicid<<24); 193 lapicw(ICRLO, INIT | LEVEL | ASSERT); 194 microdelay(200); 195 lapicw(ICRLO, INIT | LEVEL); 196 microdelay(100); // should be 10ms, but too slow in Bochs! 197 198 // Send startup IPI (twice!) to enter bootstrap code. 199 // Regular hardware is supposed to only accept a STARTUP 200 // when it is in the halted state due to an INIT. So the second 201 // should be ignored, but it is part of the official Intel algorithm. 202 // Bochs complains about the second one. Too bad for Bochs. 203 for(i = 0; i < 2; i++){ 204 lapicw(ICRHI, apicid<<24); 205 lapicw(ICRLO, STARTUP | (addr>>12)); 206 microdelay(200); 207 } 208 } 209