Skip to content

Commit

Permalink
Merge 521d59a into 3d2bcc4
Browse files Browse the repository at this point in the history
  • Loading branch information
os-d authored May 22, 2024
2 parents 3d2bcc4 + 521d59a commit d9928ba
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 42 deletions.
5 changes: 4 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@
"profilenames",
"profileid",
"profileids",
"prettyname"
"prettyname",
"efivars",
"efivarfs",
"chattr"
]
}
5 changes: 4 additions & 1 deletion SetupDataPkg/SetupDataPkg.ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@
"dmpstore",
"mschange",
"DDTHH",
"prettyname"
"prettyname",
"efivars",
"efivarfs",
"chattr"
], # words to extend to the dictionary for this package
"IgnoreStandardPaths": [], # Standard Plugin defined paths that should be ignore
"AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported)
Expand Down
20 changes: 14 additions & 6 deletions SetupDataPkg/Tools/ReadUefiVarsToConfVarList.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import struct
import uuid
import ctypes
from SettingSupport.UefiVariablesSupportLib import UefiVariable
if os.name == 'nt':
from SettingSupport.UefiVariablesSupportLib import UefiVariable
else:
from SettingSupport.UefiVariablesSupportLinuxLib import UefiVariable
from VariableList import Schema, UEFIVariable, create_vlist_buffer


Expand Down Expand Up @@ -53,7 +56,7 @@ def option_parser():
# array
#
def read_variable_into_variable_list(uefi_var, name, namespace):
(rc, var, _) = uefi_var.GetUefiVar(name, namespace)
(rc, var) = uefi_var.GetUefiVar(name, namespace)
if rc != 0:
if rc != UefiVariable.ERROR_ENVVAR_NOT_FOUND:
# only log the errors other than EFI_NOT_FOUND, because not found is normal in this case...
Expand All @@ -78,7 +81,7 @@ def main():
ret = b''
if arguments.configuration_file is None:
# Read all the variables
(rc, efi_var_names, error_string) = UefiVar.GetUefiAllVarNames()
(rc, efi_var_names) = UefiVar.GetUefiAllVarNames()
if rc != 0:
logging.error(f"Error returned from GetUefiAllVarNames: {rc}")

Expand Down Expand Up @@ -117,9 +120,14 @@ def main():
console.setLevel(logging.CRITICAL)

# check the privilege level and report error
if not ctypes.windll.shell32.IsUserAnAdmin():
print("Administrator privilege required. Please launch from an Administrator privilege level.")
sys.exit(1)
if os.name == 'nt':
if not ctypes.windll.shell32.IsUserAnAdmin():
print("Administrator privilege required. Please launch from an Administrator privilege level.")
sys.exit(1)
else:
if os.geteuid() != 0:
print("Root permission required, please run script with sudo.")
sys.exit(1)

# call main worker function
retcode = main()
Expand Down
34 changes: 5 additions & 29 deletions SetupDataPkg/Tools/SettingSupport/UefiVariablesSupportLib.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
c_wchar_p,
c_void_p,
c_int,
c_char,
create_string_buffer,
WinError,
pointer
Expand Down Expand Up @@ -90,27 +89,6 @@ def __init__(self):
)
pass

#
# Helper function to create buffer for var read/write
#
def CreateBuffer(self, init, size=None):
"""CreateBuffer(aString) -> character array
CreateBuffer(anInteger) -> character array
CreateBuffer(aString, anInteger) -> character array
"""
if isinstance(init, str):
if size is None:
size = len(init) + 1
buftype = c_char * size
buf = buftype()
buf.value = init
return buf
elif isinstance(init, int):
buftype = c_char * init
buf = buftype()
return buf
raise TypeError(init)

#
# Function to get variable
# return a tuple of error code and variable data as string
Expand All @@ -135,8 +113,8 @@ def GetUefiVar(self, name, guid):
)
logging.error(WinError())
if efi_var is None:
return (err, None, WinError(err))
return (err, efi_var[:length], WinError(err))
return (err, None)
return (err, efi_var[:length])

#
# Function to get all variable names
Expand Down Expand Up @@ -176,8 +154,8 @@ def GetUefiAllVarNames(self):
logging.error(
"EnumerateFirmwareEnvironmentVariable failed (GetLastError = 0x%x)" % status
)
return (status, None, WinError(status))
return (status, efi_var_names, None)
return (status, None)
return (status, efi_var_names)

#
# Function to set variable
Expand All @@ -186,7 +164,6 @@ def GetUefiAllVarNames(self):
def SetUefiVar(self, name, guid, var=None, attrs=None):
var_len = 0
err = 0
error_string = None
if var is None:
var = bytes(0)
else:
Expand Down Expand Up @@ -221,5 +198,4 @@ def SetUefiVar(self, name, guid, var=None, attrs=None):
"SetFirmwareEnvironmentVariable failed (GetLastError = 0x%x)" % err
)
logging.error(WinError())
error_string = WinError(err)
return (success, err, error_string)
return success
143 changes: 143 additions & 0 deletions SetupDataPkg/Tools/SettingSupport/UefiVariablesSupportLinuxLib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# @file
#
# Python lib to support Reading and writing UEFI variables from Linux
#
#
# Copyright (c), Microsoft Corporation
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
# GetUefiAllVarNames is based on information from
# https://github.com/awslabs/python-uefivars/blob/main/pyuefivars/efivarfs.py
#
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT

import os
import uuid
import struct

from ctypes import (
create_string_buffer
)

EFI_VAR_MAX_BUFFER_SIZE = 1024 * 1024


class UefiVariable(object):
ERROR_ENVVAR_NOT_FOUND = 0xcb

def __init__(self):
pass

#
# Function to get variable
# return a tuple of error code and variable data as string
#
def GetUefiVar(self, name, guid):
# success
err = 0
# the variable name is VariableName-Guid
path = '/sys/firmware/efi/efivars/' + name + '-%s' % guid

if not os.path.exists(path):
err = UefiVariable.ERROR_ENVVAR_NOT_FOUND
return (err, None)

efi_var = create_string_buffer(EFI_VAR_MAX_BUFFER_SIZE)
with open(path, 'rb') as fd:
efi_var = fd.read()

return (err, efi_var)

#
# Function to get all variable names
# return a tuple of error code and variable names byte array formatted as:
#
# typedef struct _VARIABLE_NAME {
# ULONG NextEntryOffset;
# GUID VendorGuid;
# WCHAR Name[ANYSIZE_ARRAY];
# } VARIABLE_NAME, *PVARIABLE_NAME;
#
def GetUefiAllVarNames(self):
# success
status = 0

# implementation borrowed from https://github.com/awslabs/python-uefivars/blob/main/pyuefivars/efivarfs.py
path = '/sys/firmware/efi/efivars'
if not os.path.exists(path):
status = UefiVariable.ERROR_ENVVAR_NOT_FOUND
return (status, None)

vars = os.listdir(path)

# get the total buffer length, converting to unicode
length = 0
offset = 0
for var in vars:
split_string = var.split('-')
name = '-'.join(split_string[:-5])
name = name.encode('utf-16-le')
name_len = len(name)
length += (4 + 16 + name_len)

efi_var_names = create_string_buffer(length)

for var in vars:
# efivarfs stores vars as NAME-GUID
split_string = var.split('-')
try:
# GUID is last 5 elements of split_string
guid = uuid.UUID('-'.join(split_string[-5:])).bytes_le
except ValueError:
raise Exception(f'Could not parse "{var}"')

# the other part is the name
name = '-'.join(split_string[:-5])
name = name.encode('utf-16-le')
name_len = len(name)

# NextEntryOffset
struct.pack_into('<I', efi_var_names, offset, 4 + 16 + name_len)
offset += 4

# VendorGuid
struct.pack_into('=16s', efi_var_names, offset, guid)
offset += 16

# Name
struct.pack_into(f'={name_len}s', efi_var_names, offset, name)
offset += name_len

return (status, efi_var_names)

#
# Function to set variable
# return a tuple of boolean status, error_code, error_string (None if not error)
#
def SetUefiVar(self, name, guid, var=None, attrs=None):
success = 0 # Fail

# There is a null terminator at the end of the name
path = '/sys/firmware/efi/efivars/' + name[:-1] + '-' + str(guid)
if var is None:
# we are deleting the variable
if (os.path.exists(path)):
os.remove(path)
success = 1 # expect non-zero success
return success

if attrs is None:
attrs = 0x7

# if the file exists, remove the immutable flag
if (os.path.exists(path)):
os.system('sudo chattr -i ' + path)

with open(path, 'wb') as fd:
# var data is attribute (UINT32) followed by data
packed = struct.pack('=I', attrs)
packed += var
fd.write(packed)

return 1
18 changes: 13 additions & 5 deletions SetupDataPkg/Tools/WriteConfVarListToUefiVars.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import struct
import uuid
import ctypes
from SettingSupport.UefiVariablesSupportLib import UefiVariable
if os.name == 'nt':
from SettingSupport.UefiVariablesSupportLib import UefiVariable
else:
from SettingSupport.UefiVariablesSupportLinuxLib import UefiVariable

gEfiGlobalVariableGuid = "8BE4DF61-93CA-11D2-AA0D-00E098032B8C"

Expand Down Expand Up @@ -93,7 +96,7 @@ def extract_single_var_from_file_and_write_nvram(var):
logging.debug(f"Found Variable: {VarName} {Guid} {Attributes}")

UefiVar = UefiVariable()
(rc, err, error_string) = UefiVar.SetUefiVar(
rc = UefiVar.SetUefiVar(
VarName,
Guid,
Data,
Expand Down Expand Up @@ -136,9 +139,14 @@ def main():
console.setLevel(logging.CRITICAL)

# check the privilege level and report error
if not ctypes.windll.shell32.IsUserAnAdmin():
print("Administrator privilege required. Please launch from an Administrator privilege level.")
sys.exit(1)
if os.name == 'nt':
if not ctypes.windll.shell32.IsUserAnAdmin():
print("Administrator privilege required. Please launch from an Administrator privilege level.")
sys.exit(1)
else:
if os.geteuid() != 0:
print("Root permission required, please run script with sudo.")
sys.exit(1)

# call main worker function
retcode = main()
Expand Down

0 comments on commit d9928ba

Please sign in to comment.