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