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