added base src
[xv6-db.git] / lapic.c
blob27dd868b2aab46e71ce5896eb9476ec723191b8b
1 // The local APIC manages internal (non-I/O) interrupts.
2 // See Chapter 8 & Appendix C of Intel processor manual volume 3.
4 #include "types.h"
5 #include "defs.h"
6 #include "traps.h"
7 #include "mmu.h"
8 #include "x86.h"
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
43 static void
44 lapicw(int index, int value)
46 lapic[index] = value;
47 lapic[ID]; // wait for write to finish, by reading
50 void
51 lapicinit(int c)
53 cprintf("lapicinit: %d 0x%x\n", c, lapic);
54 if(!lapic)
55 return;
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.
64 lapicw(TDCR, X1);
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).
81 lapicw(ESR, 0);
82 lapicw(ESR, 0);
84 // Ack any outstanding interrupts.
85 lapicw(EOI, 0);
87 // Send an Init Level De-Assert to synchronise arbitration ID's.
88 lapicw(ICRHI, 0);
89 lapicw(ICRLO, BCAST | INIT | LEVEL);
90 while(lapic[ICRLO] & DELIVS)
93 // Enable interrupts on the APIC (but not on the processor).
94 lapicw(TPR, 0);
97 int
98 cpunum(void)
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){
106 static int n;
107 if(n++ == 0)
108 cprintf("cpu called from %x with interrupts enabled\n",
109 __builtin_return_address(0));
112 if(lapic)
113 return lapic[ID]>>24;
114 return 0;
117 // Acknowledge interrupt.
118 void
119 lapiceoi(void)
121 if(lapic)
122 lapicw(EOI, 0);
125 // Spin for a given number of microseconds.
126 // On real hardware would want to tune this dynamically.
127 void
128 microdelay(int us)
132 #define IO_RTC 0x70
134 // Start additional processor running bootstrap code at addr.
135 // See Appendix B of MultiProcessor Specification.
136 void
137 lapicstartap(uchar apicid, uint addr)
139 int i;
140 ushort *wrv;
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
148 wrv[0] = 0;
149 wrv[1] = addr >> 4;
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);
155 microdelay(200);
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));
167 microdelay(200);