UEFI Tips: PciIO.Read 的问题

最近在编写一个小程序,需要读取PCI Configuration Space 上的寄存器。但是发现运行之后会死机,死机的位置在下面的调用中:

           Status = PciIo->Pci.Read (
                        PciIo,
                        EfiPciIoWidthUint32,
                        Index*4,
                        Sizeof(tmp),
                        &tmp);

但是如果使用EfiPciIoWidthUint8, EfiPciIoWidthUint16都是能够正常工作的。
经过一番研究,找到了问题所在,首先看一下EFI PCI IO PROTOCOL 的定义【参考1】:

typedef 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;
  UINT64                                 RomSize;
  VOID                                   *RomImage;
} EFI_PCI_IO_PROTOCOL;

我们需要关注的是EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS Pci;

typedef
EFI_STATUS
(EFIAPI *EFI_PCI_IO_PROTOCOL_MEM) (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
);

其中的参数定义如下:

仔细阅读会发现, Count的意思是有多少个 Width大小的元素,对于上面的代码来说,我只是想要1个32bits长度的INTU32。因此,Count 应该是1而不是4。
最终,完整的代码:

#include <Library/BaseLib.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/ShellCEntryLib.h>
#include <Protocol/PciIo.h>
#include <IndustryStandard/Pci22.h>
#include <Library/MemoryAllocationLib.h>

extern EFI_BOOT_SERVICES       *gBS;
extern EFI_HANDLE               gImageHandle;
       
        
/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
main (
  IN int Argc,
  IN char **Argv
  )
{

    EFI_STATUS  Status;
    EFI_PCI_IO_PROTOCOL       *PciIo;
    UINTN       Seg,Bus,Dev,Fun;
    UINT32      Index;
    UINT32     tmp;
     
    Status = gBS->LocateProtocol(
                        &gEfiPciIoProtocolGuid,
                        NULL,
                        (VOID **) &PciIo);

    if (EFI_ERROR(Status)) {
        Print(L"Couldn't find PCIIO Protocol\n");
        return EFI_SUCCESS;
    } 

    PciIo->GetLocation(PciIo,&Seg,&Bus,&Dev,&Fun);
    Print(L"Found PCI controller Bus[%d] Dev[%d] Fun[%d]\n",
          Bus,Dev,Fun);
    
    for (Index = 0; Index < 256/4; Index++) {
           Status = PciIo->Pci.Read (
                        PciIo,
                        EfiPciIoWidthUint32,
                        Index*4,
                        1,
                        &tmp);
           Print(L"%08X ",tmp);
           if ((Index+1)%4==0) {Print(L"\n");}
    }        
  return 0;
}

运行结果:

完整代码下载:
PCIIOTest


参考:
1. http://wiki.phoenix.com/wiki/index.php/EFI_PCI_IO_PROTOCOL

《UEFI Tips: PciIO.Read 的问题》有一个想法

  1. 您好,根据您的代码,在电脑上运行了EKD2, 提示 Couldn't find PCIIO Protocol,我的inf文件是在 Nt32Pkg 中编译的,都能编译过去,就是运行的时候不行,请问这是什么问题呢?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注