1 /** @file 2 This contains the installation function for the driver. 3 4 Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "8259.h" 16 17 // 18 // Global for the Legacy 8259 Protocol that is produced by this driver 19 // 20 EFI_LEGACY_8259_PROTOCOL mInterrupt8259 = { 21 Interrupt8259SetVectorBase, 22 Interrupt8259GetMask, 23 Interrupt8259SetMask, 24 Interrupt8259SetMode, 25 Interrupt8259GetVector, 26 Interrupt8259EnableIrq, 27 Interrupt8259DisableIrq, 28 Interrupt8259GetInterruptLine, 29 Interrupt8259EndOfInterrupt 30 }; 31 32 // 33 // Global for the handle that the Legacy 8259 Protocol is installed 34 // 35 EFI_HANDLE m8259Handle = NULL; 36 37 UINT8 mMasterBase = 0xff; 38 UINT8 mSlaveBase = 0xff; 39 EFI_8259_MODE mMode = Efi8259ProtectedMode; 40 UINT16 mProtectedModeMask = 0xffff; 41 UINT16 mLegacyModeMask; 42 UINT16 mProtectedModeEdgeLevel = 0x0000; 43 UINT16 mLegacyModeEdgeLevel; 44 45 // 46 // Worker Functions 47 // 48 49 /** 50 Write to mask and edge/level triggered registers of master and slave PICs. 51 52 @param[in] Mask low byte for master PIC mask register, 53 high byte for slave PIC mask register. 54 @param[in] EdgeLevel low byte for master PIC edge/level triggered register, 55 high byte for slave PIC edge/level triggered register. 56 57 **/ 58 VOID 59 Interrupt8259WriteMask ( 60 IN UINT16 Mask, 61 IN UINT16 EdgeLevel 62 ) 63 { 64 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, (UINT8) Mask); 65 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, (UINT8) (Mask >> 8)); 66 IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_MASTER, (UINT8) EdgeLevel); 67 IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_SLAVE, (UINT8) (EdgeLevel >> 8)); 68 } 69 70 /** 71 Read from mask and edge/level triggered registers of master and slave PICs. 72 73 @param[out] Mask low byte for master PIC mask register, 74 high byte for slave PIC mask register. 75 @param[out] EdgeLevel low byte for master PIC edge/level triggered register, 76 high byte for slave PIC edge/level triggered register. 77 78 **/ 79 VOID 80 Interrupt8259ReadMask ( 81 OUT UINT16 *Mask, 82 OUT UINT16 *EdgeLevel 83 ) 84 { 85 UINT16 MasterValue; 86 UINT16 SlaveValue; 87 88 if (Mask != NULL) { 89 MasterValue = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER); 90 SlaveValue = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE); 91 92 *Mask = (UINT16) (MasterValue | (SlaveValue << 8)); 93 } 94 95 if (EdgeLevel != NULL) { 96 MasterValue = IoRead8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_MASTER); 97 SlaveValue = IoRead8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_SLAVE); 98 99 *EdgeLevel = (UINT16) (MasterValue | (SlaveValue << 8)); 100 } 101 } 102 103 // 104 // Legacy 8259 Protocol Interface Functions 105 // 106 107 /** 108 Sets the base address for the 8259 master and slave PICs. 109 110 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 111 @param[in] MasterBase Interrupt vectors for IRQ0-IRQ7. 112 @param[in] SlaveBase Interrupt vectors for IRQ8-IRQ15. 113 114 @retval EFI_SUCCESS The 8259 PIC was programmed successfully. 115 @retval EFI_DEVICE_ERROR There was an error while writing to the 8259 PIC. 116 117 **/ 118 EFI_STATUS 119 EFIAPI 120 Interrupt8259SetVectorBase ( 121 IN EFI_LEGACY_8259_PROTOCOL *This, 122 IN UINT8 MasterBase, 123 IN UINT8 SlaveBase 124 ) 125 { 126 UINT8 Mask; 127 EFI_TPL OriginalTpl; 128 129 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); 130 // 131 // Set vector base for slave PIC 132 // 133 if (SlaveBase != mSlaveBase) { 134 mSlaveBase = SlaveBase; 135 136 // 137 // Initialization sequence is needed for setting vector base. 138 // 139 140 // 141 // Preserve interrtup mask register before initialization sequence 142 // because it will be cleared during intialization 143 // 144 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE); 145 146 // 147 // ICW1: cascade mode, ICW4 write required 148 // 149 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, 0x11); 150 151 // 152 // ICW2: new vector base (must be multiple of 8) 153 // 154 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, mSlaveBase); 155 156 // 157 // ICW3: slave indentification code must be 2 158 // 159 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x02); 160 161 // 162 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor 163 // 164 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x01); 165 166 // 167 // Restore interrupt mask register 168 // 169 IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, Mask); 170 } 171 172 // 173 // Set vector base for master PIC 174 // 175 if (MasterBase != mMasterBase) { 176 mMasterBase = MasterBase; 177 178 // 179 // Initialization sequence is needed for setting vector base. 180 // 181 182 // 183 // Preserve interrtup mask register before initialization sequence 184 // because it will be cleared during intialization 185 // 186 Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER); 187 188 // 189 // ICW1: cascade mode, ICW4 write required 190 // 191 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, 0x11); 192 193 // 194 // ICW2: new vector base (must be multiple of 8) 195 // 196 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, mMasterBase); 197 198 // 199 // ICW3: slave PIC is cascaded on IRQ2 200 // 201 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x04); 202 203 // 204 // ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor 205 // 206 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x01); 207 208 // 209 // Restore interrupt mask register 210 // 211 IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, Mask); 212 } 213 214 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI); 215 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI); 216 217 gBS->RestoreTPL (OriginalTpl); 218 219 return EFI_SUCCESS; 220 } 221 222 /** 223 Gets the current 16-bit real mode and 32-bit protected-mode IRQ masks. 224 225 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 226 @param[out] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15. 227 @param[out] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15. 228 @param[out] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15. 229 @param[out] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15. 230 231 @retval EFI_SUCCESS The 8259 PIC was programmed successfully. 232 @retval EFI_DEVICE_ERROR There was an error while reading the 8259 PIC. 233 234 **/ 235 EFI_STATUS 236 EFIAPI 237 Interrupt8259GetMask ( 238 IN EFI_LEGACY_8259_PROTOCOL *This, 239 OUT UINT16 *LegacyMask, OPTIONAL 240 OUT UINT16 *LegacyEdgeLevel, OPTIONAL 241 OUT UINT16 *ProtectedMask, OPTIONAL 242 OUT UINT16 *ProtectedEdgeLevel OPTIONAL 243 ) 244 { 245 if (LegacyMask != NULL) { 246 *LegacyMask = mLegacyModeMask; 247 } 248 249 if (LegacyEdgeLevel != NULL) { 250 *LegacyEdgeLevel = mLegacyModeEdgeLevel; 251 } 252 253 if (ProtectedMask != NULL) { 254 *ProtectedMask = mProtectedModeMask; 255 } 256 257 if (ProtectedEdgeLevel != NULL) { 258 *ProtectedEdgeLevel = mProtectedModeEdgeLevel; 259 } 260 261 return EFI_SUCCESS; 262 } 263 264 /** 265 Sets the current 16-bit real mode and 32-bit protected-mode IRQ masks. 266 267 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 268 @param[in] LegacyMask 16-bit mode interrupt mask for IRQ0-IRQ15. 269 @param[in] LegacyEdgeLevel 16-bit mode edge/level mask for IRQ-IRQ15. 270 @param[in] ProtectedMask 32-bit mode interrupt mask for IRQ0-IRQ15. 271 @param[in] ProtectedEdgeLevel 32-bit mode edge/level mask for IRQ0-IRQ15. 272 273 @retval EFI_SUCCESS The 8259 PIC was programmed successfully. 274 @retval EFI_DEVICE_ERROR There was an error while writing the 8259 PIC. 275 276 **/ 277 EFI_STATUS 278 EFIAPI 279 Interrupt8259SetMask ( 280 IN EFI_LEGACY_8259_PROTOCOL *This, 281 IN UINT16 *LegacyMask, OPTIONAL 282 IN UINT16 *LegacyEdgeLevel, OPTIONAL 283 IN UINT16 *ProtectedMask, OPTIONAL 284 IN UINT16 *ProtectedEdgeLevel OPTIONAL 285 ) 286 { 287 if (LegacyMask != NULL) { 288 mLegacyModeMask = *LegacyMask; 289 } 290 291 if (LegacyEdgeLevel != NULL) { 292 mLegacyModeEdgeLevel = *LegacyEdgeLevel; 293 } 294 295 if (ProtectedMask != NULL) { 296 mProtectedModeMask = *ProtectedMask; 297 } 298 299 if (ProtectedEdgeLevel != NULL) { 300 mProtectedModeEdgeLevel = *ProtectedEdgeLevel; 301 } 302 303 return EFI_SUCCESS; 304 } 305 306 /** 307 Sets the mode of the PICs. 308 309 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 310 @param[in] Mode 16-bit real or 32-bit protected mode. 311 @param[in] Mask The value with which to set the interrupt mask. 312 @param[in] EdgeLevel The value with which to set the edge/level mask. 313 314 @retval EFI_SUCCESS The mode was set successfully. 315 @retval EFI_INVALID_PARAMETER The mode was not set. 316 317 **/ 318 EFI_STATUS 319 EFIAPI 320 Interrupt8259SetMode ( 321 IN EFI_LEGACY_8259_PROTOCOL *This, 322 IN EFI_8259_MODE Mode, 323 IN UINT16 *Mask, OPTIONAL 324 IN UINT16 *EdgeLevel OPTIONAL 325 ) 326 { 327 if (Mode == mMode) { 328 return EFI_SUCCESS; 329 } 330 331 if (Mode == Efi8259LegacyMode) { 332 // 333 // In Efi8259ProtectedMode, mask and edge/level trigger registers should 334 // be changed through this protocol, so we can track them in the 335 // corresponding module variables. 336 // 337 Interrupt8259ReadMask (&mProtectedModeMask, &mProtectedModeEdgeLevel); 338 339 if (Mask != NULL) { 340 // 341 // Update the Mask for the new mode 342 // 343 mLegacyModeMask = *Mask; 344 } 345 346 if (EdgeLevel != NULL) { 347 // 348 // Update the Edge/Level triggered mask for the new mode 349 // 350 mLegacyModeEdgeLevel = *EdgeLevel; 351 } 352 353 mMode = Mode; 354 355 // 356 // Write new legacy mode mask/trigger level 357 // 358 Interrupt8259WriteMask (mLegacyModeMask, mLegacyModeEdgeLevel); 359 360 return EFI_SUCCESS; 361 } 362 363 if (Mode == Efi8259ProtectedMode) { 364 // 365 // Save the legacy mode mask/trigger level 366 // 367 Interrupt8259ReadMask (&mLegacyModeMask, &mLegacyModeEdgeLevel); 368 // 369 // Always force Timer to be enabled after return from 16-bit code. 370 // This always insures that on next entry, timer is counting. 371 // 372 mLegacyModeMask &= 0xFFFE; 373 374 if (Mask != NULL) { 375 // 376 // Update the Mask for the new mode 377 // 378 mProtectedModeMask = *Mask; 379 } 380 381 if (EdgeLevel != NULL) { 382 // 383 // Update the Edge/Level triggered mask for the new mode 384 // 385 mProtectedModeEdgeLevel = *EdgeLevel; 386 } 387 388 mMode = Mode; 389 390 // 391 // Write new protected mode mask/trigger level 392 // 393 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel); 394 395 return EFI_SUCCESS; 396 } 397 398 return EFI_INVALID_PARAMETER; 399 } 400 401 /** 402 Translates the IRQ into a vector. 403 404 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 405 @param[in] Irq IRQ0-IRQ15. 406 @param[out] Vector The vector that is assigned to the IRQ. 407 408 @retval EFI_SUCCESS The Vector that matches Irq was returned. 409 @retval EFI_INVALID_PARAMETER Irq is not valid. 410 411 **/ 412 EFI_STATUS 413 EFIAPI 414 Interrupt8259GetVector ( 415 IN EFI_LEGACY_8259_PROTOCOL *This, 416 IN EFI_8259_IRQ Irq, 417 OUT UINT8 *Vector 418 ) 419 { 420 if ((UINT32)Irq > Efi8259Irq15) { 421 return EFI_INVALID_PARAMETER; 422 } 423 424 if (Irq <= Efi8259Irq7) { 425 *Vector = (UINT8) (mMasterBase + Irq); 426 } else { 427 *Vector = (UINT8) (mSlaveBase + (Irq - Efi8259Irq8)); 428 } 429 430 return EFI_SUCCESS; 431 } 432 433 /** 434 Enables the specified IRQ. 435 436 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 437 @param[in] Irq IRQ0-IRQ15. 438 @param[in] LevelTriggered 0 = Edge triggered; 1 = Level triggered. 439 440 @retval EFI_SUCCESS The Irq was enabled on the 8259 PIC. 441 @retval EFI_INVALID_PARAMETER The Irq is not valid. 442 443 **/ 444 EFI_STATUS 445 EFIAPI 446 Interrupt8259EnableIrq ( 447 IN EFI_LEGACY_8259_PROTOCOL *This, 448 IN EFI_8259_IRQ Irq, 449 IN BOOLEAN LevelTriggered 450 ) 451 { 452 if ((UINT32)Irq > Efi8259Irq15) { 453 return EFI_INVALID_PARAMETER; 454 } 455 456 mProtectedModeMask = (UINT16) (mProtectedModeMask & ~(1 << Irq)); 457 if (LevelTriggered) { 458 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel | (1 << Irq)); 459 } else { 460 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq)); 461 } 462 463 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel); 464 465 return EFI_SUCCESS; 466 } 467 468 /** 469 Disables the specified IRQ. 470 471 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 472 @param[in] Irq IRQ0-IRQ15. 473 474 @retval EFI_SUCCESS The Irq was disabled on the 8259 PIC. 475 @retval EFI_INVALID_PARAMETER The Irq is not valid. 476 477 **/ 478 EFI_STATUS 479 EFIAPI 480 Interrupt8259DisableIrq ( 481 IN EFI_LEGACY_8259_PROTOCOL *This, 482 IN EFI_8259_IRQ Irq 483 ) 484 { 485 if ((UINT32)Irq > Efi8259Irq15) { 486 return EFI_INVALID_PARAMETER; 487 } 488 489 mProtectedModeMask = (UINT16) (mProtectedModeMask | (1 << Irq)); 490 491 mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq)); 492 493 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel); 494 495 return EFI_SUCCESS; 496 } 497 498 /** 499 Reads the PCI configuration space to get the interrupt number that is assigned to the card. 500 501 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 502 @param[in] PciHandle PCI function for which to return the vector. 503 @param[out] Vector IRQ number that corresponds to the interrupt line. 504 505 @retval EFI_SUCCESS The interrupt line value was read successfully. 506 507 **/ 508 EFI_STATUS 509 EFIAPI 510 Interrupt8259GetInterruptLine ( 511 IN EFI_LEGACY_8259_PROTOCOL *This, 512 IN EFI_HANDLE PciHandle, 513 OUT UINT8 *Vector 514 ) 515 { 516 EFI_PCI_IO_PROTOCOL *PciIo; 517 UINT8 InterruptLine; 518 EFI_STATUS Status; 519 520 Status = gBS->HandleProtocol ( 521 PciHandle, 522 &gEfiPciIoProtocolGuid, 523 (VOID **) &PciIo 524 ); 525 if (EFI_ERROR (Status)) { 526 return EFI_INVALID_PARAMETER; 527 } 528 529 PciIo->Pci.Read ( 530 PciIo, 531 EfiPciIoWidthUint8, 532 PCI_INT_LINE_OFFSET, 533 1, 534 &InterruptLine 535 ); 536 // 537 // Interrupt line is same location for standard PCI cards, standard 538 // bridge and CardBus bridge. 539 // 540 *Vector = InterruptLine; 541 542 return EFI_SUCCESS; 543 } 544 545 /** 546 Issues the End of Interrupt (EOI) commands to PICs. 547 548 @param[in] This Indicates the EFI_LEGACY_8259_PROTOCOL instance. 549 @param[in] Irq The interrupt for which to issue the EOI command. 550 551 @retval EFI_SUCCESS The EOI command was issued. 552 @retval EFI_INVALID_PARAMETER The Irq is not valid. 553 554 **/ 555 EFI_STATUS 556 EFIAPI 557 Interrupt8259EndOfInterrupt ( 558 IN EFI_LEGACY_8259_PROTOCOL *This, 559 IN EFI_8259_IRQ Irq 560 ) 561 { 562 if ((UINT32)Irq > Efi8259Irq15) { 563 return EFI_INVALID_PARAMETER; 564 } 565 566 if (Irq >= Efi8259Irq8) { 567 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI); 568 } 569 570 IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI); 571 572 return EFI_SUCCESS; 573 } 574 575 /** 576 Driver Entry point. 577 578 @param[in] ImageHandle ImageHandle of the loaded driver. 579 @param[in] SystemTable Pointer to the EFI System Table. 580 581 @retval EFI_SUCCESS One or more of the drivers returned a success code. 582 @retval !EFI_SUCCESS Error installing Legacy 8259 Protocol. 583 584 **/ 585 EFI_STATUS 586 EFIAPI 587 Install8259 ( 588 IN EFI_HANDLE ImageHandle, 589 IN EFI_SYSTEM_TABLE *SystemTable 590 ) 591 { 592 EFI_STATUS Status; 593 EFI_8259_IRQ Irq; 594 595 // 596 // Initialze mask values from PCDs 597 // 598 mLegacyModeMask = PcdGet16 (Pcd8259LegacyModeMask); 599 mLegacyModeEdgeLevel = PcdGet16 (Pcd8259LegacyModeEdgeLevel); 600 601 // 602 // Clear all pending interrupt 603 // 604 for (Irq = Efi8259Irq0; Irq <= Efi8259Irq15; Irq++) { 605 Interrupt8259EndOfInterrupt (&mInterrupt8259, Irq); 606 } 607 608 // 609 // Set the 8259 Master base to 0x68 and the 8259 Slave base to 0x70 610 // 611 Status = Interrupt8259SetVectorBase (&mInterrupt8259, PROTECTED_MODE_BASE_VECTOR_MASTER, PROTECTED_MODE_BASE_VECTOR_SLAVE); 612 613 // 614 // Set all 8259 interrupts to edge triggered and disabled 615 // 616 Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel); 617 618 // 619 // Install 8259 Protocol onto a new handle 620 // 621 Status = gBS->InstallProtocolInterface ( 622 &m8259Handle, 623 &gEfiLegacy8259ProtocolGuid, 624 EFI_NATIVE_INTERFACE, 625 &mInterrupt8259 626 ); 627 return Status; 628 } 629