1 /*
2  * Copyright (c) 1984-2008, 2011-2015 Wind River Systems, Inc.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 /*
7  * driver for x86 CPU local APIC (as an interrupt controller)
8  */
9 
10 #include <kernel.h>
11 #include <kernel_structs.h>
12 #include <arch/cpu.h>
13 #include <zephyr/types.h>
14 #include <string.h>
15 #include <sys/__assert.h>
16 #include <arch/x86/msr.h>
17 
18 #include <toolchain.h>
19 #include <linker/sections.h>
20 #include <drivers/interrupt_controller/loapic.h> /* public API declarations */
21 #include <device.h>
22 #include <drivers/interrupt_controller/sysapic.h>
23 #include <drivers/interrupt_controller/ioapic.h>
24 
25 /* Local APIC Version Register Bits */
26 
27 #define LOAPIC_VERSION_MASK 0x000000ff /* LO APIC Version mask */
28 #define LOAPIC_MAXLVT_MASK 0x00ff0000  /* LO APIC Max LVT mask */
29 #define LOAPIC_PENTIUM4 0x00000014     /* LO APIC in Pentium4 */
30 #define LOAPIC_LVT_PENTIUM4 5	  /* LO APIC LVT - Pentium4 */
31 #define LOAPIC_LVT_P6 4		       /* LO APIC LVT - P6 */
32 #define LOAPIC_LVT_P5 3		       /* LO APIC LVT - P5 */
33 
34 /* Local APIC Vector Table Bits */
35 
36 #define LOAPIC_VECTOR 0x000000ff /* vectorNo */
37 #define LOAPIC_MODE 0x00000700   /* delivery mode */
38 #define LOAPIC_FIXED 0x00000000  /* delivery mode: FIXED */
39 #define LOAPIC_SMI 0x00000200    /* delivery mode: SMI */
40 #define LOAPIC_NMI 0x00000400    /* delivery mode: NMI */
41 #define LOAPIC_EXT 0x00000700    /* delivery mode: ExtINT */
42 #define LOAPIC_IDLE 0x00000000   /* delivery status: Idle */
43 #define LOAPIC_PEND 0x00001000   /* delivery status: Pend */
44 #define LOAPIC_HIGH 0x00000000   /* polarity: High */
45 #define LOAPIC_LOW 0x00002000    /* polarity: Low */
46 #define LOAPIC_REMOTE 0x00004000 /* remote IRR */
47 #define LOAPIC_EDGE 0x00000000   /* trigger mode: Edge */
48 #define LOAPIC_LEVEL 0x00008000  /* trigger mode: Level */
49 
50 /* Local APIC Spurious-Interrupt Register Bits */
51 
52 #define LOAPIC_ENABLE 0x100	/* APIC Enabled */
53 #define LOAPIC_FOCUS_DISABLE 0x200 /* Focus Processor Checking */
54 
55 #if CONFIG_LOAPIC_SPURIOUS_VECTOR_ID == -1
56 #define LOAPIC_SPURIOUS_VECTOR_ID (CONFIG_IDT_NUM_VECTORS - 1)
57 #else
58 #define LOAPIC_SPURIOUS_VECTOR_ID CONFIG_LOAPIC_SPURIOUS_VECTOR_ID
59 #endif
60 
61 #define LOPIC_SSPND_BITS_PER_IRQ  1  /* Just the one for enable disable*/
62 #define LOPIC_SUSPEND_BITS_REQD (ROUND_UP((LOAPIC_IRQ_COUNT * LOPIC_SSPND_BITS_PER_IRQ), 32))
63 #ifdef CONFIG_PM_DEVICE
64 #include <pm/device.h>
65 __pinned_bss
66 uint32_t loapic_suspend_buf[LOPIC_SUSPEND_BITS_REQD / 32] = {0};
67 #endif
68 
69 #ifdef DEVICE_MMIO_IS_IN_RAM
70 __pinned_bss
71 mm_reg_t z_loapic_regs;
72 #endif
73 
74 __pinned_func
send_eoi(void)75 void send_eoi(void)
76 {
77 	x86_write_xapic(LOAPIC_EOI, 0);
78 }
79 
80 /**
81  * @brief Enable and initialize the local APIC.
82  *
83  * Called from early assembly layer (e.g., crt0.S).
84  */
85 __pinned_func
z_loapic_enable(unsigned char cpu_number)86 void z_loapic_enable(unsigned char cpu_number)
87 {
88 	int32_t loApicMaxLvt; /* local APIC Max LVT */
89 
90 #ifdef DEVICE_MMIO_IS_IN_RAM
91 	device_map(&z_loapic_regs, CONFIG_LOAPIC_BASE_ADDRESS, 0x1000,
92 		   K_MEM_CACHE_NONE);
93 #endif /* DEVICE_MMIO_IS_IN_RAM */
94 #ifndef CONFIG_X2APIC
95 	/*
96 	 * in xAPIC and flat model, bits 24-31 in LDR (Logical APIC ID) are
97 	 * bitmap of target logical APIC ID and it supports maximum 8 local
98 	 * APICs.
99 	 *
100 	 * The logical APIC ID could be arbitrarily selected by system software
101 	 * and is different from local APIC ID in local APIC ID register.
102 	 *
103 	 * We choose 0 for BSP, and the index to x86_cpuboot[] for secondary
104 	 * CPUs.
105 	 *
106 	 * in X2APIC, LDR is read-only.
107 	 */
108 	x86_write_xapic(LOAPIC_LDR, 1 << (cpu_number + 24));
109 #endif
110 
111 	/*
112 	 * enable the local APIC. note that we use xAPIC mode here, since
113 	 * x2APIC access is not enabled until the next step (if at all).
114 	 */
115 
116 	x86_write_xapic(LOAPIC_SVR,
117 			x86_read_xapic(LOAPIC_SVR) | LOAPIC_ENABLE);
118 
119 #ifdef CONFIG_X2APIC
120 	/*
121 	 * turn on x2APIC mode. we trust the config option, so
122 	 * we don't check CPUID to see if x2APIC is supported.
123 	 */
124 
125 	uint64_t msr = z_x86_msr_read(X86_APIC_BASE_MSR);
126 	msr |= X86_APIC_BASE_MSR_X2APIC;
127 	z_x86_msr_write(X86_APIC_BASE_MSR, msr);
128 #endif
129 
130 	loApicMaxLvt = (x86_read_loapic(LOAPIC_VER) & LOAPIC_MAXLVT_MASK) >> 16;
131 
132 	/* reset the DFR, TPR, TIMER_CONFIG, and TIMER_ICR */
133 
134 #ifndef CONFIG_X2APIC
135 	/* Flat model */
136 	x86_write_loapic(LOAPIC_DFR, 0xffffffff);  /* no DFR in x2APIC mode */
137 #endif
138 
139 	x86_write_loapic(LOAPIC_TPR, 0x0);
140 	x86_write_loapic(LOAPIC_TIMER_CONFIG, 0x0);
141 	x86_write_loapic(LOAPIC_TIMER_ICR, 0x0);
142 
143 	/* program Local Vector Table for the Virtual Wire Mode */
144 
145 	/* skip LINT0/LINT1 for Jailhouse guest case, because we won't
146 	 * ever be waiting for interrupts on those
147 	 */
148 	/* set LINT0: extInt, high-polarity, edge-trigger, not-masked */
149 
150 	x86_write_loapic(LOAPIC_LINT0, (x86_read_loapic(LOAPIC_LINT0) &
151 		~(LOAPIC_MODE | LOAPIC_LOW |
152 		  LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
153 		(LOAPIC_EXT | LOAPIC_HIGH | LOAPIC_EDGE));
154 
155 	/* set LINT1: NMI, high-polarity, edge-trigger, not-masked */
156 
157 	x86_write_loapic(LOAPIC_LINT1, (x86_read_loapic(LOAPIC_LINT1) &
158 		~(LOAPIC_MODE | LOAPIC_LOW |
159 		  LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
160 		(LOAPIC_NMI | LOAPIC_HIGH | LOAPIC_EDGE));
161 
162 	/* lock the Local APIC interrupts */
163 
164 	x86_write_loapic(LOAPIC_TIMER, LOAPIC_LVT_MASKED);
165 	x86_write_loapic(LOAPIC_ERROR, LOAPIC_LVT_MASKED);
166 
167 	if (loApicMaxLvt >= LOAPIC_LVT_P6) {
168 		x86_write_loapic(LOAPIC_PMC, LOAPIC_LVT_MASKED);
169 	}
170 
171 	if (loApicMaxLvt >= LOAPIC_LVT_PENTIUM4) {
172 		x86_write_loapic(LOAPIC_THERMAL, LOAPIC_LVT_MASKED);
173 	}
174 
175 #if CONFIG_LOAPIC_SPURIOUS_VECTOR
176 	x86_write_loapic(LOAPIC_SVR, (x86_read_loapic(LOAPIC_SVR) & 0xFFFFFF00) |
177 		     (LOAPIC_SPURIOUS_VECTOR_ID & 0xFF));
178 #endif
179 
180 	/* discard a pending interrupt if any */
181 	x86_write_loapic(LOAPIC_EOI, 0);
182 }
183 
184 /**
185  *
186  * @brief Dummy initialization function.
187  *
188  * The local APIC is initialized via z_loapic_enable() long before the
189  * kernel runs through its device initializations, so this is unneeded.
190  */
191 __boot_func
loapic_init(const struct device * unused)192 static int loapic_init(const struct device *unused)
193 {
194 	ARG_UNUSED(unused);
195 	return 0;
196 }
197 
198 
199 __pinned_func
z_loapic_irq_base(void)200 uint32_t z_loapic_irq_base(void)
201 {
202 	return z_ioapic_num_rtes();
203 }
204 
205 /**
206  *
207  * @brief Set the vector field in the specified RTE
208  *
209  * This associates an IRQ with the desired vector in the IDT.
210  *
211  * @return N/A
212  */
213 __boot_func
z_loapic_int_vec_set(unsigned int irq,unsigned int vector)214 void z_loapic_int_vec_set(unsigned int irq, /* IRQ number of the interrupt */
215 				  unsigned int vector /* vector to copy into the LVT */
216 				  )
217 {
218 	unsigned int oldLevel;   /* previous interrupt lock level */
219 
220 	/*
221 	 * The following mappings are used:
222 	 *
223 	 *   IRQ0 -> LOAPIC_TIMER
224 	 *   IRQ1 -> LOAPIC_THERMAL
225 	 *   IRQ2 -> LOAPIC_PMC
226 	 *   IRQ3 -> LOAPIC_LINT0
227 	 *   IRQ4 -> LOAPIC_LINT1
228 	 *   IRQ5 -> LOAPIC_ERROR
229 	 *
230 	 * It's assumed that LVTs are spaced by 0x10 bytes
231 	 */
232 
233 	/* update the 'vector' bits in the LVT */
234 
235 	oldLevel = irq_lock();
236 	x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
237 		     (x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) &
238 		      ~LOAPIC_VECTOR) | vector);
239 	irq_unlock(oldLevel);
240 }
241 
242 /**
243  *
244  * @brief Enable an individual LOAPIC interrupt (IRQ)
245  *
246  * @param irq the IRQ number of the interrupt
247  *
248  * This routine clears the interrupt mask bit in the LVT for the specified IRQ
249  *
250  * @return N/A
251  */
252 __pinned_func
z_loapic_irq_enable(unsigned int irq)253 void z_loapic_irq_enable(unsigned int irq)
254 {
255 	unsigned int oldLevel;   /* previous interrupt lock level */
256 
257 	/*
258 	 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
259 	 * and ths assumption concerning LVT spacing.
260 	 */
261 
262 	/* clear the mask bit in the LVT */
263 
264 	oldLevel = irq_lock();
265 	x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
266 		     x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) &
267 		     ~LOAPIC_LVT_MASKED);
268 	irq_unlock(oldLevel);
269 }
270 
271 /**
272  *
273  * @brief Disable an individual LOAPIC interrupt (IRQ)
274  *
275  * @param irq the IRQ number of the interrupt
276  *
277  * This routine clears the interrupt mask bit in the LVT for the specified IRQ
278  *
279  * @return N/A
280  */
281 __pinned_func
z_loapic_irq_disable(unsigned int irq)282 void z_loapic_irq_disable(unsigned int irq)
283 {
284 	unsigned int oldLevel;   /* previous interrupt lock level */
285 
286 	/*
287 	 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
288 	 * and ths assumption concerning LVT spacing.
289 	 */
290 
291 	/* set the mask bit in the LVT */
292 
293 	oldLevel = irq_lock();
294 	x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
295 		     x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) |
296 		     LOAPIC_LVT_MASKED);
297 	irq_unlock(oldLevel);
298 }
299 
300 
301 /**
302  * @brief Find the currently executing interrupt vector, if any
303  *
304  * This routine finds the vector of the interrupt that is being processed.
305  * The ISR (In-Service Register) register contain the vectors of the interrupts
306  * in service. And the higher vector is the identification of the interrupt
307  * being currently processed.
308  *
309  * This function must be called with interrupts locked in interrupt context.
310  *
311  * ISR registers' offsets:
312  * --------------------
313  * | Offset | bits    |
314  * --------------------
315  * | 0100H  |   0:31  |
316  * | 0110H  |  32:63  |
317  * | 0120H  |  64:95  |
318  * | 0130H  |  96:127 |
319  * | 0140H  | 128:159 |
320  * | 0150H  | 160:191 |
321  * | 0160H  | 192:223 |
322  * | 0170H  | 224:255 |
323  * --------------------
324  *
325  * @return The vector of the interrupt that is currently being processed, or -1
326  * if no IRQ is being serviced.
327  */
328 __pinned_func
z_irq_controller_isr_vector_get(void)329 int z_irq_controller_isr_vector_get(void)
330 {
331 	int pReg, block;
332 
333 	/* Block 0 bits never lit up as these are all exception or reserved
334 	 * vectors
335 	 */
336 	for (block = 7; likely(block > 0); block--) {
337 		pReg = x86_read_loapic(LOAPIC_ISR + (block * 0x10));
338 		if (pReg) {
339 			return (block * 32) + (find_msb_set(pReg) - 1);
340 		}
341 
342 	}
343 	return -1;
344 }
345 
346 #ifdef CONFIG_PM_DEVICE
347 __pinned_func
loapic_suspend(const struct device * port)348 static int loapic_suspend(const struct device *port)
349 {
350 	volatile uint32_t lvt; /* local vector table entry value */
351 	int loapic_irq;
352 
353 	ARG_UNUSED(port);
354 
355 	(void)memset(loapic_suspend_buf, 0, (LOPIC_SUSPEND_BITS_REQD >> 3));
356 
357 	for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
358 
359 		if (_irq_to_interrupt_vector[z_loapic_irq_base() + loapic_irq]) {
360 
361 			/* Since vector numbers are already present in RAM/ROM,
362 			 * We save only the mask bits here.
363 			 */
364 			lvt = x86_read_loapic(LOAPIC_TIMER + (loapic_irq * 0x10));
365 
366 			if ((lvt & LOAPIC_LVT_MASKED) == 0U) {
367 				sys_bitfield_set_bit((mem_addr_t)loapic_suspend_buf,
368 					loapic_irq);
369 			}
370 		}
371 	}
372 
373 	return 0;
374 }
375 
376 __pinned_func
loapic_resume(const struct device * port)377 int loapic_resume(const struct device *port)
378 {
379 	int loapic_irq;
380 
381 	ARG_UNUSED(port);
382 
383 	/* Assuming all loapic device registers lose their state, the call to
384 	 * z_loapic_init(), should bring all the registers to a sane state.
385 	 */
386 	loapic_init(NULL);
387 
388 	for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
389 
390 		if (_irq_to_interrupt_vector[z_loapic_irq_base() + loapic_irq]) {
391 			/* Configure vector and enable the required ones*/
392 			z_loapic_int_vec_set(loapic_irq,
393 				_irq_to_interrupt_vector[z_loapic_irq_base() +
394 							 loapic_irq]);
395 
396 			if (sys_bitfield_test_bit((mem_addr_t) loapic_suspend_buf,
397 							loapic_irq)) {
398 				z_loapic_irq_enable(loapic_irq);
399 			}
400 		}
401 	}
402 
403 	return 0;
404 }
405 
406 /*
407 * Implements the driver control management functionality
408 * the *context may include IN data or/and OUT data
409 */
410 __pinned_func
loapic_device_ctrl(const struct device * dev,enum pm_device_action action)411 static int loapic_device_ctrl(const struct device *dev,
412 			      enum pm_device_action action)
413 {
414 	int ret = 0;
415 
416 	switch (action) {
417 	case PM_DEVICE_ACTION_SUSPEND:
418 		ret = loapic_suspend(dev);
419 		break;
420 	case PM_DEVICE_ACTION_RESUME:
421 		ret = loapic_resume(dev);
422 		break;
423 	default:
424 		return -ENOTSUP;
425 	}
426 
427 	return ret;
428 }
429 
430 SYS_DEVICE_DEFINE("loapic", loapic_init, loapic_device_ctrl, PRE_KERNEL_1,
431 		  CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
432 #else
433 SYS_INIT(loapic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
434 #endif   /* CONFIG_PM_DEVICE */
435 
436 
437 #if CONFIG_LOAPIC_SPURIOUS_VECTOR
438 extern void z_loapic_spurious_handler(void);
439 
440 NANO_CPU_INT_REGISTER(z_loapic_spurious_handler, NANO_SOFT_IRQ,
441 		      LOAPIC_SPURIOUS_VECTOR_ID >> 4,
442 		      LOAPIC_SPURIOUS_VECTOR_ID, 0);
443 #endif
444