source: mainline/kernel/arch/ia32/src/smp/apic.c@ 2a103b5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2a103b5 was 2a103b5, checked in by Jakub Jermar <jakub@…>, 6 years ago

Introduce PIC operations indirection mechanism

Some architectures switch from one interrupt controller implementation
to another during runtime. By providing a cleaner indirection mechanism,
it is possible e.g. for the ia32 IRQ 7 handler to distinguish i8259
spurious interrupts from actual IRQ 7 device interrupts, even when the
i8259 interrupt controller is no longer active.

  • Property mode set to 100644
File size: 15.8 KB
RevLine 
[f761f1eb]1/*
[df4ed85]2 * Copyright (c) 2001-2004 Jakub Jermar
[f761f1eb]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[c5429fe]29/** @addtogroup kernel_ia32
[b45c443]30 * @{
31 */
32/** @file
33 */
34
[d99c1d2]35#include <typedefs.h>
[397c77f]36#include <arch/smp/apic.h>
37#include <arch/smp/ap.h>
[ed0dd65]38#include <arch/smp/mps.h>
[66def8d]39#include <arch/boot/boot.h>
[63e27ef]40#include <assert.h>
[f761f1eb]41#include <mm/page.h>
42#include <time/delay.h>
[fcfac420]43#include <interrupt.h>
[f761f1eb]44#include <arch/interrupt.h>
[b2fa1204]45#include <log.h>
[f761f1eb]46#include <arch/asm.h>
47#include <arch.h>
[3e35fd7]48#include <ddi/irq.h>
[2a103b5]49#include <genarch/pic/pic_ops.h>
[f761f1eb]50
[5f85c91]51#ifdef CONFIG_SMP
[8262010]52
[f761f1eb]53/*
[a83a802]54 * Advanced Programmable Interrupt Controller for SMP systems.
[f761f1eb]55 * Tested on:
[da1bafb]56 * Bochs 2.0.2 - Bochs 2.2.6 with 2-8 CPUs
57 * Simics 2.0.28 - Simics 2.2.19 2-15 CPUs
58 * VMware Workstation 5.5 with 2 CPUs
59 * QEMU 0.8.0 with 2-15 CPUs
60 * ASUS P/I-P65UP5 + ASUS C-P55T2D REV. 1.41 with 2x 200Mhz Pentium CPUs
61 * ASUS PCH-DL with 2x 3000Mhz Pentium 4 Xeon (HT) CPUs
62 * MSI K7D Master-L with 2x 2100MHz Athlon MP CPUs
63 *
[f761f1eb]64 */
65
[2a103b5]66static const char *apic_get_name(void);
67static bool l_apic_is_spurious(unsigned int);
68static void l_apic_handle_spurious(unsigned int);
69
70pic_ops_t apic_pic_ops = {
71 .get_name = apic_get_name,
72 .enable_irqs = io_apic_enable_irqs,
73 .disable_irqs = io_apic_disable_irqs,
74 .eoi = l_apic_eoi,
75 .is_spurious = l_apic_is_spurious,
76 .handle_spurious = l_apic_handle_spurious,
77};
78
[f761f1eb]79/*
80 * These variables either stay configured as initilalized, or are changed by
81 * the MP configuration code.
82 *
83 * Pay special attention to the volatile keyword. Without it, gcc -O2 would
84 * optimize the code too much and accesses to l_apic and io_apic, that must
85 * always be 32-bit, would use byte oriented instructions.
[da1bafb]86 *
[f761f1eb]87 */
[0f17bff]88volatile uint32_t *l_apic = (uint32_t *) L_APIC_BASE;
89volatile uint32_t *io_apic = (uint32_t *) IO_APIC_BASE;
[f761f1eb]90
[7f1c620]91uint32_t apic_id_mask = 0;
[99718a2e]92uint8_t bsp_l_apic = 0;
93
[3e35fd7]94static irq_t l_apic_timer_irq;
[f761f1eb]95
[f701b236]96static int apic_poll_errors(void);
97
[9149135]98#ifdef LAPIC_VERBOSE
[da1bafb]99static const char *delmod_str[] = {
[f701b236]100 "Fixed",
101 "Lowest Priority",
102 "SMI",
103 "Reserved",
104 "NMI",
105 "INIT",
106 "STARTUP",
107 "ExtInt"
108};
109
[da1bafb]110static const char *destmod_str[] = {
[f701b236]111 "Physical",
112 "Logical"
113};
114
[da1bafb]115static const char *trigmod_str[] = {
[f701b236]116 "Edge",
117 "Level"
118};
119
[da1bafb]120static const char *mask_str[] = {
[f701b236]121 "Unmasked",
122 "Masked"
123};
124
[da1bafb]125static const char *delivs_str[] = {
[f701b236]126 "Idle",
127 "Send Pending"
128};
129
[da1bafb]130static const char *tm_mode_str[] = {
[f701b236]131 "One-shot",
132 "Periodic"
133};
134
[da1bafb]135static const char *intpol_str[] = {
[f701b236]136 "Polarity High",
137 "Polarity Low"
138};
[9149135]139#endif /* LAPIC_VERBOSE */
[f761f1eb]140
[2a103b5]141const char *apic_get_name(void)
142{
143 return "apic";
144}
145
146bool l_apic_is_spurious(unsigned int n)
147{
148 return n == VECTOR_APIC_SPUR;
149}
150
151void l_apic_handle_spurious(unsigned int n)
152{
153}
154
[3e35fd7]155/** APIC spurious interrupt handler.
156 *
[da1bafb]157 * @param n Interrupt vector.
[3e35fd7]158 * @param istate Interrupted state.
[da1bafb]159 *
[3e35fd7]160 */
[214ec25c]161static void apic_spurious(unsigned int n __attribute__((unused)),
[da1bafb]162 istate_t *istate __attribute__((unused)))
[3e35fd7]163{
164}
[fcfac420]165
[c9b550b]166static irq_ownership_t l_apic_timer_claim(irq_t *irq)
[3e35fd7]167{
168 return IRQ_ACCEPT;
169}
170
[6cd9aa6]171static void l_apic_timer_irq_handler(irq_t *irq)
[3e35fd7]172{
[7e58979]173 /*
174 * Holding a spinlock could prevent clock() from preempting
175 * the current thread. In this case, we don't need to hold the
176 * irq->lock so we just unlock it and then lock it again.
177 */
[da1bafb]178 irq_spinlock_unlock(&irq->lock, false);
[3e35fd7]179 clock();
[da1bafb]180 irq_spinlock_lock(&irq->lock, false);
[3e35fd7]181}
[fcfac420]182
[99718a2e]183/** Get Local APIC ID.
184 *
185 * @return Local APIC ID.
186 *
187 */
188static uint8_t l_apic_id(void)
189{
190 l_apic_id_t idreg;
[a35b458]191
[99718a2e]192 idreg.value = l_apic[L_APIC_ID];
193 return idreg.apic_id;
194}
195
[8418c7d]196/** Initialize APIC on BSP. */
[f761f1eb]197void apic_init(void)
198{
[b3b7e14a]199 exc_register(VECTOR_APIC_SPUR, "apic_spurious", false,
200 (iroutine_t) apic_spurious);
[a35b458]201
[2a103b5]202 pic_ops = &apic_pic_ops;
[a35b458]203
[f761f1eb]204 /*
205 * Configure interrupt routing.
206 * IRQ 0 remains masked as the time signal is generated by l_apic's themselves.
207 * Other interrupts will be forwarded to the lowest priority CPU.
208 */
[dc0b964]209 io_apic_disable_irqs(0xffffU);
[a35b458]210
[3e35fd7]211 irq_initialize(&l_apic_timer_irq);
[7bcfbbc]212 l_apic_timer_irq.preack = true;
[3e35fd7]213 l_apic_timer_irq.inr = IRQ_CLK;
214 l_apic_timer_irq.claim = l_apic_timer_claim;
215 l_apic_timer_irq.handler = l_apic_timer_irq_handler;
216 irq_register(&l_apic_timer_irq);
[a35b458]217
[7f043c0]218 uint8_t i;
[9149135]219 for (i = 0; i < IRQ_COUNT; i++) {
[f761f1eb]220 int pin;
[a35b458]221
[3e35fd7]222 if ((pin = smp_irq_to_pin(i)) != -1)
[7f043c0]223 io_apic_change_ioredtbl((uint8_t) pin, DEST_ALL, (uint8_t) (IVT_IRQBASE + i), LOPRI);
[f761f1eb]224 }
[a35b458]225
[f761f1eb]226 /*
227 * Ensure that io_apic has unique ID.
228 */
[da1bafb]229 io_apic_id_t idreg;
[a35b458]230
[9149135]231 idreg.value = io_apic_read(IOAPICID);
[da1bafb]232 if ((1 << idreg.apic_id) & apic_id_mask) { /* See if IO APIC ID is used already */
[9149135]233 for (i = 0; i < APIC_ID_COUNT; i++) {
[3e35fd7]234 if (!((1 << i) & apic_id_mask)) {
[9149135]235 idreg.apic_id = i;
236 io_apic_write(IOAPICID, idreg.value);
[f761f1eb]237 break;
238 }
239 }
240 }
[a35b458]241
[f761f1eb]242 /*
243 * Configure the BSP's lapic.
244 */
245 l_apic_init();
[da1bafb]246 l_apic_debug();
[a35b458]247
[99718a2e]248 bsp_l_apic = l_apic_id();
[f761f1eb]249}
250
[f701b236]251/** Poll for APIC errors.
252 *
253 * Examine Error Status Register and report all errors found.
254 *
255 * @return 0 on error, 1 on success.
[da1bafb]256 *
[f701b236]257 */
[f761f1eb]258int apic_poll_errors(void)
259{
[f701b236]260 esr_t esr;
[a35b458]261
[f701b236]262 esr.value = l_apic[ESR];
[a35b458]263
[b2fa1204]264 if (esr.err_bitmap) {
265 log_begin(LF_ARCH, LVL_ERROR);
266 log_printf("APIC errors detected:");
267 if (esr.send_checksum_error)
268 log_printf("\nSend Checksum Error");
269 if (esr.receive_checksum_error)
270 log_printf("\nReceive Checksum Error");
271 if (esr.send_accept_error)
272 log_printf("\nSend Accept Error");
273 if (esr.receive_accept_error)
274 log_printf("\nReceive Accept Error");
275 if (esr.send_illegal_vector)
276 log_printf("\nSend Illegal Vector");
277 if (esr.received_illegal_vector)
278 log_printf("\nReceived Illegal Vector");
279 if (esr.illegal_register_address)
280 log_printf("\nIllegal Register Address");
281 log_end();
282 }
[a35b458]283
[f701b236]284 return !esr.err_bitmap;
[f761f1eb]285}
286
[da68871a]287/* Waits for the destination cpu to accept the previous ipi. */
[5e4f22b]288static void l_apic_wait_for_delivery(void)
289{
290 icr_t icr;
[a35b458]291
[5e4f22b]292 do {
293 icr.lo = l_apic[ICRlo];
[49e6c6b4]294 } while (icr.delivs != DELIVS_IDLE);
295}
296
297/** Send one CPU an IPI vector.
298 *
299 * @param apicid Physical APIC ID of the destination CPU.
300 * @param vector Interrupt vector to be sent.
301 *
302 * @return 0 on failure, 1 on success.
303 */
304int l_apic_send_custom_ipi(uint8_t apicid, uint8_t vector)
305{
306 icr_t icr;
307
308 /* Wait for a destination cpu to accept our previous ipi. */
[da68871a]309 l_apic_wait_for_delivery();
[a35b458]310
[49e6c6b4]311 icr.lo = l_apic[ICRlo];
312 icr.hi = l_apic[ICRhi];
[a35b458]313
[49e6c6b4]314 icr.delmod = DELMOD_FIXED;
315 icr.destmod = DESTMOD_PHYS;
316 icr.level = LEVEL_ASSERT;
317 icr.shorthand = SHORTHAND_NONE;
318 icr.trigger_mode = TRIGMOD_LEVEL;
319 icr.vector = vector;
320 icr.dest = apicid;
321
322 /* Send the IPI by writing to l_apic[ICRlo]. */
323 l_apic[ICRhi] = icr.hi;
324 l_apic[ICRlo] = icr.lo;
[a35b458]325
[49e6c6b4]326 return apic_poll_errors();
[5e4f22b]327}
328
[f701b236]329/** Send all CPUs excluding CPU IPI vector.
330 *
331 * @param vector Interrupt vector to be sent.
332 *
333 * @return 0 on failure, 1 on success.
[da1bafb]334 *
[169587a]335 */
[7f1c620]336int l_apic_broadcast_custom_ipi(uint8_t vector)
[169587a]337{
[8418c7d]338 icr_t icr;
[49e6c6b4]339
340 /* Wait for a destination cpu to accept our previous ipi. */
[da68871a]341 l_apic_wait_for_delivery();
[a35b458]342
[8418c7d]343 icr.lo = l_apic[ICRlo];
344 icr.delmod = DELMOD_FIXED;
345 icr.destmod = DESTMOD_LOGIC;
346 icr.level = LEVEL_ASSERT;
347 icr.shorthand = SHORTHAND_ALL_EXCL;
348 icr.trigger_mode = TRIGMOD_LEVEL;
349 icr.vector = vector;
[a35b458]350
[8418c7d]351 l_apic[ICRlo] = icr.lo;
[a35b458]352
[169587a]353 return apic_poll_errors();
354}
355
[f701b236]356/** Universal Start-up Algorithm for bringing up the AP processors.
357 *
358 * @param apicid APIC ID of the processor to be brought up.
359 *
360 * @return 0 on failure, 1 on success.
[da1bafb]361 *
[f761f1eb]362 */
[7f1c620]363int l_apic_send_init_ipi(uint8_t apicid)
[f761f1eb]364{
365 /*
366 * Read the ICR register in and zero all non-reserved fields.
367 */
[da1bafb]368 icr_t icr;
[a35b458]369
[8418c7d]370 icr.lo = l_apic[ICRlo];
371 icr.hi = l_apic[ICRhi];
[a35b458]372
[8418c7d]373 icr.delmod = DELMOD_INIT;
374 icr.destmod = DESTMOD_PHYS;
375 icr.level = LEVEL_ASSERT;
376 icr.trigger_mode = TRIGMOD_LEVEL;
377 icr.shorthand = SHORTHAND_NONE;
378 icr.vector = 0;
379 icr.dest = apicid;
[a35b458]380
[8418c7d]381 l_apic[ICRhi] = icr.hi;
382 l_apic[ICRlo] = icr.lo;
[a35b458]383
[f761f1eb]384 /*
385 * According to MP Specification, 20us should be enough to
386 * deliver the IPI.
387 */
388 delay(20);
[a35b458]389
[88636f68]390 if (!apic_poll_errors())
391 return 0;
[a35b458]392
[5e4f22b]393 l_apic_wait_for_delivery();
394
[8418c7d]395 icr.lo = l_apic[ICRlo];
396 icr.delmod = DELMOD_INIT;
397 icr.destmod = DESTMOD_PHYS;
398 icr.level = LEVEL_DEASSERT;
399 icr.shorthand = SHORTHAND_NONE;
400 icr.trigger_mode = TRIGMOD_LEVEL;
401 icr.vector = 0;
402 l_apic[ICRlo] = icr.lo;
[a35b458]403
[f761f1eb]404 /*
405 * Wait 10ms as MP Specification specifies.
406 */
407 delay(10000);
[a35b458]408
[c9b8c5c]409 if (!is_82489DX_apic(l_apic[LAVR])) {
410 /*
411 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
412 */
[da1bafb]413 unsigned int i;
414 for (i = 0; i < 2; i++) {
[8418c7d]415 icr.lo = l_apic[ICRlo];
[7f043c0]416 icr.vector = (uint8_t) (((uintptr_t) ap_boot) >> 12); /* calculate the reset vector */
[8418c7d]417 icr.delmod = DELMOD_STARTUP;
418 icr.destmod = DESTMOD_PHYS;
419 icr.level = LEVEL_ASSERT;
420 icr.shorthand = SHORTHAND_NONE;
421 icr.trigger_mode = TRIGMOD_LEVEL;
422 l_apic[ICRlo] = icr.lo;
[c9b8c5c]423 delay(200);
424 }
[f761f1eb]425 }
[a35b458]426
[f761f1eb]427 return apic_poll_errors();
428}
429
[f701b236]430/** Initialize Local APIC. */
[f761f1eb]431void l_apic_init(void)
432{
[8418c7d]433 /* Initialize LVT Error register. */
[da1bafb]434 lvt_error_t error;
[a35b458]435
[8418c7d]436 error.value = l_apic[LVT_Err];
437 error.masked = true;
438 l_apic[LVT_Err] = error.value;
[a35b458]439
[8418c7d]440 /* Initialize LVT LINT0 register. */
[da1bafb]441 lvt_lint_t lint;
[a35b458]442
[8418c7d]443 lint.value = l_apic[LVT_LINT0];
444 lint.masked = true;
445 l_apic[LVT_LINT0] = lint.value;
[a35b458]446
[8418c7d]447 /* Initialize LVT LINT1 register. */
448 lint.value = l_apic[LVT_LINT1];
449 lint.masked = true;
450 l_apic[LVT_LINT1] = lint.value;
[a35b458]451
[d0780b4c]452 /* Task Priority Register initialization. */
[da1bafb]453 tpr_t tpr;
[a35b458]454
[d0780b4c]455 tpr.value = l_apic[TPR];
456 tpr.pri_sc = 0;
457 tpr.pri = 0;
458 l_apic[TPR] = tpr.value;
[a35b458]459
[8418c7d]460 /* Spurious-Interrupt Vector Register initialization. */
[da1bafb]461 svr_t svr;
[a35b458]462
[8418c7d]463 svr.value = l_apic[SVR];
464 svr.vector = VECTOR_APIC_SPUR;
465 svr.lapic_enabled = true;
[d0780b4c]466 svr.focus_checking = true;
[8418c7d]467 l_apic[SVR] = svr.value;
[a35b458]468
[434f700]469 if (CPU->arch.family >= 6)
470 enable_l_apic_in_msr();
[a35b458]471
[8418c7d]472 /* Interrupt Command Register initialization. */
[da1bafb]473 icr_t icr;
[a35b458]474
[8418c7d]475 icr.lo = l_apic[ICRlo];
476 icr.delmod = DELMOD_INIT;
477 icr.destmod = DESTMOD_PHYS;
478 icr.level = LEVEL_DEASSERT;
479 icr.shorthand = SHORTHAND_ALL_INCL;
480 icr.trigger_mode = TRIGMOD_LEVEL;
481 l_apic[ICRlo] = icr.lo;
[a35b458]482
[f701b236]483 /* Timer Divide Configuration Register initialization. */
[da1bafb]484 tdcr_t tdcr;
[a35b458]485
[f701b236]486 tdcr.value = l_apic[TDCR];
487 tdcr.div_value = DIVIDE_1;
488 l_apic[TDCR] = tdcr.value;
[a35b458]489
[f701b236]490 /* Program local timer. */
[da1bafb]491 lvt_tm_t tm;
[a35b458]492
[8418c7d]493 tm.value = l_apic[LVT_Tm];
494 tm.vector = VECTOR_CLK;
495 tm.mode = TIMER_PERIODIC;
496 tm.masked = false;
497 l_apic[LVT_Tm] = tm.value;
[a35b458]498
[e20de55]499 /*
500 * Measure and configure the timer to generate timer
501 * interrupt with period 1s/HZ seconds.
502 */
[da1bafb]503 uint32_t t1 = l_apic[CCRT];
[f761f1eb]504 l_apic[ICRT] = 0xffffffff;
[a35b458]505
[1433ecda]506 while (l_apic[CCRT] == t1)
507 ;
[a35b458]508
[f761f1eb]509 t1 = l_apic[CCRT];
[da1bafb]510 delay(1000000 / HZ);
511 uint32_t t2 = l_apic[CCRT];
[a35b458]512
[da1bafb]513 l_apic[ICRT] = t1 - t2;
[a35b458]514
[93e90c7]515 /* Program Logical Destination Register. */
[63e27ef]516 assert(CPU->id < 8);
[da1bafb]517 ldr_t ldr;
[a35b458]518
[93e90c7]519 ldr.value = l_apic[LDR];
[7f043c0]520 ldr.id = (uint8_t) (1 << CPU->id);
[93e90c7]521 l_apic[LDR] = ldr.value;
[a35b458]522
[93e90c7]523 /* Program Destination Format Register for Flat mode. */
[da1bafb]524 dfr_t dfr;
[a35b458]525
[93e90c7]526 dfr.value = l_apic[DFR];
527 dfr.model = MODEL_FLAT;
528 l_apic[DFR] = dfr.value;
[f761f1eb]529}
530
[f701b236]531/** Local APIC End of Interrupt. */
[ef56a43]532void l_apic_eoi(unsigned int ignored)
[f761f1eb]533{
534 l_apic[EOI] = 0;
535}
536
[f701b236]537/** Dump content of Local APIC registers. */
[f761f1eb]538void l_apic_debug(void)
539{
540#ifdef LAPIC_VERBOSE
[b2fa1204]541 log_begin(LF_ARCH, LVL_DEBUG);
542 log_printf("LVT on cpu%u, LAPIC ID: %" PRIu8 "\n",
[99718a2e]543 CPU->id, l_apic_id());
[a35b458]544
[da1bafb]545 lvt_tm_t tm;
[f701b236]546 tm.value = l_apic[LVT_Tm];
[b2fa1204]547 log_printf("LVT Tm: vector=%" PRIu8 ", %s, %s, %s\n",
[99718a2e]548 tm.vector, delivs_str[tm.delivs], mask_str[tm.masked],
549 tm_mode_str[tm.mode]);
[a35b458]550
[da1bafb]551 lvt_lint_t lint;
[f701b236]552 lint.value = l_apic[LVT_LINT0];
[b2fa1204]553 log_printf("LVT LINT0: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
[99718a2e]554 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
555 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
556 mask_str[lint.masked]);
[a35b458]557
[99718a2e]558 lint.value = l_apic[LVT_LINT1];
[b2fa1204]559 log_printf("LVT LINT1: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
[99718a2e]560 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
561 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
562 mask_str[lint.masked]);
[a35b458]563
[da1bafb]564 lvt_error_t error;
[f701b236]565 error.value = l_apic[LVT_Err];
[b2fa1204]566 log_printf("LVT Err: vector=%" PRIu8 ", %s, %s\n", error.vector,
[99718a2e]567 delivs_str[error.delivs], mask_str[error.masked]);
[b2fa1204]568 log_end();
[f761f1eb]569#endif
570}
571
[f701b236]572/** Read from IO APIC register.
573 *
574 * @param address IO APIC register address.
575 *
576 * @return Content of the addressed IO APIC register.
[da1bafb]577 *
[f701b236]578 */
[7f1c620]579uint32_t io_apic_read(uint8_t address)
[f761f1eb]580{
[f701b236]581 io_regsel_t regsel;
[a35b458]582
[f701b236]583 regsel.value = io_apic[IOREGSEL];
584 regsel.reg_addr = address;
585 io_apic[IOREGSEL] = regsel.value;
[f761f1eb]586 return io_apic[IOWIN];
587}
588
[f701b236]589/** Write to IO APIC register.
590 *
591 * @param address IO APIC register address.
[da1bafb]592 * @param val Content to be written to the addressed IO APIC register.
593 *
[f701b236]594 */
[da1bafb]595void io_apic_write(uint8_t address, uint32_t val)
[f761f1eb]596{
[f701b236]597 io_regsel_t regsel;
[a35b458]598
[f701b236]599 regsel.value = io_apic[IOREGSEL];
600 regsel.reg_addr = address;
601 io_apic[IOREGSEL] = regsel.value;
[da1bafb]602 io_apic[IOWIN] = val;
[f761f1eb]603}
604
[f701b236]605/** Change some attributes of one item in I/O Redirection Table.
606 *
[da1bafb]607 * @param pin IO APIC pin number.
608 * @param dest Interrupt destination address.
609 * @param vec Interrupt vector to trigger.
[f701b236]610 * @param flags Flags.
[da1bafb]611 *
[f701b236]612 */
[da1bafb]613void io_apic_change_ioredtbl(uint8_t pin, uint8_t dest, uint8_t vec,
614 unsigned int flags)
[f761f1eb]615{
[da1bafb]616 unsigned int dlvr;
[a35b458]617
[f761f1eb]618 if (flags & LOPRI)
[a83a802]619 dlvr = DELMOD_LOWPRI;
[da1bafb]620 else
621 dlvr = DELMOD_FIXED;
[a35b458]622
[da1bafb]623 io_redirection_reg_t reg;
[7f043c0]624 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
625 reg.hi = io_apic_read((uint8_t) (IOREDTBL + pin * 2 + 1));
[a35b458]626
[93e90c7]627 reg.dest = dest;
[a83a802]628 reg.destmod = DESTMOD_LOGIC;
629 reg.trigger_mode = TRIGMOD_EDGE;
630 reg.intpol = POLARITY_HIGH;
631 reg.delmod = dlvr;
[da1bafb]632 reg.intvec = vec;
[a35b458]633
[7f043c0]634 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
635 io_apic_write((uint8_t) (IOREDTBL + pin * 2 + 1), reg.hi);
[f761f1eb]636}
637
[f701b236]638/** Mask IRQs in IO APIC.
639 *
640 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
[da1bafb]641 *
[f701b236]642 */
[7f1c620]643void io_apic_disable_irqs(uint16_t irqmask)
[f761f1eb]644{
[623b49f1]645 unsigned int i;
646 for (i = 0; i < 16; i++) {
647 if (irqmask & (1 << i)) {
[f761f1eb]648 /*
649 * Mask the signal input in IO APIC if there is a
650 * mapping for the respective IRQ number.
651 */
[da1bafb]652 int pin = smp_irq_to_pin(i);
[f761f1eb]653 if (pin != -1) {
[da1bafb]654 io_redirection_reg_t reg;
[a35b458]655
[7f043c0]656 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
[a83a802]657 reg.masked = true;
[7f043c0]658 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
[f761f1eb]659 }
[a35b458]660
[f761f1eb]661 }
662 }
663}
664
[f701b236]665/** Unmask IRQs in IO APIC.
666 *
667 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
[da1bafb]668 *
[f701b236]669 */
[7f1c620]670void io_apic_enable_irqs(uint16_t irqmask)
[f761f1eb]671{
[623b49f1]672 unsigned int i;
[7f043c0]673 for (i = 0; i < 16; i++) {
[623b49f1]674 if (irqmask & (1 << i)) {
[f761f1eb]675 /*
676 * Unmask the signal input in IO APIC if there is a
677 * mapping for the respective IRQ number.
678 */
[da1bafb]679 int pin = smp_irq_to_pin(i);
[f761f1eb]680 if (pin != -1) {
[da1bafb]681 io_redirection_reg_t reg;
[a35b458]682
[7f043c0]683 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
[a83a802]684 reg.masked = false;
[7f043c0]685 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
[f761f1eb]686 }
[a35b458]687
[f761f1eb]688 }
689 }
690}
691
[5f85c91]692#endif /* CONFIG_SMP */
[b45c443]693
[06e1e95]694/** @}
[b45c443]695 */
Note: See TracBrowser for help on using the repository browser.