Merge pull request #229027 from NixOS/qemu-vm/read-efi-var

nixos/lib/test-driver: enable EFI variable reads at runtime
This commit is contained in:
Jacek Galowicz 2023-05-28 09:51:29 +02:00 committed by GitHub
commit be62469b22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 0 deletions

View File

@ -0,0 +1,31 @@
from enum import IntEnum
class EfiVariableAttributes(IntEnum):
NonVolatile = 0x01
BootServiceAccess = 0x02
RuntimeAccess = 0x04
HardwareErrorRecord = 0x08
AuthenticatedWriteAccess = 0x10
TimeBasedAuthenticatedWriteAccess = 0x20
AppendWrite = 0x40
EnhancedAuthenticatedAccess = 0x80
class EfiVariable:
"""
An EFI variable represented by its attributes and raw value in bytes.
Generally, the value is not encoded in UTF-8, but UCS-2 or UTF-16-LE.
"""
attributes: EfiVariableAttributes
value: bytes
def __init__(self, value: bytes, attributes: bytes):
self.value = value
self.attributes = EfiVariableAttributes(attributes)
def value_as_null_terminated_string(self, encoding: str = 'utf-16-le'):
"""
Most often, variables are encoded with a null-terminated \x00.
This function gives you the string in a default encoding of UTF-16-LE
stripped of the null terminator.
"""
return self.value.decode(encoding).rstrip('\x00')

View File

@ -17,6 +17,7 @@ import tempfile
import threading
import time
from test_driver.efi import EfiVariable
from test_driver.logger import rootlog
CHAR_TO_KEY = {
@ -1019,6 +1020,33 @@ class Machine:
"""
self.send_monitor_command(f"hostfwd_add tcp::{host_port}-:{guest_port}")
def running_under_uefi(self) -> bool:
"""
Returns True if the current environment is running under UEFI, False otherwise.
This is achieved by inspecting by the existence of /sys/firmware/efi.
"""
rc, _ = self.execute("test -d /sys/firmware/efi")
return (rc == 0)
def read_efi_variable_from_sysfs(self, guid: str, variable_name: str) -> EfiVariable | None:
"""
Read an EFI variable located in efivars sysfs if available.
Returns None if the EFI variable does not exist.
Raises an assertion error if we are not running under an UEFI environment.
"""
assert self.running_under_uefi(), "This machine is not detected under an UEFI environment"
rc, raw_attributes_and_value = self.execute(f"base64 /sys/firmware/efi/efivars/{variable_name}-{guid}")
if rc != 0: return None
# The return value is a string which is in reality a disguised bytes, we re-encode it properly
# using raw_unicode_escape keeping all the escapes properly.
# This is not guaranteed to work for all the cases but is good enough for UTF-8/UTF-16 usecases.
attributes_and_value = base64.b64decode(raw_attributes_and_value)
# First 4 bytes are attributes: https://www.kernel.org/doc/html/latest/filesystems/efivarfs.html
attributes = attributes_and_value[:4]
value = attributes_and_value[4:]
return EfiVariable(value=value, attributes=attributes)
def block(self) -> None:
"""Make the machine unreachable by shutting down eth1 (the multicast
interface used to talk to the other VMs). We keep eth0 up so that