UEFI to Windows Communication via NVRAM Variables
Bu dökümanda, bir UEFI sürücüsü ile bir Windows sürücüsü arasında nasıl iletişim kurulacağını tartışacağız. Özellikle, iki ortam arasında veri alışverişi yapmak için NVRAM tabanlı UEFI değişkenlerinin nasıl kullanılacağını inceleyeceğiz.
What is NVRAM?
NVRAM (Non-Volatile RAM), UEFI tabanlı sistemlerde kalıcı yapılandırma ve sistem durumu verilerini depolamak için kullanılan önemli bir bileşendir. Normal RAM’den farklı olarak NVRAM, içeriğini yeniden başlatmalarda ve kapatmalarda korur.
UEFI’de NVRAM, nasıl ve ne zaman erişilebileceklerini tanımlayan meta verileri (öznitelikler) içeren değişkenler-yapılandırılmış anahtar-değer çiftleri halinde düzenlenir. Bu değişkenler önyükleme yönetimi, güvenli önyükleme anahtarı depolama, donanım yapılandırması ve OEM’e özgü ayarlar gibi kritik sistem işlevleri için kullanılır.
UEFI Variables
UEFI Değişkenleri bir GUID ve Unicode String kombinasyonu ile belirtilir. Bir değişkenin GUID’si, farklı vendorler arasındaki isim çakışmalarını önleyebilir.
UEFI’nin The Boot Manager Chapter, EDK II’de gEfiGlobalVariableGuid olarak da bilinen EFIGLOBAL_VARIABLE_GUID’yi tanımlar. UEFI Sürücümüzde SecureBoot Status gibi global bir değişken kullanmak istiyorsak, gEfiGlobalVariableGuid’den yararlanmamız gerekir. İşte EDK II Repo‘dan bazı global değişken listesi:
- EFI_PLATFORM_LANG_CODES_VARIABLE_NAME L"PlatformLangCodes"
- EFI_BOOT_CURRENT_VARIABLE_NAME L"BootCurrent"
- EFI_SIGNATURE_SUPPORT_NAME L"SignatureSupport" …
Her UEFI Değişkeni, kalıcılık ile alakalı niteliklere sahiptir. İşte özniteliklerin listesi:
- BOOTSERVICE_ACCESS
Değişken, ExitBootServices() çağrılmadan önce önyükleme sırasında yazma ve okuma erişim izinlerine sahiptir, bu da değişkenin ExitBootServices() çağrıldıktan sonra kullanılamayacağı ve değişkenin içeriğinin bir sonraki sistem sıfırlamasında silineceği anlamına gelir.
- BOOTSERVICE_ACCESS | RUNTIME_ACCESS
Değişken ExitBootServices() çağrılmadan önce yazma ve okuma erişim izinlerine sahiptir, ancak ExitBootServices() çağrıldığında içeriği salt okunur olarak kalacaktır, ayrıca bir sonraki sistem yeniden başlatıldığında içeriği silinecektir.
- NON_VOLATILE | BOOTSERVICE_ACCESS
Değişken, ExitBootServices() çağrılmadan önce yazma ve okuma erişim izinlerine sahiptir ve içeriği sistem sıfırlanana kadar kalıcıdır.
- NON_VOLATILE | BOOTSERVICE_ACCESS | RUNTIME_ACCESS
Değişken hem önyükleme öncesi hem de işletim sistemi çalışma zamanı ortamında yazma ve okuma izinlerine sahiptir. İçeriği sistem sıfırlaması boyunca kalıcıdır.
Accessing UEFI Variables with Services
Bir UEFI Sürücüsü yapılandırma bilgilerini UEFI Değişkenleri aracılığıyla sakladığında, EFI_HII_CONFIG_ACCESS_PROTOCOL tarafından sağlanan servislerle bunlara erişebilir. Bu dökümanda göreceğimiz SetVariable() ve GetVariable() servisleri yapılandırma bilgilerini ayarlamak ve almak için kullanılır.
1 - SetVariable()
SetVariable() servisi değişkenin içeriğini ayarlar, ayrıca yeni bir değişken oluşturmak, değişkeni değiştirmek veya mevcut bir değişkeni silmek için kullanılabilir. İşte servisin parametreleri:
typedef
EFI_STATUS
SetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
);
SetVariable için bir proje oluşturalım:
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
CHAR16 *VariableName = L"MyUEFIVar";
EFI_GUID VendorGuid = { 0xa1b2c3d4, 0x1234, 0x5678, {0x9a,0xbc,0xde,0xf1,0x23,0x45,0x67,0x89} };
UINT8 ContentOfVariable[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
UINTN DataSize = sizeof(ContentOfVariable);
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS;
Status = gRT->SetVariable(
VariableName,
&VendorGuid,
Attributes,
DataSize,
&ContentOfVariable
);
if (EFI_ERROR(Status)) {
Print(L"Write Operation Failed!\n");
return Status;
}
Print(L"The content successfully written!\n");
return EFI_SUCCESS;
}
Kodda ContentOfVariable’ın içeriğini yazdık. Öncelikle UEFI değişkenimiz için bir değişken adı, GUID ve değer oluşturduk, ardından SetVariable’ı çalıştırdık. Bu işlemler sonucunda UEFI Değişkenimiz şu bilgilere sahip olacaktır:
- UEFI Değişken Adı: MyUEFIVar
- GUID: A1B2C3D4-1234-5678-9ABC-DEF123456789
- İçerik: 0xDEADBEEF
İşte sonuç:

Şimdi bu bilgilerle bu değişkene erişebiliriz.
2 - GetVariable()
GetVariable() servisi ilgili değişkenin içeriğini okur. İşte servisin parametreleri:
typedef
EFI_STATUS
GetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT UINT32 *Attributes OPTIONAL,
IN OUT UINTN *DataSize,
OUT VOID *Data OPTIONAL
);
Şimdi GetVariable servisi ile projeyi geliştirelim:
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
CHAR16 *VariableName = L"MyUEFIVar";
EFI_GUID VendorGuid = { 0xa1b2c3d4, 0x1234, 0x5678, {0x9a,0xbc,0xde,0xf1,0x23,0x45,0x67,0x89} };
UINT8 ContentOfVariable[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
UINTN DataSize = sizeof(ContentOfVariable);
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS;
Status = gRT->SetVariable(
VariableName,
&VendorGuid,
Attributes,
DataSize,
&ContentOfVariable
);
if (EFI_ERROR(Status)) {
Print(L"Failed to write!\n");
return Status;
}
Print(L"The content successfully written!\n");
UINT8 Content[4] = { 0 };
DataSize = sizeof(Content);
Status = gRT->GetVariable(
VariableName,
&VendorGuid,
&Attributes,
&DataSize,
&Content
);
if (EFI_ERROR(Status)) {
Print(L"Failed to read the variable!\n");
return Status;
}
for (int x = 0; x < DataSize; x++) {
Print(L"Content: 0x%02x\n", Content[x]);
}
return EFI_SUCCESS;
}
SetVariable çağrıldıktan sonra, UEFI Değişkenimizin içeriğini almak için GetVariable servisini çağırdık ve ardından sonucu yazdırdık. İşte sonuç:

Coding Windows Driver
Esasen ntoskrnl’de amacımız için kullanabileceğimiz bir rutin var. ExGetFirmwareEnvironmentVariable UEFI Değişkenlerini okumak için kullanılabilir. Böylece windows sürücüsünün kodlanması zor olmayacaktır. Bu rutini UEFI Değişken bilgisi ile çağırabiliriz:
#include <ntddk.h>
NTSTATUS DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DbgPrint("Driver unloaded.\n");
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
UNICODE_STRING VariableName = RTL_CONSTANT_STRING(L"MyUEFIVar");
UINT8 Content[4] = { 0 };
ULONG BufferSize = sizeof(Content);
GUID VendorGuid = {
0xa1b2c3d4, 0x1234, 0x5678, {0x9a,0xbc,0xde,0xf1,0x23,0x45,0x67,0x89}
};
NTSTATUS Status = STATUS_SUCCESS;
Status = ExGetFirmwareEnvironmentVariable(&VariableName, &VendorGuid, &Content, &BufferSize, NULL);
if (!NT_SUCCESS(Status)) {
DbgPrintEx(0, 0, "Failed to Read Data!\n");
return Status;
}
for (ULONG x = 0; x < BufferSize; x++) {
DbgPrintEx(0, 0, "The value from UEFI Variable: 0x%02x\n", Content[x]);
}
return STATUS_SUCCESS;
}
Projede ExGetFirmwareEnvironmentVariable rutini ile UEFI Değişkenimizin içeriğine eriştik. İşte sonuç:

ExGetFirmwareEnvironmentVariable çağrıldığında, aşağıdaki rutinler çağırılmakta:

Her şeyden önce ExpGetFirmwareEnvironmentVariable için çağrı yapar:

Ve sonra ExGetFirmwareEnvironmentVariable parametreleri IoGetEnvironmentVariableEx’e aktarılır:

Creating UEFI Variable from Windows Driver
Ayrıca Windows Sürücüsünden UEFI Değişkeni oluşturabilir ve ayarlayabiliriz. ExSetFirmwareEnvironmentVariable bu amaç için kullanılabilir:
#include <ntddk.h>
#pragma warning(disable: 4057)
#define EFI_VARIABLE_NON_VOLATILE 0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
NTSTATUS DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DbgPrintEx(0, 0, "Driver unloaded.\n");
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
UNICODE_STRING VariableName = RTL_CONSTANT_STRING(L"YUPIIIIIII");
WCHAR Buffer[] = L"HELLOOO from Windows!!";
ULONG BufferSize = sizeof(Buffer);
GUID VendorGuid = {
0xa1b2c3d4, 0x1234, 0x5678, {0x9a,0xbc,0xde,0xf1,0x23,0x45,0x67,0x89}
};
NTSTATUS Status = STATUS_SUCCESS;
UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
Status = ExSetFirmwareEnvironmentVariable(&VariableName, &VendorGuid, &Buffer, BufferSize, Attributes);
if (!NT_SUCCESS(Status)) {
DbgPrintEx(0, 0, "Failed to write the value! Error Code: 0x%x\n", Status);
return Status;
}
DbgPrintEx(0, 0, "Done!\n");
return STATUS_SUCCESS;
}
Ve UEFI sürücüsü:
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
CHAR16 *VariableName = L"YUPIIIIIII";
EFI_GUID VendorGuid = { 0xa1b2c3d4, 0x1234, 0x5678, {0x9a,0xbc,0xde,0xf1,0x23,0x45,0x67,0x89} };
CHAR16 Buffer[32];
UINTN DataSize = sizeof(Buffer);
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS;
Status = gRT->GetVariable(VariableName, &VendorGuid, &Attributes, &DataSize, &Buffer);
if (EFI_ERROR(Status)) {
Print(L"Failed to read the data!\n");
return Status;
}
Print(L"The Content: %s\n", Buffer);
return EFI_SUCCESS;
}
İşte sonuç:

Conclusion
Bu dökümanda, UEFI ve Windows sürücülerinin NVRAM değişkenlerini kullanarak birbirleriyle nasıl iletişim kurabileceğini gösterdik. UEFI’de SetVariable ve GetVariable ve Windows’ta ExGetFirmwareEnvironmentVariable / ExSetFirmwareEnvironmentVariable’dan yararlanarak, iki ortam arasında kalıcı ve güvenilir bir veri kanalı kurduk.
Bu yöntem, geliştiricilerin ürün yazılımı ve işletim sistemi arasında sorunsuz entegrasyonlar oluşturmasına olanak tanıyarak yeniden başlatmalarda esnek yapılandırmalara, özellik geçişlerine veya telemetri mekanizmalarına izin verir.