xref: /xv6-public/lapic.c (revision 5c596bb3)
1 #include "types.h"
2 #include "mp.h"
3 #include "defs.h"
4 #include "param.h"
5 #include "x86.h"
6 #include "traps.h"
7 #include "mmu.h"
8 #include "proc.h"
9 
10 enum {  // Local APIC registers
11   LAPIC_ID  = 0x0020,   // ID
12   LAPIC_VER = 0x0030,   // Version
13   LAPIC_TPR = 0x0080,   // Task Priority
14   LAPIC_APR = 0x0090,   // Arbitration Priority
15   LAPIC_PPR = 0x00A0,   // Processor Priority
16   LAPIC_EOI = 0x00B0,   // EOI
17   LAPIC_LDR = 0x00D0,   // Logical Destination
18   LAPIC_DFR = 0x00E0,   // Destination Format
19   LAPIC_SVR = 0x00F0,   // Spurious Interrupt Vector
20   LAPIC_ISR = 0x0100,   // Interrupt Status (8 registers)
21   LAPIC_TMR = 0x0180,   // Trigger Mode (8 registers)
22   LAPIC_IRR = 0x0200,   // Interrupt Request (8 registers)
23   LAPIC_ESR = 0x0280,   // Error Status
24   LAPIC_ICRLO = 0x0300, // Interrupt Command
25   LAPIC_ICRHI = 0x0310, // Interrupt Command [63:32]
26   LAPIC_TIMER = 0x0320, // Local Vector Table 0 (TIMER)
27   LAPIC_PCINT = 0x0340, // Performance Counter LVT
28   LAPIC_LINT0 = 0x0350, // Local Vector Table 1 (LINT0)
29   LAPIC_LINT1 = 0x0360, // Local Vector Table 2 (LINT1)
30   LAPIC_ERROR = 0x0370, // Local Vector Table 3 (ERROR)
31   LAPIC_TICR = 0x0380,  // Timer Initial Count
32   LAPIC_TCCR = 0x0390,  // Timer Current Count
33   LAPIC_TDCR = 0x03E0,  // Timer Divide Configuration
34 };
35 
36 enum {  // LAPIC_SVR
37   LAPIC_ENABLE  = 0x00000100,   // Unit Enable
38   LAPIC_FOCUS   = 0x00000200,   // Focus Processor Checking Disable
39 };
40 
41 enum {  // LAPIC_ICRLO
42   // [14] IPI Trigger Mode Level (RW)
43   LAPIC_DEASSERT = 0x00000000,  // Deassert level-sensitive interrupt
44   LAPIC_ASSERT  = 0x00004000,   // Assert level-sensitive interrupt
45 
46   // [17:16] Remote Read Status
47   LAPIC_INVALID = 0x00000000,   // Invalid
48   LAPIC_WAIT    = 0x00010000,   // In-Progress
49   LAPIC_VALID   = 0x00020000,   // Valid
50 
51   // [19:18] Destination Shorthand
52   LAPIC_FIELD   = 0x00000000,   // No shorthand
53   LAPIC_SELF    = 0x00040000,   // Self is single destination
54   LAPIC_ALLINC  = 0x00080000,   // All including self
55   LAPIC_ALLEXC  = 0x000C0000,   // All Excluding self
56 };
57 
58 enum {  // LAPIC_ESR
59   LAPIC_SENDCS  = 0x00000001,     // Send CS Error
60   LAPIC_RCVCS   = 0x00000002,     // Receive CS Error
61   LAPIC_SENDACCEPT = 0x00000004,  // Send Accept Error
62   LAPIC_RCVACCEPT = 0x00000008,   // Receive Accept Error
63   LAPIC_SENDVECTOR = 0x00000020,  // Send Illegal Vector
64   LAPIC_RCVVECTOR = 0x00000040,   // Receive Illegal Vector
65   LAPIC_REGISTER = 0x00000080,    // Illegal Register Address
66 };
67 
68 enum {  // LAPIC_TIMER
69   // [17] Timer Mode (RW)
70   LAPIC_ONESHOT = 0x00000000,   // One-shot
71   LAPIC_PERIODIC = 0x00020000,  // Periodic
72 
73   // [19:18] Timer Base (RW)
74   LAPIC_CLKIN   = 0x00000000,   // use CLKIN as input
75   LAPIC_TMBASE  = 0x00040000,   // use TMBASE
76   LAPIC_DIVIDER = 0x00080000,   // use output of the divider
77 };
78 
79 enum {  // LAPIC_TDCR
80   LAPIC_X2 = 0x00000000,        // divide by 2
81   LAPIC_X4 = 0x00000001,        // divide by 4
82   LAPIC_X8 = 0x00000002,        // divide by 8
83   LAPIC_X16 = 0x00000003,       // divide by 16
84   LAPIC_X32 = 0x00000008,       // divide by 32
85   LAPIC_X64 = 0x00000009,       // divide by 64
86   LAPIC_X128 = 0x0000000A,      // divide by 128
87   LAPIC_X1 = 0x0000000B,        // divide by 1
88 };
89 
90 uint *lapicaddr;
91 
92 static int
93 lapic_read(int r)
94 {
95   return *(lapicaddr+(r/sizeof(*lapicaddr)));
96 }
97 
98 static void
99 lapic_write(int r, int data)
100 {
101   *(lapicaddr+(r/sizeof(*lapicaddr))) = data;
102 }
103 
104 
105 void
106 lapic_timerinit(void)
107 {
108   if (!lapicaddr)
109     return;
110 
111   lapic_write(LAPIC_TDCR, LAPIC_X1);
112   lapic_write(LAPIC_TIMER, LAPIC_CLKIN | LAPIC_PERIODIC |
113 	      (IRQ_OFFSET + IRQ_TIMER));
114   lapic_write(LAPIC_TCCR, 10000000);
115   lapic_write(LAPIC_TICR, 10000000);
116 }
117 
118 void
119 lapic_timerintr(void)
120 {
121   if (lapicaddr)
122     lapic_write(LAPIC_EOI, 0);
123 }
124 
125 void
126 lapic_init(int c)
127 {
128   uint r, lvt;
129 
130   if (!lapicaddr)
131     return;
132 
133   lapic_write(LAPIC_DFR, 0xFFFFFFFF);    // Set dst format register
134   r = (lapic_read(LAPIC_ID)>>24) & 0xFF; // Read APIC ID
135   lapic_write(LAPIC_LDR, (1<<r)<<24);    // Set logical dst register to r
136   lapic_write(LAPIC_TPR, 0xFF);          // No interrupts for now
137 
138   // Enable APIC
139   lapic_write(LAPIC_SVR, LAPIC_ENABLE|(IRQ_OFFSET+IRQ_SPURIOUS));
140 
141   // In virtual wire mode, set up the LINT0 and LINT1 as follows:
142   lapic_write(LAPIC_LINT0, APIC_IMASK | APIC_EXTINT);
143   lapic_write(LAPIC_LINT1, APIC_IMASK | APIC_NMI);
144 
145   lapic_write(LAPIC_EOI, 0); // Ack any outstanding interrupts.
146 
147   lvt = (lapic_read(LAPIC_VER)>>16) & 0xFF;
148   if(lvt >= 4)
149     lapic_write(LAPIC_PCINT, APIC_IMASK);
150   lapic_write(LAPIC_ERROR, IRQ_OFFSET+IRQ_ERROR);
151   lapic_write(LAPIC_ESR, 0);
152   lapic_read(LAPIC_ESR);
153 
154   // Issue an INIT Level De-Assert to synchronise arbitration ID's.
155   lapic_write(LAPIC_ICRHI, 0);
156   lapic_write(LAPIC_ICRLO, LAPIC_ALLINC|APIC_LEVEL|
157                            LAPIC_DEASSERT|APIC_INIT);
158   while(lapic_read(LAPIC_ICRLO) & APIC_DELIVS)
159     ;
160 }
161 
162 void
163 lapic_enableintr(void)
164 {
165   if (lapicaddr)
166     lapic_write(LAPIC_TPR, 0);
167 }
168 
169 void
170 lapic_disableintr(void)
171 {
172   if (lapicaddr)
173     lapic_write(LAPIC_TPR, 0xFF);
174 }
175 
176 void
177 lapic_eoi(void)
178 {
179   if (lapicaddr)
180     lapic_write(LAPIC_EOI, 0);
181 }
182 
183 int
184 cpu(void)
185 {
186   int x;
187   if (lapicaddr)
188     x = (lapic_read(LAPIC_ID)>>24) & 0xFF;
189   else
190     x = 0;
191   return x;
192 }
193 
194 void
195 lapic_startap(uchar apicid, int v)
196 {
197   int crhi, i;
198   volatile int j = 0;
199 
200   crhi = apicid<<24;
201   lapic_write(LAPIC_ICRHI, crhi);
202   lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|
203                            LAPIC_ASSERT|APIC_INIT);
204 
205   while(j++ < 10000) {;}
206   lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|
207                            LAPIC_DEASSERT|APIC_INIT);
208 
209   while(j++ < 1000000) {;}
210 
211   // in p9 code, this was i < 2, which is what the spec says on page B-3
212   for(i = 0; i < 1; i++){
213     lapic_write(LAPIC_ICRHI, crhi);
214     lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_EDGE|APIC_STARTUP|(v/4096));
215     while(j++ < 100000) {;}
216   }
217 }
218