UEFI学习——在qemu上读取设备PCI信息

1.编写读取设备PCI信息的Application

       代码参考罗斌大佬,博客地址:UEFI开发探索13 – 访问PCI/PCI-E设备1
       感谢罗斌大佬的贡献,让我在学习UEFI的道路上站在了巨人的肩膀上。

       代码:

#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>


#include <stdio.h>
#include <stdlib.h>




EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *gPciRootBridgeIo;

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);



int main(IN int Argc, IN char **Argv)
{
  EFI_STATUS Status;
  PCI_TYPE00 Pci;


  Status = LocatePciRootBridgeIo();
  if(EFI_ERROR(Status))
  {
    printf("Call LocatePciRootBridgeIo failed,Can't find protocol!\n");
  }
  else
  {
    printf("Call LocatePciRootBridgeIo successed,Find protocol!\n");
  }


  ListPciInformation();
  
  return 0;
}

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

  Status = gBS->LocateHandleBuffer(
    ByProtocol,
    &gEfiPciRootBridgeIoProtocolGuid,
    NULL,
    &HandleCount,
    &PciHandleBuffer  //二级指针
    );
  if(EFI_ERROR(Status))  return Status;

  for(HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++)
  {
    Status = gBS->HandleProtocol(
      PciHandleBuffer[HandleIndex],
      &gEfiPciRootBridgeIoProtocolGuid,
      (VOID **)&gPciRootBridgeIo
      );
    if(EFI_ERROR(Status))  continue;
    else                   return EFI_SUCCESS;
  }

  return Status;
  
}


EFI_STATUS ListPciInformation()
{
  EFI_STATUS Status = EFI_SUCCESS;
  PCI_TYPE00 Pci;
  UINT16 Dev,Func,Bus,PciDevicecount = 0;

  printf("PciDeviceNo\tBus\tDev\tFunc | Vendor.Device.ClassCode\n");
  for(Bus=0; Bus<64; 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)
          {
            PciDevicecount++;
            printf("%d\t%x\t%x\t%x\t",PciDevicecount,(UINT8)Bus,(UINT8)Dev,(UINT8)Func);
            printf("%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) {
    //
    // 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;
}
  



注意:
在这里插入图片描述
这是我这篇博客之前的描述,这个描述不对!下面是修改过的:

       在LocatePciRootBridgeIo函数中根据guid利用LocateHandleBuffer找到支持gEfiPciRootBridgeIoProtocolGuid的所有handle,再调用HandleProtocol从handle数组里寻找安装了protocol的handle,找到了就会通过参数传递给定义的全局变量EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *gPciRootBridgeIo中。接下来就可以利用全局变量EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *gPciRootBridgeIo查询Pci设备的信息了。

       查询并显示PCI信息的函数是ListPciInformation,函数中主要用PciDevicePresent函数查询PCI的信息。
       在PciDevicePresent中,先创造PCI的地址映射,再根据映射的地址和gPciRootBridgeIo,读取PCI的信息到参数Pci中,然后在验证返回值是否正确,验证VendorId是否有效(当VendorID寄存器为0xFFFF时,为无效VendorID)。验证成功后读取PCI设备的整个配置头的信息,配置头信息结构体为:

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

       而我们在打印时只打印Hdr中的信息,Hdr中的信息都有:

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;

       我们只打印VendorId,DeviceId和ClassCode[0]。
       代码写好后将其编译为.efi文件,我编译的是64位的.efi文件,因为PCI信息的查看在windows的模拟环境下查看不了,我第一次查看PCI信息时是用DUET做了个启动盘,将.efi文件放在启动盘的根目录下,在我的笔记本上用U启动,查看PCI信息。后面我会放一张在实体机(即我的笔记本)上查看PCI信息的截图。
       下面就是在qemu虚拟机上查看PCI信息的步骤了。

2.制作OVMF固件

       参照戴正华《UEFI编程与原理》,制作OVMF固件的命令:
       ①制作64位OVMF固件:
在这里插入图片描述
       ②制作32位OVMF固件:
在这里插入图片描述

3.将生成的.efi文件用UltraISO制作成镜像文件

       打开UltraISO,New->Disk Image,然后
在这里插入图片描述
       点击OK,将.efi文件拖入UltraISO,Ctrl+s,保存为PCI.img,保存到和OVMF.fd相同的目录下。
在这里插入图片描述

4.用qemu运行OVMF固件并将PCI.img挂载到UEFIShell下

在这里插入图片描述
在这里插入图片描述
       FS0就是我们挂载的PCI.img。
在这里插入图片描述
       先用pci命令查看PCI信息:
在这里插入图片描述
       然后运行我们生成的.efi文件:
在这里插入图片描述

       到这里就成功在qemu上读取PCI信息了,撒花花。
       最后,再附上再我笔记本上用U盘启动进入UEFIShell查看PCI信息的图片:
       ①运行.efi文件
在这里插入图片描述

       ②PCI命令
在这里插入图片描述

       如有错误,欢迎指出。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值