diff options
Diffstat (limited to 'classes')
-rw-r--r-- | classes/rmc-boot.bbclass | 17 | ||||
-rw-r--r-- | classes/rmc-db.bbclass | 92 | ||||
-rw-r--r-- | classes/uefi-comboapp.bbclass | 151 | ||||
-rw-r--r-- | classes/uefi-sign.bbclass | 50 |
4 files changed, 310 insertions, 0 deletions
diff --git a/classes/rmc-boot.bbclass b/classes/rmc-boot.bbclass new file mode 100644 index 0000000..37c3e30 --- /dev/null +++ b/classes/rmc-boot.bbclass | |||
@@ -0,0 +1,17 @@ | |||
1 | # rmc-boot bbclass | ||
2 | # Deploy central RMC database file to ESP | ||
3 | |||
4 | IMAGE_INSTALL_append = " rmc" | ||
5 | RMC_BOOTLOADER ?= "systemd-boot" | ||
6 | |||
7 | inherit ${RMC_BOOTLOADER} | ||
8 | |||
9 | do_bootimg[depends] += "${MLPREFIX}rmc-db:do_deploy" | ||
10 | |||
11 | efi_populate_append() { | ||
12 | if [ -f ${DEPLOY_DIR_IMAGE}/rmc.db ]; then | ||
13 | install -m 0400 ${DEPLOY_DIR_IMAGE}/rmc.db ${DEST}/rmc.db | ||
14 | else | ||
15 | rm -f ${DEST}/rmc.db | ||
16 | fi | ||
17 | } | ||
diff --git a/classes/rmc-db.bbclass b/classes/rmc-db.bbclass new file mode 100644 index 0000000..72594d6 --- /dev/null +++ b/classes/rmc-db.bbclass | |||
@@ -0,0 +1,92 @@ | |||
1 | # RMC database bbclass | ||
2 | # provide functions to generate RMC database file on build host (native) | ||
3 | |||
4 | DEPENDS += "rmc-native" | ||
5 | |||
6 | # rmc_generate_db() | ||
7 | # $1: a list of directories. Each directory holds directories for a group of | ||
8 | # boards. | ||
9 | # $2: path_name of rmc generates database file and records | ||
10 | # | ||
11 | # WARNING: content of directory of database file will be removed. | ||
12 | # | ||
13 | # Each board directory shall contain a fingerprint file (*.fp) at least, with | ||
14 | # optional file blob(s) associated to the type of board. If a board directory | ||
15 | # has no file blob, no record is created for that board. | ||
16 | # | ||
17 | # An example of two directories each of which contains two boards for RMC: | ||
18 | # (All file and directory names are for illustration purpose.) | ||
19 | # | ||
20 | # dir_1/ | ||
21 | # board_1/ | ||
22 | # board_1_fingerprint.fp | ||
23 | # file_1.blob | ||
24 | # board_2/ | ||
25 | # board_2.fp | ||
26 | # dir_2/ | ||
27 | # board_3/ | ||
28 | # b3.fp | ||
29 | # file_1.blob | ||
30 | # file_2.conf | ||
31 | # board_4/ | ||
32 | # board_foo.fp | ||
33 | # mylib.config | ||
34 | # | ||
35 | # To generate a RMC database "rmc.db" with data of all (actually 3) of boards in | ||
36 | # a directory "deploy_dir": | ||
37 | # | ||
38 | # rmc_generate_db "dir_1 dir_2" "deploy_dir/rmc.db" | ||
39 | # | ||
40 | # The board_2 will be skipped. No record or any data for it is packed in | ||
41 | # generated database because it only contains a fingerprint file. | ||
42 | # | ||
43 | |||
44 | rmc_generate_db () { | ||
45 | RMC_BOARD_DIRS=$1 | ||
46 | |||
47 | if [ "$#" -ne 2 ]; then | ||
48 | echo "rmc_generate_db(): Wrong number of arguments: $#" | ||
49 | return 1 | ||
50 | fi | ||
51 | |||
52 | RMC_DB_DIR=$(dirname "$2") | ||
53 | RMC_RECORDS="" | ||
54 | |||
55 | rm -rf ${RMC_DB_DIR} | ||
56 | mkdir -p ${RMC_DB_DIR} | ||
57 | |||
58 | # generate rmc database | ||
59 | for topdir in ${RMC_BOARD_DIRS}; do | ||
60 | # For all board dirs in a topdir: | ||
61 | CUR_BOARD_DIRS=$(find ${topdir}/* -type d) | ||
62 | for board_dir in ${CUR_BOARD_DIRS}; do | ||
63 | CUR_FINGERPRINT=$(find ${board_dir}/ -name "*.fp") | ||
64 | |||
65 | # disallow a board directory without any fingerprint file in it. | ||
66 | if [ -z "${CUR_FINGERPRINT}" ]; then | ||
67 | echo "Cannot find RMC fingerprint file in ${board_dir}" | ||
68 | return 1 | ||
69 | fi | ||
70 | |||
71 | CUR_FILES=$(find ${board_dir}/ -type f |grep -v '\.fp$' || true) | ||
72 | |||
73 | # allow a directory only with fingerprint file. Developer may | ||
74 | # check in fingerprint for future use. | ||
75 | if [ -z "${CUR_FILES}" ]; then | ||
76 | continue | ||
77 | fi | ||
78 | |||
79 | for fp in ${CUR_FINGERPRINT}; do | ||
80 | fullname=$(basename ${fp}) | ||
81 | CUR_TAG="${fullname%.*}" | ||
82 | CUR_RECORD=${RMC_DB_DIR}/${CUR_TAG}.rec | ||
83 | rmc -R -f ${fp} -b ${CUR_FILES} -o ${CUR_RECORD} | ||
84 | RMC_RECORDS="${RMC_RECORDS} ${CUR_RECORD}" | ||
85 | done | ||
86 | done | ||
87 | done | ||
88 | |||
89 | if [ ! -z "${RMC_RECORDS}" ]; then | ||
90 | rmc -D ${RMC_RECORDS} -o "$2" | ||
91 | fi | ||
92 | } | ||
diff --git a/classes/uefi-comboapp.bbclass b/classes/uefi-comboapp.bbclass new file mode 100644 index 0000000..5c3ca8c --- /dev/null +++ b/classes/uefi-comboapp.bbclass | |||
@@ -0,0 +1,151 @@ | |||
1 | # This class brings a more generic version of the UEFI combo app from refkit to meta-intel. | ||
2 | # It uses a combo file, containing kernel, initramfs and | ||
3 | # command line, presented to the BIOS as UEFI application, by prepending | ||
4 | # it with the efi stub obtained from systemd-boot. | ||
5 | |||
6 | # Don't add syslinux or build an ISO | ||
7 | PCBIOS_forcevariable = "0" | ||
8 | NOISO_forcevariable = "1" | ||
9 | |||
10 | # image-live.bbclass will default INITRD_LIVE to the image INITRD_IMAGE creates. | ||
11 | # We want behavior to be consistent whether or not "live" is in IMAGE_FSTYPES, so | ||
12 | # we default INITRD_LIVE to the INITRD_IMAGE as well. | ||
13 | INITRD_IMAGE ?= "core-image-minimal-initramfs" | ||
14 | INITRD_LIVE ?= " ${@ ('${DEPLOY_DIR_IMAGE}/' + d.getVar('INITRD_IMAGE', expand=True) + '-${MACHINE}.cpio.gz') if d.getVar('INITRD_IMAGE', True) else ''}" | ||
15 | |||
16 | do_uefiapp[depends] += " \ | ||
17 | intel-microcode:do_deploy \ | ||
18 | systemd-boot:do_deploy \ | ||
19 | virtual/kernel:do_deploy \ | ||
20 | " | ||
21 | |||
22 | # INITRD_IMAGE is added to INITRD_LIVE, which we use to create our initrd, so depend on it if it is set | ||
23 | do_uefiapp[depends] += "${@ '${INITRD_IMAGE}:do_image_complete' if d.getVar('INITRD_IMAGE') else ''}" | ||
24 | |||
25 | # The image does without traditional bootloader. | ||
26 | # In its place, instead, it uses a single UEFI executable binary, which is | ||
27 | # composed by: | ||
28 | # - an UEFI stub | ||
29 | # The linux kernel can generate a UEFI stub, however the one from systemd-boot can fetch | ||
30 | # the command line from a separate section of the EFI application, avoiding the need to | ||
31 | # rebuild the kernel. | ||
32 | # - the kernel | ||
33 | # - an initramfs (optional) | ||
34 | |||
35 | def create_uefiapp(d, uuid=None, app_suffix=''): | ||
36 | import glob, re | ||
37 | from subprocess import check_call | ||
38 | |||
39 | build_dir = d.getVar('B') | ||
40 | deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') | ||
41 | image_link_name = d.getVar('IMAGE_LINK_NAME') | ||
42 | |||
43 | cmdline = '%s/cmdline.txt' % build_dir | ||
44 | linux = '%s/%s' % (deploy_dir_image, d.getVar('KERNEL_IMAGETYPE')) | ||
45 | initrd = '%s/initrd' % build_dir | ||
46 | |||
47 | stub_path = '%s/linux*.efi.stub' % deploy_dir_image | ||
48 | stub = glob.glob(stub_path)[0] | ||
49 | m = re.match(r"\S*(ia32|x64)(.efi)\S*", os.path.basename(stub)) | ||
50 | app = "boot%s%s%s" % (m.group(1), app_suffix, m.group(2)) | ||
51 | executable = '%s/%s.%s' % (deploy_dir_image, image_link_name, app) | ||
52 | |||
53 | if d.getVar('INITRD_LIVE'): | ||
54 | with open(initrd, 'wb') as dst: | ||
55 | for cpio in d.getVar('INITRD_LIVE').split(): | ||
56 | with open(cpio, 'rb') as src: | ||
57 | dst.write(src.read()) | ||
58 | initrd_cmd = "--add-section .initrd=%s --change-section-vma .initrd=0x3000000 " % initrd | ||
59 | else: | ||
60 | initrd_cmd = "" | ||
61 | |||
62 | root = 'root=PARTUUID=%s' % uuid if uuid else '' | ||
63 | |||
64 | with open(cmdline, 'w') as f: | ||
65 | f.write('%s %s' % (d.getVar('APPEND'), root)) | ||
66 | |||
67 | objcopy_cmd = ("objcopy " | ||
68 | "--add-section .cmdline=%s --change-section-vma .cmdline=0x30000 " | ||
69 | "--add-section .linux=%s --change-section-vma .linux=0x40000 " | ||
70 | "%s %s %s") % \ | ||
71 | (cmdline, linux, initrd_cmd, stub, executable) | ||
72 | |||
73 | check_call(objcopy_cmd, shell=True) | ||
74 | |||
75 | python create_uefiapps () { | ||
76 | # We must clean up anything that matches the expected output pattern, to ensure that | ||
77 | # the next steps do not accidentally use old files. | ||
78 | import glob | ||
79 | pattern = d.expand('${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.boot*.efi') | ||
80 | for old_efi in glob.glob(pattern): | ||
81 | os.unlink(old_efi) | ||
82 | uuid = d.getVar('DISK_SIGNATURE_UUID') | ||
83 | create_uefiapp(d, uuid=uuid) | ||
84 | } | ||
85 | |||
86 | # This is intentionally split into different parts. This way, derived | ||
87 | # classes or images can extend the individual parts. We can also use | ||
88 | # whatever language (shell script or Python) is more suitable. | ||
89 | python do_uefiapp() { | ||
90 | bb.build.exec_func('create_uefiapps', d) | ||
91 | } | ||
92 | |||
93 | do_uefiapp[vardeps] += "APPEND DISK_SIGNATURE_UUID INITRD_LIVE KERNEL_IMAGETYPE IMAGE_LINK_NAME" | ||
94 | |||
95 | uefiapp_deploy_at() { | ||
96 | dest=$1 | ||
97 | for i in ${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.boot*.efi; do | ||
98 | target=`basename $i` | ||
99 | target=`echo $target | sed -e 's/${IMAGE_LINK_NAME}.//'` | ||
100 | cp --preserve=timestamps -r $i $dest/$target | ||
101 | done | ||
102 | } | ||
103 | |||
104 | do_uefiapp_deploy() { | ||
105 | rm -rf ${IMAGE_ROOTFS}/boot/* | ||
106 | dest=${IMAGE_ROOTFS}/boot/EFI/BOOT | ||
107 | mkdir -p $dest | ||
108 | uefiapp_deploy_at $dest | ||
109 | } | ||
110 | |||
111 | do_uefiapp_deploy[depends] += "${PN}:do_uefiapp" | ||
112 | |||
113 | |||
114 | # This decides when/how we add our tasks to the image | ||
115 | python () { | ||
116 | image_fstypes = d.getVar('IMAGE_FSTYPES', True) | ||
117 | initramfs_fstypes = d.getVar('INITRAMFS_FSTYPES', True) | ||
118 | |||
119 | # Don't add any of these tasks to initramfs images | ||
120 | if initramfs_fstypes not in image_fstypes: | ||
121 | bb.build.addtask('uefiapp', 'do_image', 'do_rootfs', d) | ||
122 | bb.build.addtask('uefiapp_deploy', 'do_image', 'do_rootfs', d) | ||
123 | } | ||
124 | |||
125 | SIGN_AFTER ?= "do_uefiapp" | ||
126 | SIGN_BEFORE ?= "do_uefiapp_deploy" | ||
127 | SIGNING_DIR ?= "${DEPLOY_DIR_IMAGE}" | ||
128 | SIGNING_BINARIES ?= "${IMAGE_LINK_NAME}.boot*.efi" | ||
129 | inherit uefi-sign | ||
130 | |||
131 | # Legacy hddimg support below this line | ||
132 | efi_hddimg_populate() { | ||
133 | uefiapp_deploy_at "$1" | ||
134 | } | ||
135 | |||
136 | build_efi_cfg() { | ||
137 | # The command line is built into the combo app, so this is a null op | ||
138 | : | ||
139 | } | ||
140 | |||
141 | populate_kernel_append() { | ||
142 | # The kernel and initrd are built into the app, so we don't need these | ||
143 | if [ -f $dest/initrd ]; then | ||
144 | rm $dest/initrd | ||
145 | fi | ||
146 | if [ -f $dest/vmlinuz ]; then | ||
147 | rm $dest/vmlinuz | ||
148 | fi | ||
149 | } | ||
150 | |||
151 | IMAGE_FEATURES[validitems] += "secureboot" | ||
diff --git a/classes/uefi-sign.bbclass b/classes/uefi-sign.bbclass new file mode 100644 index 0000000..e8f203b --- /dev/null +++ b/classes/uefi-sign.bbclass | |||
@@ -0,0 +1,50 @@ | |||
1 | # By default, sign all .efi binaries in ${B} after compiling and before deploying | ||
2 | SIGNING_DIR ?= "${B}" | ||
3 | SIGNING_BINARIES ?= "*.efi" | ||
4 | SIGN_AFTER ?= "do_compile" | ||
5 | SIGN_BEFORE ?= "do_deploy" | ||
6 | |||
7 | python () { | ||
8 | import os | ||
9 | import hashlib | ||
10 | |||
11 | # Ensure that if the signing key or cert change, we rerun the uefiapp process | ||
12 | if bb.utils.contains('IMAGE_FEATURES', 'secureboot', True, False, d): | ||
13 | for varname in ('SECURE_BOOT_SIGNING_CERT', 'SECURE_BOOT_SIGNING_KEY'): | ||
14 | filename = d.getVar(varname) | ||
15 | if filename is None: | ||
16 | bb.fatal('%s is not set.' % varname) | ||
17 | if not os.path.isfile(filename): | ||
18 | bb.fatal('%s=%s is not a file.' % (varname, filename)) | ||
19 | with open(filename, 'rb') as f: | ||
20 | data = f.read() | ||
21 | hash = hashlib.sha256(data).hexdigest() | ||
22 | d.setVar('%s_HASH' % varname, hash) | ||
23 | |||
24 | # Must reparse and thus rehash on file changes. | ||
25 | bb.parse.mark_dependency(d, filename) | ||
26 | |||
27 | bb.build.addtask('uefi_sign', d.getVar('SIGN_BEFORE'), d.getVar('SIGN_AFTER'), d) | ||
28 | |||
29 | # Original binary needs to be regenerated if the hash changes since we overwrite it | ||
30 | # SIGN_AFTER isn't necessarily when it gets generated, but its our best guess | ||
31 | d.appendVarFlag(d.getVar('SIGN_AFTER'), 'vardeps', 'SECURE_BOOT_SIGNING_CERT_HASH SECURE_BOOT_SIGNING_KEY_HASH') | ||
32 | } | ||
33 | |||
34 | do_uefi_sign() { | ||
35 | if [ -f ${SECURE_BOOT_SIGNING_KEY} ] && [ -f ${SECURE_BOOT_SIGNING_CERT} ]; then | ||
36 | for i in `find ${SIGNING_DIR}/ -name '${SIGNING_BINARIES}'`; do | ||
37 | sbsign --key ${SECURE_BOOT_SIGNING_KEY} --cert ${SECURE_BOOT_SIGNING_CERT} $i | ||
38 | sbverify --cert ${SECURE_BOOT_SIGNING_CERT} $i.signed | ||
39 | mv $i.signed $i | ||
40 | done | ||
41 | fi | ||
42 | } | ||
43 | |||
44 | do_uefi_sign[depends] += "sbsigntool-native:do_populate_sysroot" | ||
45 | |||
46 | do_uefi_sign[vardeps] += "SECURE_BOOT_SIGNING_CERT_HASH \ | ||
47 | SECURE_BOOT_SIGNING_KEY_HASH \ | ||
48 | SIGNING_BINARIES SIGNING_DIR \ | ||
49 | SIGN_BEFORE SIGN_AFTER \ | ||
50 | " | ||