Files
coreboot/capsule.sh
Wiktor Mowinski 05872a32d6 capsule.sh: name capsule consistently with ROMs built by build.sh
build.sh uses _ instead of - to separate components.

Change-Id: I06d11509c130eb1556b907e0d8393cd995d118c1
Upstream-Status: Inappropriate [Dasharo downstream]
Signed-off-by: Wiktor Mowinski <wiktor.mowinski@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
2025-08-26 01:16:09 +03:00

364 lines
9.9 KiB
Bash
Executable File

#!/bin/bash
set -e
edk_workspace=payloads/external/edk2/workspace
edk_basetools=${edk_workspace}/Dasharo/BaseTools
edk_tools=${edk_basetools}/BinWrappers/PosixLike
edk_scripts=${edk_basetools}/Scripts
function die() {
echo error: "$@" 1>&2
exit 1
}
function confirm() {
local msg=$1
read -r -n1 -p "$msg [y/N] "
echo
if [ "$REPLY" != y ]; then
die "operation cancelled by the user"
fi
}
function print_banner() {
local msg=$1
echo
echo ========== "${msg^^*}" ==========
echo
}
function info() {
echo info: "$@"
}
function print_usage() {
echo "Usage: $(basename "$0") subcommand [subcommand-args...]"
echo
echo 'Subcommands:'
echo ' box export standalone GenerateCapsule out of EDK2'
echo ' help print this message'
echo ' keygen use OpenSSL to auto-generate test keys suitable for signing'
echo ' positional argument: directory-path'
echo ' make build a capsule, options:'
echo ' -t root-certificate-file'
echo ' -o subroot-certificate-file'
echo ' -s signing-certificate-file'
echo ' -b (the flag adds battery check DXE into the capsule)'
}
function help_subcommand() {
print_usage
}
function keygen_subcommand() {
local dir=$1
if [ $# -ne 1 ]; then
echo "Usage: $(basename "$0") keygen keys-dir"
exit 1
fi
if [ -e "$dir" ]; then
confirm "OK to remove '$dir'?"
rm -r "$dir"
fi
mkdir "$dir"
cd "$dir"
# this is needed to make `openssl req` work non-interactively
cat > "openssl.cnf" << 'EOF'
.include /etc/ssl/openssl.cnf
[ CA_default ]
dir = ./test-ca
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
serial = $dir/serial
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
private_key = $dir/private/cakey.pem
[ req_root ]
prompt = no
distinguished_name = req_root_dn
x509_extensions = v3_ca
string_mask = utf8only
[ req_root_dn ]
countryName = XX
stateOrProvinceName = Province
organizationName = Org
commonName = root
[ req_sub ]
prompt = no
distinguished_name = req_sub_dn
x509_extensions = v3_ca
string_mask = utf8only
[ req_sub_dn ]
countryName = XX
stateOrProvinceName = Province
organizationName = Org
commonName = sub
[ req_sign ]
prompt = no
distinguished_name = req_sign_dn
x509_extensions = v3_ca
string_mask = utf8only
[ req_sign_dn ]
countryName = XX
stateOrProvinceName = Province
organizationName = Org
commonName = sign
EOF
print_banner 'Making root certificate'
# make root certificate
openssl genrsa -out root.p8e 2048
openssl req -config openssl.cnf -section req_root -new -x509 -days 3650 -key root.p8e -out root.pub.pem
# dump certificate information like `openssl ca` does for completeness
openssl x509 -in root.pub.pem -text -certopt no_sigdump,no_pubkey -nocert
# create a CA
mkdir -p test-ca/newcerts
touch test-ca/index.txt
echo 01 > test-ca/serial
openssl x509 -in root.pub.pem -out root.cer -outform DER
python "${OLDPWD}/${edk_scripts}/BinToPcd.py" \
-p gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr \
-i root.cer -x -o CapsuleRootKey.inc
print_banner 'Making subroot certificate'
# make subroot certificate
openssl genrsa -out sub.p8e 2048
openssl req -config openssl.cnf -section req_sub -new -key sub.p8e -out sub.csr
yes | openssl ca -config openssl.cnf -extensions v3_ca -in sub.csr -days 3650 -cert root.pub.pem -keyfile root.p8e -notext -out sub.pub.pem
print_banner 'Making signing certificate'
# make signing certificate
openssl genrsa -out sign.p8e 2048
openssl req -config openssl.cnf -section req_sign -new -key sign.p8e -out sign.csr
yes | openssl ca -config openssl.cnf -in sign.csr -days 3650 -cert sub.pub.pem -keyfile sub.p8e -notext -out sign.crt
# create binary PKCS12 (certificate + private key) from signing certificate
openssl pkcs12 -export -passout pass: -inkey sign.p8e -in sign.crt -out sign.pfx
# now convert binary PKCS12 into PEM PKCS12 with no password
openssl pkcs12 -in sign.pfx -passin pass: -nodes -out sign.p12
print_banner 'Usage examples'
echo "Installing root certificate (before build):"
echo " cp $dir/CapsuleRootKey.inc ${edk_workspace}/Dasharo/DasharoPayloadPkg/"
echo
echo "Signing a capsule (after build):"
echo " $0 make -t $dir/root.pub.pem -o $dir/sub.pub.pem -s $dir/sign.p12"
}
function check_cert() {
local name=$1
local path=$2
if [ -z "$path" ]; then
die "$name certificate wasn't provided"
fi
if [ ! -f "$path" ]; then
die "can't read $name certificate at '$path'"
fi
}
function make_subcommand() {
if [ ! -f .config ]; then
die "no '.config' file in current directory"
fi
if [ ! -f build/coreboot.rom ]; then
die "no 'build/coreboot.rom'; the firmware wasn't built?"
fi
if [ ! build/coreboot.rom -nt .config ]; then
die "'build/coreboot.rom' is not newer than .config'; need a re-build?"
fi
if [ ! -x "${edk_tools}/GenerateCapsule" ]; then
die "'${edk_tools}/GenerateCapsule' can't be executed"
fi
# import coreboot's config file replacing $(...) with ${...}
while read -r line; do
if ! eval "$line"; then
die "failed to source '.config'"
fi
done <<< "$(sed 's/\$(\([^)]\+\))/${\1}/g' .config)"
if [ "$CONFIG_DRIVERS_EFI_UPDATE_CAPSULES" != y ]; then
die "current board configuration lacks support of update capsules"
fi
# Option names match terminology of GenerateCapsule which conveniently start
# with different letters:
# * t - trusted
# * o - other
# * s - signer
local root_cert sub_cert sign_cert include_battery_check
while getopts "t:o:s:b" OPTION; do
case $OPTION in
t) root_cert="$OPTARG" ;;
o) sub_cert="$OPTARG" ;;
s) sign_cert="$OPTARG" ;;
b) include_battery_check=1 ;;
*) exit 1 ;;
esac
done
check_cert root "$root_cert"
check_cert sub "$sub_cert"
check_cert sign "$sign_cert"
local cap_file=${CONFIG_MAINBOARD_DIR//[\/-]/_}
if [[ ${CONFIG_MAINBOARD_PART_NUMBER} =~ DDR4 ]]; then
cap_file+=_ddr4
fi
cap_file+=_${CONFIG_LOCALVERSION}
cap_file+=.cap
if [ -e "$cap_file" ]; then
confirm "Overwrite already existing '$cap_file'?"
fi
local build_type
if [ "$CONFIG_EDK2_RELEASE" = y ]; then
build_type=RELEASE
else
build_type=DEBUG
fi
local json_file
json_file=$(mktemp --tmpdir --suffix -cap.json XXXXXXXX)
trap "$(printf 'rm -f %q' "$json_file")" EXIT
cat > "$json_file" << EOF
{
"EmbeddedDrivers": [
EOF
# Ensure the charger check driver module is first
if [ "$include_battery_check" = 1 ]; then
cat >> "$json_file" << EOF
{
"Driver": "${edk_workspace}/Build/DasharoPayloadPkgX64/${build_type}_COREBOOT/X64/CapsuleChargerCheckDxe.efi"
},
EOF
fi
cat >> "$json_file" << EOF
{
"Driver": "${edk_workspace}/Build/DasharoPayloadPkgX64/${build_type}_COREBOOT/X64/CapsuleSplashDxe.efi"
},
{
"Driver": "${edk_workspace}/Build/DasharoPayloadPkgX64/${build_type}_COREBOOT/X64/FmpDxe.efi"
}
],
"Payloads": [
{
"Payload": "build/coreboot.rom",
"Guid": "${CONFIG_DRIVERS_EFI_MAIN_FW_GUID}",
"FwVersion": "${CONFIG_DRIVERS_EFI_MAIN_FW_VERSION}",
"LowestSupportedVersion": "${CONFIG_DRIVERS_EFI_MAIN_FW_LSV}",
"OpenSslSignerPrivateCertFile": "${sign_cert}",
"OpenSslOtherPublicCertFile": "${sub_cert}",
"OpenSslTrustedPublicCertFile": "${root_cert}"
}
]
}
EOF
# Linux doesn't support InitiateReset flag, omitting it to rely on manual
# warm reset
if ! "${edk_tools}/GenerateCapsule" --encode \
--capflag PersistAcrossReset \
--json-file "$json_file" \
--output "$cap_file"; then
die "GenerateCapsule failed"
fi
echo "Created the capsule at '$cap_file'"
}
function box_subcommand() {
local src=${edk_basetools}/Source/Python
local dst=gencap
local keys=${src}/Pkcs7Sign
if [ -e "$dst" ]; then
confirm "Overwrite already existing '$dst'?"
rm -r "${dst}"
fi
info "using '${src}'"
info "constructing a standalone version in '${dst}'"
mkdir -p "${dst}"/{Common,keys}
cp "${src}/Capsule/GenerateCapsule.py" "${dst}"
cp -r "${src}/Common/Edk2" "${dst}/Common"
cp -r "${src}/Common/Uefi" "${dst}/Common"
info "using keys from '${keys}'"
cp "$keys"/TestRoot.pub.pem "${dst}/keys/root.pub.pem"
cp "$keys"/TestSub.pub.pem "${dst}/keys/sub.pub.pem"
cp "$keys"/TestCert.pem "${dst}/keys/sign.crt"
cat > "${dst}/GenerateCapsule" <<'EOF'
#!/usr/bin/env bash
python=python
if command -v python3 >/dev/null; then
python=python3
elif command -v python2 >/dev/null; then
python=python2
fi
dir=$(dirname "${BASH_SOURCE:-$0}")
exec "${python}" "${dir}/GenerateCapsule.py" "$@"
EOF
chmod +x "${dst}/GenerateCapsule"
print_banner 'Help'
echo "Location of capsule signing keys:"
echo " ${dst}/keys/"
echo
echo "Usage examples:"
echo " ${dst}/GenerateCapsule --help"
echo " ${dst}/GenerateCapsule --output decoded --decode coreboot.cap"
}
if [ $# -eq 0 ]; then
print_usage
exit 1
fi
subcommand=$1
shift
case "$subcommand" in
box|help|keygen|make)
"$subcommand"_subcommand "$@" ;;
*)
echo "Unexpected subcommand: $subcommand"
echo
print_usage
exit 1 ;;
esac