summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaito Bezarius <masterancpp@gmail.com>2023-04-30 03:02:11 +0200
committerRaito Bezarius <masterancpp@gmail.com>2023-04-30 03:02:14 +0200
commit6d840bcb8bde8b5a20f1ccd6b55d01e4d8daf2e2 (patch)
tree98575203fe6819d9ca06ddf69f26cdeafef396ae
parentMerge pull request #228743 from fabaff/userpath-bump (diff)
downloadnixpkgs-origin/qemu-vm/read-efi-var.tar.gz
nixos/lib/test-driver: enable EFI variable reads at runtimeorigin/qemu-vm/read-efi-var
This is useful whenever you want to diagnose the current state of UEFI variables, to assert that bootloaders or boot programs (systemd-stub) did their job correctly and set their variables accordingly. In the future, it can enable inspecting SecureBoot keys also.
-rw-r--r--nixos/lib/test-driver/test_driver/efi.py31
-rw-r--r--nixos/lib/test-driver/test_driver/machine.py28
2 files changed, 59 insertions, 0 deletions
diff --git a/nixos/lib/test-driver/test_driver/efi.py b/nixos/lib/test-driver/test_driver/efi.py
new file mode 100644
index 000000000000..05d5d85e3958
--- /dev/null
+++ b/nixos/lib/test-driver/test_driver/efi.py
@@ -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')
diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py
index 9de98c217a58..b647011158a8 100644
--- a/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixos/lib/test-driver/test_driver/machine.py
@@ -16,6 +16,7 @@ import tempfile
import threading
import time
+from test_driver.efi import EfiVariable
from test_driver.logger import rootlog
CHAR_TO_KEY = {
@@ -1003,6 +1004,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"cat /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 = raw_attributes_and_value.encode('raw_unicode_escape')
+ # 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