Pull request #61: add devtool generate-uki command
Merge in ICO/coreos from feat/devtool-kernel to master * commit '1c8f7e9163ba5fec161bb858643a57274ea07882': feat(devtool): add a generate-uki command fix(coreos-image-uki.bbclass): use APPENDS to set the kernel arguments refactor: use black to format python code in vscode
This commit is contained in:
commit
e2a53121a5
|
|
@ -4,5 +4,6 @@
|
|||
"**/build/downloads/**": true,
|
||||
"**/build/sstate-cache/**": true,
|
||||
"**/build/tmp/**": true
|
||||
}
|
||||
},
|
||||
"python.formatting.provider": "black"
|
||||
}
|
||||
|
|
@ -13,7 +13,9 @@ COREOS_KERNEL_EXT ??= ".efi"
|
|||
COREOS_KERNEL0_NAME ??= "kernel0-${MACHINE}"
|
||||
COREOS_KERNEL1_NAME ??= "kernel1-${MACHINE}"
|
||||
COREOS_KERNEL0_FILENAME ??= "${COREOS_KERNEL0_NAME}${COREOS_KERNEL_EXT}"
|
||||
COREOS_KERNEL0 ??= "${DEPLOY_DIR_IMAGE}/${COREOS_KERNEL0_FILENAME}"
|
||||
COREOS_KERNEL1_FILENAME ??= "${COREOS_KERNEL1_NAME}${COREOS_KERNEL_EXT}"
|
||||
COREOS_KERNEL1 ??= "${DEPLOY_DIR_IMAGE}/${COREOS_KERNEL1_FILENAME}"
|
||||
|
||||
# Kernel command line
|
||||
# ==============================================================================
|
||||
|
|
@ -24,12 +26,17 @@ COREOS_PLATFORM1_ROOT ??= "PARTLABEL=platform1"
|
|||
COREOS_KERNEL0_CMDLINE ??= "root=${COREOS_PLATFORM0_ROOT} ${APPEND}"
|
||||
COREOS_KERNEL1_CMDLINE ??= "root=${COREOS_PLATFORM0_ROOT} ${APPEND}"
|
||||
|
||||
COREOS_UKI_PART_KERNEL_FILENAME ??= "${KERNEL_IMAGETYPE}-${MACHINE}${KERNEL_IMAGE_BIN_EXT}"
|
||||
COREOS_UKI_PART_KERNEL ??= "${DEPLOY_DIR_IMAGE}/${COREOS_UKI_PART_KERNEL_FILENAME}"
|
||||
COREOS_UKI_PART_STUB_FILENAME ??= "kernel-stub${EFI_ARCH}.efi"
|
||||
COREOS_UKI_PART_STUB ??= "${STAGING_LIBDIR}/efibootguard/${COREOS_UKI_PART_STUB_FILENAME}"
|
||||
|
||||
|
||||
# UKI Generation
|
||||
# ==============================================================================
|
||||
|
||||
do_bundle_uki() {
|
||||
deployDir="${DEPLOY_DIR_IMAGE}"
|
||||
kernel=${KERNEL_IMAGETYPE}-${MACHINE}${KERNEL_IMAGE_BIN_EXT}
|
||||
|
||||
# Create an array with device tree if any
|
||||
DTB_PARAMS=""
|
||||
|
|
@ -40,21 +47,23 @@ do_bundle_uki() {
|
|||
DTB_PARAMS="${DTB_PARAMS} --dtb=${deployDir}/${dtb}"
|
||||
done
|
||||
|
||||
echo "kernel: ${kernel}"
|
||||
echo "kernel: ${COREOS_UKI_PART_KERNEL_FILENAME}"
|
||||
echo "dtb: ${DTB_PARAMS}"
|
||||
echo "cmdline0: ${COREOS_KERNEL0_CMDLINE}"
|
||||
echo "cmdline1: ${COREOS_KERNEL1_CMDLINE}"
|
||||
|
||||
bg_gen_unified_kernel \
|
||||
"${STAGING_LIBDIR}/efibootguard/kernel-stub${EFI_ARCH}.efi" \
|
||||
"${deployDir}/${kernel}" \
|
||||
"${deployDir}/${COREOS_KERNEL0_FILENAME}" \
|
||||
--cmdline "console=ttyS0,115200 root=${COREOS_PLATFORM0_ROOT} rootwait " \
|
||||
"${COREOS_UKI_PART_STUB}" \
|
||||
"${COREOS_UKI_PART_KERNEL}" \
|
||||
"${COREOS_KERNEL0}" \
|
||||
--cmdline "${COREOS_KERNEL0_CMDLINE}" \
|
||||
${DTB_PARAMS}
|
||||
|
||||
bg_gen_unified_kernel \
|
||||
"${STAGING_LIBDIR}/efibootguard/kernel-stub${EFI_ARCH}.efi" \
|
||||
"${deployDir}/${kernel}" \
|
||||
"${deployDir}/${COREOS_KERNEL1_FILENAME}" \
|
||||
--cmdline "console=ttyS0,115200 root=${COREOS_PLATFORM1_ROOT} rootwait " \
|
||||
"${COREOS_UKI_PART_STUB}" \
|
||||
"${COREOS_UKI_PART_KERNEL}" \
|
||||
"${COREOS_KERNEL1}" \
|
||||
--cmdline "${COREOS_KERNEL1_CMDLINE}" \
|
||||
${DTB_PARAMS}
|
||||
|
||||
coreos_efi_secureboot_sign_app "${deployDir}/${COREOS_KERNEL0_FILENAME}"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,307 @@
|
|||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# This scripts use bitbake api, so it has to be under the GPL-2.0 license
|
||||
|
||||
"""
|
||||
devtool wrapper for the coreos-device script
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import textwrap
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from devtool import setup_tinfoil, parse_recipe
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from shlex import quote
|
||||
|
||||
def register_commands(subparsers, context):
|
||||
"""Register devtool subcommands from this plugin"""
|
||||
subparsers.add_subparser_group("efibootguard", "Developers tools for efibootguard")
|
||||
cmd_uki = subparsers.add_parser(
|
||||
"generate-uki",
|
||||
help="Generate signed UKI kernel images",
|
||||
description="""
|
||||
Helper to generate Unified Kernel Image using bg_generate_uki. The UKI is
|
||||
signed and contain a EFI bootstraper, a kernel image, the kernel command
|
||||
line and one or multiple device tree
|
||||
|
||||
Before calling this function, theses recipes should be build by bitbake:
|
||||
`virtual/kernel efibootguard efibootguard-native`
|
||||
""",
|
||||
group="efibootguard",
|
||||
)
|
||||
cmd_uki.add_argument("imagename", help="Image recipe used to get the configuration")
|
||||
cmd_uki.add_argument(
|
||||
"--cmdline-append",
|
||||
default="",
|
||||
help="string to append to the kernel command line",
|
||||
)
|
||||
|
||||
cmd_uki.add_argument(
|
||||
"--cmdline",
|
||||
default=None,
|
||||
help="""
|
||||
Replace the common part of the cmdline. root= parameter will be added
|
||||
automatically if --cmdline-no-root is not used
|
||||
""",
|
||||
)
|
||||
|
||||
cmd_uki.add_argument(
|
||||
"--cmdline-no-root",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't automatically append root= if --cmdline is used",
|
||||
)
|
||||
|
||||
cmd_uki.add_argument(
|
||||
"--dtb",
|
||||
default=None,
|
||||
help="Embedded the given dtb instead of the default set",
|
||||
)
|
||||
|
||||
cmd_uki.add_argument(
|
||||
"--kernel",
|
||||
default=None,
|
||||
help="Use a custom kernel binary",
|
||||
)
|
||||
|
||||
cmd_uki.set_defaults(
|
||||
func=efibootguard_generate_uki, fixed_setup=context.fixed_setup
|
||||
)
|
||||
|
||||
|
||||
def run(cmd):
|
||||
return subprocess.run(cmd).returncode
|
||||
|
||||
|
||||
class InvalidImage(Exception):
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class UKIGeneratorArgs:
|
||||
"""
|
||||
Class used to generate and run commands needed to build and sign
|
||||
the UKI image
|
||||
"""
|
||||
|
||||
kernel: str
|
||||
output: str
|
||||
cmdline: str
|
||||
dtb: str
|
||||
stub: list
|
||||
root: str
|
||||
build_binary: str
|
||||
keydir: str
|
||||
|
||||
def process_args(self, args):
|
||||
"""
|
||||
This apply args passed to the program to override or modify the
|
||||
parameters that we received from the bitbake context
|
||||
"""
|
||||
if args.dtb:
|
||||
self.dtb = [args.dtb]
|
||||
|
||||
if args.kernel:
|
||||
self.kernel = args.kernel
|
||||
|
||||
if args.cmdline:
|
||||
self.cmdline = args.cmdline
|
||||
if not args.cmdline_no_root:
|
||||
self.cmdline = f"{self.cmdline} root={self.root}"
|
||||
|
||||
if args.cmdline_append:
|
||||
self.cmdline = f"{self.cmdline} {args.cmdline_append}"
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
f"stub: {self.stub}\n"
|
||||
f"kernel: {self.kernel}\n"
|
||||
f"cmdline: {self.cmdline}\n"
|
||||
f"dtb: {self.dtb}\n"
|
||||
f"output: {self.output}\n"
|
||||
f"keydir: {self.keydir}\n"
|
||||
)
|
||||
|
||||
def get_buid_cmd(self) -> list[str]:
|
||||
"""
|
||||
Return the command needed to build the UKI as an array
|
||||
"""
|
||||
args = [
|
||||
self.build_binary,
|
||||
self.stub,
|
||||
self.kernel,
|
||||
self.output,
|
||||
"--cmdline",
|
||||
self.cmdline,
|
||||
]
|
||||
for dtb in self.dtb:
|
||||
args += ["--dtb", dtb]
|
||||
return args
|
||||
|
||||
def get_sign_cmd(self) -> list[str]:
|
||||
"""
|
||||
Return the command needed to sign the UKI in place as an array
|
||||
"""
|
||||
return [
|
||||
"sbsign",
|
||||
"--key",
|
||||
os.path.join(self.keydir, "db.key"),
|
||||
"--cert",
|
||||
os.path.join(self.keydir, "db.crt"),
|
||||
self.output,
|
||||
"--output",
|
||||
self.output,
|
||||
]
|
||||
|
||||
def print_cmd(self, cmd):
|
||||
"""
|
||||
Pretty print the command in the terminal with indentation and line
|
||||
splitting using standard \ keyword
|
||||
"""
|
||||
first = True
|
||||
previous_was_an_option = False
|
||||
for index, arg in enumerate(cmd):
|
||||
# Quote the string as needed, so that copy past from the printed
|
||||
# command works.
|
||||
arg = quote(arg)
|
||||
end = " \\\n"
|
||||
if index == len(cmd) - 1:
|
||||
# For the last
|
||||
end = " \n"
|
||||
|
||||
if first:
|
||||
# only print the command name not the full path
|
||||
print(os.path.basename(arg), end=end)
|
||||
first = False
|
||||
continue
|
||||
|
||||
if previous_was_an_option:
|
||||
print(arg, end=end)
|
||||
previous_was_an_option = False
|
||||
continue
|
||||
|
||||
if arg.startswith("-"):
|
||||
previous_was_an_option = True
|
||||
printi(arg, 1, end=" ")
|
||||
else:
|
||||
printi(arg, 1, end=end)
|
||||
|
||||
def build_and_sign(self) -> int:
|
||||
"""
|
||||
Run the sign and build command and return the sum of both commands
|
||||
exit status
|
||||
"""
|
||||
build_cmd = self.get_buid_cmd()
|
||||
sign_cmd = self.get_sign_cmd()
|
||||
|
||||
self.print_cmd(build_cmd)
|
||||
r = run(build_cmd)
|
||||
|
||||
self.print_cmd(sign_cmd)
|
||||
r += run(sign_cmd)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def printi(txt, indent=1, *, iprefix=" ", end="\n"):
|
||||
"""
|
||||
Helper function to print multiples lines with some indentation
|
||||
"""
|
||||
print(textwrap.indent(txt, iprefix * indent), end=end)
|
||||
|
||||
|
||||
def dtb_to_list(dtb, deploy_dir) -> list[str]:
|
||||
"""
|
||||
Convert the string array from bitbake into a python list. Only the basename
|
||||
of the dtb is taken and DEPLOY_DIR is appended
|
||||
"""
|
||||
return list(
|
||||
map(
|
||||
lambda s: os.path.join(deploy_dir, os.path.basename(s)),
|
||||
filter(lambda s: s.strip() != "", dtb.split(" ")),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def efibootguard_generate_uki(args, config, basepath, workspace):
|
||||
"""
|
||||
Entrypoint for devtool generate-uki
|
||||
"""
|
||||
image = args.imagename
|
||||
tinfoil = setup_tinfoil(basepath=basepath)
|
||||
|
||||
rd = parse_recipe(config, tinfoil, image, True)
|
||||
if not rd:
|
||||
# Error already shown
|
||||
return 1
|
||||
|
||||
if not bb.data.inherits_class("coreos-image", rd):
|
||||
raise InvalidImage("Image should be based on the coreos-image class")
|
||||
|
||||
if rd.getVar("COREOS_IMAGE_GENERATE_UKI") != "1":
|
||||
raise InvalidImage("COREOS_IMAGE_GENERATE_UKI should be set to '1'")
|
||||
|
||||
deploy_dir = os.path.relpath(rd.getVar("DEPLOY_DIR_IMAGE"))
|
||||
kernel = os.path.relpath(rd.getVar("COREOS_UKI_PART_KERNEL"))
|
||||
dtb = dtb_to_list(rd.getVar("KERNEL_DEVICETREE"), deploy_dir)
|
||||
|
||||
rd_efibootguard = parse_recipe(config, tinfoil, "efibootguard", True)
|
||||
|
||||
stub = os.path.relpath(
|
||||
os.path.join(
|
||||
rd_efibootguard.getVar("WORKDIR"),
|
||||
"package/usr/lib/efibootguard",
|
||||
rd.getVar("COREOS_UKI_PART_STUB_FILENAME"),
|
||||
)
|
||||
)
|
||||
|
||||
build_binary = os.path.join(
|
||||
rd.getVar("COMPONENTS_DIR"),
|
||||
rd.getVar("BUILD_ARCH"),
|
||||
"efibootguard-native",
|
||||
rd.getVar("bindir_native").lstrip(os.path.sep),
|
||||
"bg_gen_unified_kernel",
|
||||
)
|
||||
|
||||
keydir = os.path.relpath(rd.getVar("COREOS_EFI_SECUREBOOT_KEYDIR"))
|
||||
|
||||
uki0 = UKIGeneratorArgs(
|
||||
kernel=kernel,
|
||||
output=os.path.relpath(rd.getVar("COREOS_KERNEL0")),
|
||||
cmdline=rd.getVar("COREOS_KERNEL0_CMDLINE"),
|
||||
dtb=dtb,
|
||||
stub=stub,
|
||||
root=rd.getVar("COREOS_PLATFORM0_ROOT"),
|
||||
build_binary=build_binary,
|
||||
keydir=keydir,
|
||||
)
|
||||
uki1 = UKIGeneratorArgs(
|
||||
kernel=kernel,
|
||||
output=os.path.relpath(rd.getVar("COREOS_KERNEL1")),
|
||||
cmdline=rd.getVar("COREOS_KERNEL1_CMDLINE"),
|
||||
dtb=dtb,
|
||||
stub=stub,
|
||||
root=rd.getVar("COREOS_PLATFORM1_ROOT"),
|
||||
build_binary=build_binary,
|
||||
keydir=keydir,
|
||||
)
|
||||
|
||||
print(f"Applying passed parameters...")
|
||||
uki0.process_args(args)
|
||||
uki1.process_args(args)
|
||||
|
||||
print(f"KERNEL0 image will be generated with the following settings:")
|
||||
printi(f"{uki0}", 1)
|
||||
print()
|
||||
print(f"KERNEL1 image will be generated with the following settings:")
|
||||
printi(f"{uki1}", 1)
|
||||
print()
|
||||
|
||||
print(f"Generating the files...")
|
||||
r = uki0.build_and_sign()
|
||||
r += uki1.build_and_sign()
|
||||
|
||||
return r
|
||||
Loading…
Reference in New Issue