| Commit | Line | Data |
|---|---|---|
| 06f5be02 MD |
1 | /* |
| 2 | * Copyright (c) 2005 The DragonFly Project. All rights reserved. | |
| 10ff1029 MD |
3 | * Copyright (c) 1996, by Steve Passe. All rights reserved. |
| 4 | * Copyright (c) 1991 The Regents of the University of California. | |
| 5 | * All rights reserved. | |
| 06f5be02 MD |
6 | * |
| 7 | * This code is derived from software contributed to The DragonFly Project | |
| 8 | * by Matthew Dillon <dillon@backplane.com> | |
| 10ff1029 MD |
9 | * |
| 10 | * This code is derived from software contributed to Berkeley by | |
| 11 | * William Jolitz. | |
| 06f5be02 MD |
12 | * |
| 13 | * Redistribution and use in source and binary forms, with or without | |
| 14 | * modification, are permitted provided that the following conditions | |
| 15 | * are met: | |
| 16 | * | |
| 17 | * 1. Redistributions of source code must retain the above copyright | |
| 18 | * notice, this list of conditions and the following disclaimer. | |
| 19 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 20 | * notice, this list of conditions and the following disclaimer in | |
| 21 | * the documentation and/or other materials provided with the | |
| 22 | * distribution. | |
| 23 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 24 | * contributors may be used to endorse or promote products derived | |
| 25 | * from this software without specific, prior written permission. | |
| 26 | * | |
| 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 28 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 29 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 30 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 31 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 32 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 33 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 34 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 35 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 36 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 37 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 38 | * SUCH DAMAGE. | |
| 37e7efec | 39 | * |
| 0b692e79 | 40 | * $DragonFly: src/sys/platform/pc32/apic/apic_abi.c,v 1.12 2007/04/30 16:45:55 dillon Exp $ |
| 37e7efec MD |
41 | */ |
| 42 | ||
| 43 | #include <sys/param.h> | |
| 44 | #include <sys/systm.h> | |
| 45 | #include <sys/kernel.h> | |
| 46 | #include <sys/machintr.h> | |
| 10ff1029 MD |
47 | #include <sys/interrupt.h> |
| 48 | #include <sys/bus.h> | |
| 0b692e79 | 49 | |
| 37e7efec | 50 | #include <machine/smp.h> |
| 10ff1029 MD |
51 | #include <machine/segments.h> |
| 52 | #include <machine/md_var.h> | |
| 53 | #include <machine/clock.h> /* apic_8254_intr */ | |
| a9295349 MD |
54 | #include <machine_base/isa/intr_machdep.h> |
| 55 | #include <machine_base/icu/icu.h> | |
| 0b692e79 MD |
56 | #include <machine/globaldata.h> |
| 57 | ||
| 58 | #include <sys/thread2.h> | |
| 59 | ||
| 10ff1029 | 60 | #include "apic_ipl.h" |
| 37e7efec | 61 | |
| 79b62055 | 62 | #ifdef SMP /* APIC-IO */ |
| 37e7efec MD |
63 | |
| 64 | extern void APIC_INTREN(int); | |
| 65 | extern void APIC_INTRDIS(int); | |
| 66 | ||
| 10ff1029 MD |
67 | extern inthand_t |
| 68 | IDTVEC(apic_fastintr0), IDTVEC(apic_fastintr1), | |
| 69 | IDTVEC(apic_fastintr2), IDTVEC(apic_fastintr3), | |
| 70 | IDTVEC(apic_fastintr4), IDTVEC(apic_fastintr5), | |
| 71 | IDTVEC(apic_fastintr6), IDTVEC(apic_fastintr7), | |
| 72 | IDTVEC(apic_fastintr8), IDTVEC(apic_fastintr9), | |
| 73 | IDTVEC(apic_fastintr10), IDTVEC(apic_fastintr11), | |
| 74 | IDTVEC(apic_fastintr12), IDTVEC(apic_fastintr13), | |
| 75 | IDTVEC(apic_fastintr14), IDTVEC(apic_fastintr15), | |
| 76 | IDTVEC(apic_fastintr16), IDTVEC(apic_fastintr17), | |
| 77 | IDTVEC(apic_fastintr18), IDTVEC(apic_fastintr19), | |
| 78 | IDTVEC(apic_fastintr20), IDTVEC(apic_fastintr21), | |
| fe5f755a SZ |
79 | IDTVEC(apic_fastintr22), IDTVEC(apic_fastintr23), |
| 80 | IDTVEC(apic_fastintr24), IDTVEC(apic_fastintr25), | |
| 81 | IDTVEC(apic_fastintr26), IDTVEC(apic_fastintr27), | |
| 82 | IDTVEC(apic_fastintr28), IDTVEC(apic_fastintr29), | |
| 83 | IDTVEC(apic_fastintr30), IDTVEC(apic_fastintr31); | |
| 10ff1029 | 84 | |
| 37e7efec MD |
85 | static int apic_setvar(int, const void *); |
| 86 | static int apic_getvar(int, void *); | |
| 10ff1029 | 87 | static int apic_vectorctl(int, int, int); |
| 37e7efec | 88 | static void apic_finalize(void); |
| 0b692e79 | 89 | static void apic_cleanup(void); |
| 37e7efec | 90 | |
| 5f456c40 | 91 | static inthand_t *apic_fastintr[APIC_HWI_VECTORS] = { |
| 10ff1029 MD |
92 | &IDTVEC(apic_fastintr0), &IDTVEC(apic_fastintr1), |
| 93 | &IDTVEC(apic_fastintr2), &IDTVEC(apic_fastintr3), | |
| 94 | &IDTVEC(apic_fastintr4), &IDTVEC(apic_fastintr5), | |
| 95 | &IDTVEC(apic_fastintr6), &IDTVEC(apic_fastintr7), | |
| 96 | &IDTVEC(apic_fastintr8), &IDTVEC(apic_fastintr9), | |
| 97 | &IDTVEC(apic_fastintr10), &IDTVEC(apic_fastintr11), | |
| 98 | &IDTVEC(apic_fastintr12), &IDTVEC(apic_fastintr13), | |
| 99 | &IDTVEC(apic_fastintr14), &IDTVEC(apic_fastintr15), | |
| 100 | &IDTVEC(apic_fastintr16), &IDTVEC(apic_fastintr17), | |
| 101 | &IDTVEC(apic_fastintr18), &IDTVEC(apic_fastintr19), | |
| 102 | &IDTVEC(apic_fastintr20), &IDTVEC(apic_fastintr21), | |
| fe5f755a SZ |
103 | &IDTVEC(apic_fastintr22), &IDTVEC(apic_fastintr23), |
| 104 | &IDTVEC(apic_fastintr24), &IDTVEC(apic_fastintr25), | |
| 105 | &IDTVEC(apic_fastintr26), &IDTVEC(apic_fastintr27), | |
| 106 | &IDTVEC(apic_fastintr28), &IDTVEC(apic_fastintr29), | |
| 107 | &IDTVEC(apic_fastintr30), &IDTVEC(apic_fastintr31) | |
| 10ff1029 MD |
108 | }; |
| 109 | ||
| e96ee753 | 110 | static int apic_imcr_present; |
| 37e7efec | 111 | |
| 79b62055 | 112 | struct machintr_abi MachIntrABI_APIC = { |
| 37e7efec | 113 | MACHINTR_APIC, |
| 0b692e79 MD |
114 | .intrdis = APIC_INTRDIS, |
| 115 | .intren = APIC_INTREN, | |
| 116 | .vectorctl = apic_vectorctl, | |
| 117 | .setvar = apic_setvar, | |
| 118 | .getvar = apic_getvar, | |
| 119 | .finalize = apic_finalize, | |
| 120 | .cleanup = apic_cleanup | |
| 37e7efec MD |
121 | }; |
| 122 | ||
| 123 | static int | |
| 124 | apic_setvar(int varid, const void *buf) | |
| 125 | { | |
| 126 | int error = 0; | |
| 127 | ||
| 128 | switch(varid) { | |
| e96ee753 MD |
129 | case MACHINTR_VAR_IMCR_PRESENT: |
| 130 | apic_imcr_present = *(const int *)buf; | |
| 37e7efec MD |
131 | break; |
| 132 | default: | |
| 133 | error = ENOENT; | |
| 134 | break; | |
| 135 | } | |
| 136 | return (error); | |
| 137 | } | |
| 138 | ||
| 139 | static int | |
| 140 | apic_getvar(int varid, void *buf) | |
| 141 | { | |
| 142 | int error = 0; | |
| 143 | ||
| 144 | switch(varid) { | |
| e96ee753 MD |
145 | case MACHINTR_VAR_IMCR_PRESENT: |
| 146 | *(int *)buf = apic_imcr_present; | |
| 37e7efec MD |
147 | break; |
| 148 | default: | |
| 149 | error = ENOENT; | |
| 150 | break; | |
| 151 | } | |
| 152 | return (error); | |
| 153 | } | |
| 154 | ||
| 155 | /* | |
| 0b692e79 MD |
156 | * Called before interrupts are physically enabled, this routine does the |
| 157 | * final configuration of the BSP's local APIC: | |
| 158 | * | |
| 37e7efec MD |
159 | * - disable 'pic mode'. |
| 160 | * - disable 'virtual wire mode'. | |
| 161 | * - enable NMI. | |
| 06f5be02 | 162 | */ |
| 37e7efec MD |
163 | static void |
| 164 | apic_finalize(void) | |
| 165 | { | |
| 37e7efec MD |
166 | u_int32_t temp; |
| 167 | ||
| e96ee753 MD |
168 | /* |
| 169 | * If an IMCR is present, program bit 0 to disconnect the 8259 | |
| 170 | * from the BSP. The 8259 may still be connected to LINT0 on | |
| 171 | * the BSP's LAPIC. | |
| 172 | */ | |
| 173 | if (apic_imcr_present) { | |
| 37e7efec | 174 | outb(0x22, 0x70); /* select IMCR */ |
| e96ee753 | 175 | outb(0x23, 0x01); /* disconnect 8259 */ |
| 37e7efec MD |
176 | } |
| 177 | ||
| 9d6bf2df MD |
178 | /* |
| 179 | * Setup lint0 (the 8259 'virtual wire' connection). We | |
| e96ee753 MD |
180 | * mask the interrupt, completing the disconnection of the |
| 181 | * 8259. | |
| 9d6bf2df | 182 | */ |
| 37e7efec | 183 | temp = lapic.lvt_lint0; |
| 9d6bf2df | 184 | temp |= APIC_LVT_MASKED; |
| 37e7efec MD |
185 | lapic.lvt_lint0 = temp; |
| 186 | ||
| 9d6bf2df MD |
187 | /* |
| 188 | * setup lint1 to handle an NMI | |
| 189 | */ | |
| 37e7efec | 190 | temp = lapic.lvt_lint1; |
| 9d6bf2df | 191 | temp &= ~APIC_LVT_MASKED; |
| 37e7efec MD |
192 | lapic.lvt_lint1 = temp; |
| 193 | ||
| 194 | if (bootverbose) | |
| 195 | apic_dump("bsp_apic_configure()"); | |
| 196 | } | |
| 197 | ||
| 0b692e79 MD |
198 | /* |
| 199 | * This routine is called after physical interrupts are enabled but before | |
| 200 | * the critical section is released. We need to clean out any interrupts | |
| 201 | * that had already been posted to the cpu. | |
| 202 | */ | |
| 203 | static void | |
| 204 | apic_cleanup(void) | |
| 205 | { | |
| 206 | mdcpu->gd_fpending = 0; | |
| 0b692e79 MD |
207 | } |
| 208 | ||
| 10ff1029 MD |
209 | static |
| 210 | int | |
| 211 | apic_vectorctl(int op, int intr, int flags) | |
| 212 | { | |
| 213 | int error; | |
| 214 | int vector; | |
| 215 | int select; | |
| 216 | u_int32_t value; | |
| 217 | u_long ef; | |
| 218 | ||
| 5f456c40 | 219 | if (intr < 0 || intr >= APIC_HWI_VECTORS) |
| 10ff1029 MD |
220 | return (EINVAL); |
| 221 | ||
| 222 | ef = read_eflags(); | |
| 223 | cpu_disable_intr(); | |
| 224 | error = 0; | |
| 225 | ||
| 226 | switch(op) { | |
| 227 | case MACHINTR_VECTOR_SETUP: | |
| 729e15a8 SZ |
228 | vector = TPR_FAST_INTS + intr; |
| 229 | setidt(vector, apic_fastintr[intr], | |
| 230 | SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); | |
| 35408d22 | 231 | |
| 10ff1029 | 232 | /* |
| 65de6d19 MD |
233 | * Now reprogram the vector in the IO APIC. In order to avoid |
| 234 | * losing an EOI for a level interrupt, which is vector based, | |
| 235 | * make sure that the IO APIC is programmed for edge-triggering | |
| 236 | * first, then reprogrammed with the new vector. This should | |
| 237 | * clear the IRR bit. | |
| 10ff1029 MD |
238 | */ |
| 239 | if (int_to_apicintpin[intr].ioapic >= 0) { | |
| 65de6d19 | 240 | imen_lock(); |
| 10ff1029 | 241 | select = int_to_apicintpin[intr].redirindex; |
| 65de6d19 | 242 | value = io_apic_read(int_to_apicintpin[intr].ioapic, select); |
| 69f1d879 | 243 | value |= IOART_INTMSET; |
| 10ff1029 | 244 | io_apic_write(int_to_apicintpin[intr].ioapic, |
| 65de6d19 MD |
245 | select, (value & ~APIC_TRIGMOD_MASK)); |
| 246 | io_apic_write(int_to_apicintpin[intr].ioapic, | |
| 247 | select, (value & ~IOART_INTVEC) | vector); | |
| 248 | imen_unlock(); | |
| 10ff1029 MD |
249 | } |
| 250 | machintr_intren(intr); | |
| 251 | break; | |
| 252 | case MACHINTR_VECTOR_TEARDOWN: | |
| 35408d22 MD |
253 | /* |
| 254 | * Teardown an interrupt vector. The vector should already be | |
| 255 | * installed in the cpu's IDT, but make sure. | |
| 256 | */ | |
| 10ff1029 | 257 | machintr_intrdis(intr); |
| 729e15a8 SZ |
258 | vector = TPR_FAST_INTS + intr; |
| 259 | setidt(vector, apic_fastintr[intr], SDT_SYS386IGT, SEL_KPL, | |
| 35408d22 MD |
260 | GSEL(GCODE_SEL, SEL_KPL)); |
| 261 | ||
| 262 | /* | |
| 263 | * And then reprogram the IO APIC to point to the SLOW vector (it may | |
| 264 | * have previously been pointed to the FAST version of the vector). | |
| 265 | * This will allow us to keep track of spurious interrupts. | |
| 65de6d19 MD |
266 | * |
| 267 | * In order to avoid losing an EOI for a level interrupt, which is | |
| 268 | * vector based, make sure that the IO APIC is programmed for | |
| 269 | * edge-triggering first, then reprogrammed with the new vector. | |
| 270 | * This should clear the IRR bit. | |
| 35408d22 MD |
271 | */ |
| 272 | if (int_to_apicintpin[intr].ioapic >= 0) { | |
| 65de6d19 | 273 | imen_lock(); |
| 35408d22 | 274 | select = int_to_apicintpin[intr].redirindex; |
| 65de6d19 MD |
275 | value = io_apic_read(int_to_apicintpin[intr].ioapic, select); |
| 276 | io_apic_write(int_to_apicintpin[intr].ioapic, | |
| 277 | select, (value & ~APIC_TRIGMOD_MASK)); | |
| 35408d22 | 278 | io_apic_write(int_to_apicintpin[intr].ioapic, |
| 65de6d19 MD |
279 | select, (value & ~IOART_INTVEC) | vector); |
| 280 | imen_unlock(); | |
| 35408d22 MD |
281 | } |
| 282 | break; | |
| 283 | case MACHINTR_VECTOR_SETDEFAULT: | |
| 284 | /* | |
| 65de6d19 MD |
285 | * This is a just-in-case an int pin is running through the 8259 |
| 286 | * when we don't expect it to, or an IO APIC pin somehow wound | |
| 287 | * up getting enabled without us specifically programming it in | |
| 288 | * this ABI. Note that IO APIC pins are by default programmed | |
| 289 | * to IDT_OFFSET + intr. | |
| 35408d22 MD |
290 | */ |
| 291 | vector = IDT_OFFSET + intr; | |
| 729e15a8 | 292 | setidt(vector, apic_fastintr[intr], SDT_SYS386IGT, SEL_KPL, |
| 10ff1029 MD |
293 | GSEL(GCODE_SEL, SEL_KPL)); |
| 294 | break; | |
| 295 | default: | |
| 296 | error = EOPNOTSUPP; | |
| 297 | break; | |
| 298 | } | |
| 299 | ||
| 300 | write_eflags(ef); | |
| 301 | return (error); | |
| 302 | } | |
| 303 | ||
| 37e7efec | 304 | #endif |
| 06f5be02 | 305 |