1 /* 2 * i8259 interrupt controller emulation 3 * 4 * Copyright (c) 2003-2004 Fabrice Bellard 5 * Copyright (c) 2005 Intel Corperation 6 * Copyright (c) 2006 Keir Fraser, XenSource Inc. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to 10 * deal in the Software without restriction, including without limitation the 11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 * IN THE SOFTWARE. 25 */ 26 27 #include <xen/types.h> 28 #include <xen/event.h> 29 #include <xen/lib.h> 30 #include <xen/errno.h> 31 #include <xen/sched.h> 32 #include <xen/trace.h> 33 #include <asm/hvm/hvm.h> 34 #include <asm/hvm/io.h> 35 #include <asm/hvm/support.h> 36 37 #define vpic_domain(v) (container_of((v), struct domain, \ 38 arch.hvm_domain.vpic[!vpic->is_master])) 39 #define __vpic_lock(v) &container_of((v), struct hvm_domain, \ 40 vpic[!(v)->is_master])->irq_lock 41 #define vpic_lock(v) spin_lock(__vpic_lock(v)) 42 #define vpic_unlock(v) spin_unlock(__vpic_lock(v)) 43 #define vpic_is_locked(v) spin_is_locked(__vpic_lock(v)) 44 #define vpic_elcr_mask(v) (vpic->is_master ? (uint8_t)0xf8 : (uint8_t)0xde); 45 46 /* Return the highest priority found in mask. Return 8 if none. */ 47 #define VPIC_PRIO_NONE 8 vpic_get_priority(struct hvm_hw_vpic * vpic,uint8_t mask)48 static int vpic_get_priority(struct hvm_hw_vpic *vpic, uint8_t mask) 49 { 50 int prio; 51 52 ASSERT(vpic_is_locked(vpic)); 53 54 if ( mask == 0 ) 55 return VPIC_PRIO_NONE; 56 57 /* prio = ffs(mask ROR vpic->priority_add); */ 58 asm ( "ror %%cl,%b1 ; rep; bsf %1,%0" 59 : "=r" (prio) : "q" ((uint32_t)mask), "c" (vpic->priority_add) ); 60 return prio; 61 } 62 63 /* Return the PIC's highest priority pending interrupt. Return -1 if none. */ vpic_get_highest_priority_irq(struct hvm_hw_vpic * vpic)64 static int vpic_get_highest_priority_irq(struct hvm_hw_vpic *vpic) 65 { 66 int cur_priority, priority, irq; 67 uint8_t mask; 68 69 ASSERT(vpic_is_locked(vpic)); 70 71 mask = vpic->irr & ~vpic->imr; 72 priority = vpic_get_priority(vpic, mask); 73 if ( priority == VPIC_PRIO_NONE ) 74 return -1; 75 76 irq = (priority + vpic->priority_add) & 7; 77 78 /* 79 * Compute current priority. If special fully nested mode on the master, 80 * the IRQ coming from the slave is not taken into account for the 81 * priority computation. In special mask mode, masked interrupts do not 82 * block lower-priority interrupts even if their IS bit is set. 83 */ 84 mask = vpic->isr; 85 if ( vpic->special_fully_nested_mode && vpic->is_master && (irq == 2) ) 86 mask &= ~(1 << 2); 87 if ( vpic->special_mask_mode ) 88 mask &= ~vpic->imr; 89 cur_priority = vpic_get_priority(vpic, mask); 90 91 /* If a higher priority is found then an irq should be generated. */ 92 return (priority < cur_priority) ? irq : -1; 93 } 94 vpic_update_int_output(struct hvm_hw_vpic * vpic)95 static void vpic_update_int_output(struct hvm_hw_vpic *vpic) 96 { 97 int irq; 98 99 ASSERT(vpic_is_locked(vpic)); 100 101 irq = vpic_get_highest_priority_irq(vpic); 102 TRACE_3D(TRC_HVM_EMUL_PIC_INT_OUTPUT, vpic->int_output, vpic->is_master, 103 irq); 104 if ( vpic->int_output == (irq >= 0) ) 105 return; 106 107 /* INT line transition L->H or H->L. */ 108 vpic->int_output = !vpic->int_output; 109 110 if ( vpic->int_output ) 111 { 112 if ( vpic->is_master ) 113 { 114 /* Master INT line is connected in Virtual Wire Mode. */ 115 struct vcpu *v = vpic_domain(vpic)->arch.hvm_domain.i8259_target; 116 if ( v != NULL ) 117 { 118 TRACE_1D(TRC_HVM_EMUL_PIC_KICK, irq); 119 vcpu_kick(v); 120 } 121 } 122 else 123 { 124 /* Assert slave line in master PIC. */ 125 (--vpic)->irr |= 1 << 2; 126 vpic_update_int_output(vpic); 127 } 128 } 129 else if ( !vpic->is_master ) 130 { 131 /* Clear slave line in master PIC. */ 132 (--vpic)->irr &= ~(1 << 2); 133 vpic_update_int_output(vpic); 134 } 135 } 136 __vpic_intack(struct hvm_hw_vpic * vpic,int irq)137 static void __vpic_intack(struct hvm_hw_vpic *vpic, int irq) 138 { 139 uint8_t mask = 1 << irq; 140 141 ASSERT(vpic_is_locked(vpic)); 142 143 TRACE_2D(TRC_HVM_EMUL_PIC_INTACK, vpic->is_master, irq); 144 /* Edge-triggered: clear the IRR (forget the edge). */ 145 if ( !(vpic->elcr & mask) ) 146 vpic->irr &= ~mask; 147 148 if ( !vpic->auto_eoi ) 149 vpic->isr |= mask; 150 else if ( vpic->rotate_on_auto_eoi ) 151 vpic->priority_add = (irq + 1) & 7; 152 153 vpic_update_int_output(vpic); 154 } 155 vpic_intack(struct hvm_hw_vpic * vpic)156 static int vpic_intack(struct hvm_hw_vpic *vpic) 157 { 158 int irq = -1; 159 160 vpic_lock(vpic); 161 162 if ( !vpic->int_output ) 163 goto out; 164 165 irq = vpic_get_highest_priority_irq(vpic); 166 BUG_ON(irq < 0); 167 __vpic_intack(vpic, irq); 168 169 if ( (irq == 2) && vpic->is_master ) 170 { 171 vpic++; /* Slave PIC */ 172 irq = vpic_get_highest_priority_irq(vpic); 173 BUG_ON(irq < 0); 174 __vpic_intack(vpic, irq); 175 irq += 8; 176 } 177 178 out: 179 vpic_unlock(vpic); 180 return irq; 181 } 182 vpic_ioport_write(struct hvm_hw_vpic * vpic,uint32_t addr,uint32_t val)183 static void vpic_ioport_write( 184 struct hvm_hw_vpic *vpic, uint32_t addr, uint32_t val) 185 { 186 int priority, cmd, irq; 187 uint8_t mask, unmasked = 0; 188 189 vpic_lock(vpic); 190 191 if ( (addr & 1) == 0 ) 192 { 193 if ( val & 0x10 ) 194 { 195 /* ICW1 */ 196 /* Clear edge-sensing logic. */ 197 vpic->irr &= vpic->elcr; 198 199 unmasked = vpic->imr; 200 /* No interrupts masked or in service. */ 201 vpic->imr = vpic->isr = 0; 202 203 /* IR7 is lowest priority. */ 204 vpic->priority_add = 0; 205 vpic->rotate_on_auto_eoi = 0; 206 207 vpic->special_mask_mode = 0; 208 vpic->readsel_isr = 0; 209 vpic->poll = 0; 210 211 if ( !(val & 1) ) 212 { 213 /* NO ICW4: ICW4 features are cleared. */ 214 vpic->auto_eoi = 0; 215 vpic->special_fully_nested_mode = 0; 216 } 217 218 vpic->init_state = ((val & 3) << 2) | 1; 219 } 220 else if ( val & 0x08 ) 221 { 222 /* OCW3 */ 223 if ( val & 0x04 ) 224 vpic->poll = 1; 225 if ( val & 0x02 ) 226 vpic->readsel_isr = val & 1; 227 if ( val & 0x40 ) 228 vpic->special_mask_mode = (val >> 5) & 1; 229 } 230 else 231 { 232 /* OCW2 */ 233 cmd = val >> 5; 234 switch ( cmd ) 235 { 236 case 0: /* Rotate in AEOI Mode (Clear) */ 237 case 4: /* Rotate in AEOI Mode (Set) */ 238 vpic->rotate_on_auto_eoi = cmd >> 2; 239 break; 240 case 1: /* Non-Specific EOI */ 241 case 5: /* Non-Specific EOI & Rotate */ 242 mask = vpic->isr; 243 if ( vpic->special_mask_mode ) 244 mask &= ~vpic->imr; /* SMM: ignore masked IRs. */ 245 priority = vpic_get_priority(vpic, mask); 246 if ( priority == VPIC_PRIO_NONE ) 247 break; 248 irq = (priority + vpic->priority_add) & 7; 249 vpic->isr &= ~(1 << irq); 250 if ( cmd == 5 ) 251 vpic->priority_add = (irq + 1) & 7; 252 break; 253 case 3: /* Specific EOI */ 254 case 7: /* Specific EOI & Rotate */ 255 irq = val & 7; 256 vpic->isr &= ~(1 << irq); 257 if ( cmd == 7 ) 258 vpic->priority_add = (irq + 1) & 7; 259 /* Release lock and EOI the physical interrupt (if any). */ 260 vpic_update_int_output(vpic); 261 vpic_unlock(vpic); 262 hvm_dpci_eoi(current->domain, 263 hvm_isa_irq_to_gsi((addr >> 7) ? (irq|8) : irq), 264 NULL); 265 return; /* bail immediately */ 266 case 6: /* Set Priority */ 267 vpic->priority_add = (val + 1) & 7; 268 break; 269 } 270 } 271 } 272 else 273 { 274 switch ( vpic->init_state & 3 ) 275 { 276 case 0: 277 /* OCW1 */ 278 unmasked = vpic->imr & (~val); 279 vpic->imr = val; 280 break; 281 case 1: 282 /* ICW2 */ 283 vpic->irq_base = val & 0xf8; 284 vpic->init_state++; 285 if ( !(vpic->init_state & 8) ) 286 break; /* CASCADE mode: wait for write to ICW3. */ 287 /* SNGL mode: fall through (no ICW3). */ 288 case 2: 289 /* ICW3 */ 290 vpic->init_state++; 291 if ( !(vpic->init_state & 4) ) 292 vpic->init_state = 0; /* No ICW4: init done */ 293 break; 294 case 3: 295 /* ICW4 */ 296 vpic->special_fully_nested_mode = (val >> 4) & 1; 297 vpic->auto_eoi = (val >> 1) & 1; 298 vpic->init_state = 0; 299 break; 300 } 301 } 302 303 vpic_update_int_output(vpic); 304 305 vpic_unlock(vpic); 306 307 if ( unmasked ) 308 pt_may_unmask_irq(vpic_domain(vpic), NULL); 309 } 310 vpic_ioport_read(struct hvm_hw_vpic * vpic,uint32_t addr)311 static uint32_t vpic_ioport_read(struct hvm_hw_vpic *vpic, uint32_t addr) 312 { 313 if ( vpic->poll ) 314 { 315 vpic->poll = 0; 316 return vpic_intack(vpic); 317 } 318 319 if ( (addr & 1) == 0 ) 320 return (vpic->readsel_isr ? vpic->isr : vpic->irr); 321 322 return vpic->imr; 323 } 324 vpic_intercept_pic_io(int dir,unsigned int port,unsigned int bytes,uint32_t * val)325 static int vpic_intercept_pic_io( 326 int dir, unsigned int port, unsigned int bytes, uint32_t *val) 327 { 328 struct hvm_hw_vpic *vpic; 329 330 if ( bytes != 1 ) 331 { 332 gdprintk(XENLOG_WARNING, "PIC_IO bad access size %d\n", bytes); 333 *val = ~0; 334 return X86EMUL_OKAY; 335 } 336 337 vpic = ¤t->domain->arch.hvm_domain.vpic[port >> 7]; 338 339 if ( dir == IOREQ_WRITE ) 340 vpic_ioport_write(vpic, port, (uint8_t)*val); 341 else 342 *val = (uint8_t)vpic_ioport_read(vpic, port); 343 344 return X86EMUL_OKAY; 345 } 346 vpic_intercept_elcr_io(int dir,unsigned int port,unsigned int bytes,uint32_t * val)347 static int vpic_intercept_elcr_io( 348 int dir, unsigned int port, unsigned int bytes, uint32_t *val) 349 { 350 struct hvm_hw_vpic *vpic; 351 uint32_t data; 352 353 BUG_ON(bytes != 1); 354 355 vpic = ¤t->domain->arch.hvm_domain.vpic[port & 1]; 356 357 if ( dir == IOREQ_WRITE ) 358 { 359 /* Some IRs are always edge trig. Slave IR is always level trig. */ 360 data = *val & vpic_elcr_mask(vpic); 361 if ( vpic->is_master ) 362 data |= 1 << 2; 363 vpic->elcr = data; 364 } 365 else 366 { 367 /* Reader should not see hardcoded level-triggered slave IR. */ 368 *val = vpic->elcr & vpic_elcr_mask(vpic); 369 } 370 371 return X86EMUL_OKAY; 372 } 373 vpic_save(struct domain * d,hvm_domain_context_t * h)374 static int vpic_save(struct domain *d, hvm_domain_context_t *h) 375 { 376 struct hvm_hw_vpic *s; 377 int i; 378 379 if ( !has_vpic(d) ) 380 return 0; 381 382 /* Save the state of both PICs */ 383 for ( i = 0; i < 2 ; i++ ) 384 { 385 s = &d->arch.hvm_domain.vpic[i]; 386 if ( hvm_save_entry(PIC, i, h, s) ) 387 return 1; 388 } 389 390 return 0; 391 } 392 vpic_load(struct domain * d,hvm_domain_context_t * h)393 static int vpic_load(struct domain *d, hvm_domain_context_t *h) 394 { 395 struct hvm_hw_vpic *s; 396 uint16_t inst; 397 398 if ( !has_vpic(d) ) 399 return -ENODEV; 400 401 /* Which PIC is this? */ 402 inst = hvm_load_instance(h); 403 if ( inst > 1 ) 404 return -EINVAL; 405 s = &d->arch.hvm_domain.vpic[inst]; 406 407 /* Load the state */ 408 if ( hvm_load_entry(PIC, h, s) != 0 ) 409 return -EINVAL; 410 411 return 0; 412 } 413 414 HVM_REGISTER_SAVE_RESTORE(PIC, vpic_save, vpic_load, 2, HVMSR_PER_DOM); 415 vpic_reset(struct domain * d)416 void vpic_reset(struct domain *d) 417 { 418 struct hvm_hw_vpic *vpic; 419 420 if ( !has_vpic(d) ) 421 return; 422 423 /* Master PIC. */ 424 vpic = &d->arch.hvm_domain.vpic[0]; 425 memset(vpic, 0, sizeof(*vpic)); 426 vpic->is_master = 1; 427 vpic->elcr = 1 << 2; 428 429 /* Slave PIC. */ 430 vpic++; 431 memset(vpic, 0, sizeof(*vpic)); 432 } 433 vpic_init(struct domain * d)434 void vpic_init(struct domain *d) 435 { 436 if ( !has_vpic(d) ) 437 return; 438 439 vpic_reset(d); 440 441 register_portio_handler(d, 0x20, 2, vpic_intercept_pic_io); 442 register_portio_handler(d, 0xa0, 2, vpic_intercept_pic_io); 443 444 register_portio_handler(d, 0x4d0, 1, vpic_intercept_elcr_io); 445 register_portio_handler(d, 0x4d1, 1, vpic_intercept_elcr_io); 446 } 447 vpic_irq_positive_edge(struct domain * d,int irq)448 void vpic_irq_positive_edge(struct domain *d, int irq) 449 { 450 struct hvm_hw_vpic *vpic = &d->arch.hvm_domain.vpic[irq >> 3]; 451 uint8_t mask = 1 << (irq & 7); 452 453 ASSERT(has_vpic(d)); 454 ASSERT(irq <= 15); 455 ASSERT(vpic_is_locked(vpic)); 456 457 TRACE_1D(TRC_HVM_EMUL_PIC_POSEDGE, irq); 458 if ( irq == 2 ) 459 return; 460 461 vpic->irr |= mask; 462 if ( !(vpic->imr & mask) ) 463 vpic_update_int_output(vpic); 464 } 465 vpic_irq_negative_edge(struct domain * d,int irq)466 void vpic_irq_negative_edge(struct domain *d, int irq) 467 { 468 struct hvm_hw_vpic *vpic = &d->arch.hvm_domain.vpic[irq >> 3]; 469 uint8_t mask = 1 << (irq & 7); 470 471 ASSERT(has_vpic(d)); 472 ASSERT(irq <= 15); 473 ASSERT(vpic_is_locked(vpic)); 474 475 TRACE_1D(TRC_HVM_EMUL_PIC_NEGEDGE, irq); 476 if ( irq == 2 ) 477 return; 478 479 vpic->irr &= ~mask; 480 if ( !(vpic->imr & mask) ) 481 vpic_update_int_output(vpic); 482 } 483 vpic_ack_pending_irq(struct vcpu * v)484 int vpic_ack_pending_irq(struct vcpu *v) 485 { 486 int irq, vector; 487 struct hvm_hw_vpic *vpic = &v->domain->arch.hvm_domain.vpic[0]; 488 489 ASSERT(has_vpic(v->domain)); 490 491 TRACE_2D(TRC_HVM_EMUL_PIC_PEND_IRQ_CALL, vlapic_accept_pic_intr(v), 492 vpic->int_output); 493 if ( !vlapic_accept_pic_intr(v) || !vpic->int_output ) 494 return -1; 495 496 irq = vpic_intack(vpic); 497 if ( irq == -1 ) 498 return -1; 499 500 vector = vpic[irq >> 3].irq_base + (irq & 7); 501 return vector; 502 } 503 504 /* 505 * Local variables: 506 * mode: C 507 * c-file-style: "BSD" 508 * c-basic-offset: 4 509 * indent-tabs-mode: nil 510 * End: 511 */ 512