27. dmem/EFI_SMBIOS_PROTOCOL/smbiosview를 통해서 SMBIOS 정보 가져오기

이전 장에서 확인한 시스템 테이블 중 SMBIOS에 대해서 알아보도록 하겠다.\

"System Management BIOS(SMBIOS) 참조 스펙"의 최신 버전은 아래의 사이트에서 확인할 수 있으며 현재는 3.6.0이 최신 버전인 것을 알 수 있다. https://www.dmtf.org/dsp/DSP0134

이전 시간에 우리는 SMBIOS 테이블이 gEfiSmbiosTableGuid 에 선언된 것을 확인했다.

해당 테이블의 주소를 인쇄하는 간단한 앱을 만들어 보겠다.

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

#include <Library/BaseMemoryLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  for (UINTN i=0; i<SystemTable->NumberOfTableEntries; i++) {
    if (CompareGuid(&(SystemTable->ConfigurationTable[i].VendorGuid), &gEfiSmbiosTableGuid)) {
      Print(L"SMBIOS table is placed at %p\n", SystemTable->ConfigurationTable[i].VendorTable);
    }
  }
  return EFI_SUCCESS;
}

해당 코드에서 사용된 CompareGuid 함수는 아래의 코드에서 정의가 되어있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/BaseMemoryLib.h

잊지 말고 inf파일에GUID에 값을 추가 해주자.

빌드 후 실행하면 아래와 같이 주소가 나오는 것을 볼 수 있다.

dmem을 통한 SMBIOS 테이블 가져오기

UEFI Shell에는 메모리를 덤프하는 dmem이라는 명령어가 존재한다.

SMBIOS 테이블 포인터로 출력된 0x793F000주소에서 0x30 바이트를 출력해보겠다.

SMBIOS 스펙에 나와있는 SMBIOS Entry point 구조에 따르면 _SM__DMI_가 해당 구조에서 미리 정의된 값임을 알 수 있다.

SMBIOS에 대한 구조는 EDKII에서 또한찾을 수 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h

해당 구조를 따라서 덤프된 메모리를 계산하면 아래와 같이 볼 수 있다. 시스템에는 0xBF53E000에서 (0xBF53E000+0x1B2)까지 배치된 0xA개의 SMBIOS 구조가 존재한다. (환경별로 상이할 가능성 존재)

SMBIOS구조의 주소를 구하는 것이 가능하기 때문에 이를 통해서 덤프도 가능해진다.

EFI_SMBIOS_PROTOCOL을 사용하여 SMBIOS 데이터 파싱하기

위에서 한 방식대로 직접 포인터를 이용하여 SMBIOS 테이블을 계산을 통하여 구할 수 있지만, 매우 비합리적인 일이다. 다행히 UEFI PI 스펙은 SMBIOS 데이터를 가져오는데 사용할 수 있는 EFI_SMBIOS_PROTOCOL을 정의하고 있다.

지금 우리는 SMBIOS 테이블의 파싱을 하기 위해서 GetNext 함수를 사용하겠다.

우선 해당 프로토콜을 불러와야 한다.

해당 기능을 사용하기 위해서는 헤더 파일을 명시해야 한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h

그리고 프로토콜의 GUID 값을 inf 파일에 작성한다.

이제 SMBIOS 테이블을 가져오도록 하겠다. 프로토콜 호출에서 SMBIOS_HANDLE_PI_RESERVED(0 xFFE)EFI_SMBIOS_HANDLE로 사용할 것이다. SMBIOS_HANDLE_PI_RESERVED에 대한 설명은 아래의 링크에서 볼 수 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h

SMBIOS_HANDLE_PI_RESERVED를 사용하는 이유는 GetNext()함수의 두번째 파라미터인 SmbiosHandle을 충족하기 위해서다.

또한 코드 작성전 GetNext 호출에서 파라미터로 사용되는 EFI_SMBIOS_TABLE_HEADER 구조의 정의를 살펴보겠다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h

https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h

이를 통해서 몇가지 코드 작성 준비가 되었다. 시스템에 있는 모든 유형의 SMBIOS 테이블을 인쇄하는 코드를 작성하겠다.

빌드 후 실행시 아래와 같은 결과를 볼 수 있다.

이제는 이 테이블의 정보를 파싱할 수 있는 코드를 Type 0 테이블부터 시작하겠다. 아래 링크에 EDKII에서 해당 구조의 정의에대한 설명이 존재한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h

해당 구조에서 SMBIOS_STRUCTURE Hdr은 프로토콜 함수 호출에서 수신한 EFI_SMBIOS_TABLE_HEADER와 동일하게 필수적인 필드이다. 또한 SMBIOS_TABLE_STRING은 ASCII 문자열 배열의 문자형 숫자를 정의하는 UINT8 값이다.

따라서 EFI_SMBIOS_TABLE_HEADER* 및 문자형 숫자에서 실제 ASCII 문자열을 반환하는 간단한 함수를 작성하겠다.

해당 함수에서는 아래의 링크에 정의된 AsciiStrSize 함수를 사용했다. https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/String.c

위의 모든 것을 바탕으로 while문을 이용해서 실제적으로 파싱을 하는 코드를 작성할 수 있다.

여기서 ASCII 문자열을 인쇄하기 위해서 Print 함수의 %a 옵션을 사용했다. 이후 빌드를 하면 아래와 같은 결과가 나온다.

해당 결과는 dmem을 통해서 dump한 결과가 실제로 메모리에 존재하는 문자열임을 알 수 있다. Type 0 이외에도 다른 SMBIOS 테이블을 parse하는 코드를 추가해보겠다.

이번에는 Type 1에 대해서 파싱하는 코드를 추가했고, 이에 대한 구조는 아래와 같다.

빌드 후 실행시 아래와 같은 결과가 나온다.

이러한 SMBIOS 테이블에 있는 정보와 동일한 정보가 기본 BIOS 메뉴에 존재하는 것을 기억하고 있어야 한다. 해당 SMBIOS 구조에 대한 정보의 위치와 EFI_SMBIOS_PROTOCOL의 구현은 아래의 링크에 위치한다. https://github.com/tianocore/edk2/blob/master/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c

smbiosview 명령어

EFI_SMBIOS_PROTOCOL을 사용하여 SMBIOS 테이블 정보를 파싱할 수 있지만 실제로 SMBIOS 정보만 보려면 더 좋은 옵션이 있다. UEFI Shell에는 정확히 필요한 작업을 수행하는 smbiosview라는 명령어가 있다. 이는 아래의 소스를 통해서 확인이 가능하다. https://github.com/tianocore/edk2/tree/master/ShellPkg/Library/UefiShellDebug1CommandsLib/SmbiosView

해당 명령어에 대한 설명을 확인하자.

위에서 파싱한 구조 중 하나를 명령어를 통해서 덤프 해보겠다.

보다시피 EFI_SMBIOS_PROTOCOL을 사용한 것과 동일한 정보가 나왔다. 이외에도 SMBIOS 구조 내부에 무엇이 있는지 확인하는 명령어도 존재한다. 해당 결과 값은 너무 크기 때문에 직접 확인해보자.

Last updated