x86_64: Handle I/O APIC probing failure
[dragonfly.git] / sys / platform / pc64 / apic / ioapic.c
1 /*
2  * Copyright (c) 1996, by Steve Passe
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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. The name of the developer may NOT be used to endorse or promote products
11  *    derived from this software without specific prior written permission.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
26  */
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/bus.h>
32 #include <sys/machintr.h>
33 #include <machine/globaldata.h>
34 #include <machine/smp.h>
35 #include <machine/md_var.h>
36 #include <machine/pmap.h>
37 #include <machine_base/apic/lapic.h>
38 #include <machine_base/apic/ioapic.h>
39 #include <machine_base/apic/ioapic_abi.h>
40 #include <machine/segments.h>
41 #include <sys/thread2.h>
42 #include <vm/pmap.h>
43
44 #include <machine/intr_machdep.h>
45
46 #include "apicvar.h"
47
48 #define IOAPIC_COUNT_MAX        16
49 #define IOAPIC_ID_MASK          (IOAPIC_COUNT_MAX - 1)
50
51 struct ioapic_info {
52         int             io_idx;
53         int             io_apic_id;
54         void            *io_addr;
55         int             io_npin;
56         int             io_gsi_base;
57
58         TAILQ_ENTRY(ioapic_info) io_link;
59 };
60 TAILQ_HEAD(ioapic_info_list, ioapic_info);
61
62 struct ioapic_intsrc {
63         int             int_gsi;
64         enum intr_trigger int_trig;
65         enum intr_polarity int_pola;
66 };
67
68 struct ioapic_conf {
69         struct ioapic_info_list ioc_list;
70         struct ioapic_intsrc ioc_intsrc[16];    /* XXX magic number */
71 };
72
73 static void     ioapic_setup(const struct ioapic_info *);
74 static int      ioapic_alloc_apic_id(int);
75 static void     ioapic_set_apic_id(const struct ioapic_info *);
76 static void     ioapic_gsi_setup(int);
77 static const struct ioapic_info *
78                 ioapic_gsi_search(int);
79 static void     ioapic_pin_prog(void *, int, int,
80                     enum intr_trigger, enum intr_polarity, uint32_t);
81
82 static struct ioapic_conf       ioapic_conf;
83
84 static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
85         TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
86
87 int
88 ioapic_config(void)
89 {
90         struct ioapic_enumerator *e;
91         struct ioapic_info *info;
92         int start_apic_id = 0;
93         int error, i, probe;
94         register_t ef = 0;
95
96         TAILQ_INIT(&ioapic_conf.ioc_list);
97         /* XXX magic number */
98         for (i = 0; i < 16; ++i)
99                 ioapic_conf.ioc_intsrc[i].int_gsi = -1;
100
101         probe = 1;
102         TUNABLE_INT_FETCH("hw.ioapic_probe", &probe);
103         if (!probe) {
104                 kprintf("IOAPIC: warning I/O APIC will not be probed\n");
105                 return ENXIO;
106         }
107
108         TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
109                 error = e->ioapic_probe(e);
110                 if (!error)
111                         break;
112         }
113         if (e == NULL) {
114                 kprintf("IOAPIC: can't find I/O APIC\n");
115                 return ENXIO;
116         }
117
118         crit_enter();
119
120         ef = read_rflags();
121         cpu_disable_intr();
122
123         /*
124          * Switch to I/O APIC MachIntrABI and reconfigure
125          * the default IDT entries.
126          */
127         MachIntrABI = MachIntrABI_IOAPIC;
128         MachIntrABI.setdefault();
129
130         e->ioapic_enumerate(e);
131
132         /*
133          * Setup index
134          */
135         i = 0;
136         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
137                 info->io_idx = i++;
138
139         if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
140                 panic("ioapic_config: more than 16 I/O APIC\n");
141
142         /*
143          * Setup APIC ID
144          */
145         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
146                 int apic_id;
147
148                 apic_id = ioapic_alloc_apic_id(start_apic_id);
149                 if (apic_id == NAPICID) {
150                         kprintf("IOAPIC: can't alloc APIC ID for "
151                                 "%dth I/O APIC\n", info->io_idx);
152                         break;
153                 }
154                 info->io_apic_id = apic_id;
155
156                 start_apic_id = apic_id + 1;
157         }
158         if (info != NULL) {
159                 /*
160                  * xAPIC allows I/O APIC's APIC ID to be same
161                  * as the LAPIC's APIC ID
162                  */
163                 kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
164                         "for I/O APIC\n");
165
166                 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
167                         info->io_apic_id = info->io_idx;
168         }
169
170         /*
171          * Warning about any GSI holes
172          */
173         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
174                 const struct ioapic_info *prev_info;
175
176                 prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
177                 if (prev_info != NULL) {
178                         if (info->io_gsi_base !=
179                         prev_info->io_gsi_base + prev_info->io_npin) {
180                                 kprintf("IOAPIC: warning gsi hole "
181                                         "[%d, %d]\n",
182                                         prev_info->io_gsi_base +
183                                         prev_info->io_npin,
184                                         info->io_gsi_base - 1);
185                         }
186                 }
187         }
188
189         if (bootverbose) {
190                 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
191                         kprintf("IOAPIC: idx %d, apic id %d, "
192                                 "gsi base %d, npin %d\n",
193                                 info->io_idx,
194                                 info->io_apic_id,
195                                 info->io_gsi_base,
196                                 info->io_npin);
197                 }
198         }
199
200         /*
201          * Setup all I/O APIC
202          */
203         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
204                 ioapic_setup(info);
205         ioapic_abi_fixup_irqmap();
206
207         write_rflags(ef);
208
209         MachIntrABI.cleanup();
210
211         crit_exit();
212
213         return 0;
214 }
215
216 void
217 ioapic_enumerator_register(struct ioapic_enumerator *ne)
218 {
219         struct ioapic_enumerator *e;
220
221         TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
222                 if (e->ioapic_prio < ne->ioapic_prio) {
223                         TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
224                         return;
225                 }
226         }
227         TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
228 }
229
230 void
231 ioapic_add(void *addr, int gsi_base, int npin)
232 {
233         struct ioapic_info *info, *ninfo;
234         int gsi_end;
235
236         gsi_end = gsi_base + npin - 1;
237         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
238                 if ((gsi_base >= info->io_gsi_base &&
239                      gsi_base < info->io_gsi_base + info->io_npin) ||
240                     (gsi_end >= info->io_gsi_base &&
241                      gsi_end < info->io_gsi_base + info->io_npin)) {
242                         panic("ioapic_add: overlapped gsi, base %d npin %d, "
243                               "hit base %d, npin %d\n", gsi_base, npin,
244                               info->io_gsi_base, info->io_npin);
245                 }
246                 if (info->io_addr == addr)
247                         panic("ioapic_add: duplicated addr %p\n", addr);
248         }
249
250         ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
251         ninfo->io_addr = addr;
252         ninfo->io_npin = npin;
253         ninfo->io_gsi_base = gsi_base;
254         ninfo->io_apic_id = -1;
255
256         /*
257          * Create IOAPIC list in ascending order of GSI base
258          */
259         TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
260             ioapic_info_list, io_link) {
261                 if (ninfo->io_gsi_base > info->io_gsi_base) {
262                         TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
263                             info, ninfo, io_link);
264                         break;
265                 }
266         }
267         if (info == NULL)
268                 TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
269 }
270
271 void
272 ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
273 {
274         struct ioapic_intsrc *int_src;
275
276         KKASSERT(irq < 16);
277         int_src = &ioapic_conf.ioc_intsrc[irq];
278
279         if (gsi == 0) {
280                 /* Don't allow mixed mode */
281                 kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
282                 return;
283         }
284
285         if (int_src->int_gsi != -1) {
286                 if (int_src->int_gsi != gsi) {
287                         kprintf("IOAPIC: warning intsrc irq %d, gsi "
288                                 "%d -> %d\n", irq, int_src->int_gsi, gsi);
289                 }
290                 if (int_src->int_trig != trig) {
291                         kprintf("IOAPIC: warning intsrc irq %d, trig "
292                                 "%s -> %s\n", irq,
293                                 intr_str_trigger(int_src->int_trig),
294                                 intr_str_trigger(trig));
295                 }
296                 if (int_src->int_pola != pola) {
297                         kprintf("IOAPIC: warning intsrc irq %d, pola "
298                                 "%s -> %s\n", irq,
299                                 intr_str_polarity(int_src->int_pola),
300                                 intr_str_polarity(pola));
301                 }
302         }
303         int_src->int_gsi = gsi;
304         int_src->int_trig = trig;
305         int_src->int_pola = pola;
306 }
307
308 static void
309 ioapic_set_apic_id(const struct ioapic_info *info)
310 {
311         uint32_t id;
312         int apic_id;
313
314         id = ioapic_read(info->io_addr, IOAPIC_ID);
315
316         id &= ~APIC_ID_MASK;
317         id |= (info->io_apic_id << 24);
318
319         ioapic_write(info->io_addr, IOAPIC_ID, id);
320
321         /*
322          * Re-read && test
323          */
324         id = ioapic_read(info->io_addr, IOAPIC_ID);
325         apic_id = (id & APIC_ID_MASK) >> 24;
326
327         /*
328          * I/O APIC ID is a 4bits field
329          */
330         if ((apic_id & IOAPIC_ID_MASK) !=
331             (info->io_apic_id & IOAPIC_ID_MASK)) {
332                 panic("ioapic_set_apic_id: can't set apic id to %d, "
333                       "currently set to %d\n", info->io_apic_id, apic_id);
334         }
335 }
336
337 static void
338 ioapic_gsi_setup(int gsi)
339 {
340         enum intr_trigger trig;
341         enum intr_polarity pola;
342         int irq;
343
344         if (gsi == 0) {
345                 /* ExtINT */
346                 imen_lock();
347                 ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
348                     ioapic_gsi_pin(gsi), 0);
349                 imen_unlock();
350                 return;
351         }
352
353         for (irq = 0; irq < 16; ++irq) {
354                 const struct ioapic_intsrc *int_src =
355                     &ioapic_conf.ioc_intsrc[irq];
356
357                 if (gsi == int_src->int_gsi) {
358                         trig = int_src->int_trig;
359                         pola = int_src->int_pola;
360                         break;
361                 }
362         }
363
364         if (irq == 16) {
365                 if (gsi < 16) {
366                         trig = INTR_TRIGGER_EDGE;
367                         pola = INTR_POLARITY_HIGH;
368                 } else {
369                         trig = INTR_TRIGGER_LEVEL;
370                         pola = INTR_POLARITY_LOW;
371                 }
372                 irq = gsi;
373         }
374
375         ioapic_abi_set_irqmap(irq, gsi, trig, pola);
376 }
377
378 void *
379 ioapic_gsi_ioaddr(int gsi)
380 {
381         const struct ioapic_info *info;
382
383         info = ioapic_gsi_search(gsi);
384         return info->io_addr;
385 }
386
387 int
388 ioapic_gsi_pin(int gsi)
389 {
390         const struct ioapic_info *info;
391
392         info = ioapic_gsi_search(gsi);
393         return gsi - info->io_gsi_base;
394 }
395
396 static const struct ioapic_info *
397 ioapic_gsi_search(int gsi)
398 {
399         const struct ioapic_info *info;
400
401         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
402                 if (gsi >= info->io_gsi_base &&
403                     gsi < info->io_gsi_base + info->io_npin)
404                         return info;
405         }
406         panic("ioapic_gsi_search: no I/O APIC\n");
407 }
408
409 int
410 ioapic_gsi(int idx, int pin)
411 {
412         const struct ioapic_info *info;
413
414         TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
415                 if (info->io_idx == idx)
416                         break;
417         }
418         if (info == NULL)
419                 return -1;
420         if (pin >= info->io_npin)
421                 return -1;
422         return info->io_gsi_base + pin;
423 }
424
425 void
426 ioapic_extpin_setup(void *addr, int pin, int vec)
427 {
428         ioapic_pin_prog(addr, pin, vec,
429             INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
430 }
431
432 int
433 ioapic_extpin_gsi(void)
434 {
435         return 0;
436 }
437
438 void
439 ioapic_pin_setup(void *addr, int pin, int vec,
440     enum intr_trigger trig, enum intr_polarity pola)
441 {
442         /*
443          * Always clear an I/O APIC pin before [re]programming it.  This is
444          * particularly important if the pin is set up for a level interrupt
445          * as the IOART_REM_IRR bit might be set.   When we reprogram the
446          * vector any EOI from pending ints on this pin could be lost and
447          * IRR might never get reset.
448          *
449          * To fix this problem, clear the vector and make sure it is 
450          * programmed as an edge interrupt.  This should theoretically
451          * clear IRR so we can later, safely program it as a level 
452          * interrupt.
453          */
454         ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
455             IOART_DELFIXED);
456         ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
457 }
458
459 static void
460 ioapic_pin_prog(void *addr, int pin, int vec,
461     enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
462 {
463         uint32_t flags, target;
464         int select;
465
466         KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
467
468         select = IOAPIC_REDTBL0 + (2 * pin);
469
470         flags = ioapic_read(addr, select) & IOART_RESV;
471         flags |= IOART_INTMSET | IOART_DESTPHY;
472 #ifdef foo
473         flags |= del_mode;
474 #else
475         /*
476          * We only support limited I/O APIC mixed mode,
477          * so even for ExtINT, we still use "fixed"
478          * delivery mode.
479          */
480         flags |= IOART_DELFIXED;
481 #endif
482
483         if (del_mode == IOART_DELEXINT) {
484                 KKASSERT(trig == INTR_TRIGGER_CONFORM &&
485                          pola == INTR_POLARITY_CONFORM);
486                 flags |= IOART_TRGREDG | IOART_INTAHI;
487         } else {
488                 switch (trig) {
489                 case INTR_TRIGGER_EDGE:
490                         flags |= IOART_TRGREDG;
491                         break;
492
493                 case INTR_TRIGGER_LEVEL:
494                         flags |= IOART_TRGRLVL;
495                         break;
496
497                 case INTR_TRIGGER_CONFORM:
498                         panic("ioapic_pin_prog: trig conform is not "
499                               "supported\n");
500                 }
501                 switch (pola) {
502                 case INTR_POLARITY_HIGH:
503                         flags |= IOART_INTAHI;
504                         break;
505
506                 case INTR_POLARITY_LOW:
507                         flags |= IOART_INTALO;
508                         break;
509
510                 case INTR_POLARITY_CONFORM:
511                         panic("ioapic_pin_prog: pola conform is not "
512                               "supported\n");
513                 }
514         }
515
516         target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
517         target |= (CPUID_TO_APICID(0) << IOART_HI_DEST_SHIFT) &
518                   IOART_HI_DEST_MASK;
519
520         ioapic_write(addr, select, flags | vec);
521         ioapic_write(addr, select + 1, target);
522 }
523
524 static void
525 ioapic_setup(const struct ioapic_info *info)
526 {
527         int i;
528
529         ioapic_set_apic_id(info);
530
531         for (i = 0; i < info->io_npin; ++i)
532                 ioapic_gsi_setup(info->io_gsi_base + i);
533 }
534
535 static int
536 ioapic_alloc_apic_id(int start)
537 {
538         for (;;) {
539                 const struct ioapic_info *info;
540                 int apic_id, apic_id16;
541
542                 apic_id = lapic_unused_apic_id(start);
543                 if (apic_id == NAPICID) {
544                         kprintf("IOAPIC: can't find unused APIC ID\n");
545                         return apic_id;
546                 }
547                 apic_id16 = apic_id & IOAPIC_ID_MASK;
548
549                 /*
550                  * Check against other I/O APIC's APIC ID's lower 4bits.
551                  *
552                  * The new APIC ID will have to be different from others
553                  * in the lower 4bits, no matter whether xAPIC is used
554                  * or not.
555                  */
556                 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
557                         if (info->io_apic_id == -1) {
558                                 info = NULL;
559                                 break;
560                         }
561                         if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
562                                 break;
563                 }
564                 if (info == NULL)
565                         return apic_id;
566
567                 kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
568                         "%dth I/O APIC, keep searching...\n",
569                         apic_id, info->io_idx);
570
571                 start = apic_id + 1;
572         }
573         panic("ioapic_unused_apic_id: never reached\n");
574 }
575
576 /*
577  * Map a physical memory address representing I/O into KVA.  The I/O
578  * block is assumed not to cross a page boundary.
579  */
580 void *
581 ioapic_map(vm_paddr_t pa)
582 {
583         KKASSERT(pa < 0x100000000LL);
584
585         return pmap_mapdev_uncacheable(pa, PAGE_SIZE);
586 }