1/*++
2
3Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
4This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution.  The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13    PcatPciRootBridgeIo.c
14
15Abstract:
16
17    EFI PC AT PCI Root Bridge Io Protocol
18
19Revision History
20
21--*/
22
23#include "PcatPciRootBridge.h"
24
25BOOLEAN                  mPciOptionRomTableInstalled = FALSE;
26EFI_PCI_OPTION_ROM_TABLE mPciOptionRomTable          = {0, NULL};
27
28EFI_STATUS
29EFIAPI
30PcatRootBridgeIoIoRead (
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
47EFI_STATUS
48EFIAPI
49PcatRootBridgeIoIoWrite (
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
67EFI_STATUS
68PcatRootBridgeIoGetIoPortMapping (
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
84EFI_STATUS
85PcatRootBridgeIoPciRW (
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
213VOID
214ScanPciBus(
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
301VOID
302CheckForRom (
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
534VOID
535SaveCommandRegister (
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
571VOID
572RestoreCommandRegister (
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
600EFI_STATUS
601ScanPciRootBridgeForRoms(
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