31. ShellLib/PrintLib 함수를 사용해 PCI Vendor/Device 정보 가져오기

이번 장에서는 ListPCI 유틸리티를 수정하여 PCI Vendor와 Device에 대한 정보를 확인한다. 여기서 출력할 정보는 UEFI Shell에서 pci 명령어을 통해서 확인할 수 없는 정보이다. pci 명령은 PCI class와 subclass 코드에 대한 정보만 표시하기 때문에 여기서 하는 작업은 유용하게 사용할 수 있다.

pci 명령어 소스 코드 확인 링크

이번 장은 fpmurphy의 ShowPCIx(https://github.com/fpmurphy/UEFI-Utilities-2019/tree/master/MyApps/ShowPCIx)애플리케이션을 참고하여 작성하였다.

먼저, 이전에 만들었던 ListPCI.c에 코드를 기반으로 Vendor와 Device 설명을 채울 수 있는 FindPCIDevDescription 함수를 생성한다.

EFI_STATUS FindPCIDevDescription(IN UINT16 VendorId,
                                 IN UINT16 DeviceId,
                                 OUT CHAR16* VendorDesc,
                                 OUT CHAR16* DeviceDesc,
                                 IN UINTN DescBufferSize)

아래 코드는 메인 PCI loop인 PrintRootBridge 함수에 작성해야 한다.

if (PCIConfHdr.VendorId != 0xffff) {
  Print(L"  %02x:%02x.%02x - Vendor:%04x, Device:%04x",
                                                          Bus,
                                                          Device,
                                                          Func,
                                                          PCIConfHdr.VendorId,
                                                          PCIConfHdr.DeviceId);

  CHAR16 VendorDesc[DESCRIPTOR_STR_MAX_SIZE];
  CHAR16 DeviceDesc[DESCRIPTOR_STR_MAX_SIZE];
  Status = FindPCIDevDescription(PCIConfHdr.VendorId,
                                 PCIConfHdr.DeviceId,
                                 VendorDesc,
                                 DeviceDesc,
                                 DESCRIPTOR_STR_MAX_SIZE);
  if (!EFI_ERROR(Status)) {
    Print(L":    %s, %s\n", VendorDesc, DeviceDesc);
  } else {
    Print(L"\n");
  }
}

해당 함수의 전체 코드를 보여주면 아래와 같다.

EFI_STATUS PrintRootBridge(EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL* PciRootBridgeIo)
{
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR* AddressDescriptor;
  EFI_STATUS Status = PciRootBridgeIo->Configuration(
                                         PciRootBridgeIo,
                                         (VOID**)&AddressDescriptor
                                       );
  if (EFI_ERROR(Status)) {
    Print(L"\tError! Can't get EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR: %r\n", Status);
    return Status;
  }
  while (AddressDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {
    if (AddressDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) {
      for (UINT8 Bus = AddressDescriptor->AddrRangeMin; Bus <= AddressDescriptor->AddrRangeMax; Bus++) {
        for (UINT8 Device = 0; Device <= PCI_MAX_DEVICE; Device++) {
          for (UINT8 Func = 0; Func <= PCI_MAX_FUNC; Func++) {
            UINT64 Address = PciConfigurationAddress(Bus, Device, Func, 0);
            PCI_DEVICE_INDEPENDENT_REGION PCIConfHdr;
            Status = PciRootBridgeIo->Pci.Read(
              PciRootBridgeIo,
              EfiPciWidthUint8,
              Address,
              sizeof(PCI_DEVICE_INDEPENDENT_REGION),
              &PCIConfHdr
            );
            if (!EFI_ERROR(Status)) {
              if (PCIConfHdr.VendorId != 0xffff) {
                Print(L"  %02x:%02x.%02x - Vendor:%04x, Device:%04x",
                                                                        Bus,
                                                                        Device,
                                                                        Func,
                                                                        PCIConfHdr.VendorId, 
                                                                        PCIConfHdr.DeviceId);

                CHAR16 VendorDesc[DESCRIPTOR_STR_MAX_SIZE];
                CHAR16 DeviceDesc[DESCRIPTOR_STR_MAX_SIZE];
                Status = FindPCIDevDescription(PCIConfHdr.VendorId,
                                               PCIConfHdr.DeviceId,
                                               VendorDesc,
                                               DeviceDesc,
                                               DESCRIPTOR_STR_MAX_SIZE);
                if (!EFI_ERROR(Status)) {
                  Print(L":    %s, %s\n", VendorDesc, DeviceDesc);
                } else {
                  Print(L"\n");
                }
              }
            } else {
              Print(L"  Error in PCI read: %r\n", Status);
            }
          }
        }
      }
    }
    AddressDescriptor++;
  }
  return Status;
}

이 코드에서 DESCRIPTOR_STR_MAX_SIZE는 Vendor와 Device 설명의 최대 크기이다. VendorDescDeviceDesc를 단순화하기 위해 정적 배열을 선언하고 모든 설명을 포함할 수 있을 만큼 충분한 크기의 배열 크기를 선택해야 한다. ListPCI.c에 추가한다.

#define DESCRIPTOR_STR_MAX_SIZE 200

이제 FindPCIDevDescription함수의 코드를 작성한다.

Public PCI ID Repository(https://pci-ids.ucw.cz/)에서 PCI Vendor와 Device 정보를 얻을 수 있다. 이 사이트에서는 공개된 PCI Vendor와 Devcie 조합이 포함된 pci.ids(https://pci-ids.ucw.cz/v2.2/pci.ids) 파일이 배포된다.

파일의 시작에는 정보가 표시되는 방법에 대한 설명이 있다.

# Syntax:
# vendor  vendor_name
#	device  device_name				<-- single tab
#		subvendor subdevice  subsystem_name	<-- two tabs

필요한 pci.ids 파일을 QEMU 공유 폴더에 다운로드 받는다.

$ cd ~/UEFI_disk
$ wget https://pci-ids.ucw.cz/v2.2/pci.ids

이제 함수를 작성한다.

먼저 PCI 데이터베이스 파일이 실제로 존재하는지 확인해야 한다.

해당 작업은 ShellLib의 함수를 활용할 수 있다. https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Library/ShellLib.h

/**
  Function to determine if a given filename exists.
  @param[in] Name         Path to test.
  @retval EFI_SUCCESS     The Path represents a file.
  @retval EFI_NOT_FOUND   The Path does not represent a file.
  @retval other           The path failed to open.
**/
EFI_STATUS
EFIAPI
ShellFileExists(
  IN CONST CHAR16 *Name
  );

파일을 확인하는 코드는 아래와 같이 간단하게 작성할 수 있다. 이 코드는 위에서 작성했던 FindPCIDevDescription 함수 안에 작성한다.

EFI_STATUS Status = ShellFileExists(L"pci.ids");
if (EFI_ERROR(Status))
{
  Print(L"No file pci.ids: %r\n", Status);
  return Status;
}

다음으로 파일을 읽기 위해서는 해당 파일을 열어야 한다. 파일을 읽는 코드는 아래와 같고 위에서 작성한 코드 밑에 작성한다.

SHELL_FILE_HANDLE FileHandle;
Status = ShellOpenFileByName(L"pci.ids",
                             &FileHandle,
                             EFI_FILE_MODE_READ,
                             0);
if (EFI_ERROR(Status))
{
  Print(L"Can't open file pci.ids: %r\n", Status);
  return Status;
}

파싱 프로세스에서는 pci.ids 파일의 크기가 필요하다. 따라서 ShellLib의 함수를 다시 사용한다.

/**
  Retrieve the size of a file.
  This function extracts the file size info from the FileHandle's EFI_FILE_INFO
  data.
  @param[in] FileHandle         The file handle from which size is retrieved.
  @param[out] Size              The pointer to size.
  @retval EFI_SUCCESS           The operation was completed sucessfully.
  @retval EFI_DEVICE_ERROR      Cannot access the file.
**/
EFI_STATUS
EFIAPI
ShellGetFileSize (
  IN SHELL_FILE_HANDLE          FileHandle,
  OUT UINT64                    *Size
  );

파싱 프로세스에서 많은 문제가 발생할 수 있지만 열려 있는 파일 핸들을 닫아야 하는 경우에는 상관없다. 함수의 모든 지점에서 특정 위치로 이동하는 가장 쉬운 방법은 goto문을 사용하는 것이다. goto문을 사용하는 것은 문제가 많다고 하지만 어떤 상황에서는 코드를 다루기 쉽다. 리눅스 커널 코드에서도 정리 목적으로 많이 사용되기 때문에 여기서도 사용한다.

EFI_STATUS FindPCIDevDescription(IN UINT16 VendorId,
                                 IN UINT16 DeviceId,
                                 OUT CHAR16* VendorDesc,
                                 OUT CHAR16* DeviceDesc,
                                 IN UINTN DescBufferSize)
{
  BOOLEAN Vendor_found = FALSE;
  BOOLEAN Device_found = FALSE;

  ...

  UINT64 FileSize;
  Status = ShellGetFileSize(FileHandle, &FileSize);
  if (EFI_ERROR(Status))
  {
    Print(L"Can't get file size for file pci.ids: %r\n", Status);
    goto end;
  }

  ...

end:
  if (!Vendor_found) {
    UnicodeSPrint(VendorDesc, DescBufferSize, L"Undefined");
  }
  if (!Device_found) {
    UnicodeSPrint(DeviceDesc, DescBufferSize, L"Undefined");
  }
  ShellCloseFile(&FileHandle);

  return Status;
}

데이터베이스 파일을 파싱할 때는 문자를 비교하기 때문에 VendorDevice에 대한 UINT16 값 코드를 16진수 문자열로 변환해야 한다.

여기서는 AsciiValueToStringS를 사용할 수 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PrintLib.h

/**
  Converts a decimal value to a Null-terminated Ascii string.
  Converts the decimal number specified by Value to a Null-terminated Ascii
  string specified by Buffer containing at most Width characters. No padding of
  spaces is ever performed. 
  ...
  If RADIX_HEX is set in Flags, then the output buffer will be formatted in
  hexadecimal format.
  ...
  If PREFIX_ZERO is set in Flags and PREFIX_ZERO is not being ignored, then
  Buffer is padded with '0' characters so the combination of the optional '-'
  sign character, '0' characters, digit characters for Value, and the
  Null-terminator add up to Width characters.
  If an error would be returned, then the function will ASSERT().
  @param  Buffer      The pointer to the output buffer for the produced
                      Null-terminated Ascii string.
  @param  BufferSize  The size of Buffer in bytes, including the
                      Null-terminator.
  @param  Flags       The bitmask of flags that specify left justification,
                      zero pad, and commas.
  @param  Value       The 64-bit signed value to convert to a string.
  @param  Width       The maximum number of Ascii characters to place in
                      Buffer, not including the Null-terminator.
  @retval RETURN_SUCCESS           The decimal value is converted.
  @retval RETURN_BUFFER_TOO_SMALL  If BufferSize cannot hold the converted
                                   value.
  @retval RETURN_INVALID_PARAMETER If Buffer is NULL.
                                   If PcdMaximumAsciiStringLength is not
                                   zero, and BufferSize is greater than
                                   PcdMaximumAsciiStringLength.
                                   If unsupported bits are set in Flags.
                                   If both COMMA_TYPE and RADIX_HEX are set in
                                   Flags.
                                   If Width >= MAXIMUM_VALUE_CHARACTERS.
**/
RETURN_STATUS
EFIAPI
AsciiValueToStringS (
  IN OUT CHAR8   *Buffer,
  IN UINTN       BufferSize,
  IN UINTN       Flags,
  IN INT64       Value,
  IN UINTN       Width
  );

AsciiValueToStringS 함수를 사용할 때는 충분히 큰 배열을 만들고 올바른 플래그(RADIX_HEX | PREFIX_ZERO)를 사용한다. 마찬가지로 위에서 만든 함수 안에 작성한다.

CHAR8 VendorStr[5];
CHAR8 DeviceStr[5];
AsciiValueToStringS(VendorStr,
                    5,
                    RADIX_HEX | PREFIX_ZERO,
                    VendorId,
                    4);
AsciiValueToStringS(DeviceStr,
                    5,
                    RADIX_HEX | PREFIX_ZERO,
                    DeviceId,
                    4);

추가적으로 AsciiValueToStringS는 16진수 값을 대문자로 저장하지만 데이터베이스 파일에서는 소문자로 저장되기 때문에 이를 변환해야 한다. 따라서 대소문자 변환을 위한 간단한 함수를 작성한다.

VOID ToLowerASCII(CHAR8* Str, UINTN Size)
{
  for (UINT8 i=0; i<Size; i++) {
    if ((Str[i]>='A')&&(Str[i]<='Z')) {
      Str[i]+=32;
    }
  }
}

사용 방법은 아래와 같이 하면 된다.

ToLowerASCII(VendorStr, 4);
ToLowerASCII(DeviceStr, 4);

이제 파싱 부분의 메인이다.

CHAR8 Buffer[BLOCK_READ_SIZE];
UINTN Size;
UINT64 FilePos = 0;
while (TRUE)
{
  Size = BLOCK_READ_SIZE;
  Status = ShellReadFile(FileHandle, &Size, Buffer);
  if (EFI_ERROR(Status))
  {
    Print(L"Can't read file pci.ids: %r\n", Status);
    goto end;
  }
  UINTN StrStart = 0;
  UINTN StrEnd = 0;
  for (UINTN i=0; i<Size; i++) {
    if (Buffer[i]=='\n') {
      StrEnd=i;
      <...>
      StrStart=StrEnd;
    }
  }

  if (FilePos+Size >= FileSize) {
    break;
  }
  FilePos += StrEnd;
  Status = ShellSetFilePosition(FileHandle, FilePos);
  if (EFI_ERROR(Status))
  {
    Print(L"Can't set file position pci.ids: %r\n", Status);
    goto end;
  }
}

아래 내용은 위 코드에 대한 설명이다.

  • 파일을 블록 단위로 읽는다. (#define BLOCK_READ_SIZE (1024*4)) *ListPCI.c에 추가

  • 각 블록에서 기호를 검색하고 변수 StrStartStrEnd를 채우기 위해 실제 검색은 두 기호 사이의 데이터에 대해서 검색이 진행된다.

  • 각 블록 파싱이 끝난 후 파일 포인터를 마지막으로 발견된 (=StrEnd)에 설정한다. 이 작업을 위해 ShellLib의 다른 함수를 사용한다.

EFI_STATUS
EFIAPI
ShellSetFilePosition (
  IN SHELL_FILE_HANDLE  FileHandle,
  IN UINT64             Position
  );
  • 파일의 끝에 도달했고 더이상 읽을 수 없을 경우 검색을 종료한다.

if (FilePos+Size >= FileSize) {
  break;
}

이제 위의 코드에서 <...>으로 있던 부분을 채워넣는다.

      if (!Vendor_found){
        // 0123456         7
        //\nVVVV  |<desc>|\n
        if ((StrEnd - StrStart) > 7) {
          if ((Buffer[StrStart+1]==VendorStr[0]) &&
              (Buffer[StrStart+2]==VendorStr[1]) &&
              (Buffer[StrStart+3]==VendorStr[2]) &&
              (Buffer[StrStart+4]==VendorStr[3])) {
            Buffer[StrEnd] = 0;
            UnicodeSPrintAsciiFormat(VendorDesc, DescBufferSize, "%a", &Buffer[StrStart+1+4+2]);
            Vendor_found = TRUE;
          }
        }
      } else {
        // 0 1234567         8
        //\n\tDDDD  |<desc>|\n
        if ((StrEnd - StrStart) > 8) {
          if ((Buffer[StrStart+1]=='\t') &&
              (Buffer[StrStart+2]==DeviceStr[0]) &&
              (Buffer[StrStart+3]==DeviceStr[1]) &&
              (Buffer[StrStart+4]==DeviceStr[2]) &&
              (Buffer[StrStart+5]==DeviceStr[3])) {
            Buffer[StrEnd] = 0;
            UnicodeSPrintAsciiFormat(DeviceDesc, DescBufferSize, "%a", &Buffer[StrStart+1+1+4+2]);
            Device_found = TRUE;
            goto end;
          }
        }
      }
  • Vendor가 검색되지 않을 경우 해당 패턴을 검색하고, 발견된 경우 Device 패턴을 검색한다.

  • StrStartStrEnd는 모두 다른 을 가리키며, 둘 사이의 정보가 필요로 하는 정보인지 생각해봐야 한다.

  • 찾고 있는 최소한의 형식은 아래와 같다.

// 0123456         7
//\nVVVV  |<desc>|\n

즉, StrStart가 (i+0)에서 을 가리키면 StrEndVendor 설명이 심볼(i+1-i+4)로 이루어지므로 최소 7( i+7)에서 을 가리키고, 그 다음에는 정확히 두 개의 공백이 존재해야 한다. 그래서 실제 설명이 비어 있더라도 문자열에 최소 8개의 심볼이 있어야 한다.

/**
  Produces a Null-terminated Unicode string in an output buffer based on a Null-terminated
  ASCII format string and  variable argument list.
  This function is similar as snprintf_s defined in C11.
  Produces a Null-terminated Unicode string in the output buffer specified by StartOfBuffer
  and BufferSize.
  The Unicode string is produced by parsing the format string specified by FormatString.
  Arguments are pulled from the variable argument list based on the contents of the
  format string.
  ...
  @param  StartOfBuffer   A pointer to the output buffer for the produced Null-terminated
                          Unicode string.
  @param  BufferSize      The size, in bytes, of the output buffer specified by StartOfBuffer.
  @param  FormatString    A Null-terminated ASCII format string.
  @param  ...             Variable argument list whose contents are accessed based on the
                          format string specified by FormatString.
  @return The number of Unicode characters in the produced output buffer not including the
          Null-terminator.
**/
UINTN
EFIAPI
UnicodeSPrintAsciiFormat (
  OUT CHAR16       *StartOfBuffer,
  IN  UINTN        BufferSize,
  IN  CONST CHAR8  *FormatString,
  ...
  );

위에서 ShellLib에 있는 함수와 PrintLib에 있는 함수를 사용하였기 때문에 해당 헤더 파일을 포함해야 한다.

#include <Library/ShellLib.h>
#include <Library/PrintLib.h>

또한 ShellLib을 사용하기 위해 *.inf 파일에도 추가해야 한다.

[Packages]
  ...
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  ...
  ShellLib

빌드하고 ListPCI.efi 앱을 실행하면 아래와 같은 결과를 확인 수 있다.

FS0:\> ListPCI.efi
Number of PCI root bridges in the system: 1

PCI Root Bridge 0
  00:00.00 - Vendor:8086, Device:1237
  :    Intel Corporation, 440FX - 82441FX PMC [Natoma]
  00:01.00 - Vendor:8086, Device:7000
  :    Intel Corporation, 82371SB PIIX3 ISA [Natoma/Triton II]
  00:01.01 - Vendor:8086, Device:7010
  :    Intel Corporation, 82371SB PIIX3 IDE [Natoma/Triton II]
  00:01.03 - Vendor:8086, Device:7113
  :    Intel Corporation, 82371AB/EB/MB PIIX4 ACPI
  00:02.00 - Vendor:1234, Device:1111
  :    Undefined, Undefined

Vendor:1234, Device:1111 는 QEMU VGA 컨트롤러이다. https://github.com/qemu/qemu/blob/master/docs/specs/standard-vga.txt

PCI expander bridges와 PCI root bridges를 QEMU에 추가

지금까지는 아래 명령으로 QEMU를 실행했다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=../OVMF_VARS.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none

아래 명령으로 QEMU를 실행하면 다양한 PCI expender bridgesPCI root bridges를 추가할 수 있다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=../OVMF_VARS.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \
  -device pci-bridge,id=bridge0,chassis_nr=1 \
  -device virtio-scsi-pci,id=scsi0,bus=bridge0,addr=0x3 \
  -device pci-bridge,id=bridge1,chassis_nr=2 \
  -device virtio-scsi-pci,id=scsi1,bus=bridge1,addr=0x3 \
  -device virtio-scsi-pci,id=scsi2,bus=bridge1,addr=0x4 \
  -device pxb,id=bridge2,bus=pci.0,bus_nr=3 \
  -device virtio-scsi-pci,bus=bridge2,addr=0x3 \
  -device pxb,id=bridge3,bus=pci.0,bus_nr=8 \
  -device virtio-scsi-pci,bus=bridge3,addr=0x3 \
  -device virtio-scsi-pci,bus=bridge3,addr=0x4

위 명령으로 QEMU를 실행하고 ListPCI.efi 애플리케이션을 실행하면 아래와 같은 결과를 확인할 수 있다.

FS0:\> ListPCI.efi
Number of PCI root bridges in the system: 3

PCI Root Bridge 0
  00:00.00 - Vendor:8086, Device:1237
:    Intel Corporation, 440FX - 82441FX PMC [Natoma]
  00:01.00 - Vendor:8086, Device:7000
:    Intel Corporation, 82371SB PIIX3 ISA [Natoma/Triton II]
  00:01.01 - Vendor:8086, Device:7010
:    Intel Corporation, 82371SB PIIX3 IDE [Natoma/Triton II]
  00:01.03 - Vendor:8086, Device:7113
:    Intel Corporation, 82371AB/EB/MB PIIX4 ACPI
  00:02.00 - Vendor:1234, Device:1111
:    Undefined, Undefined
  00:03.00 - Vendor:1B36, Device:0001
:    Red Hat, Inc., QEMU PCI-PCI bridge
  00:04.00 - Vendor:1B36, Device:0001
:    Red Hat, Inc., QEMU PCI-PCI bridge
  00:05.00 - Vendor:1B36, Device:0009
:    Red Hat, Inc., QEMU PCI Expander bridge
  00:06.00 - Vendor:1B36, Device:0009
:    Red Hat, Inc., QEMU PCI Expander bridge
  01:03.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI
  02:03.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI
  02:04.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI

PCI Root Bridge 1
  03:00.00 - Vendor:1B36, Device:0001
:    Red Hat, Inc., QEMU PCI-PCI bridge
  04:03.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI

PCI Root Bridge 2
  08:00.00 - Vendor:1B36, Device:0001
:    Red Hat, Inc., QEMU PCI-PCI bridge
  09:03.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI
  09:04.00 - Vendor:1AF4, Device:1004
:    Red Hat, Inc., Virtio SCSI

QEMU q35에서는 PCI-express root complexes를 추가할 수도 있다.

qemu-system-x86_64 \
  -machine q35 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=../OVMF_VARS.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \
  -device pxb-pcie,id=pcie.1,bus_nr=2,bus=pcie.0 \
  -device ioh3420,id=pcie_port1,bus=pcie.1,chassis=1 \
  -device virtio-scsi-pci,bus=pcie_port1 \
  -device ioh3420,id=pcie_port2,bus=pcie.1,chassis=2 \
  -device virtio-scsi-pci,bus=pcie_port2 \
  -device pxb-pcie,id=pcie.2,bus_nr=8,bus=pcie.0 \
  -device ioh3420,id=pcie_port3,bus=pcie.2,chassis=3 \
  -device virtio-scsi-pci,bus=pcie_port3
FS0:\> ListPCI.efi
Number of PCI root bridges in the system: 3

PCI Root Bridge 0
  00:00.00 - Vendor:8086, Device:29C0
:    Intel Corporation, 82G33/G31/P35/P31 Express DRAM Controller
  00:01.00 - Vendor:1234, Device:1111
:    Undefined, Undefined
  00:02.00 - Vendor:1B36, Device:000B
:    Red Hat, Inc., QEMU PCIe Expander bridge
  00:03.00 - Vendor:1B36, Device:000B
:    Red Hat, Inc., QEMU PCIe Expander bridge
  00:1F.00 - Vendor:8086, Device:2918
:    Intel Corporation, 82801IB (ICH9) LPC Interface Controller
  00:1F.02 - Vendor:8086, Device:2922
:    Intel Corporation, 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode]
  00:1F.03 - Vendor:8086, Device:2930
:    Intel Corporation, 82801I (ICH9 Family) SMBus Controller

PCI Root Bridge 1
  02:00.00 - Vendor:8086, Device:3420
:    Intel Corporation, 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0
  02:01.00 - Vendor:8086, Device:3420
:    Intel Corporation, 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0
  03:00.00 - Vendor:1AF4, Device:1048
:    Red Hat, Inc., Virtio SCSI
  04:00.00 - Vendor:1AF4, Device:1048
:    Red Hat, Inc., Virtio SCSI

PCI Root Bridge 2
  08:00.00 - Vendor:8086, Device:3420
:    Intel Corporation, 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0
  09:00.00 - Vendor:1AF4, Device:1048
:    Red Hat, Inc., Virtio SCSI

QEMU 파라미터에 대한 내용은 아래 링크를 통해 확인할 수 있다. https://blogs.oracle.com/linux/post/a-study-of-the-linux-kernel-pci-subsystem-with-qemu

UTF-8은 file 명령어로 확인했을 때 ASCII로 나오기 때문에 제외함.

Last updated