Archive for the EFI/UEFI Category

Note: Read/Write UEFI variables under Windows 7 and later

Posted in EFI/UEFI with tags on 2014 年 04 月 30 日 by Kun-Yi

簡單講, 用 GetFirmwareEnvironmentVariable 可以讀取 UEFI 的變數, SetFirmwareEnvironmentVariable 可以寫回變數內容! 很簡單的, 但是如果 Google 會發現一堆人求救需要 Example code, 我自己歸納有幾點問題要克服

  • 系統到底是不是用UEFI mode 安裝的, 有的系統實際上安裝因為開了CSM 模式, 最後裝出來的系統是走legacy mode, 如標準UEFI 目前大家應該都只有 FAT32 Filesystem support 但是很多人把install 檔案放到 NTFS system 上面, 根本就是透過 legacy mode 裝起來的. MSDN 有說. 透過下面的dummy variable string & dummy guid 可以透過 ERROR code 判斷到底是不是 UEFI mode 的系統!

call the function with a dummy firmware environment name such as an empty string (“") for the lpName parameter and a dummy GUID such as “{00000000-0000-0000-0000-000000000000}" for the lpGuid parameter. On a legacy BIOS-based system, or on a system that supports both legacy BIOS and UEFI where Windows was installed using legacy BIOS, the function will fail with ERROR_INVALID_FUNCTION. On a UEFI-based system, the function will fail with an error specific to the firmware, such as ERROR_NOACCESS, to indicate that the dummy GUID namespace does not exist.

  • 執行權限的問題實際上他需要比較高的權限才能執行, 執行帳號要有 SE_SYSTEM_ENVIRONMENT_NAME 的權限, 或是程式內要求提權
  • 然後就是 UEFI Variable 的 Name String & GUID, 這個可以透過 DXE App DumpVar(Phoenix)/DumpVariable(Intel) or uefidump(Ubuntu Linux) 等工具, 但是這些只能觀察到變數的 Name String & GUID, 變數到底是怎樣組成的結構無法得知! 比較好的方式是跟BIOS Engineer 合作 取得 Header File 才能理解哪些變數有啥功能, 跟位於struct 中的offset

下面就是簡單的範例, 範例中的 Name String & GUID 是針對某特定版本的機種, 所以實際上無法完全直接換到不同型號的主機板上測試

 

#include "stdafx.h"


void RasiePrivileges(void)
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(), 
			TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
			&hToken)) {
				printf("Failed OpenProcessToken\r\n");
				return;
	}

	LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME,
		&tkp.Privileges[0].Luid);
	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	
	DWORD len;
	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, &len);

	if (GetLastError() != ERROR_SUCCESS) {
		printf("Failed RasiePrivileges()\r\n");
		return;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	BYTE Val[4096];
	DWORD dwLen = 0;
	const TCHAR name[] = TEXT("BootOrder");
	const TCHAR guid[] = TEXT("{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}");
	const TCHAR system[] = TEXT("System");
	const TCHAR sys_guid[] = TEXT("{E947FCf9-DD01-4965-B808-32A7B6815657}");
	const unsigned int offset_TPM = 11; // TPM On/Off offset

#if 0
	const TCHAR name[] = TEXT("");
	const TCHAR guid[] = TEXT("{00000000-0000-0000-0000-000000000000}");
#endif

	printf("Change Boot Order\r\n");
	RasiePrivileges();

	dwLen = GetFirmwareEnvironmentVariable(
		name, guid, Val, 4096);

	if (dwLen == 0)
	{
		DWORD dwErr = GetLastError();
		printf("Failed, GetFirmwareEnvironmentVariable(), GetLastError return %d(0x%08x)\r\n",
			dwErr, dwErr);
		return 1;
	}

	printf("Dump BootOrder variable, Len:%d(0x%02x)\r\n", dwLen, dwLen);
	for(UINT32 i = 0; i < dwLen; i++) {
		printf("%02x", Val[i]);
		if (i%16 == 0 && i != 0) {
			printf("\r\n");
		} else {
			putchar(' ');
		}
	}

	printf("\r\nExchange BootOrder Item1 & Item2\r\n");
	BYTE Tmp[2];
	Tmp[0] = Val[0];
	Tmp[1] = Val[1];
	
	Val[0] = Val[2];
	Val[1] = Val[3];
	
	Val[2] = Tmp[0];
	Val[3] = Tmp[1];

	if (SetFirmwareEnvironmentVariable(name, guid, Val, dwLen) == TRUE) {
		printf("Success!! SetFirmwareEnvironmentVariable\r\n");
	} else {
		printf("Failed!! SetFirmwareEnvironmentVariable\r\n");
	}

	printf("Read TPM Support state\r\n");
	dwLen = GetFirmwareEnvironmentVariable(
		system, sys_guid, Val, 4096);

	if (dwLen == 0)
	{
		DWORD dwErr = GetLastError();
		printf("Failed, GetFirmwareEnvironmentVariable(), GetLastError return %d(0x%08x)\r\n",
			dwErr, dwErr);
		return 2;
	}
	
	if (Val[offset_TPM]) {
		printf("TPM Support is Enabled, will change to Disable\r\n");
	} else {
		printf("TPM Support is Disabled, will change to Enable\r\n");
	}
	Val[offset_TPM] = (Val[offset_TPM]) ? 0 : 1;

	if (SetFirmwareEnvironmentVariable(system, sys_guid, Val, dwLen) == TRUE) {
		printf("Success!! Change TPM Support Setting\r\n");
	} else {
		printf("Failed!! Can't change TPM Support Setting\r\n");
	}
	return 0;
}
廣告

Note: Phoenix & AMI UEFI Bootloader location

Posted in BIOS, EFI/UEFI with tags , on 2012 年 03 月 27 日 by Kun-Yi

在 Phonenix 的 SCT 2.x 中, 會去找 remove device 下的 \EFI\BOOT\Boot(Machine).efi

而 AMI 的Aptio 則是透過 Create a Boot Optional 去指定 Path, Filename

Phoenix 中的 (Machine) 目前已知有 IA32/IA64/IPF , 實際上可以透過 BootManager\Dxe\(Machine)\FileName.H 去 modify Path & Filename

Bookmark: EDK2 with VirtualBox

Posted in BIOS, EFI/UEFI on 2012 年 02 月 23 日 by Kun-Yi

VirtualBox 的codebase 中有一個EDK II 相容的 pakcage, 可以用來產生 支援 VirtualBox 的 EFI Boot Firmware (VBoxEFI32.fd or VBoxEFI64.fd)

詳細說明可以看 該package 的 readme (update: https://www.virtualbox.org/browser/vbox/trunk/src/VBox?order=name#Devices/EFI/Firmware)

等於除了 NT32/DUET 外的又多了另一個可以用來學習的環境!

Note: UEFI 定義的四種VFR 的 VARSTORE的型別

Posted in BIOS, EFI/UEFI, VFR on 2011 年 05 月 26 日 by Kun-Yi

根據 28.2.5.6 Storage in UEFI 2.3 spec. 寫到UEFI目前可用在VFR Question的有 4種型別

1. Buffer Storage, 它是一個Buffer Storage Type, 用 EFI_IFR_VARSTORE OpCode表示, VFR 語法則是 varstore,  在Driver內要建立 EFI_HII_CONFIG_ACCESS_PROTOCOL 中的 ExtraceConfig() 與 RouteConfig(), 讓Framwork去存取 Var 的Buffer

2.  EFI Storage, 它是一種特定的 Buffer Storage, 用 EFI_IFR_EFIVARSTORE OpCode表示, VFR語法是 efivarstore, 在Driver內它是透過 EFI runtimer Service 中的 GetVariable()/SetVariable()存取變數, 因此這個會回存 NVRAM(Flash)

3. Name/Value Storage, 主要是一個 String 型別, 用 EFI_IFR_VARSTORE_NAME_VALUE Opcode表示, VFR語法是 namevaluevarstore,  在Driver內一樣要提供EFI_HII_CONFIG_ACCESS_PROTOCOL 來存取與儲存變數.

4. Date/Time Storage, 這就是Date/Time的專屬型別, 用 EFI_HII_DATE/EFI_HII_TIME OpCode表示,  VFR則直接就是data/time, 在Driver內就透過 GetTime()/SetTime(), GetWakeupTime()/SetWakeupTime() 存取之

當FormBrowse DXE使用 Buffer Storage 時, 會透過 EFI_HII_CONFIG_ACCESS_PROTOCOL,呼叫 ExtraceConfig(), 會透過Request 的EFI_STRING 將需要的 Var information 傳進來, 底下是該 Request String的一些 Example. 如果是一個Varstore的集合, 會有Byte offset, Width 的值是以Byte為單位, width為1時代表是一個byte. 而NameValue則直接傳進 Var Name

  • GUID=f4274aa000df424db55239511302113d&NAME=004d0079004900660072004e00560044006100740061&PATH=010414008db653c1fceb8e48b110662867745b877fff0400&OFFSET=DE&WIDTH=10
  • GUID=f4274aa000df424db55239511302113d&NAME=004d0079004900660072004e00560044006100740061&PATH=010414008db653c1fceb8e48b110662867745b877fff0400&OFFSET=EF&WIDTH=1
  • GUID=f4274aa000df424db55239511302113d&NAME=004d0079004900660072004e00560044006100740061&PATH=010414008db653c1fceb8e48b110662867745b877fff0400&OFFSET=D5&WIDTH=1&O
  • GUID=f4274aa000df424db55239511302113d&NAME=&PATH=010414008db653c1fceb8e48b110662867745b877fff0400&NameValueVar0&NameValueVar1&NameValueVar2

Buffer Storage 可以是Struct.Member 的變數形式, 在Form的 DXE Driver被載入時要自己將Struct 給初始化, 並透過 NewPackageList 去加入HII 的Database

Note: VFR with Dynamic Refresh

Posted in BIOS, EFI/UEFI, VFR on 2011 年 05 月 26 日 by Kun-Yi

在 EDKII 的 DriverSampleDxe 中可以看到 VFR.vfr中有 下面一個Form3的宣告, 重點的地方用Color標示

form formid = 3, title = STRING_TOKEN(STR_FORM3_TITLE);  // note formid is a variable (for readability) (UINT16) – also added Form to the line to signify the Op-Code

  suppressif  ideqval MyEfiVar == 111;
    text
      help = STRING_TOKEN(STR_TEXT_HELP),
      text = STRING_TOKEN(STR_TEXT_TEXT_1);
  endif;

  goto 1,
    prompt = STRING_TOKEN(STR_GOTO_FORM1), //MainSetupPage
    help   = STRING_TOKEN(STR_GOTO_HELP);

  numeric varid   = MyIfrNVData.DynamicRefresh,
          prompt  = STRING_TOKEN(STR_NUMERIC_MANUAL_PROMPT),
          help    = STRING_TOKEN(STR_NUMERIC_HELP0),
         flags   = INTERACTIVE,
          key     = 0x5678,
          minimum = 0,
          maximum = 0xff,
          step    = 0,
          default = 0,
          refresh interval = 3             // Refresh interval in seconds
  endnumeric;

然後在 DriverSample.c 中可以找到對應的Callback function “DriverCallBack()”, 在Dxe Entry Point“DriverSampleInit()”時, 透過 EFI_HII_CONFIG_ACCESS_PROTOCOL 來建立 CallBack link.

在CallBack 中是透過 EFI_BROWSER_ACTION_CHANGING 通知要 Refersh 了,  如果是原本 EDKII 的code, 會發現畫面一直閃, 原因是它會動態插入 Exit Item, 所以整個Form 會重畫! 只要簡單移除該段插入的code就可以, 如下

    case 0x5678:
      //
      // We will reach here once the Question is refreshed
      //
    #if 0 // By KunYi , remove dynamic add Exit Item
      //
      // Initialize the container for dynamic opcodes
      //
      StartOpCodeHandle = HiiAllocateOpCodeHandle ();
      ASSERT (StartOpCodeHandle != NULL);

      //
      // Create Hii Extend Label OpCode as the start opcode
      //
      StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
      StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
      StartLabel->Number       = LABEL_UPDATE2;

      HiiCreateActionOpCode (
        StartOpCodeHandle,                // Container for dynamic created opcodes
        0x1237,                           // Question ID
        STRING_TOKEN(STR_EXIT_TEXT),      // Prompt text
        STRING_TOKEN(STR_EXIT_TEXT),      // Help text
        EFI_IFR_FLAG_CALLBACK,            // Question flag
        0                                 // Action String ID
      );

      HiiUpdateForm (
        PrivateData->HiiHandle[0],  // HII handle
        &mFormSetGuid,              // Formset GUID
        0x3,                        // Form ID
        StartOpCodeHandle,          // Label for where to insert opcodes
        NULL                        // Insert data
        );

      HiiFreeOpCodeHandle (StartOpCodeHandle);
    #endif
      //
      // Refresh the Question value
      //
      PrivateData->Configuration.DynamicRefresh++;
      Status = gRT->SetVariable(
                      VariableName,
                      &mFormSetGuid,
                      EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                      sizeof (DRIVER_SAMPLE_CONFIGURATION),
                      &PrivateData->Configuration
                      );

      //
      // Change an EFI Variable storage (MyEfiVar) asynchronous, this will cause
      // the first statement in Form 3 be suppressed
      //
      MyVarSize = 1;
      MyVar = 111;
      Status = gRT->SetVariable(
                      L"MyVar",
                      &mFormSetGuid,
                      EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                      MyVarSize,
                      &MyVar
                      );
      break;

Note: UEFI & PI are independent Interfaces.

Posted in BIOS, EFI/UEFI on 2011 年 05 月 21 日 by Kun-Yi

The below picture get form http://nchc.dl.sourceforge.net/project/efidevkit/Member%20Documents/2009%20Beijing%20UEFI%20Training%20Stuff/102%20The%20framework%20Overview%20with%20UEFI-PI1-2.pdf , to see page 9

image

So UEFI Forum provide both PI and UEFI spec.

Normally an OEM BIOS enginee focus on to modify PI part, to meet the HW specificaiton and collection platform infomation. such as GPIO setting/special device initilization sequence/DMI(SMBIOS)/ACPI Tables …

Addition: PI SCT : Platform Initialization Self Certification Test

Note: EADK, EDK II Application Development Kit

Posted in BIOS, EFI/UEFI on 2011 年 05 月 19 日 by Kun-Yi

EADK Alpha II Relase 用來提供 UEFI Application 能使用 Standard C Library 的環境, 等於是在HII Layer上架構一個 C Stanard Library Interface, 有助於 porting DOS program to UEFI 環境. 改天來試試!