Home | History | Annotate | Download | only in X64
      1 /*++
      2 
      3 Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
      4 This program and the accompanying materials
      5 are licensed and made available under the terms and conditions of the BSD License
      6 which accompanies this distribution.  The full text of the license may be found at
      7 http://opensource.org/licenses/bsd-license.php
      8 
      9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 
     12 Module Name:
     13     PcatPciRootBridgeIo.c
     14 
     15 Abstract:
     16 
     17     EFI PC AT PCI Root Bridge Io Protocol
     18 
     19 Revision History
     20 
     21 --*/
     22 
     23 #include "PcatPciRootBridge.h"
     24 
     25 BOOLEAN                  mPciOptionRomTableInstalled = FALSE;
     26 EFI_PCI_OPTION_ROM_TABLE mPciOptionRomTable          = {0, NULL};
     27 
     28 EFI_STATUS
     29 EFIAPI
     30 PcatRootBridgeIoIoRead (
     31   IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL        *This,
     32   IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH  Width,
     33   IN     UINT64                                 UserAddress,
     34   IN     UINTN                                  Count,
     35   IN OUT VOID                                   *UserBuffer
     36   )
     37 {
     38   return gCpuIo->Io.Read (
     39                       gCpuIo,
     40                       (EFI_CPU_IO_PROTOCOL_WIDTH) Width,
     41                       UserAddress,
     42                       Count,
     43                       UserBuffer
     44                       );
     45 }
     46 
     47 EFI_STATUS
     48 EFIAPI
     49 PcatRootBridgeIoIoWrite (
     50   IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL        *This,
     51   IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH  Width,
     52   IN UINT64                                 UserAddress,
     53   IN UINTN                                  Count,
     54   IN OUT VOID                               *UserBuffer
     55   )
     56 {
     57   return gCpuIo->Io.Write (
     58                       gCpuIo,
     59                       (EFI_CPU_IO_PROTOCOL_WIDTH) Width,
     60                       UserAddress,
     61                       Count,
     62                       UserBuffer
     63                       );
     64 
     65 }
     66 
     67 EFI_STATUS
     68 PcatRootBridgeIoGetIoPortMapping (
     69   OUT EFI_PHYSICAL_ADDRESS  *IoPortMapping,
     70   OUT EFI_PHYSICAL_ADDRESS  *MemoryPortMapping
     71   )
     72 /*++
     73 
     74   Get the IO Port Mapping.  For IA-32 it is always 0.
     75 
     76 --*/
     77 {
     78   *IoPortMapping = 0;
     79   *MemoryPortMapping = 0;
     80 
     81   return EFI_SUCCESS;
     82 }
     83 
     84 EFI_STATUS
     85 PcatRootBridgeIoPciRW (
     86   IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL        *This,
     87   IN BOOLEAN                                Write,
     88   IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH  Width,
     89   IN UINT64                                 UserAddress,
     90   IN UINTN                                  Count,
     91   IN OUT VOID                               *UserBuffer
     92   )
     93 {
     94   PCI_CONFIG_ACCESS_CF8             Pci;
     95   PCI_CONFIG_ACCESS_CF8             PciAligned;
     96   UINT32                            InStride;
     97   UINT32                            OutStride;
     98   UINTN                             PciData;
     99   UINTN                             PciDataStride;
    100   PCAT_PCI_ROOT_BRIDGE_INSTANCE     *PrivateData;
    101   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS  PciAddress;
    102   UINT64                            PciExpressRegAddr;
    103   BOOLEAN                           UsePciExpressAccess;
    104 
    105   if ((UINT32)Width >= EfiPciWidthMaximum) {
    106     return EFI_INVALID_PARAMETER;
    107   }
    108 
    109   if ((Width & 0x03) >= EfiPciWidthUint64) {
    110     return EFI_INVALID_PARAMETER;
    111   }
    112 
    113   PrivateData = DRIVER_INSTANCE_FROM_PCI_ROOT_BRIDGE_IO_THIS(This);
    114 
    115   InStride    = 1 << (Width & 0x03);
    116   OutStride   = InStride;
    117   if (Width >= EfiPciWidthFifoUint8 && Width <= EfiPciWidthFifoUint64) {
    118     InStride = 0;
    119   }
    120 
    121   if (Width >= EfiPciWidthFillUint8 && Width <= EfiPciWidthFillUint64) {
    122     OutStride = 0;
    123   }
    124 
    125   UsePciExpressAccess = FALSE;
    126 
    127   CopyMem (&PciAddress, &UserAddress, sizeof(UINT64));
    128 
    129   if (PciAddress.ExtendedRegister > 0xFF) {
    130     //
    131     // Check PciExpressBaseAddress
    132     //
    133     if ((PrivateData->PciExpressBaseAddress == 0) ||
    134         (PrivateData->PciExpressBaseAddress >= MAX_ADDRESS)) {
    135       return EFI_UNSUPPORTED;
    136     } else {
    137       UsePciExpressAccess = TRUE;
    138     }
    139   } else {
    140     if (PciAddress.ExtendedRegister != 0) {
    141       Pci.Bits.Reg = PciAddress.ExtendedRegister & 0xFF;
    142     } else {
    143       Pci.Bits.Reg = PciAddress.Register;
    144     }
    145     //
    146     // Note: We can also use PciExpress access here, if wanted.
    147     //
    148   }
    149 
    150   if (!UsePciExpressAccess) {
    151     Pci.Bits.Func     = PciAddress.Function;
    152     Pci.Bits.Dev      = PciAddress.Device;
    153     Pci.Bits.Bus      = PciAddress.Bus;
    154     Pci.Bits.Reserved = 0;
    155     Pci.Bits.Enable   = 1;
    156 
    157     //
    158     // PCI Config access are all 32-bit alligned, but by accessing the
    159     //  CONFIG_DATA_REGISTER (0xcfc) with different widths more cycle types
    160     //  are possible on PCI.
    161     //
    162     // To read a byte of PCI config space you load 0xcf8 and
    163     //  read 0xcfc, 0xcfd, 0xcfe, 0xcff
    164     //
    165     PciDataStride = Pci.Bits.Reg & 0x03;
    166 
    167     while (Count) {
    168       PciAligned = Pci;
    169       PciAligned.Bits.Reg &= 0xfc;
    170       PciData = (UINTN)PrivateData->PciData + PciDataStride;
    171       EfiAcquireLock(&PrivateData->PciLock);
    172       This->Io.Write (This, EfiPciWidthUint32, PrivateData->PciAddress, 1, &PciAligned);
    173       if (Write) {
    174         This->Io.Write (This, Width, PciData, 1, UserBuffer);
    175       } else {
    176         This->Io.Read (This, Width, PciData, 1, UserBuffer);
    177       }
    178       EfiReleaseLock(&PrivateData->PciLock);
    179       UserBuffer = ((UINT8 *)UserBuffer) + OutStride;
    180       PciDataStride = (PciDataStride + InStride) % 4;
    181       Pci.Bits.Reg += InStride;
    182       Count -= 1;
    183     }
    184   } else {
    185     //
    186     // Access PCI-Express space by using memory mapped method.
    187     //
    188     PciExpressRegAddr = (PrivateData->PciExpressBaseAddress) |
    189                         (PciAddress.Bus      << 20) |
    190                         (PciAddress.Device   << 15) |
    191                         (PciAddress.Function << 12);
    192     if (PciAddress.ExtendedRegister != 0) {
    193       PciExpressRegAddr += PciAddress.ExtendedRegister;
    194     } else {
    195       PciExpressRegAddr += PciAddress.Register;
    196     }
    197     while (Count) {
    198       if (Write) {
    199         This->Mem.Write (This, Width, (UINTN) PciExpressRegAddr, 1, UserBuffer);
    200       } else {
    201         This->Mem.Read (This, Width, (UINTN) PciExpressRegAddr, 1, UserBuffer);
    202       }
    203 
    204       UserBuffer = ((UINT8 *) UserBuffer) + OutStride;
    205       PciExpressRegAddr += InStride;
    206       Count -= 1;
    207     }
    208   }
    209 
    210   return EFI_SUCCESS;
    211 }
    212 
    213 VOID
    214 ScanPciBus(
    215   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *IoDev,
    216   UINT16                           MinBus,
    217   UINT16                           MaxBus,
    218   UINT16                           MinDevice,
    219   UINT16                           MaxDevice,
    220   UINT16                           MinFunc,
    221   UINT16                           MaxFunc,
    222   EFI_PCI_BUS_SCAN_CALLBACK        Callback,
    223   VOID                             *Context
    224   )
    225 
    226 {
    227   UINT16      Bus;
    228   UINT16      Device;
    229   UINT16      Func;
    230   UINT64      Address;
    231   PCI_TYPE00  PciHeader;
    232 
    233   //
    234   // Loop through all busses
    235   //
    236   for (Bus = MinBus; Bus <= MaxBus; Bus++) {
    237     //
    238     // Loop 32 devices per bus
    239     //
    240     for (Device = MinDevice; Device <= MaxDevice; Device++) {
    241       //
    242       // Loop through 8 functions per device
    243       //
    244       for (Func = MinFunc; Func <= MaxFunc; Func++) {
    245 
    246         //
    247         // Compute the EFI Address required to access the PCI Configuration Header of this PCI Device
    248         //
    249         Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0);
    250 
    251         //
    252         // Read the VendorID from this PCI Device's Confioguration Header
    253         //
    254         IoDev->Pci.Read (IoDev, EfiPciWidthUint16, Address, 1, &PciHeader.Hdr.VendorId);
    255 
    256         //
    257         // If VendorId = 0xffff, there does not exist a device at this
    258         // location. For each device, if there is any function on it,
    259         // there must be 1 function at Function 0. So if Func = 0, there
    260         // will be no more functions in the same device, so we can break
    261         // loop to deal with the next device.
    262         //
    263         if (PciHeader.Hdr.VendorId == 0xffff && Func == 0) {
    264           break;
    265         }
    266 
    267         if (PciHeader.Hdr.VendorId != 0xffff) {
    268 
    269           //
    270           // Read the HeaderType to determine if this is a multi-function device
    271           //
    272           IoDev->Pci.Read (IoDev, EfiPciWidthUint8, Address + 0x0e, 1, &PciHeader.Hdr.HeaderType);
    273 
    274           //
    275           // Call the callback function for the device that was found
    276           //
    277           Callback(
    278             IoDev,
    279             MinBus, MaxBus,
    280             MinDevice, MaxDevice,
    281             MinFunc, MaxFunc,
    282             Bus,
    283             Device,
    284             Func,
    285             Context
    286             );
    287 
    288           //
    289           // If this is not a multi-function device, we can leave the loop
    290           // to deal with the next device.
    291           //
    292           if ((PciHeader.Hdr.HeaderType & HEADER_TYPE_MULTI_FUNCTION) == 0x00 && Func == 0) {
    293             break;
    294           }
    295         }
    296       }
    297     }
    298   }
    299 }
    300 
    301 VOID
    302 CheckForRom (
    303   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *IoDev,
    304   UINT16                           MinBus,
    305   UINT16                           MaxBus,
    306   UINT16                           MinDevice,
    307   UINT16                           MaxDevice,
    308   UINT16                           MinFunc,
    309   UINT16                           MaxFunc,
    310   UINT16                           Bus,
    311   UINT16                           Device,
    312   UINT16                           Func,
    313   IN VOID                          *VoidContext
    314   )
    315 {
    316   EFI_STATUS                                 Status;
    317   PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT  *Context;
    318   UINT64                                     Address;
    319   PCI_TYPE00                                 PciHeader;
    320   PCI_TYPE01                                 *PciBridgeHeader;
    321   UINT32                                     Register;
    322   UINT32                                     RomBar;
    323   UINT32                                     RomBarSize;
    324   EFI_PHYSICAL_ADDRESS                       RomBuffer;
    325   UINT32                                     MaxRomSize;
    326   EFI_PCI_EXPANSION_ROM_HEADER               EfiRomHeader;
    327   PCI_DATA_STRUCTURE                         Pcir;
    328   EFI_PCI_OPTION_ROM_DESCRIPTOR              *TempPciOptionRomDescriptors;
    329   BOOLEAN                                    LastImage;
    330 
    331   Context = (PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT *)VoidContext;
    332 
    333   Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0);
    334 
    335   //
    336   // Save the contents of the PCI Configuration Header
    337   //
    338   IoDev->Pci.Read (IoDev, EfiPciWidthUint32, Address, sizeof(PciHeader)/sizeof(UINT32), &PciHeader);
    339 
    340   if (IS_PCI_BRIDGE(&PciHeader)) {
    341 
    342     PciBridgeHeader = (PCI_TYPE01 *)(&PciHeader);
    343 
    344     //
    345     // See if the PCI-PCI Bridge has its secondary interface enabled.
    346     //
    347     if (PciBridgeHeader->Bridge.SubordinateBus >= PciBridgeHeader->Bridge.SecondaryBus) {
    348 
    349       //
    350       // Disable the Prefetchable Memory Window
    351       //
    352       Register = 0x00000000;
    353       IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 0x26, 1, &Register);
    354       IoDev->Pci.Write (IoDev, EfiPciWidthUint32, Address + 0x2c, 1, &Register);
    355       Register = 0xffffffff;
    356       IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 0x24, 1, &Register);
    357       IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 0x28, 1, &Register);
    358 
    359       //
    360       // Program Memory Window to the PCI Root Bridge Memory Window
    361       //
    362       IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 0x20, 4, &Context->PpbMemoryWindow);
    363 
    364       //
    365       // Enable the Memory decode for the PCI-PCI Bridge
    366       //
    367       IoDev->Pci.Read (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    368       Register |= 0x02;
    369       IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    370 
    371       //
    372       // Recurse on the Secondary Bus Number
    373       //
    374       ScanPciBus(
    375         IoDev,
    376         PciBridgeHeader->Bridge.SecondaryBus, PciBridgeHeader->Bridge.SecondaryBus,
    377         0, PCI_MAX_DEVICE,
    378         0, PCI_MAX_FUNC,
    379         CheckForRom, Context
    380         );
    381     }
    382   } else {
    383 
    384     //
    385     // Check if an Option ROM Register is present and save the Option ROM Window Register
    386     //
    387     RomBar = 0xffffffff;
    388     IoDev->Pci.Write (IoDev, EfiPciWidthUint32, Address + 0x30, 1, &RomBar);
    389     IoDev->Pci.Read (IoDev, EfiPciWidthUint32, Address + 0x30, 1, &RomBar);
    390 
    391     RomBarSize = (~(RomBar & 0xfffff800)) + 1;
    392 
    393     //
    394     // Make sure the size of the ROM is between 0 and 16 MB
    395     //
    396     if (RomBarSize > 0 && RomBarSize <= 0x01000000) {
    397 
    398       //
    399       // Program Option ROM Window Register to the PCI Root Bridge Window and Enable the Option ROM Window
    400       //
    401       RomBar = (Context->PpbMemoryWindow & 0xffff) << 16;
    402       RomBar = ((RomBar - 1) & (~(RomBarSize - 1))) + RomBarSize;
    403       if (RomBar < (Context->PpbMemoryWindow & 0xffff0000)) {
    404         MaxRomSize = (Context->PpbMemoryWindow & 0xffff0000) - RomBar;
    405         RomBar = RomBar + 1;
    406         IoDev->Pci.Write (IoDev, EfiPciWidthUint32, Address + 0x30, 1, &RomBar);
    407         IoDev->Pci.Read  (IoDev, EfiPciWidthUint32, Address + 0x30, 1, &RomBar);
    408         RomBar = RomBar - 1;
    409 
    410         //
    411         // Enable the Memory decode for the PCI Device
    412         //
    413         IoDev->Pci.Read (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    414         Register |= 0x02;
    415         IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    416 
    417         //
    418         // Follow the chain of images to determine the size of the Option ROM present
    419         // Keep going until the last image is found by looking at the Indicator field
    420         // or the size of an image is 0, or the size of all the images is bigger than the
    421         // size of the window programmed into the PPB.
    422         //
    423         RomBarSize = 0;
    424         do {
    425 
    426           LastImage = TRUE;
    427 
    428           ZeroMem (&EfiRomHeader, sizeof(EfiRomHeader));
    429           IoDev->Mem.Read (
    430             IoDev,
    431             EfiPciWidthUint8,
    432             RomBar + RomBarSize,
    433             sizeof(EfiRomHeader),
    434             &EfiRomHeader
    435             );
    436 
    437           Pcir.ImageLength = 0;
    438 
    439           if (EfiRomHeader.Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE &&
    440               EfiRomHeader.PcirOffset != 0 &&
    441               (EfiRomHeader.PcirOffset & 3) == 0 &&
    442               RomBarSize + EfiRomHeader.PcirOffset + sizeof (PCI_DATA_STRUCTURE) <= MaxRomSize) {
    443             ZeroMem (&Pcir, sizeof(Pcir));
    444             IoDev->Mem.Read (
    445               IoDev,
    446               EfiPciWidthUint8,
    447               RomBar + RomBarSize + EfiRomHeader.PcirOffset,
    448               sizeof(Pcir),
    449               &Pcir
    450               );
    451 
    452             if (Pcir.Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
    453               break;
    454             }
    455             if (RomBarSize + Pcir.ImageLength * 512 > MaxRomSize) {
    456               break;
    457             }
    458             if ((Pcir.Indicator & 0x80) == 0x00) {
    459               LastImage = FALSE;
    460             }
    461 
    462             RomBarSize += Pcir.ImageLength * 512;
    463           }
    464         } while (!LastImage && RomBarSize < MaxRomSize && Pcir.ImageLength !=0);
    465 
    466         if (RomBarSize > 0) {
    467 
    468           //
    469           // Allocate a memory buffer for the Option ROM contents.
    470           //
    471           Status = gBS->AllocatePages(
    472                           AllocateAnyPages,
    473                           EfiBootServicesData,
    474                           EFI_SIZE_TO_PAGES(RomBarSize),
    475                           &RomBuffer
    476                           );
    477 
    478           if (!EFI_ERROR (Status)) {
    479 
    480             //
    481             // Copy the contents of the Option ROM to the memory buffer
    482             //
    483             IoDev->Mem.Read (IoDev, EfiPciWidthUint32, RomBar, RomBarSize / sizeof(UINT32), (VOID *)(UINTN)RomBuffer);
    484 
    485             Status = gBS->AllocatePool(
    486                             EfiBootServicesData,
    487                             ((UINT32)mPciOptionRomTable.PciOptionRomCount + 1) * sizeof(EFI_PCI_OPTION_ROM_DESCRIPTOR),
    488                             (VOID **) &TempPciOptionRomDescriptors
    489                             );
    490             if (mPciOptionRomTable.PciOptionRomCount > 0) {
    491               CopyMem(
    492                 TempPciOptionRomDescriptors,
    493                 mPciOptionRomTable.PciOptionRomDescriptors,
    494                 (UINT32)mPciOptionRomTable.PciOptionRomCount * sizeof(EFI_PCI_OPTION_ROM_DESCRIPTOR)
    495                 );
    496 
    497               gBS->FreePool(mPciOptionRomTable.PciOptionRomDescriptors);
    498             }
    499 
    500             mPciOptionRomTable.PciOptionRomDescriptors = TempPciOptionRomDescriptors;
    501 
    502             TempPciOptionRomDescriptors = &(mPciOptionRomTable.PciOptionRomDescriptors[(UINT32)mPciOptionRomTable.PciOptionRomCount]);
    503 
    504             TempPciOptionRomDescriptors->RomAddress              = RomBuffer;
    505             TempPciOptionRomDescriptors->MemoryType              = EfiBootServicesData;
    506             TempPciOptionRomDescriptors->RomLength               = RomBarSize;
    507             TempPciOptionRomDescriptors->Seg                     = (UINT32)IoDev->SegmentNumber;
    508             TempPciOptionRomDescriptors->Bus                     = (UINT8)Bus;
    509             TempPciOptionRomDescriptors->Dev                     = (UINT8)Device;
    510             TempPciOptionRomDescriptors->Func                    = (UINT8)Func;
    511             TempPciOptionRomDescriptors->ExecutedLegacyBiosImage = TRUE;
    512             TempPciOptionRomDescriptors->DontLoadEfiRom          = FALSE;
    513 
    514             mPciOptionRomTable.PciOptionRomCount++;
    515           }
    516         }
    517 
    518         //
    519         // Disable the Memory decode for the PCI-PCI Bridge
    520         //
    521         IoDev->Pci.Read (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    522         Register &= (~0x02);
    523         IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address + 4, 1, &Register);
    524       }
    525     }
    526   }
    527 
    528   //
    529   // Restore the PCI Configuration Header
    530   //
    531   IoDev->Pci.Write (IoDev, EfiPciWidthUint32, Address, sizeof(PciHeader)/sizeof(UINT32), &PciHeader);
    532 }
    533 
    534 VOID
    535 SaveCommandRegister (
    536   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *IoDev,
    537   UINT16                           MinBus,
    538   UINT16                           MaxBus,
    539   UINT16                           MinDevice,
    540   UINT16                           MaxDevice,
    541   UINT16                           MinFunc,
    542   UINT16                           MaxFunc,
    543   UINT16                           Bus,
    544   UINT16                           Device,
    545   UINT16                           Func,
    546   IN VOID                          *VoidContext
    547   )
    548 
    549 {
    550   PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT  *Context;
    551   UINT64  Address;
    552   UINTN   Index;
    553   UINT16  Command;
    554 
    555   Context = (PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT *)VoidContext;
    556 
    557   Address = EFI_PCI_ADDRESS (Bus, Device, Func, 4);
    558 
    559   Index = (Bus - MinBus) * (PCI_MAX_DEVICE+1) * (PCI_MAX_FUNC+1) + Device * (PCI_MAX_FUNC+1) + Func;
    560 
    561   IoDev->Pci.Read (IoDev, EfiPciWidthUint16, Address, 1, &Context->CommandRegisterBuffer[Index]);
    562 
    563   //
    564   // Clear the memory enable bit
    565   //
    566   Command = (UINT16) (Context->CommandRegisterBuffer[Index] & (~0x02));
    567 
    568   IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address, 1, &Command);
    569 }
    570 
    571 VOID
    572 RestoreCommandRegister (
    573   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *IoDev,
    574   UINT16                           MinBus,
    575   UINT16                           MaxBus,
    576   UINT16                           MinDevice,
    577   UINT16                           MaxDevice,
    578   UINT16                           MinFunc,
    579   UINT16                           MaxFunc,
    580   UINT16                           Bus,
    581   UINT16                           Device,
    582   UINT16                           Func,
    583   IN VOID                          *VoidContext
    584   )
    585 
    586 {
    587   PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT  *Context;
    588   UINT64                                     Address;
    589   UINTN                                      Index;
    590 
    591   Context = (PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT *)VoidContext;
    592 
    593   Address = EFI_PCI_ADDRESS (Bus, Device, Func, 4);
    594 
    595   Index = (Bus - MinBus) * (PCI_MAX_DEVICE+1) * (PCI_MAX_FUNC+1) + Device * (PCI_MAX_FUNC+1) + Func;
    596 
    597   IoDev->Pci.Write (IoDev, EfiPciWidthUint16, Address, 1, &Context->CommandRegisterBuffer[Index]);
    598 }
    599 
    600 EFI_STATUS
    601 ScanPciRootBridgeForRoms(
    602   EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *IoDev
    603   )
    604 
    605 {
    606   EFI_STATUS                                 Status;
    607   EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR          *Descriptors;
    608   UINT16                                     MinBus;
    609   UINT16                                     MaxBus;
    610   UINT64                                     RootWindowBase;
    611   UINT64                                     RootWindowLimit;
    612   PCAT_PCI_ROOT_BRIDGE_SCAN_FOR_ROM_CONTEXT  Context;
    613 
    614   if (mPciOptionRomTableInstalled == FALSE) {
    615     gBS->InstallConfigurationTable(&gEfiPciOptionRomTableGuid, &mPciOptionRomTable);
    616     mPciOptionRomTableInstalled = TRUE;
    617   }
    618 
    619   Status = IoDev->Configuration(IoDev, (VOID **) &Descriptors);
    620   if (EFI_ERROR (Status) || Descriptors == NULL) {
    621     return EFI_NOT_FOUND;
    622   }
    623 
    624   MinBus = 0xffff;
    625   MaxBus = 0xffff;
    626   RootWindowBase  = 0;
    627   RootWindowLimit = 0;
    628   while (Descriptors->Desc != ACPI_END_TAG_DESCRIPTOR) {
    629     //
    630     // Find bus range
    631     //
    632     if (Descriptors->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) {
    633       MinBus = (UINT16)Descriptors->AddrRangeMin;
    634       MaxBus = (UINT16)Descriptors->AddrRangeMax;
    635     }
    636     //
    637     // Find memory descriptors that are not prefetchable
    638     //
    639     if (Descriptors->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM && Descriptors->SpecificFlag == 0) {
    640       //
    641       // Find Memory Descriptors that are less than 4GB, so the PPB Memory Window can be used for downstream devices
    642       //
    643       if (Descriptors->AddrRangeMax < 0x100000000ULL) {
    644         //
    645         // Find the largest Non-Prefetchable Memory Descriptor that is less than 4GB
    646         //
    647         if ((Descriptors->AddrRangeMax - Descriptors->AddrRangeMin) > (RootWindowLimit - RootWindowBase)) {
    648           RootWindowBase  = Descriptors->AddrRangeMin;
    649           RootWindowLimit = Descriptors->AddrRangeMax;
    650         }
    651       }
    652     }
    653     Descriptors ++;
    654   }
    655 
    656   //
    657   // Make sure a bus range was found
    658   //
    659   if (MinBus == 0xffff || MaxBus == 0xffff) {
    660     return EFI_NOT_FOUND;
    661   }
    662 
    663   //
    664   // Make sure a non-prefetchable memory region was found
    665   //
    666   if (RootWindowBase == 0 && RootWindowLimit == 0) {
    667     return EFI_NOT_FOUND;
    668   }
    669 
    670   //
    671   // Round the Base and Limit values to 1 MB boudaries
    672   //
    673   RootWindowBase  = ((RootWindowBase - 1) & 0xfff00000) + 0x00100000;
    674   RootWindowLimit = ((RootWindowLimit + 1) & 0xfff00000) - 1;
    675 
    676   //
    677   // Make sure that the size of the rounded window is greater than zero
    678   //
    679   if (RootWindowLimit <= RootWindowBase) {
    680     return EFI_NOT_FOUND;
    681   }
    682 
    683   //
    684   // Allocate buffer to save the Command register from all the PCI devices
    685   //
    686   Context.CommandRegisterBuffer = NULL;
    687   Status = gBS->AllocatePool(
    688                   EfiBootServicesData,
    689                   sizeof(UINT16) * (MaxBus - MinBus + 1) * (PCI_MAX_DEVICE+1) * (PCI_MAX_FUNC+1),
    690                   (VOID **) &Context.CommandRegisterBuffer
    691                   );
    692 
    693   if (EFI_ERROR (Status)) {
    694     return Status;
    695   }
    696 
    697   Context.PpbMemoryWindow   = (((UINT32)RootWindowBase) >> 16) | ((UINT32)RootWindowLimit & 0xffff0000);
    698 
    699   //
    700   // Save the Command register from all the PCI devices, and disable the I/O, Mem, and BusMaster bits
    701   //
    702   ScanPciBus(
    703     IoDev,
    704     MinBus, MaxBus,
    705     0, PCI_MAX_DEVICE,
    706     0, PCI_MAX_FUNC,
    707     SaveCommandRegister, &Context
    708     );
    709 
    710   //
    711   // Recursively scan all the busses for PCI Option ROMs
    712   //
    713   ScanPciBus(
    714     IoDev,
    715     MinBus, MinBus,
    716     0, PCI_MAX_DEVICE,
    717     0, PCI_MAX_FUNC,
    718     CheckForRom, &Context
    719     );
    720 
    721   //
    722   // Restore the Command register in all the PCI devices
    723   //
    724   ScanPciBus(
    725     IoDev,
    726     MinBus, MaxBus,
    727     0, PCI_MAX_DEVICE,
    728     0, PCI_MAX_FUNC,
    729     RestoreCommandRegister, &Context
    730     );
    731 
    732   //
    733   // Free the buffer used to save all the Command register values
    734   //
    735   gBS->FreePool(Context.CommandRegisterBuffer);
    736 
    737   return EFI_SUCCESS;
    738 }
    739