UEFI——访问PCI/PCIE设备(二)

一、支持访问PCI/PCIE设备的Protocol

UEFI中提供了两个主要的模块来支持PCI总线,一是PCI Host Bridge(PCI主桥)控制器驱动,另一个是PCI总线驱动。这两个模块是和特定的平台硬件绑定的,在这种机制下,屏蔽了不同的CPU架构差异,为软件开发者提供了比较一致的Protocol接口。

UEFI标准中提供了两类访问PCI/PCIE设备的Protocol——EFI_PCI_ROOT_BRIDGE_IO_PROTOCOLEFI_PCI_IO_PROTOCOL。前者为PCI根桥提供了抽象的IO功能,它由PCI Host Bus Controller(PCI主总线驱动器)产生,一般由PCI/PCIe总线驱动用来枚举设备、获得Option ROM、分配PCI设备资源等;后者由PCI/PCIE总线驱动为PCI/PCIE设备产生,一般由PCI/PCIE设备驱动用来访问PCI/PCIE设备的IO空间、Memory空间和配置空间。

1.1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL

EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL提供了基本的内存、输入/输出(I/O)、PCI配置和直接访问内存的接口,这些接口用于将PCI控制器后面的PCI根桥控制器的访问进行抽象。

typedef struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL;
/// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are
/// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller.
///提供了基本内存,I/O,PCI配置和直接内存访问(DMA)接口,这些接口用于抽象PCI控制器后PCI根桥的访问
struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL {
  ///
  /// The EFI_HANDLE of the PCI Host Bridge of which this PCI Root Bridge is a member.
  /// 这是PCI根桥所属的PCI主机桥的EFI_HANDLE

  EFI_HANDLE                                         ParentHandle; //包含这个PCI根桥的PCI主机桥的Handle
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM        PollMem; //一个函数指针,指向用于轮询内存映射I/O空间的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM        PollIo; //一个函数指针,指向用于轮询I/O空间的协议的方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS             Mem; //一个函数指针,指向用于访问内存的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS             Io; //一个函数指针,指向用于访问I/O空间的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS             Pci; //一个函数指针,指向用于访问PCI配置空间的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM           CopyMem; //一个函数指针,指向用于复制内存的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP                Map; //一个函数指针,指向用于映射内存或I/O空间的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP              Unmap; //一个函数指针,指向用于取消映射内存或I/O空间的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER    AllocateBuffer; //一个函数指针,用于指向分配内存缓冲区的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER        FreeBuffer; //一个函数指针,指向释放内存缓冲区的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH              Flush; //一个函数指针,指向用于刷新缓存的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES     GetAttributes; //一个函数指针,指向用于获取PCI根桥属性的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES     SetAttributes; //一个函数指针,指向用于设置PCI根桥属性的协议方法
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION      Configuration; //一个函数指针,指向配置PCI根桥的协议方法

  ///
  /// The segment number that this PCI root bridge resides. PCI根桥所在的段号
  ///
  UINT32                                             SegmentNumber;
};

在这里只介绍访问内存的接口Mem、访问I/O空间的接口Io、访问PCI配置空间的接口Pci。这三个接口的参数类型是一样的,都是EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS

typedef struct {

  /// Read PCI controller registers in the PCI root bridge memory space.
  /// 读数据
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM    Read;

  /// Write PCI controller registers in the PCI root bridge memory space.
  /// 写数据
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM    Write;
} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS;

typedef
EFI_STATUS
(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM)(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL              *This, //指向protocol实例
  IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH    Width, //标识内存操作的宽度,一般有8位、16位、32位、64位几种
  IN     UINT64                                   Address, //内存操作的基地址
  IN     UINTN                                    Count, //读写的数据个数,以width为单位
  IN OUT VOID                                     *Buffer
  );

其中,参数Adress在访问I/O空间、内存空间、配置空间时是不一样的,

对于配置空间,Address由BDF地址和Register偏移决定,与一般使用宏EFI_PCI_ADDRESS来组合BDF和Register偏移,在EDK2中,宏定义如下:

#define EFI_PCI_ADDRESS(bus, dev, func, reg) \
  (UINT64) ( \
  (((UINTN) bus) << 24) | \
  (((UINTN) dev) << 16) | \
  (((UINTN) func) << 8) | \
  (((UINTN) (reg)) < 256 ? ((UINTN) (reg)) : (UINT64) (LShiftU64 ((UINT64) (reg), 32))))

对于IO空间而言,参数Address是指PCI设备IO空间的IO地址;

对于Memory空间而言,参数Address是指PCI设备Memory空间的Memory地址。

它们是由BAR和偏移量决定的 。

1.2  EFI_PCI_IO_PROTOCOL

在PCI/PCIE设备驱动中,一般使用EFI_PCI_IO_PROTOCOL来访问设备的内部资源,Protocol挂载在PCI/PCIE控制器上,运行在EFI启动环境中,对PCI/PCIE设备进行Memory空间和I/O空间访问。

/// Global ID for the PCI I/O Protocol
///
#define EFI_PCI_IO_PROTOCOL_GUID \
  { \
    0x4cf5b200, 0x68b8, 0x4ca5, {0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a } \
  }

typedef struct _EFI_PCI_IO_PROTOCOL EFI_PCI_IO_PROTOCOL;

 EFI_PCI_IO_PROTOCOL的接口如下:

///
/// The EFI_PCI_IO_PROTOCOL provides the basic Memory, I/O, PCI configuration,
/// and DMA interfaces used to abstract accesses to PCI controllers.
/// There is one EFI_PCI_IO_PROTOCOL instance for each PCI controller on a PCI bus.
/// A device driver that wishes to manage a PCI controller in a system will have to
/// retrieve the EFI_PCI_IO_PROTOCOL instance that is associated with the PCI controller.
///
struct _EFI_PCI_IO_PROTOCOL {
  EFI_PCI_IO_PROTOCOL_POLL_IO_MEM           PollMem;
  EFI_PCI_IO_PROTOCOL_POLL_IO_MEM           PollIo;
  EFI_PCI_IO_PROTOCOL_ACCESS                Mem;
  EFI_PCI_IO_PROTOCOL_ACCESS                Io;
  EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS         Pci;
  EFI_PCI_IO_PROTOCOL_COPY_MEM              CopyMem;
  EFI_PCI_IO_PROTOCOL_MAP                   Map;
  EFI_PCI_IO_PROTOCOL_UNMAP                 Unmap;
  EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER       AllocateBuffer;
  EFI_PCI_IO_PROTOCOL_FREE_BUFFER           FreeBuffer;
  EFI_PCI_IO_PROTOCOL_FLUSH                 Flush;
  EFI_PCI_IO_PROTOCOL_GET_LOCATION          GetLocation;
  EFI_PCI_IO_PROTOCOL_ATTRIBUTES            Attributes;
  EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES    GetBarAttributes;
  EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES    SetBarAttributes;

  ///
  /// The size, in bytes, of the ROM image.
  ///
  UINT64                                    RomSize;

  ///
  /// A pointer to the in memory copy of the ROM image. The PCI Bus Driver is responsible
  /// for allocating memory for the ROM image, and copying the contents of the ROM to memory.
  /// The contents of this buffer are either from the PCI option ROM that can be accessed
  /// through the ROM BAR of the PCI controller, or it is from a platform-specific location.
  /// The Attributes() function can be used to determine from which of these two sources
  /// the RomImage buffer was initialized.
  ///
  VOID    *RomImage;
};

【注!!!】在这里需要注意的是EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的实例,在大部分办公用的个人电脑中只存在一个,因此直接用全局指针变量gPCIRootBridgeIO存储;而EFI_PCI_IO_PROTOCOL的实例存在多个,一般有多少个PCI/PCIE设备,就存在多少个实例,因此使用全局指针数组gPCIIOArray[256]来存储这些实例。

二、访问PCI/PCIE设备的简单实现

2.1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL遍历PCI设备

在使用EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL获取PCI/PCIE设备的时,首先需要获取EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的所有句柄,获取句柄可以利用gBS->LocateHandleBuffer()函数,当获取到句柄后,利用gBS->HandleProtocol()函数获取到protocol实例,然后就可以调用其中的函数接口了。利用BDF找到某个设备,读取设备的配置空间。

PCI设备的配置空间用结构体PCI_TYPE00表示,其代码原型为:

/// PCI Device Configuration Space

typedef struct {
  PCI_DEVICE_INDEPENDENT_REGION    Hdr;
  PCI_DEVICE_HEADER_TYPE_REGION    Device;
} PCI_TYPE00;

其中Hdr表示PCI配置空间的通用头部区域,其包括的字段:

// Common header region in PCI Configuration Space

typedef struct {
  UINT16    VendorId;
  UINT16    DeviceId;
  UINT16    Command;
  UINT16    Status;
  UINT8     RevisionID;
  UINT8     ClassCode[3];
  UINT8     CacheLineSize;
  UINT8     LatencyTimer;
  UINT8     HeaderType;
  UINT8     BIST;
} PCI_DEVICE_INDEPENDENT_REGION;

 Device表示PCI设备配置空间中的头部区域

/// PCI Device header region in PCI Configuration Space

typedef struct {
  UINT32    Bar[6];
  UINT32    CISPtr;
  UINT16    SubsystemVendorID;
  UINT16    SubsystemID;
  UINT32    ExpansionRomBar;
  UINT8     CapabilityPtr;
  UINT8     Reserved1[3];
  UINT32    Reserved2;
  UINT8     InterruptLine;
  UINT8     InterruptPin;
  UINT8     MinGnt;
  UINT8     MaxLat;
} PCI_DEVICE_HEADER_TYPE_REGION;

利用EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL来访问PCIe设备的应用简单实现:

编写MyPCIEProtocol.c代码:

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/DebugLib.h>

#include <Protocol/PciIo.h>
#include <Protocol/PciRootBridgeIo.h>
#include <IndustryStandard/Pci.h>


EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *gPciRootBridgeIo; //创建一个EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL类型的指针变量

EFI_STATUS LocatePciRootBridgeIo(void); //函数声明,告诉编译器函数名称、返回值类型、参数类型;如果函数定义在函数调用之后,那么在函数调用之前必须进行函数声明

//函数声明
EFI_STATUS PciDevicePresent(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL * PciRootBridgeIo,
  OUT PCI_TYPE00 * Pci,
  IN UINT8 Bus,
  IN UINT8 Device,
  IN UINT8 Func
  );

EFI_STATUS ListPciInformation(void); //函数声明


EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;


  Status = LocatePciRootBridgeIo(); //调用LocatePciRootBridgeIo()函数,定位根桥
  if(EFI_ERROR(Status))
  {
    DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciRootBridgeIo failed,Can't find protocol!\n"));
  }
  else
  {
    DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciRootBridgeIo successed,Find protocol!\n"));
  }

  //列出PCI设备的信息
  ListPciInformation();
  
  return EFI_SUCCESS;
}

EFI_STATUS LocatePciRootBridgeIo()
{
  EFI_STATUS Status;
  EFI_HANDLE *PciHandleBuffer = NULL;
  UINTN      HandleIndex = 0;
  UINTN      HandleCount = 0;

  //获取 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的所有句柄
  Status = gBS->LocateHandleBuffer(
    ByProtocol, //查找句柄的方式,这里采用返回指定协议的句柄
    &gEfiPciRootBridgeIoProtocolGuid, //要查找的协议的GUID
    NULL,
    &HandleCount, //输出参数,缓冲区中返回的句柄数量
    &PciHandleBuffer  //输出参数,指向用于返回支持协议的请求句柄数组的缓冲区的指针。

    );
  if(EFI_ERROR(Status))  return Status; //如果没成功找到,直接结束程序

  //查找支持 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL句柄的protocol实例
  for(HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++)
  {
    Status = gBS->HandleProtocol(
      PciHandleBuffer[HandleIndex],
      &gEfiPciRootBridgeIoProtocolGuid,
      (VOID **)&gPciRootBridgeIo
      );
    if(EFI_ERROR(Status))  continue; //如果没找到就继续寻找
    else                   return EFI_SUCCESS; //如果找到了就退出程序
    //这里找到就退出的原因是大部分办公用的个人电脑中就只存在一个EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
  }

  return Status;
  
}

EFI_STATUS ListPciInformation()
{
  EFI_STATUS Status = EFI_SUCCESS;
  PCI_TYPE00 Pci; //PCI_TYPE00是一个结构体,表示PCI的配置空间
  UINT16 Dev,Func,Bus,PciDevicecount = 0;

  DEBUG((DEBUG_ERROR, "[CSDN] PciDeviceNo\tBus\tDev\tFunc | Vendor.Device.ClassCode\n"));
  for(Bus=0; Bus<256; Bus++)
    for(Dev=0; Dev<= PCI_MAX_DEVICE; Dev++)
      for(Func=0; Func<=PCI_MAX_FUNC; Func++)
      {
        //判断设备是否存在
        Status = PciDevicePresent(gPciRootBridgeIo,&Pci,(UINT8)Bus,(UINT8)Dev,(UINT8)Func);
          if(Status == EFI_SUCCESS)
          {
            //如果找到了设备,PCI设备数量加1
            PciDevicecount++;
             //打印设备信息
             DEBUG((DEBUG_ERROR, "[CSDN] %d\t\t%x\t%x\t%x\t",PciDevicecount,(UINT8)Bus,(UINT8)Dev,(UINT8)Func));
             DEBUG((DEBUG_ERROR, "%x\t%x\t%x\n",Pci.Hdr.VendorId,Pci.Hdr.DeviceId,Pci.Hdr.ClassCode[0]));
          }
      }

  return EFI_SUCCESS;
}

EFI_STATUS
PciDevicePresent (
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL     *PciRootBridgeIo,
  OUT PCI_TYPE00                          *Pci,
  IN  UINT8                               Bus,
  IN  UINT8                               Device,
  IN  UINT8                               Func
  )
{
  UINT64      Address;
  EFI_STATUS  Status;

  //
  // Create PCI address map in terms of Bus, Device and Func
  //
  Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0);

  //
  // Read the Vendor ID register
  //
  Status = PciRootBridgeIo->Pci.Read (
                                  PciRootBridgeIo,
                                  EfiPciWidthUint32,
                                  Address,
                                  1,
                                  Pci
                                  );

  if (!EFI_ERROR (Status) && (Pci->Hdr).VendorId != 0xffff) { //0xffff通常标识一个无效的,未分配的VentorId,如果能读取设备信息就进行打印。
    //
    // Read the entire config header for the device
    //
    Status = PciRootBridgeIo->Pci.Read (
                                    PciRootBridgeIo,
                                    EfiPciWidthUint32,
                                    Address,
                                    sizeof (PCI_TYPE00) / sizeof (UINT32),
                                    Pci
                                    );

    return EFI_SUCCESS;
  }

  return EFI_NOT_FOUND;
}

 编写INF文件,并运行,运行结果为:

 

2.2 EFI_PCI_IO_PROTOCOL遍历PCI设备

使用EFI_PCI_IO_PROTOCOL遍历设备比较简单,因此为之前所得到的Protocol的实例,就是为PCI/PCIE设备产生的,实际上相当于找到了宿设备,只需要将设备的信息打印出来即可。 

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/DebugLib.h>

#include <Protocol/PciIo.h>
#include <Protocol/PciRootBridgeIo.h>
#include <IndustryStandard/Pci.h>


EFI_PCI_IO_PROTOCOL *gPciIoArray[256];
UINTN gPciIoCount = 0;
EFI_STATUS LocatePciIo(void);

EFI_STATUS ListPciInformation(void);


EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;


  Status = LocatePciIo();
  if(EFI_ERROR(Status))
  {
    DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciIo failed,Can't find protocol!\n"));
  }
  else
  {
    DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciIo successed,Find protocol!\n"));
  }

  ListPciInformation();
  
  return EFI_SUCCESS;
}

EFI_STATUS LocatePciIo()
{
  EFI_STATUS Status;
  EFI_HANDLE *PciHandleBuffer = NULL;
  UINTN      HandleIndex = 0;
  UINTN      HandleCount = 0;

  Status = gBS->LocateHandleBuffer(
    ByProtocol,
    &gEfiPciIoProtocolGuid,
    NULL,
    &HandleCount,
    &PciHandleBuffer 
    );
  if(EFI_ERROR(Status))  return Status;

  gPciIoCount = HandleCount;
  DEBUG((DEBUG_ERROR, "[CSDN] the number of PCIn devices is %d", HandleCount));

  for(HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++)
  {
    Status = gBS->HandleProtocol(
        PciHandleBuffer[HandleIndex],
        &gEfiPciIoProtocolGuid,
        (VOID **)&(gPciIoArray[HandleIndex])
        );
  }

  return Status;
  
}

EFI_STATUS ListPciInformation()
{
  UINTN i, count = 0;
  PCI_TYPE00 Pci;
  DEBUG((DEBUG_ERROR, "[CSDN] PciDeviceNo VendorID DeviceID ClassCode\n"));
  for (i = 0; i < gPciIoCount;i++)
  {
    gPciIoArray[i]->Pci.Read(gPciIoArray[i],EfiPciIoWidthUint32,0,sizeof(Pci)/sizeof(PCI_TYPE00),&Pci);
    ++count;
    DEBUG((DEBUG_ERROR, "[CSDN] %d\t\t%x\t%x\t%x\n", count, Pci.Hdr.VendorId, Pci.Hdr.DeviceId, Pci.Hdr.ClassCode[0]));
  }

  return EFI_SUCCESS;
}

 运行结果如下,发现读到的Class Code[0]的数据不一样,不知道为什么?

 

首先,我们需要定义一个EFI应用程序的入口函数和一个INF文件。在EDK2中,我们可以使用C语言来编写UEFI程序,而INF文件则是一个配置文件,用于指定程序的构建选项和依赖关系。 下面是一个简单的UEFI程序的入口函数,它将打开一个文件并将所有的PCI/PCIe设备信息写入该文件: ```c #include <Uefi.h> #include <Library/UefiLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/PciIo.h> EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Segment; UINTN Bus; UINTN Device; UINTN Function; UINTN Index; CHAR16 FileName[] = L"\\EFI\\pci_info.txt"; EFI_FILE_PROTOCOL *File; CHAR8 Buffer[256]; // Open the file for writing Status = gBS->OpenProtocol(ImageHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FileSystem, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(Status)) { Print(L"Failed to open the file system protocol: %r\n", Status); return Status; } Status = FileSystem->Open(FileSystem, &File, FileName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (EFI_ERROR(Status)) { Print(L"Failed to open the file %s: %r\n", FileName, Status); return Status; } // Traverse all PCI/PCIe devices for (Segment = 0; Segment <= 0xFFFF; Segment++) { for (Bus = 0; Bus <= 0xFF; Bus++) { for (Device = 0; Device <= 0x1F; Device++) { for (Function = 0; Function <= 0x7; Function++) { // Check if the device exists Status = gBS->LocateProtocol(&gEfiPciIoProtocolGuid, NULL, (VOID **)&PciIo); if (EFI_ERROR(Status)) { continue; } // Read the device's PCI configuration space Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, 0, sizeof(Buffer), &Buffer); if (EFI_ERROR(Status)) { continue; } // Write the device information to the file UnicodeSPrint(Buffer, sizeof(Buffer), L"%04x:%02x:%02x.%x\n", Segment, Bus, Device, Function); Status = File->Write(File, &sizeof(Buffer), Buffer); if (EFI_ERROR(Status)) { Print(L"Failed to write device information to the file: %r\n", Status); return Status; } } } } } // Close the file Status = File->Close(File); if (EFI_ERROR(Status)) { Print(L"Failed to close the file: %r\n", Status); return Status; } return EFI_SUCCESS; } ``` 接下来,我们需要创建一个INF文件,以指定构建选项和依赖关系。下面是一个示例INF文件: ```inf [Defines] INF_VERSION = 0x00010006 BASE_NAME = pci_info FILE_GUID = 12345678-1234-1234-1234-1234567890AB MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 1.0 ENTRY_POINT = UefiMain [Sources] pci_info.c [Packages] MdePkg/MdePkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib UefiBootServicesTableLib PciIo [Protocols] PciIo [BuildOptions] -D NDEBUG ``` 在这个INF文件中,我们定义了以下内容: - `BASE_NAME`: 程序的基本名称,用于构建输出文件名。 - `FILE_GUID`: 程序的GUID,用于唯一标识程序。 - `MODULE_TYPE`: 程序的类型,这里是UEFI应用程序。 - `VERSION_STRING`: 程序的版本号。 - `ENTRY_POINT`: 程序的入口函数。 - `Sources`: 程序的源代码文件。 - `Packages`: 指定依赖的EDK2软件包。 - `LibraryClasses`: 指定需要链接的库文件。 - `Protocols`: 指定需要使用的协议。 - `BuildOptions`: 指定编译选项。 现在,我们可以使用EDK2的构建工具来构建程序: ``` build -p pci_info.inf -a X64 -b RELEASE ``` 这将生成一个名为pci_info.efi的可执行文件,它可以在UEFI固件中运行,并将所有PCI/PCIe设备信息写入一个名为pci_info.txt的文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值