From: Tomasz Lauda tomasz.lauda@linux.intel.com
This patch allows rimage to sign FW binaries using MEU tool. Paths to MEU and private key have to be provided during config step.
Signed-off-by: Tomasz Lauda tomasz.lauda@linux.intel.com --- .gitignore | 1 + configure.ac | 31 ++++++- m4/ax_compare_version.m4 | 178 ++++++++++++++++++++++++++++++++++++ rimage/manifest.c | 172 ++++++++++++++++++++++++++-------- rimage/plat_auth.c | 7 +- rimage/plat_auth.h | 3 +- rimage/rimage.c | 10 +- rimage/rimage.h | 3 +- src/arch/xtensa/Makefile.am | 12 ++- 9 files changed, 368 insertions(+), 49 deletions(-) create mode 100644 m4/ax_compare_version.m4
diff --git a/.gitignore b/.gitignore index c03c83e..8558441 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ .build *.dis *.lst +*.log .*
Makefile diff --git a/configure.ac b/configure.ac index 0d810b3..3420530 100644 --- a/configure.ac +++ b/configure.ac @@ -27,8 +27,35 @@ AC_SUBST(ASFLAGS)
# Cross compiler tool libgcc and headers AC_ARG_WITH([root-dir], - AS_HELP_STRING([--with-root-dir], [Specify location of cross gcc libraries and headers]), - [], [with_root_dir=no]) + AS_HELP_STRING([--with-root-dir], [Specify location of cross gcc libraries and headers]), + [], [with_root_dir=no]) + +# MEU location +AC_ARG_WITH([meu], + AS_HELP_STRING([--with-meu], [Specify location of MEU tool]), + [], [with_meu=no]) +if test "x$with_meu" != "xno"; then + MEU_PATH="$with_meu" + AC_SUBST(MEU_PATH) + + MEU_VERSION=$($with_meu/meu -ver | grep "Version:" | cut -d" " -f6) + AX_COMPARE_VERSION([$MEU_VERSION], [ge], [12.0.0.1035], [MEU_OFFSET=1088], [MEU_OFFSET=1152]) + AC_SUBST(MEU_OFFSET) +fi +AM_CONDITIONAL(USE_MEU, test "x$with_meu" != "xno") + +# Private key location +AC_ARG_WITH([key], + AS_HELP_STRING([--with-key], [Specify location of private key]), + [], [with_key=no]) +if test "x$with_meu" != "xno"; then + if test "x$with_key" != "xno"; then + PRIVATE_KEY="$with_key" + AC_SUBST(PRIVATE_KEY) + else + AC_MSG_ERROR([Private key location not specified]) + fi +fi
# check if we are building FW image or library AC_ARG_ENABLE(library, [AS_HELP_STRING([--enable-library],[build library])], have_library=$enableval, have_library=no) diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 new file mode 100644 index 0000000..6df1c53 --- /dev/null +++ b/m4/ax_compare_version.m4 @@ -0,0 +1,178 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compare_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# This macro compares two version strings. Due to the various number of +# minor-version numbers that can exist, and the fact that string +# comparisons are not compatible with numeric comparisons, this is not +# necessarily trivial to do in a autoconf script. This macro makes doing +# these comparisons easy. +# +# The six basic comparisons are available, as well as checking equality +# limited to a certain number of minor-version levels. +# +# The operator OP determines what type of comparison to do, and can be one +# of: +# +# eq - equal (test A == B) +# ne - not equal (test A != B) +# le - less than or equal (test A <= B) +# ge - greater than or equal (test A >= B) +# lt - less than (test A < B) +# gt - greater than (test A > B) +# +# Additionally, the eq and ne operator can have a number after it to limit +# the test to that number of minor versions. +# +# eq0 - equal up to the length of the shorter version +# ne0 - not equal up to the length of the shorter version +# eqN - equal up to N sub-version levels +# neN - not equal up to N sub-version levels +# +# When the condition is true, shell commands ACTION-IF-TRUE are run, +# otherwise shell commands ACTION-IF-FALSE are run. The environment +# variable 'ax_compare_version' is always set to either 'true' or 'false' +# as well. +# +# Examples: +# +# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) +# +# would both be true. +# +# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) +# +# would both be false. +# +# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) +# +# would be true because it is only comparing two minor versions. +# +# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) +# +# would be true because it is only comparing the lesser number of minor +# versions of the two values. +# +# Note: The characters that separate the version numbers do not matter. An +# empty string is the same as version 0. OP is evaluated by autoconf, not +# configure, so must be a string, not a variable. +# +# The author would like to acknowledge Guido Draheim whose advice about +# the m4_case and m4_ifvaln functions make this macro only include the +# portions necessary to perform the specific comparison specified by the +# OP argument in the final configure script. +# +# LICENSE +# +# Copyright (c) 2008 Tim Toolan toolan@ele.uri.edu +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +dnl ######################################################################### +AC_DEFUN([AX_COMPARE_VERSION], [ + AC_REQUIRE([AC_PROG_AWK]) + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/([[0-9]]*)/Z\1Z/g' \ + -e 's/Z([[0-9]])Z/Z0\1Z/g' \ + -e 's/Z([[0-9]][[0-9]])Z/Z0\1Z/g' \ + -e 's/Z([[0-9]][[0-9]][[0-9]])Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/([[0-9]]*)/Z\1Z/g' \ + -e 's/Z([[0-9]])Z/Z0\1Z/g' \ + -e 's/Z([[0-9]][[0-9]])Z/Z0\1Z/g' \ + -e 's/Z([[0-9]][[0-9]][[0-9]])Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/(.{$ax_compare_version_len_B}).*/\1/"` + B=`echo "$B" | sed "s/(.{$ax_compare_version_len_A}).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/(([[0-9]]{4}){m4_substr($2,2)}).*/\1/"` + B=`echo "$B" | sed "s/(([[0-9]]{4}){m4_substr($2,2)}).*/\1/"` + ], + [.+],[ + AC_WARNING( + [invalid OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + AC_WARNING([invalid OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION + diff --git a/rimage/manifest.c b/rimage/manifest.c index 257aa0d..b758717 100644 --- a/rimage/manifest.c +++ b/rimage/manifest.c @@ -539,12 +539,13 @@ static int man_module_create_reloc(struct image *image, struct module *module, return 0; }
-static int man_write_unsigned_mod(struct image *image) +static int man_write_unsigned_mod(struct image *image, int meta_start_offset, + int meta_end_offset) { int count;
/* write metadata file for unsigned FW */ - count = fwrite(image->fw_image + MAN_META_EXT_OFFSET, + count = fwrite(image->fw_image + meta_start_offset, sizeof(struct sof_man_adsp_meta_file_ext), 1, image->out_man_fd);
@@ -557,8 +558,8 @@ static int man_write_unsigned_mod(struct image *image) fclose(image->out_man_fd);
/* now prepare the unsigned rimage */ - count = fwrite(image->fw_image + MAN_FW_DESC_OFFSET, - image->image_end - MAN_FW_DESC_OFFSET, + count = fwrite(image->fw_image + meta_end_offset, + image->image_end - meta_end_offset, 1, image->out_unsigned_fd);
/* did the unsigned FW write succeed ? */ @@ -601,13 +602,59 @@ static int man_write_fw_mod(struct image *image) return 0; }
+static int man_create_modules(struct image *image, struct sof_man_fw_desc *desc) +{ + struct module *module; + struct sof_man_module *man_module; + int err; + int i; + + for (i = 0; i < image->num_modules; i++) { + man_module = sof_man_get_module(desc, i); + module = &image->module[i]; + + /* set module file offset */ + if (i == 0) + module->foffset = FILE_TEXT_OFFSET; + else + module->foffset = image->image_end; + + if (image->reloc) + err = man_module_create_reloc(image, module, + man_module); + else + err = man_module_create(image, module, man_module); + + if (err < 0) + return err; + } + + return 0; +} + +static int man_hash_modules(struct image *image, struct sof_man_fw_desc *desc) +{ + struct sof_man_module *man_module; + int i; + + for (i = 0; i < image->num_modules; i++) { + man_module = sof_man_get_module(desc, i); + + ri_hash(image, + man_module->segment[SOF_MAN_SEGMENT_TEXT].file_offset, + (man_module->segment[SOF_MAN_SEGMENT_TEXT].flags.r.length + + man_module->segment[SOF_MAN_SEGMENT_RODATA].flags.r.length) * + MAN_PAGE_SIZE, man_module->hash); + } + + return 0; +} + /* used by others */ static int man_write_fw(struct image *image) { struct sof_man_fw_desc *desc; struct fw_image_manifest *m; - struct module *module; - struct sof_man_module *man_module; uint8_t hash[SOF_MAN_MOD_SHA256_LEN]; int ret, i;
@@ -637,31 +684,13 @@ static int man_write_fw(struct image *image)
/* create each module */ m->desc.header.num_module_entries = image->num_modules; - for (i = 0; i < image->num_modules; i++) { - - man_module = sof_man_get_module(desc, i); - module = &image->module[i]; - - /* set module file offset */ - if (i == 0) { - module->foffset = FILE_TEXT_OFFSET; - } else { - module->foffset = image->image_end; - } - - if (image->reloc) - ret = man_module_create_reloc(image, module, - man_module); - else - ret = man_module_create(image, module, man_module); - if (ret < 0) - goto err; - } + man_create_modules(image, desc);
fprintf(stdout, "Firmware completing manifest\n");
/* create structures from end of file to start of file */ - ri_adsp_meta_data_create(image); + ri_adsp_meta_data_create(image, MAN_META_EXT_OFFSET, + MAN_FW_DESC_OFFSET); ri_plat_ext_data_create(image); ri_css_hdr_create(image); ri_cse_create(image); @@ -671,16 +700,7 @@ static int man_write_fw(struct image *image) desc->header.preload_page_count);
/* calculate hash for each module */ - for (i = 0; i < image->num_modules; i++) { - - module = &image->module[i]; - man_module = sof_man_get_module(desc, i); - - ri_hash(image, man_module->segment[SOF_MAN_SEGMENT_TEXT].file_offset, - (man_module->segment[SOF_MAN_SEGMENT_TEXT].flags.r.length + - man_module->segment[SOF_MAN_SEGMENT_RODATA].flags.r.length) * - MAN_PAGE_SIZE, man_module->hash); - } + man_hash_modules(image, desc);
/* calculate hash for ADSP meta data extension - 0x480 to end */ ri_hash(image, MAN_FW_DESC_OFFSET, image->image_end @@ -708,7 +728,8 @@ static int man_write_fw(struct image *image) goto err;
/* write the unsigned files*/ - ret = man_write_unsigned_mod(image); + ret = man_write_unsigned_mod(image, MAN_META_EXT_OFFSET, + MAN_FW_DESC_OFFSET); if (ret < 0) goto err;
@@ -723,6 +744,79 @@ err: return ret; }
+/* used to sign with MEU */ +static int man_write_fw_meu(struct image *image) +{ + const int meta_start_offset = image->meu_offset - + sizeof(struct sof_man_adsp_meta_file_ext) - MAN_EXT_PADDING; + struct sof_man_adsp_meta_file_ext *meta; + struct sof_man_fw_desc *desc; + uint32_t preload_size; + int ret; + + /* allocate image */ + image->fw_image = calloc(image->adsp->image_size, 1); + if (image->fw_image == NULL) { + ret = -ENOMEM; + goto err; + } + + /* open unsigned firmware */ + ret = man_open_unsigned_file(image); + if (ret < 0) + goto err; + + /* create the manifest */ + ret = man_open_manifest_file(image); + if (ret < 0) + goto err; + + /* create the module */ + meta = image->fw_image + meta_start_offset; + desc = image->fw_image + MAN_DESC_OFFSET; + + /* copy data */ + memcpy(meta, &image->adsp->man->adsp_file_ext, + sizeof(struct sof_man_adsp_meta_file_ext)); + memcpy(desc, &image->adsp->man->desc, + sizeof(struct sof_man_fw_desc)); + + /* create each module */ + desc->header.num_module_entries = image->num_modules; + man_create_modules(image, desc); + + fprintf(stdout, "Firmware completing manifest\n"); + + /* create structures from end of file to start of file */ + ri_adsp_meta_data_create(image, meta_start_offset, image->meu_offset); + + /* write preload page count */ + preload_size = meta->comp_desc[0].limit_offset - MAN_DESC_OFFSET; + preload_size += MAN_PAGE_SIZE - (preload_size % MAN_PAGE_SIZE); + desc->header.preload_page_count = preload_size / MAN_PAGE_SIZE; + + /* calculate hash for each module */ + man_hash_modules(image, desc); + + /* calculate hash for ADSP meta data extension */ + ri_hash(image, image->meu_offset, image->image_end - + image->meu_offset, meta->comp_desc[0].hash); + + /* write the unsigned files */ + ret = man_write_unsigned_mod(image, meta_start_offset, + image->meu_offset); + if (ret < 0) + goto err; + + fprintf(stdout, "Firmware manifest completed!\n"); + return 0; + +err: + free(image->fw_image); + unlink(image->out_file); + return ret; +} + #define ADSP_APL_DSP_ROM_BASE 0xBEFE0000 #define ADSP_APL_DSP_ROM_SIZE 0x00002000 #define APL_DSP_BASE_ENTRY 0xa000a000 @@ -743,6 +837,7 @@ const struct adsp machine_apl = { .dram_offset = 0, .machine_id = MACHINE_APOLLOLAKE, .write_firmware = man_write_fw, + .write_firmware_meu = man_write_fw_meu, .man = &apl_manifest, };
@@ -758,5 +853,6 @@ const struct adsp machine_cnl = { .dram_offset = 0, .machine_id = MACHINE_CANNONLAKE, .write_firmware = man_write_fw, + .write_firmware_meu = man_write_fw_meu, .man = &cnl_manifest, }; diff --git a/rimage/plat_auth.c b/rimage/plat_auth.c index 21c7138..2fd62aa 100644 --- a/rimage/plat_auth.c +++ b/rimage/plat_auth.c @@ -18,15 +18,16 @@ #include "manifest.h" #include "plat_auth.h"
-void ri_adsp_meta_data_create(struct image *image) +void ri_adsp_meta_data_create(struct image *image, int meta_start_offset, + int meta_end_offset) { struct sof_man_adsp_meta_file_ext *meta = - image->fw_image + MAN_META_EXT_OFFSET; + image->fw_image + meta_start_offset;
fprintf(stdout, " meta: completing ADSP manifest\n");
meta->comp_desc[0].limit_offset = MAN_DESC_OFFSET + image->image_end - - MAN_FW_DESC_OFFSET; + - meta_end_offset;
fprintf(stdout, " meta: limit is 0x%x\n", meta->comp_desc[0].limit_offset); diff --git a/rimage/plat_auth.h b/rimage/plat_auth.h index 135f2d1..253a78b 100644 --- a/rimage/plat_auth.h +++ b/rimage/plat_auth.h @@ -86,7 +86,8 @@ struct partition_info_ext { (sizeof(struct partition_info_ext) + \ sizeof(struct signed_pkg_info_ext))
-void ri_adsp_meta_data_create(struct image *image); +void ri_adsp_meta_data_create(struct image *image, int meta_start_offset, + int meta_end_offset); void ri_plat_ext_data_create(struct image *image);
#endif diff --git a/rimage/rimage.c b/rimage/rimage.c index cc0a610..e8b9dc8 100644 --- a/rimage/rimage.c +++ b/rimage/rimage.c @@ -39,6 +39,7 @@ static void usage(char *name) name); fprintf(stdout, "\t -v enable verbose output\n"); fprintf(stdout, "\t -r enable relocatable ELF files\n"); + fprintf(stdout, "\t -s MEU signing offset\n"); exit(0); }
@@ -50,7 +51,7 @@ int main(int argc, char *argv[])
memset(&image, 0, sizeof(image));
- while ((opt = getopt(argc, argv, "ho:m:vba:sk:l:r")) != -1) { + while ((opt = getopt(argc, argv, "ho:m:vba:s:k:l:r")) != -1) { switch (opt) { case 'o': image.out_file = optarg; @@ -62,7 +63,7 @@ int main(int argc, char *argv[]) image.verbose = 1; break; case 's': - image.dump_sections = 1; + image.meu_offset = atoi(optarg); break; case 'a': image.abi = atoi(optarg); @@ -130,7 +131,10 @@ found: }
/* process and write output */ - ret = image.adsp->write_firmware(&image); + if (image.meu_offset) + ret = image.adsp->write_firmware_meu(&image); + else + ret = image.adsp->write_firmware(&image); out: /* close files */ if (image.out_fd) diff --git a/rimage/rimage.h b/rimage/rimage.h index 888e7e8..3398ece 100644 --- a/rimage/rimage.h +++ b/rimage/rimage.h @@ -99,7 +99,7 @@ struct image { int num_modules; struct module module[MAX_MODULES]; uint32_t image_end;/* module end, equal to output image size */ - int dump_sections; + int meu_offset;
/* SHA 256 */ const char *key_name; @@ -140,6 +140,7 @@ struct adsp {
enum machine_id machine_id; int (*write_firmware)(struct image *image); + int (*write_firmware_meu)(struct image *image); struct fw_image_manifest *man; };
diff --git a/src/arch/xtensa/Makefile.am b/src/arch/xtensa/Makefile.am index 50d3ded..4ffe15f 100644 --- a/src/arch/xtensa/Makefile.am +++ b/src/arch/xtensa/Makefile.am @@ -151,13 +151,23 @@ MODULE_COPY= MODULE_INSERT= endif
+if USE_MEU +RIMAGE=rimage -o sof-$(FW_NAME).ri -m $(FW_NAME) $(RIMAGE_BOOT_FLAGS) $(RIMAGE_FLAGS) -s $(MEU_OFFSET) +MEU=$(MEU_PATH)/meu -w ./ -s sof-$(FW_NAME) -key $(PRIVATE_KEY) -stp /usr/bin/openssl -f $(MEU_PATH)/generic_meu_conf.xml \ + -mnver 0.0.0.0 -o sof-$(FW_NAME).ri +else +RIMAGE=rimage -o sof-$(FW_NAME).ri -m $(FW_NAME) $(RIMAGE_BOOT_FLAGS) $(RIMAGE_FLAGS) +MEU= +endif + bin-local: $(BIN_FLAGS) cp sof sof-$(FW_NAME) $(MODULE_COPY) $(MODULE_INSERT) $(OBJDUMP) -S sof-$(FW_NAME) > sof-$(FW_NAME).lst $(OBJDUMP) -D sof-$(FW_NAME) > sof-$(FW_NAME).dis - rimage -o sof-$(FW_NAME).ri -m $(FW_NAME) $(RIMAGE_BOOT_FLAGS) $(RIMAGE_FLAGS) + $(RIMAGE) + $(MEU)
vminstall-local: scp -P 5555 sof-*.ri root@localhost:/lib/firmware/intel/