1 // The local APIC manages internal (non-I/O) interrupts.
2 // See Chapter 8 & Appendix C of Intel processor manual volume 3.
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
41 volatile uint
*lapic
; // Initialized in mp.c
44 lapicw(int index
, int value
)
47 lapic
[ID
]; // wait for write to finish, by reading
53 cprintf("lapicinit: %d 0x%x\n", c
, lapic
);
57 // Enable local APIC; set spurious interrupt vector.
58 lapicw(SVR
, ENABLE
| (T_IRQ0
+ IRQ_SPURIOUS
));
60 // The timer repeatedly counts down at bus frequency
61 // from lapic[TICR] and then issues an interrupt.
62 // If xv6 cared more about precise timekeeping,
63 // TICR would be calibrated using an external time source.
65 lapicw(TIMER
, PERIODIC
| (T_IRQ0
+ IRQ_TIMER
));
66 lapicw(TICR
, 10000000);
68 // Disable logical interrupt lines.
69 lapicw(LINT0
, MASKED
);
70 lapicw(LINT1
, MASKED
);
72 // Disable performance counter overflow interrupts
73 // on machines that provide that interrupt entry.
74 if(((lapic
[VER
]>>16) & 0xFF) >= 4)
75 lapicw(PCINT
, MASKED
);
77 // Map error interrupt to IRQ_ERROR.
78 lapicw(ERROR
, T_IRQ0
+ IRQ_ERROR
);
80 // Clear error status register (requires back-to-back writes).
84 // Ack any outstanding interrupts.
87 // Send an Init Level De-Assert to synchronise arbitration ID's.
89 lapicw(ICRLO
, BCAST
| INIT
| LEVEL
);
90 while(lapic
[ICRLO
] & DELIVS
)
93 // Enable interrupts on the APIC (but not on the processor).
100 // Cannot call cpu when interrupts are enabled:
101 // result not guaranteed to last long enough to be used!
102 // Would prefer to panic but even printing is chancy here:
103 // almost everything, including cprintf and panic, calls cpu,
104 // often indirectly through acquire and release.
105 if(readeflags()&FL_IF
){
108 cprintf("cpu called from %x with interrupts enabled\n",
109 __builtin_return_address(0));
113 return lapic
[ID
]>>24;
117 // Acknowledge interrupt.
125 // Spin for a given number of microseconds.
126 // On real hardware would want to tune this dynamically.
134 // Start additional processor running bootstrap code at addr.
135 // See Appendix B of MultiProcessor Specification.
137 lapicstartap(uchar apicid
, uint addr
)
142 // "The BSP must initialize CMOS shutdown code to 0AH
143 // and the warm reset vector (DWORD based at 40:67) to point at
144 // the AP startup code prior to the [universal startup algorithm]."
145 outb(IO_RTC
, 0xF); // offset 0xF is shutdown code
146 outb(IO_RTC
+1, 0x0A);
147 wrv
= (ushort
*)(0x40<<4 | 0x67); // Warm reset vector
151 // "Universal startup algorithm."
152 // Send INIT (level-triggered) interrupt to reset other CPU.
153 lapicw(ICRHI
, apicid
<<24);
154 lapicw(ICRLO
, INIT
| LEVEL
| ASSERT
);
156 lapicw(ICRLO
, INIT
| LEVEL
);
157 microdelay(100); // should be 10ms, but too slow in Bochs!
159 // Send startup IPI (twice!) to enter bootstrap code.
160 // Regular hardware is supposed to only accept a STARTUP
161 // when it is in the halted state due to an INIT. So the second
162 // should be ignored, but it is part of the official Intel algorithm.
163 // Bochs complains about the second one. Too bad for Bochs.
164 for(i
= 0; i
< 2; i
++){
165 lapicw(ICRHI
, apicid
<<24);
166 lapicw(ICRLO
, STARTUP
| (addr
>>12));