Skip to content

Instantly share code, notes, and snippets.

@joevt
Last active October 9, 2023 16:15
Show Gist options
  • Save joevt/477fe842d16095c2bfd839e2ab4794ff to your computer and use it in GitHub Desktop.
Save joevt/477fe842d16095c2bfd839e2ab4794ff to your computer and use it in GitHub Desktop.
macOS nvram boot variables, device properties, EFI device paths
#!/bin/bash
# joevt Jun 13, 2023
# https://forums.macrumors.com/threads/documentation-on-all-parameters-for-nvram.2239034/post-28518123
gfxutilrepository="/Volumes/Work/Programming/EFIProjects/gfxutil/joevt-gfxutil"
for gfxutilcmd in \
~/Downloads/gfxutil/gfxutil \
~/Downloads/gfxutil \
"$gfxutilrepository/build/Release/gfxutil" \
"$gfxutilrepository/build/Debug/gfxutil" \
"$gfxutilrepository/DerivedData/gfxutil/Build/Products/Debug/gfxutil" \
"" \
; do
if [[ -f $gfxutilcmd ]] && file $gfxutilcmd | grep -q "$(uname -m)"; then
break
fi
done
if [[ ! -f "$gfxutilcmd" ]]; then
echo "# Download and build gfxutil from https://github.com/joevt/gfxutil , then update the path of gfxutil defined in gfxutil.sh"
fi
alias gfxutil='"$gfxutilcmd"'
directblesscmd="/Volumes/Work/Programming/XcodeProjects/bless/bless-204.40.27 joevt/DerivedData/bless/Build/Products/Debug/bless"
usedirectbless=0
if [[ -d /System/Library/PrivateFrameworks/APFS.framework/Versions/A ]]; then
if [[ ! -f "$directblesscmd" ]]; then
echo "# Download and build bless from https://github.com/joevt/bless , then update the path of directbless defined in gfxutil.sh"
else
usedirectbless=1
fi
fi
if ((usedirectbless)); then
directbless=$directblesscmd
alias directbless='"$directbless"'
else
directbless=bless
alias directbless=bless
fi
pipestatus=( ) # to clear an error in ShellCheck
patmatch () {
# substitute for [[ =~ ]] for Mac OS X 10.4
perl -0777 -ne '<>; exit !( $_ =~ /'"$1"'/ )'
}
nvramp () {
local thename="$1"
local thedata="" # must declare local separately for $? to get the error
thedata="$(nvram "$thename")"
local theerr=$?
# we want to use variables in the printf format string
printf "$(sed -E '/^'"$thename"'./s///;s/\\/\\\\/g;s/%([0-9a-f]{2})/\\x\1/g;s/%/%%/g' <<< "$thedata")"
return $theerr
}
efiguid=8BE4DF61-93CA-11D2-AA0D-00E098032B8C
ocguid=4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102
dumponebootvar () {
local pathonly=0
if [[ $1 == '-p' ]]; then
pathonly=1
shift
fi
local thebootvar="$1"
local thebytes="$2"
# typedef struct _EFI_LOAD_OPTION
local theerr=""
local theAttributes=$((0x${thebytes:6:2}${thebytes:4:2}${thebytes:2:2}${thebytes:0:2}))
# 0x00000001 LOAD_OPTION_ACTIVE
# 0x00000002 LOAD_OPTION_FORCE_RECONNECT
# 0x00000008 LOAD_OPTION_HIDDEN
# 0x00001F00 LOAD_OPTION_CATEGORY
# 0x00000000 LOAD_OPTION_CATEGORY_BOOT
# 0x00000100 LOAD_OPTION_CATEGORY_APP
local theFilePathListLength=$((0x${thebytes:10:2}${thebytes:8:2}))
local theDescription=""
theDescription=$(xxd -p -r <<< "${thebytes:12}" | iconv -f UTF-16LE -t UTF-8 | tr '\0' '\n' | sed -n -E '1p' | tr -d '\n')
local theoffset=$(( 6 + (${#theDescription}+1) * 2 ))
local theFilePathList=${thebytes:$theoffset * 2:$theFilePathListLength*2}
((theoffset += theFilePathListLength))
local theOptionalData=${thebytes:$theoffset * 2}
local theOptionalDatastring=""
theOptionalDatastring=$(xxd -p -r <<< "${theOptionalData}" | iconv -f UTF-16LE -t UTF-8 | tr '\0' '\n' | sed -n -E '1p' | tr -d '\n')
if (( pathonly )); then
echo "$theFilePathList"
else
printf "%s %s \"%s\"" "$thebootvar" "$theAttributes" "$theDescription"
local parts=0
while [[ -n $theFilePathList ]]; do
(( parts++ ))
local thepath=""
local pathbytes=""
thepath=$(gfxutil "$theFilePathList")
theerr=$?
if (( theerr )); then
(( parts == 1 )) && printf " "
printf "\"%s\"" "$theFilePathList"
theFilePathList=""
theerr=0
else
(( parts == 1 )) && printf " "
printf "\"%s\"" "$thepath"
pathbytes=$(gfxutil "$thepath")
if patmatch "$pathbytes" <<< "$theFilePathList"; then
theFilePathList=${theFilePathList:${#pathbytes}}
else
printf " # Device path %s does not match %s" "$pathbytes" "$theFilePathList"
theFilePathList=""
fi
fi
done
[[ -n $theOptionalData ]] && printf " \"%s\"" "$theOptionalDatastring"
echo
[[ -n $theOptionalData ]] && {
printf "%s\n" "$theOptionalData" | xxd -p -r | xxd -o "$theoffset" -g $((${#theOptionalData}/2)) -c $((${#theOptionalData}/2)) | perl -pe "s/^([0-9A-Fa-f]+: )([0-9A-Fa-f]+) (.*)/ \1\2\n \1\3/"
}
fi
return "$theerr"
}
bootvar () {
local pathonly=""
for ((;;)); do
if [[ $1 == '-p' ]]; then
pathonly="-p"
shift
else
break
fi
done
local thebootvar="$1"
local theguid=$efiguid
if patmatch '^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}:' <<< "$thebootvar"; then
theguid=${thebootvar:0:36}
thebootvar=${thebootvar:37}
fi
local thebytes=""
thebytes="$(nvramp "$theguid:$thebootvar" | xxd -p -c 99999; echo "${pipestatus[1]}${PIPESTATUS[0]}")"
local theerr=""
theerr=$(sed -n \$p <<< "$thebytes")
if (( ! theerr )); then
thebytes=$(sed \$d <<< "$thebytes")
eval 'dumponebootvar '"$pathonly"' "'"$thebootvar"'" '"$thebytes"
theerr=$?
fi
return "$theerr"
}
setnvramhex () {
sudo nvram "$1=$(sed -E 's/(..)/%\1/g' <<< "${2}")"
}
setbootvar () {
local thebootvar=$1
local theAttributes=$2
local theDescription=$3
local theFilePathList=$4
local theOptionalData=$5
local theAttributesBytes=""
theAttributesBytes=$(printf "%08x" "$theAttributes")
local theDescriptionBytes=""
theDescriptionBytes=$(printf "%s\0" "$theDescription" | iconv -f UTF-8 -t UTF-16LE | xxd -p -c 999999)
local theFilePathListBytes=""
local thepat='^([a-z0-9]{2})*7fff0400$'
if patmatch "$thepat" <<< "$theFilePathList"; then
theFilePathListBytes="$theFilePathList"
elif [[ -e $theFilePathList ]]; then
theFilePathListBytes=$(getefipath "$theFilePathList")
else
theFilePathListBytes=$(gfxutil "$theFilePathList")
fi
local theFilePathListLength=$((${#theFilePathListBytes} / 2))
local theFilePathListLengthBytes=0
theFilePathListLengthBytes=$(printf "%04x" $theFilePathListLength)
local thebytes="${theAttributesBytes:6:2}${theAttributesBytes:4:2}${theAttributesBytes:2:2}${theAttributesBytes:0:2}${theFilePathListLengthBytes:2:2}${theFilePathListLengthBytes:0:2}${theDescriptionBytes}${theFilePathListBytes}${theOptionalData}"
local theguid=$efiguid
if patmatch '^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}:' <<< "$thebootvar"; then
theguid=${thebootvar:0:36}
thebootvar=${thebootvar:38}
fi
setnvramhex "${theguid}:${thebootvar}" "${thebytes}"
}
setbootorder () {
IFS=''
local theguid=$efiguid
local thestring="$*"
sudo nvram "${theguid}:BootOrder=$(sed -E "s/[Bb]oot//g;s/(..)(..)/%\2%\1/g" <<< "$thestring")"
}
setdriverorder () {
IFS=''
local thestring="$*"
sudo nvram "${efiguid}:DriverOrder=$(sed -E "s/[Dd]river//g;s/(..)(..)/%\2%\1/g" <<< "$thestring")"
}
dumpallbootvars () {
for theguid in $efiguid; do
local theboot=""
for theboot in Current Next; do
local BootWhat=""
BootWhat=$(nvramp "$theguid:Boot$theboot" 2> /dev/null | xxd -u -p -c 99999 | sed -E 's/(..)(..)/Boot\2\1/g')
echo "Boot$theboot: $BootWhat"
done
local Timeout=""
Timeout=$((0x$(nvramp "$theguid:Timeout" 2> /dev/null | xxd -u -p -c 99999 | sed -E 's/(..)(..)/\2\1/g')))
echo "Timeout: ${Timeout}s"
echo
local needlinefeed=0
local theType=""
for theType in Boot Driver; do
local BootOrder=""
BootOrder=$(nvramp "$theguid:${theType}Order" 2> /dev/null | xxd -u -p -c 99999 | sed -E 's/(..)(..)/'"${theType}"'\2\1 /g;/ $/s///')
echo "${theType}Order: $BootOrder"
IFS=$' '
for theboot in $(printf "%s" "$BootOrder"); do
bootvar "${theguid}:${theboot}" 2> /dev/null
done
#echo "Search loop"
IFS=$'\n'
local lowboot=-1
local boot=""
for boot in $( {
eval "$(nvramp "$theguid:${theType}Order" 2> /dev/null | xxd -u -p -c 99999 | sed -E 's/(..)(..)/echo ''$''((0x\2\1 + 1)):1;echo ''$''((0x\2\1 - 1)):-1;/g')"; echo 0:1; echo 127:-1; echo 128:1; echo $((0xFFFF)):-1
} | sort -u -t : -k 1n,2n
) ; do
#echo "checking range $boot"
local inc="${boot#*:}"
local boot=$((${boot%:*}))
local first=1
while ((1)); do
#echo " checking boot:$boot inc:$inc lowboot:$lowboot"
thebootvar=${theType}$(printf "%04X" $boot)
[[ $BootOrder != *"$thebootvar"* ]] || break
((boot > lowboot)) || break
((inc > 0)) && ((lowboot = boot))
if ((first)); then
if ((needlinefeed)); then
printf ", "
else
printf "#Searching: "
fi
printf "%s" "$thebootvar($inc)"
needlinefeed=1
first=0
fi
local bootinfo=""
bootinfo="$(bootvar "${theguid}:${thebootvar}" 2> /dev/null)"
local theerr=$?
if ((theerr)); then
break
fi
if ((needlinefeed)); then
echo
needlinefeed=0
fi
printf "%s\n" "$bootinfo"
((boot+=inc))
done
((inc < 0)) && ((lowboot = boot))
done
if ((needlinefeed)); then
echo
needlinefeed=0
fi
echo
done
done
}
getefipath () {
# Takes a path to a file or directory or volume and outputs the EFI device path in hex.
# Note: Overwrites BootNext
local thefile="$1"
local theguid=$efiguid
# First, get current boot vars
local BootOrderValue=""
BootOrderValue=$(nvramp $theguid:BootOrder 2> /dev/null | xxd -u -p -c 99999)
local BootOrder=""
BootOrder=$(sed -E 's/(..)(..)/Boot\2\1 /g;/ $/s///' <<< "$BootOrderValue")
eval "$(sed -E 's/(..)(..)/local Boot\2\1=''$''(nvramp '"$theguid"':Boot\2\1 2> \/dev\/null | xxd -u -p -c 99999) ; /g' <<< "$BootOrderValue")"
# We won't try to preserve BootNext - that would require preserving efi-boot-next-data
#local BootNextValue=""
#local BootNextName=""
#BootNextValue=$(nvramp $theguid:BootNext 2> /dev/null | xxd -u -p -c 99999)
#BootNextName="Boot${BootNextValue:2:2}${BootNextValue:0:2}"
# Use bless to convert file path to EFI device path - this affects BootNext and one of the boot vars
if ( sudo "$directbless" --mount "$thefile" --file "$thefile" --nextonly --setBoot ); then
local thebootvar=""
thebootvar=$(nvramp $theguid:BootNext 2> /dev/null | xxd -u -p -c 99999 | sed -E 's/(..)(..)/Boot\2\1/g')
local thepath=""
thepath=$(bootvar -p "${theguid}:${thebootvar}" 2> /dev/null)
local theerr=$?
if (( theerr == 0 )); then
# if one of the existing boot vars was affected, then restore it
if patmatch "$thebootvar" <<< "$BootOrder"; then
setnvramhex "$theguid:$thebootvar" "$(eval 'echo $'"${thebootvar}")"
fi
# output the result
printf "%s" "$thepath"
else
echo "# BootNext:$thebootvar not set" 1>&2
return 1
fi
# cleanup BootNext
sudo nvram -d $theguid:BootNext
sudo nvram -d efi-boot-next-data
else
echo '# This bless command failed:' 1>&2
echo '# sudo "'"$directbless"'" --mount "'"$thefile"'" --file "'"$thefile"'" --nextonly --setBoot' 1>&2
return 1
fi
return 0
}
dumpallioregefipaths () {
eval "$(
(ioreg -lw0 -p IODeviceTree; ioreg -lw0) | perl -e '
$thepath=""; while (<>) {
if ( /^([ |]*)\+\-o (.+) </ ) { $indent = (length $1) / 2; $name = $2; $thepath =~ s|^((/[^/]*){$indent}).*|$1/$name| }
if ( /^[ |]*"([^"]+)" = <(.*7fff0400.*)>/i ) { print $thepath . "/" . $1 . " = <" . $2 . ">\n" }
}
' | sed -E '/device-properties/d;/(.*) = <(.*)>/s//printf "%s = " "\1"; gfxutil \2 | cat; echo/'
)"
}
ioregp () {
ioreg -n "$2" -w0 -p "$1" -k "$3" | sed -nE 's/^[ |]+"'"$3"'" = <(.*)>/\1/p' | xxd -p -r
}
getdeviceprops () {
ioreg -rw0 -p IODeviceTree -n efi | grep device-properties | sed 's/.*<//;s/>.*//;' | xxd -p -r
}
convertnvramstring () {
local thevar="$1"
local thevar="$2"
# we want to use variables in the printf format string
printf "$(printf "%s" "$theval" | sed -E '/^'"$thevar"'./s///;s/\\/\\\\/g;s/%/\\x/g')"
}
getaaplpathprops () {
# Get device properties from nvram AAPL,PathProperties0000,0001,etc.
# (max 768 per nvram var)
i=0
while (( 1 )); do
thevar="4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:AAPL,PathProperties$(printf "%04X" $i)"
theval="$(nvram "$thevar" 2> /dev/null)"
[[ -z $theval ]] && break
convertnvramstring "$thevar" "$theval"
((i++))
done
}
setaaplpathprops () {
local thefile="$1"
local theproperties=""
theproperties=$(xxd -p -c 99999 "$1")
local thevar=0
while ((1)); do
local thepart=${theproperties:$thevar*768*2:768*2}
local thename=""
thename="4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:AAPL,PathProperties"$(printf "%04X" thevar)
if [[ -n $thepart ]]; then
sudo nvram "${thename}=$(sed -E 's/(..)/%\1/g' <<< "${thepart}")"
elif nvram "${thename}" > /dev/null 2>&1; then
sudo nvram -d "$thename"
else
break
fi
((thevar++))
done
}
getpanic () {
# Get panic log from nvram AAPL,PanicInfo000K,000M,etc.
# (max 768 per nvram var)
i=0
while (( 1 )); do
thevar="AAPL,PanicInfo000$(printf "%02x" $((0x$(printf 'K' | xxd -p) + i)) | xxd -p -r)"
theval="$(nvram "$thevar" 2> /dev/null)"
[[ -z $theval ]] && break
convertnvramstring "$thevar" "$theval"
((i++))
done
}
getpanic2 () {
# panic log from nvram aapl,panic-info
# (max 768 per nvram var)
i=0
while (( 1 )); do
thevar="aapl,panic-info"
theval="$(nvram "$thevar" 2> /dev/null)"
[[ -z $theval ]] && break
convertnvramstring "$thevar" "$theval"
((i++))
break
done
}
testbit () {
local isset=$1
local fbits=$(($2))
local fmask=$(($3))
local fbit=$(($4))
local fname=$5
if [[ $isset = "printset" ]]; then
if ((fbit & fbits)); then
printf "%s" "$fname"
if ((!(fbit & fmask))); then
printf "(error: mask is 0)"
fi
printf "\n"
fi
elif [[ $isset = "printunset" ]]; then
if ((!(fbit & fbits))); then
if ((fbit & fmask)); then
printf "not %s" "$fname"
printf "\n"
fi
fi
elif [[ $isset = "printignored" ]]; then
if ((!(fbit & fmask))); then
printf "ignore %s" "$fname"
printf "\n"
fi
fi
}
GetNum64 () {
local thenum="$1"
printf "%s" $(( (0x${thenum:0:1} << 60) | 0x${thenum:1} ))
}
binary () {
local thenum="$1" # input decimal number
local numbits="$2"
local binary=""
binary=$(bc <<< "obase=2;ibase=10;$thenum")
printf "%${numbits}s" "${binary}" | tr ' ' '0'
}
parseflags () {
local fbits=$(($1))
local fmask=$(($2))
local numbits=$(($3))
local numhex=$(((numbits + 3) / 4))
printf "=========================================================================\n"
if ((numbits > 32)); then
echo ExtendedFirmwareFeatures
else
echo FirmwareFeatures
fi
printf "features:%0${numhex}X %s\n" $fbits "$(binary $fbits $numbits)"
if [[ -z $2 ]]; then
fmask=$fbits
else
printf " mask:%0${numhex}X %s\n" $fmask "$(binary "$(printf "%u" $fmask)" $numbits)"
fi
local isset=""
for isset in printset printunset printignored; do
#https://github.com/acidanthera/OpenCorePkg/blob/master/Include/Apple/IndustryStandard/AppleFeatures.h
testbit $isset $fbits $fmask 0x00000001 SUPPORTS_CSM_LEGACY_MODE
testbit $isset $fbits $fmask 0x00000002 SUPPORTS_CD_DRIVE_BOOT
testbit $isset $fbits $fmask 0x00000004 SUPPORTS_TARGET_DISK_MODE
testbit $isset $fbits $fmask 0x00000008 UNKNOWN_BIT3
testbit $isset $fbits $fmask 0x00000010 SUPPORTS_NET_BOOT
testbit $isset $fbits $fmask 0x00000020 SUPPORTS_SLING_SHOT
testbit $isset $fbits $fmask 0x00000040 UNKNOWN_BIT6
testbit $isset $fbits $fmask 0x00000080 UNKNOWN_BIT7
testbit $isset $fbits $fmask 0x00000100 SUPPORTS_WIRELESS
testbit $isset $fbits $fmask 0x00000200 UNKNOWN_BIT9
testbit $isset $fbits $fmask 0x00000400 PLATFORM_SECURITY_POLICY_01
testbit $isset $fbits $fmask 0x00000800 PLATFORM_SECURITY_POLICY_02
testbit $isset $fbits $fmask 0x00001000 SUPPORTS_TRB
testbit $isset $fbits $fmask 0x00002000 UNKNOWN_BIT13
testbit $isset $fbits $fmask 0x00004000 SUPPORTS_HIGH_SPEED_USB
testbit $isset $fbits $fmask 0x00008000 UNKNOWN_BIT15
testbit $isset $fbits $fmask 0x00010000 UNKNOWN_BIT16
testbit $isset $fbits $fmask 0x00020000 DISABLE_USB_SUBSTITUTE_WORKAROUND
testbit $isset $fbits $fmask 0x00040000 UNKNOWN_BIT18
testbit $isset $fbits $fmask 0x00080000 SUPPORTS_APFS
testbit $isset $fbits $fmask 0x00100000 SUPPORTS_APFS_EXTRA
testbit $isset $fbits $fmask 0x00200000 UNKNOWN_BIT21
testbit $isset $fbits $fmask 0x00400000 SUPPORTS_TRBX
testbit $isset $fbits $fmask 0x00800000 UNKNOWN_BIT23
testbit $isset $fbits $fmask 0x01000000 SUPPORTS_PLATFORM_SECURITY_POLICY
testbit $isset $fbits $fmask 0x02000000 SUPPORTS_EXTENDED_FEATURES
testbit $isset $fbits $fmask 0x04000000 UNKNOWN_BIT26
testbit $isset $fbits $fmask 0x08000000 UNKNOWN_BIT27
testbit $isset $fbits $fmask 0x10000000 DISABLE_MBA_S4_WORKAROUND
testbit $isset $fbits $fmask 0x20000000 SUPPORTS_UEFI_WINDOWS_BOOT
testbit $isset $fbits $fmask 0x40000000 UNKNOWN_BIT30
testbit $isset $fbits $fmask 0x80000000 DISABLE_BOOTSCRIPT_WORKAROUND
testbit $isset $fbits $fmask 0x800000000 SUPPORTS_LARGE_BASESYSTEM
done
}
showfirmwarefeatures () {
local ExtendedFirmwareFeatures=""
ExtendedFirmwareFeatures=$(GetNum64 "$(nvramp 4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:ExtendedFirmwareFeatures 2> /dev/null | xxd -g 8 -e | sed -E '/^[^:]+: +([^ ]+).*/s//\1/')")
local ExtendedFirmwareFeaturesMask=""
ExtendedFirmwareFeaturesMask=$(GetNum64 "$(nvramp 4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:ExtendedFirmwareFeaturesMask 2> /dev/null | xxd -g 8 -e | sed -E '/^[^:]+: +([^ ]+).*/s//\1/')")
if [[ -n $ExtendedFirmwareFeatures ]]; then
parseflags "$ExtendedFirmwareFeatures" "$ExtendedFirmwareFeaturesMask" 36
fi
local FirmwareFeatures=""
FirmwareFeatures=$(nvramp 4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:FirmwareFeatures 2> /dev/null | xxd -g 8 -e | sed -E '/^[^:]+: +([^ ]+).*/s//0x\1/')
local FirmwareFeaturesMask=""
FirmwareFeaturesMask=$(nvramp 4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:FirmwareFeaturesMask 2> /dev/null | xxd -g 8 -e | sed -E '/^[^:]+: +([^ ]+).*/s//0x\1/')
if [[ -n $FirmwareFeatures ]]; then
parseflags "$FirmwareFeatures" "$FirmwareFeaturesMask" 32
fi
}
@joevt
Copy link
Author

joevt commented Jan 29, 2021

Update 1:

  • Can now pass volume or directory or file path to setbootvar. See examples in first comment.

Jun 5, 2021:

  • getefipath now ensures that the boot order variables are not modified (but BootNext is still deleted).

Aug 22, 2021:

  • Sometimes a bootvar has multiple UEFI device paths so we'll try to list them all.
  • Use printf because echo might do weird stuff with some back-slashes (e.g. \b is backspace).

Oct 11, 2021:

  • Add SUPPORTS_LARGE_BASESYSTEM firmware features flag.
  • Check if gfxutil was downloaded to a folder.
  • Correctly output features and feature masks that are greater than 32 bit.
  • Output both features and extended features.

Dec 24, 2021:

  • Add some Mac OS X 10.4 compatibility.
  • Fix directbless check.
  • If gfxutil can't get a device path then at least dump the hex for the bootvar.

Mar 5, 2023:

  • bootvar and setbootvar can work with Boot#### variables from a specific guid instead of just the default EFI guid. For example, Open Core has its own copy of Boot#### variables which you can get using $ocguid
  • Use workaround GetNum64 to parse 64-bit unsigned numbers so that showfirmwarefeatures shows all 64 bits of the ExtendedFirmwareFeatures mask instead of just 60 bits. Don't use the shell to parse thenum in the binary method; let bc handle the unparsed value since it won't complain about overflow.

Jun 12, 2023:

  • dumponebootvar parses a hex string as a bootvar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment