/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- lapicw
- lapicinit
- cpunum
- lapiceoi
- microdelay
- lapicstartap
- cmos_read
- fill_rtcdate
- cmostime
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 }