28. EFI_SHELL_PROTOCOL을 통하여 ACPI 테이블을 파일에 저장하기

최신 ACPI 스펙은 UEFI 스펙 페이지(https://uefi.org/specifications)에서 찾을 수 있다. 현재 최신 사양은 "ACPI Specification Version 6.4 (released January 2021"(https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf)이다.

SMBIOS 테이블에 사용한 것과 동일한 방법을 사용하여 ACPI Entry point 테이블 주소를 출력하겠다.

#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), &gEfiAcpi20TableGuid)) {
      Print(L"ACPI table is placed at %p\n\n", SystemTable->ConfigurationTable[i].VendorTable);
    }
  }
  return EFI_SUCCESS;
}

출력된 ACPI 테이블 주소를 바탕으로 dmem을 사용하여 메모리 내부 값을 확인하겠다.

시그니처 값인 RSP PTRRSDP(Root System Description Pointer (RSDP) Structure)를 나타낸다. https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-pointer-rsdp-structure

여기에는 RSDTXSDT 테이블에 대한 주소가 포함되며 오프셋을 계산시 메모리 덤프에서 다음과 같은 주소를 얻을 수 있다.

이러한 테이블은 실제로 OS에 유용한 데이터를 포함하는 다른 ACPI 테이블에 대한 포인터를 포함한다.

규격에 따르면 "플랫폼은 ACPI 1.0 운영체제와 호환성을 가능하게 하는 RSDT를 제공합니다. XSDT는 RSDT 기능을 대체합니다." 따라서 dmem을 사용하여 이러한 주소를 출력하면 테이블 내용은 테이블 서명을 제외하고는 거의 동일하다. 따라서 XSDT 테이블 데이터를 파싱하겠다.

이제 코드를 작성해보겠다. ACPI 구조에 대한 정의는 아래의 헤더 파일들에 존재하니 확인하는 것도 좋다.

최신 헤더에는 이전 헤더들이 포함되어 있음을 명심하자.

최신 ACPI 표준 헤더 파일에서 RSDP 구조에 대한 정의를 살펴보겠다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Acpi63.h

위의 구조를 이용하여 RSDT/XSDT 테이블의 주소를 출력이 가능하다.

위의 구조가 설명되어 있는 부분에서 XSDT에 대한 설명도 또한찾아볼 수 있다.

해당 설명에서 나온 EFI_ACPI_DESCRIPTION_HEADER에 대한 정의는 아래와 같다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Acpi10.h

이제 시스템에 있는 다른 ACPI 테이블에 대한 정보를 확인해 보겠다.

여기서 한가지 확인하고 넘어가야 할 것이 있다. 일부 ACPi 테이블은 다른 ACPI 테이블에 대한 포인터를 포함할 수 있다. 예를들어 Fixed ACPI Description Table(FADT)에는 DSDT 및 FACS 테이블에 대한 포인터가 포함될 수 있다.

FADT에 대한 구조와 설명은 아래의 링크에 언급되어 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Acpi63.h https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fadt

아래에서 나올 설명은 위의 FADT 구조의 필드이기 때문에 구조를 확인하는 것을 권장한다.

FADTFirmwareCtrl 필드에는 FACS 테이블에 대한 포인터가 포함되며 Dsdt 필드에는 DSDT 테이블에 대한 포인터가 포함된다. CheckSubtables 함수를 작성하여 해당 ACPI 테이블이 FADT인지 그리고 하위 테이블을 찾을 수 있는지 확인해 보겠다.

위에서 만든 함수를 Print문 바로 뒤에 존재하는 while문에서 호출하게 만든다.

이후 빌드 후 실행을 하면 아래와 같은 결과가 나온다.

결과를 보다시피 우리 시스템에는 ACPI 데이터 테이블이 여러개가 존재한다.

SMBIOS 테이블과 마찬가지로 프로토콜을 사용하여 동일한 데이터를 얻을 수 있음을 명심해야한다. EFI_ACPI_SDT_PROTOCOLGetAcpiTable()함수는 동일한 정보를 얻는데 도움이 될 수 있다. 해당 프로토콜은 또한 UEFI PI 사양에 의해 정의된다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/AcpiSystemDescriptionTable.h

EFI_SHELL_PROTOCOL을 통한 테이블 데이터 저장

이제 메모리에서 파일로 ACPI 테이블을 저장해 보겠다. 이를 위해서 UEFI 쉘 사양에 정의된 EFI_SHELL_PROTOCOL을 활용할 수 있다. File I/O를 위한 많은 기능이 존재하기 때문에 도움이 될 수 있다. https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Shell.h

해당 프로토콜에서 우리는 OpenFileByName/WriteFile/CloseFile 3가지 함수를 사용한다.

해당 함수들을 이용하기 위해서 c 파일에 헤더를 추가 및 inf 파일에 프로토콜 GUID 값을 넣어야 한다.

이후 프로그램에서 EFI_SHELLPROTOCOL을 사용하기 위해서 BootServicesLocateProtocol 기능을 통해 프로토콜을획득한다.

SaveACPITable 함수를 만들어 파일 저장 기능에 대해서 한번에 처리할 수 있도록 만들어 주겠다.

mainwhile문은 이전과 다르게 SaveACPITable함수를 추가하여 아래와 같이 변경되었다.

또한 DSDTFACS 테이블을 저장하기 위해 CheckSubtable 기능에 추가하는 것을 잊으면 안된다.\

SaveACPITable 함수를 호출할 때마다 EFI_SHELL_PROTOCOL* ShellProtocol을 사용하므로 모든 곳의 매개변수로 전달하거나 ShellProtocol을 전역변수로 옮기면 된다. 이제 SaveACPITable 함수를 작성해야한다. ACPI 테이블 데이터를 .aml 파일에 저장한다. ACPI 언어 소스 파일에는 일반적으로 .asl/.dsl 확장자(ACPI 소스 언어)가 있고 컴파일된 파일에는 .aml확장자(ACPI 기계 언어)가 있기 때문에 파일에 .aml확장자를 사용한다.

파일 이름을 문자열로 생성하기 위해 StrCatSStrCpyS 함수를 사용한다. 이들은 C++에서 사용되는 strcat_s/strcpy_s와 유사한 문자열 연결/복사 함수의 안전한 버전이다. https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/SafeString.c

앱을 빌드하고 OVMF에서 실행하면 UEF_disk 공유 폴더에 4개의 파일이 생길 것이다.

iasl 컴파일러를 사용하여 ACPI 테이블 데이터를 disassemble 할 수 있다.

명령어를 수행하면 UEFI_disk 공유 폴더에 .dsl 파일이 생성될 것이다.

Last updated