virtbuilder_pel7x64builder0
5 years ago
3 changed files with 424 additions and 0 deletions
After Width: | Height: | Size: 127 KiB |
@ -0,0 +1,273 @@
@@ -0,0 +1,273 @@
|
||||
#!/bin/python |
||||
# Copyright (C) 2017 Red Hat |
||||
# Authors: |
||||
# - Patrick Uiterwijk <puiterwijk@redhat.com> |
||||
# - Kashyap Chamarthy <kchamart@redhat.com> |
||||
# |
||||
# Licensed under MIT License, for full text see LICENSE |
||||
# |
||||
# Purpose: Launch a QEMU guest and enroll ithe UEFI keys into an OVMF |
||||
# variables ("VARS") file. Then boot a Linux kernel with QEMU. |
||||
# Finally, perform a check to verify if Secure Boot |
||||
# is enabled. |
||||
|
||||
from __future__ import print_function |
||||
|
||||
import argparse |
||||
import os |
||||
import logging |
||||
import tempfile |
||||
import shutil |
||||
import string |
||||
import subprocess |
||||
|
||||
|
||||
def strip_special(line): |
||||
return ''.join([c for c in str(line) if c in string.printable]) |
||||
|
||||
|
||||
def generate_qemu_cmd(args, readonly, *extra_args): |
||||
if args.disable_smm: |
||||
machinetype = 'pc' |
||||
else: |
||||
machinetype = 'q35,smm=on' |
||||
machinetype += ',accel=%s' % ('kvm' if args.enable_kvm else 'tcg') |
||||
return [ |
||||
args.qemu_binary, |
||||
'-machine', machinetype, |
||||
'-display', 'none', |
||||
'-no-user-config', |
||||
'-nodefaults', |
||||
'-m', '256', |
||||
'-smp', '2,sockets=2,cores=1,threads=1', |
||||
'-chardev', 'pty,id=charserial1', |
||||
'-device', 'isa-serial,chardev=charserial1,id=serial1', |
||||
'-global', 'driver=cfi.pflash01,property=secure,value=%s' % ( |
||||
'off' if args.disable_smm else 'on'), |
||||
'-drive', |
||||
'file=%s,if=pflash,format=raw,unit=0,readonly=on' % ( |
||||
args.ovmf_binary), |
||||
'-drive', |
||||
'file=%s,if=pflash,format=raw,unit=1,readonly=%s' % ( |
||||
args.out_temp, 'on' if readonly else 'off'), |
||||
'-serial', 'stdio'] + list(extra_args) |
||||
|
||||
|
||||
def download(url, target, suffix, no_download): |
||||
istemp = False |
||||
if target and os.path.exists(target): |
||||
return target, istemp |
||||
if not target: |
||||
temped = tempfile.mkstemp(prefix='qosb.', suffix='.%s' % suffix) |
||||
os.close(temped[0]) |
||||
target = temped[1] |
||||
istemp = True |
||||
if no_download: |
||||
raise Exception('%s did not exist, but downloading was disabled' % |
||||
target) |
||||
import requests |
||||
logging.debug('Downloading %s to %s', url, target) |
||||
r = requests.get(url, stream=True) |
||||
with open(target, 'wb') as f: |
||||
for chunk in r.iter_content(chunk_size=1024): |
||||
if chunk: |
||||
f.write(chunk) |
||||
return target, istemp |
||||
|
||||
|
||||
def enroll_keys(args): |
||||
shutil.copy(args.ovmf_template_vars, args.out_temp) |
||||
|
||||
logging.info('Starting enrollment') |
||||
|
||||
cmd = generate_qemu_cmd( |
||||
args, |
||||
False, |
||||
'-drive', |
||||
'file=%s,format=raw,if=none,media=cdrom,id=drive-cd1,' |
||||
'readonly=on' % args.uefi_shell_iso, |
||||
'-device', |
||||
'ide-cd,drive=drive-cd1,id=cd1,' |
||||
'bootindex=1') |
||||
p = subprocess.Popen(cmd, |
||||
stdin=subprocess.PIPE, |
||||
stdout=subprocess.PIPE, |
||||
stderr=subprocess.STDOUT) |
||||
logging.info('Performing enrollment') |
||||
# Wait until the UEFI shell starts (first line is printed) |
||||
read = p.stdout.readline() |
||||
#if b'char device redirected' in read: |
||||
# read = p.stdout.readline() |
||||
#if args.print_output: |
||||
# print(strip_special(read), end='') |
||||
# print() |
||||
# Send the escape char to enter the UEFI shell early |
||||
p.stdin.write(b'\x1b') |
||||
p.stdin.flush() |
||||
# And then run the following three commands from the UEFI shell: |
||||
# change into the first file system device; install the default |
||||
# keys and certificates, and reboot |
||||
p.stdin.write(b'fs0:\r\n') |
||||
p.stdin.write(b'EnrollDefaultKeys.efi\r\n') |
||||
p.stdin.write(b'reset -s\r\n') |
||||
p.stdin.flush() |
||||
#while True: |
||||
# read = p.stdout.readline() |
||||
# if args.print_output: |
||||
# print('OUT: %s' % strip_special(read), end='') |
||||
# print() |
||||
# if b'info: success' in read: |
||||
# break |
||||
#p.wait() |
||||
if args.print_output: |
||||
print(strip_special(p.stdout.read()), end='') |
||||
logging.info('Finished enrollment') |
||||
|
||||
|
||||
def test_keys(args): |
||||
logging.info('Grabbing test kernel') |
||||
kernel, kerneltemp = download(args.kernel_url, args.kernel_path, |
||||
'kernel', args.no_download) |
||||
|
||||
logging.info('Starting verification') |
||||
try: |
||||
cmd = generate_qemu_cmd( |
||||
args, |
||||
True, |
||||
'-append', 'console=tty0 console=ttyS0,115200n8', |
||||
'-kernel', kernel) |
||||
p = subprocess.Popen(cmd, |
||||
stdin=subprocess.PIPE, |
||||
stdout=subprocess.PIPE, |
||||
stderr=subprocess.STDOUT) |
||||
logging.info('Performing verification') |
||||
while True: |
||||
read = p.stdout.readline() |
||||
if args.print_output: |
||||
print('OUT: %s' % strip_special(read), end='') |
||||
print() |
||||
if b'Secure boot disabled' in read: |
||||
raise Exception('Secure Boot was disabled') |
||||
elif b'Secure boot enabled' in read: |
||||
logging.info('Confirmed: Secure Boot is enabled') |
||||
break |
||||
elif b'Kernel is locked down from EFI secure boot' in read: |
||||
logging.info('Confirmed: Secure Boot is enabled') |
||||
break |
||||
p.kill() |
||||
if args.print_output: |
||||
print(strip_special(p.stdout.read()), end='') |
||||
logging.info('Finished verification') |
||||
finally: |
||||
if kerneltemp: |
||||
os.remove(kernel) |
||||
|
||||
|
||||
def parse_args(): |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('output', help='Filename for output vars file') |
||||
parser.add_argument('--out-temp', help=argparse.SUPPRESS) |
||||
parser.add_argument('--force', help='Overwrite existing output file', |
||||
action='store_true') |
||||
parser.add_argument('--print-output', help='Print the QEMU guest output', |
||||
action='store_true') |
||||
parser.add_argument('--verbose', '-v', help='Increase verbosity', |
||||
action='count') |
||||
parser.add_argument('--quiet', '-q', help='Decrease verbosity', |
||||
action='count') |
||||
parser.add_argument('--qemu-binary', help='QEMU binary path', |
||||
default='/usr/bin/qemu-system-x86_64') |
||||
parser.add_argument('--enable-kvm', help='Enable KVM acceleration', |
||||
action='store_true') |
||||
parser.add_argument('--ovmf-binary', help='OVMF secureboot code file', |
||||
default='/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd') |
||||
parser.add_argument('--ovmf-template-vars', help='OVMF empty vars file', |
||||
default='/usr/share/edk2/ovmf/OVMF_VARS.fd') |
||||
parser.add_argument('--uefi-shell-iso', help='Path to uefi shell iso', |
||||
default='/usr/share/edk2/ovmf/UefiShell.iso') |
||||
parser.add_argument('--skip-enrollment', |
||||
help='Skip enrollment, only test', action='store_true') |
||||
parser.add_argument('--skip-testing', |
||||
help='Skip testing generated "VARS" file', |
||||
action='store_true') |
||||
parser.add_argument('--kernel-path', |
||||
help='Specify a consistent path for kernel') |
||||
parser.add_argument('--no-download', action='store_true', |
||||
help='Never download a kernel') |
||||
parser.add_argument('--fedora-version', |
||||
help='Fedora version to get kernel for checking', |
||||
default='27') |
||||
parser.add_argument('--kernel-url', help='Kernel URL', |
||||
default='https://download.fedoraproject.org/pub/fedora' |
||||
'/linux/releases/%(version)s/Everything/x86_64' |
||||
'/os/images/pxeboot/vmlinuz') |
||||
parser.add_argument('--disable-smm', |
||||
help=('Don\'t restrict varstore pflash writes to ' |
||||
'guest code that executes in SMM. Use this ' |
||||
'option only if your OVMF binary doesn\'t have ' |
||||
'the edk2 SMM driver stack built into it ' |
||||
'(possibly because your QEMU binary lacks SMM ' |
||||
'emulation). Note that without restricting ' |
||||
'varstore pflash writes to guest code that ' |
||||
'executes in SMM, a malicious guest kernel, ' |
||||
'used for testing, could undermine Secure ' |
||||
'Boot.'), |
||||
action='store_true') |
||||
args = parser.parse_args() |
||||
args.kernel_url = args.kernel_url % {'version': args.fedora_version} |
||||
|
||||
validate_args(args) |
||||
return args |
||||
|
||||
|
||||
def validate_args(args): |
||||
if (os.path.exists(args.output) |
||||
and not args.force |
||||
and not args.skip_enrollment): |
||||
raise Exception('%s already exists' % args.output) |
||||
|
||||
if args.skip_enrollment and not os.path.exists(args.output): |
||||
raise Exception('%s does not yet exist' % args.output) |
||||
|
||||
verbosity = (args.verbose or 1) - (args.quiet or 0) |
||||
if verbosity >= 2: |
||||
logging.basicConfig(level=logging.DEBUG) |
||||
elif verbosity == 1: |
||||
logging.basicConfig(level=logging.INFO) |
||||
elif verbosity < 0: |
||||
logging.basicConfig(level=logging.ERROR) |
||||
else: |
||||
logging.basicConfig(level=logging.WARN) |
||||
|
||||
if args.skip_enrollment: |
||||
args.out_temp = args.output |
||||
else: |
||||
temped = tempfile.mkstemp(prefix='qosb.', suffix='.vars') |
||||
os.close(temped[0]) |
||||
args.out_temp = temped[1] |
||||
logging.debug('Temp output: %s', args.out_temp) |
||||
|
||||
|
||||
def move_to_dest(args): |
||||
shutil.copy(args.out_temp, args.output) |
||||
os.remove(args.out_temp) |
||||
|
||||
|
||||
def main(): |
||||
args = parse_args() |
||||
if not args.skip_enrollment: |
||||
enroll_keys(args) |
||||
if not args.skip_testing: |
||||
test_keys(args) |
||||
if not args.skip_enrollment: |
||||
move_to_dest(args) |
||||
if args.skip_testing: |
||||
logging.info('Created %s' % args.output) |
||||
else: |
||||
logging.info('Created and verified %s' % args.output) |
||||
else: |
||||
logging.info('Verified %s', args.output) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,151 @@
@@ -0,0 +1,151 @@
|
||||
%global debug_package %{nil} |
||||
%global __python %{__python} |
||||
%define GITDATE 201905 |
||||
%define OPENSSLVER 1_1_1b |
||||
|
||||
Name: ovmf |
||||
Version: %{GITDATE} |
||||
Release: 1%{?dist} |
||||
Summary: UEFI firmware for 64-bit virtual machines |
||||
Group: Applications/Emulators |
||||
License: BSD and OpenSSL and MIT |
||||
URL: http://www.tianocore.org |
||||
Source0: https://github.com/tianocore/edk2/archive/edk2-stable%{GITDATE}.tar.gz |
||||
Source1: https://github.com/openssl/openssl/archive/OpenSSL_%{OPENSSLVER}.tar.gz |
||||
Source2: ovmf-vars-generator |
||||
Source3: edk2-ovmf-logo.bmp |
||||
ExclusiveArch: x86_64 |
||||
BuildRequires: python2-devel |
||||
BuildRequires: libuuid-devel |
||||
BuildRequires: /usr/bin/iasl |
||||
BuildRequires: binutils gcc git |
||||
BuildRequires: nasm |
||||
BuildRequires: dosfstools |
||||
BuildRequires: mtools |
||||
BuildRequires: genisoimage |
||||
BuildRequires: qemu-kvm >= 1.5.3-44 |
||||
BuildRequires: kernel >= 3.10.0-52 |
||||
BuildRequires: rpmdevtools |
||||
|
||||
|
||||
%description |
||||
EDK II is a modern, feature-rich, cross-platform firmware development |
||||
environment for the UEFI and PI specifications. This package contains sample |
||||
64-bit UEFI firmware builds for QEMU and KVM. |
||||
|
||||
|
||||
%prep |
||||
%setup -q -n edk2-edk2-stable%{GITDATE} |
||||
tar xvf %{SOURCE1} -C CryptoPkg/Library/OpensslLib/ |
||||
mv CryptoPkg/Library/OpensslLib/openssl-OpenSSL_%{OPENSSLVER}/* CryptoPkg/Library/OpensslLib/openssl/ |
||||
cp %{SOURCE2} . |
||||
cp %{SOURCE3} MdeModulePkg/Logo/Logo.bmp |
||||
|
||||
|
||||
%build |
||||
make -j32 -C BaseTools/ |
||||
source ./edksetup.sh |
||||
make -C "$EDK_TOOLS_PATH" |
||||
|
||||
SMP_MFLAGS="%{?_smp_mflags}" |
||||
if [[ x"$SMP_MFLAGS" = x-j* ]]; then |
||||
CC_FLAGS="$CC_FLAGS -n ${SMP_MFLAGS#-j}" |
||||
elif [ -n "%{?jobs}" ]; then |
||||
CC_FLAGS="$CC_FLAGS -n %{?jobs}" |
||||
fi |
||||
|
||||
CC_FLAGS="$CC_FLAGS --cmd-len=65536 -t GCC48 -b DEBUG --hash" |
||||
|
||||
# Build with SB but without SMM; include UEFI shell. |
||||
build ${CC_FLAGS} -D FD_SIZE_4MB -a X64 -p OvmfPkg/OvmfPkgX64.dsc -D SECURE_BOOT_ENABLE |
||||
# Build with SB and SMM; exclude UEFI shell. |
||||
build -D SECURE_BOOT_ENABLE -D EXCLUDE_SHELL_FROM_FD ${CC_FLAGS} -a IA32 -a X64 -p OvmfPkg/OvmfPkgIa32X64.dsc -D SMM_REQUIRE -D FD_SIZE_4MB |
||||
# Sanity check: the varstore templates must be identical. |
||||
cmp Build/OvmfX64/DEBUG_GCC4?/FV/OVMF_VARS.fd Build/Ovmf3264/DEBUG_GCC4?/FV/OVMF_VARS.fd |
||||
|
||||
# Prepare an ISO image that boots the UEFI shell. |
||||
( |
||||
UEFI_SHELL_BINARY=Build/Ovmf3264/DEBUG_GCC48/X64/Shell.efi |
||||
ENROLLER_BINARY=Build/Ovmf3264/DEBUG_GCC48/X64/EnrollDefaultKeys.efi |
||||
UEFI_SHELL_IMAGE=uefi_shell.img |
||||
ISO_IMAGE=UefiShell.iso |
||||
|
||||
UEFI_SHELL_BINARY_BNAME=$(basename -- "$UEFI_SHELL_BINARY") |
||||
UEFI_SHELL_SIZE=$(stat --format=%s -- "$UEFI_SHELL_BINARY") |
||||
ENROLLER_SIZE=$(stat --format=%s -- "$ENROLLER_BINARY") |
||||
|
||||
# add 1MB then 10% for metadata |
||||
UEFI_SHELL_IMAGE_KB=$(( |
||||
(UEFI_SHELL_SIZE + ENROLLER_SIZE + 1 * 1024 * 1024) * 11 / 10 / 1024 |
||||
)) |
||||
|
||||
# create non-partitioned FAT image |
||||
rm -f -- "$UEFI_SHELL_IMAGE" |
||||
mkdosfs -C "$UEFI_SHELL_IMAGE" -n UEFI_SHELL -- "$UEFI_SHELL_IMAGE_KB" |
||||
|
||||
# copy the shell binary into the FAT image |
||||
export MTOOLS_SKIP_CHECK=1 |
||||
mmd -i "$UEFI_SHELL_IMAGE" ::efi |
||||
mmd -i "$UEFI_SHELL_IMAGE" ::efi/boot |
||||
mcopy -i "$UEFI_SHELL_IMAGE" "$UEFI_SHELL_BINARY" ::efi/boot/bootx64.efi |
||||
mcopy -i "$UEFI_SHELL_IMAGE" "$ENROLLER_BINARY" :: |
||||
mdir -i "$UEFI_SHELL_IMAGE" -/ :: |
||||
|
||||
# build ISO with FAT image file as El Torito EFI boot image |
||||
genisoimage -input-charset ASCII -J -rational-rock \ |
||||
-efi-boot "$UEFI_SHELL_IMAGE" -no-emul-boot \ |
||||
-o "$ISO_IMAGE" -- "$UEFI_SHELL_IMAGE" |
||||
) |
||||
|
||||
# Enroll the default certificates in a separate variable store template. Base |
||||
# RHEL7 qemu-kvm does not emulate SMM, but we don't need SMM for the enrollment |
||||
# here. |
||||
chmod +x ./ovmf-vars-generator |
||||
./ovmf-vars-generator --verbose --verbose \ |
||||
--qemu-binary /usr/bin/qemu-system-x86_64 \ |
||||
--ovmf-binary Build/OvmfX64/DEBUG_GCC4?/FV/OVMF_CODE.fd \ |
||||
--ovmf-template-vars Build/OvmfX64/DEBUG_GCC4?/FV/OVMF_VARS.fd \ |
||||
--uefi-shell-iso UefiShell.iso \ |
||||
--skip-testing \ |
||||
--disable-smm \ |
||||
OVMF_VARS.secboot.fd |
||||
|
||||
|
||||
%install |
||||
|
||||
copy_license() { |
||||
install -m 0644 $1 $RPM_BUILD_ROOT%{_docdir}/%{name}/Licenses/$2-License.txt |
||||
} |
||||
|
||||
mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{name}/Licenses |
||||
copy_license License.txt edk2 |
||||
copy_license OvmfPkg/License.txt OvmfPkg |
||||
|
||||
mkdir -p $RPM_BUILD_ROOT%{_datadir}/OVMF |
||||
|
||||
install -m 0644 Build/OvmfX64/DEBUG_GCC4?/FV/OVMF_CODE.fd $RPM_BUILD_ROOT%{_datadir}/OVMF/OVMF_CODE.fd |
||||
install -m 0644 Build/Ovmf3264/DEBUG_GCC4?/FV/OVMF_CODE.fd $RPM_BUILD_ROOT%{_datadir}/OVMF/OVMF_CODE.secboot.fd |
||||
install -m 0644 Build/OvmfX64/DEBUG_GCC4?/FV/OVMF_VARS.fd $RPM_BUILD_ROOT%{_datadir}/OVMF/OVMF_VARS.fd |
||||
install -m 0644 OVMF_VARS.secboot.fd $RPM_BUILD_ROOT%{_datadir}/OVMF/OVMF_VARS.secboot.fd |
||||
install -m 0644 UefiShell.iso $RPM_BUILD_ROOT%{_datadir}/OVMF/UefiShell.iso |
||||
install -m 0644 OvmfPkg/README $RPM_BUILD_ROOT%{_docdir}/%{name}/README |
||||
|
||||
copy_license CryptoPkg/Library/OpensslLib/openssl/LICENSE OpensslLib |
||||
|
||||
|
||||
%files |
||||
%defattr(-,root,root,-) |
||||
%dir %{_docdir}/%{name}/Licenses |
||||
%doc %{_docdir}/%{name}/Licenses/edk2-License.txt |
||||
%doc %{_docdir}/%{name}/Licenses/OvmfPkg-License.txt |
||||
%doc %{_docdir}/%{name}/Licenses/OpensslLib-License.txt |
||||
%doc %{_docdir}/%{name}/README |
||||
%dir %{_datadir}/OVMF/ |
||||
%{_datadir}/OVMF/OVMF_CODE.fd |
||||
%{_datadir}/OVMF/OVMF_CODE.secboot.fd |
||||
%{_datadir}/OVMF/OVMF_VARS.fd |
||||
%{_datadir}/OVMF/OVMF_VARS.secboot.fd |
||||
%{_datadir}/OVMF/UefiShell.iso |
||||
|
||||
|
||||
%changelog |
Loading…
Reference in new issue