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:
commit
be62469b22
31
nixos/lib/test-driver/test_driver/efi.py
Normal file
31
nixos/lib/test-driver/test_driver/efi.py
Normal 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')
|
@ -17,6 +17,7 @@ import tempfile
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from test_driver.efi import EfiVariable
|
||||||
from test_driver.logger import rootlog
|
from test_driver.logger import rootlog
|
||||||
|
|
||||||
CHAR_TO_KEY = {
|
CHAR_TO_KEY = {
|
||||||
@ -1019,6 +1020,33 @@ class Machine:
|
|||||||
"""
|
"""
|
||||||
self.send_monitor_command(f"hostfwd_add tcp::{host_port}-:{guest_port}")
|
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:
|
def block(self) -> None:
|
||||||
"""Make the machine unreachable by shutting down eth1 (the multicast
|
"""Make the machine unreachable by shutting down eth1 (the multicast
|
||||||
interface used to talk to the other VMs). We keep eth0 up so that
|
interface used to talk to the other VMs). We keep eth0 up so that
|
||||||
|
Loading…
Reference in New Issue
Block a user