diff options
Diffstat (limited to 'tools')
22 files changed, 1365 insertions, 395 deletions
diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py index 3a4dbdd6d7..aad3b61ae2 100644 --- a/tools/binman/btool/openssl.py +++ b/tools/binman/btool/openssl.py @@ -15,6 +15,13 @@ import hashlib from binman import bintool from u_boot_pylib import tools + +VALID_SHAS = [256, 384, 512, 224] +SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1', + 384:'2.16.840.1.101.3.4.2.2', + 512:'2.16.840.1.101.3.4.2.3', + 224:'2.16.840.1.101.3.4.2.4'} + class Bintoolopenssl(bintool.Bintool): """openssl tool @@ -74,6 +81,243 @@ imageSize = INTEGER:{len(indata)} '-sha512'] return self.run_cmd(*args) + def x509_cert_sysfw(self, cert_fname, input_fname, key_fname, sw_rev, + config_fname, req_dist_name_dict): + """Create a certificate to be booted by system firmware + + Args: + cert_fname (str): Filename of certificate to create + input_fname (str): Filename containing data to sign + key_fname (str): Filename of .pem file + sw_rev (int): Software revision + config_fname (str): Filename to write fconfig into + req_dist_name_dict (dict): Dictionary containing key-value pairs of + req_distinguished_name section extensions, must contain extensions for + C, ST, L, O, OU, CN and emailAddress + + Returns: + str: Tool output + """ + indata = tools.read_file(input_fname) + hashval = hashlib.sha512(indata).hexdigest() + with open(config_fname, 'w', encoding='utf-8') as outf: + print(f'''[ req ] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no +dirstring_type = nobmp + +[ req_distinguished_name ] +C = {req_dist_name_dict['C']} +ST = {req_dist_name_dict['ST']} +L = {req_dist_name_dict['L']} +O = {req_dist_name_dict['O']} +OU = {req_dist_name_dict['OU']} +CN = {req_dist_name_dict['CN']} +emailAddress = {req_dist_name_dict['emailAddress']} + +[ v3_ca ] +basicConstraints = CA:true +1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv +1.3.6.1.4.1.294.1.34 = ASN1:SEQUENCE:sysfw_image_integrity +1.3.6.1.4.1.294.1.35 = ASN1:SEQUENCE:sysfw_image_load + +[ swrv ] +swrv = INTEGER:{sw_rev} + +[ sysfw_image_integrity ] +shaType = OID:2.16.840.1.101.3.4.2.3 +shaValue = FORMAT:HEX,OCT:{hashval} +imageSize = INTEGER:{len(indata)} + +[ sysfw_image_load ] +destAddr = FORMAT:HEX,OCT:00000000 +authInPlace = INTEGER:2 +''', file=outf) + args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', + '-outform', 'DER', '-out', cert_fname, '-config', config_fname, + '-sha512'] + return self.run_cmd(*args) + + def x509_cert_rom(self, cert_fname, input_fname, key_fname, sw_rev, + config_fname, req_dist_name_dict, cert_type, bootcore, + bootcore_opts, load_addr, sha): + """Create a certificate + + Args: + cert_fname (str): Filename of certificate to create + input_fname (str): Filename containing data to sign + key_fname (str): Filename of .pem file + sw_rev (int): Software revision + config_fname (str): Filename to write fconfig into + req_dist_name_dict (dict): Dictionary containing key-value pairs of + req_distinguished_name section extensions, must contain extensions for + C, ST, L, O, OU, CN and emailAddress + cert_type (int): Certification type + bootcore (int): Booting core + load_addr (int): Load address of image + sha (int): Hash function + + Returns: + str: Tool output + """ + indata = tools.read_file(input_fname) + hashval = hashlib.sha512(indata).hexdigest() + with open(config_fname, 'w', encoding='utf-8') as outf: + print(f''' +[ req ] + distinguished_name = req_distinguished_name + x509_extensions = v3_ca + prompt = no + dirstring_type = nobmp + + [ req_distinguished_name ] +C = {req_dist_name_dict['C']} +ST = {req_dist_name_dict['ST']} +L = {req_dist_name_dict['L']} +O = {req_dist_name_dict['O']} +OU = {req_dist_name_dict['OU']} +CN = {req_dist_name_dict['CN']} +emailAddress = {req_dist_name_dict['emailAddress']} + + [ v3_ca ] + basicConstraints = CA:true + 1.3.6.1.4.1.294.1.1 = ASN1:SEQUENCE:boot_seq + 1.3.6.1.4.1.294.1.2 = ASN1:SEQUENCE:image_integrity + 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv +# 1.3.6.1.4.1.294.1.4 = ASN1:SEQUENCE:encryption + 1.3.6.1.4.1.294.1.8 = ASN1:SEQUENCE:debug + + [ boot_seq ] + certType = INTEGER:{cert_type} + bootCore = INTEGER:{bootcore} + bootCoreOpts = INTEGER:{bootcore_opts} + destAddr = FORMAT:HEX,OCT:{load_addr:08x} + imageSize = INTEGER:{len(indata)} + + [ image_integrity ] + shaType = OID:{SHA_OIDS[sha]} + shaValue = FORMAT:HEX,OCT:{hashval} + + [ swrv ] + swrv = INTEGER:{sw_rev} + +# [ encryption ] +# initalVector = FORMAT:HEX,OCT:TEST_IMAGE_ENC_IV +# randomString = FORMAT:HEX,OCT:TEST_IMAGE_ENC_RS +# iterationCnt = INTEGER:TEST_IMAGE_KEY_DERIVE_INDEX +# salt = FORMAT:HEX,OCT:TEST_IMAGE_KEY_DERIVE_SALT + + [ debug ] + debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 + debugType = INTEGER:4 + coreDbgEn = INTEGER:0 + coreDbgSecEn = INTEGER:0 +''', file=outf) + args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', + '-outform', 'DER', '-out', cert_fname, '-config', config_fname, + '-sha512'] + return self.run_cmd(*args) + + def x509_cert_rom_combined(self, cert_fname, input_fname, key_fname, sw_rev, + config_fname, req_dist_name_dict, load_addr, sha, total_size, num_comps, + sysfw_inner_cert_ext_boot_sequence_string, dm_data_ext_boot_sequence_string, + imagesize_sbl, hashval_sbl, load_addr_sysfw, imagesize_sysfw, + hashval_sysfw, load_addr_sysfw_data, imagesize_sysfw_data, + hashval_sysfw_data, sysfw_inner_cert_ext_boot_block, + dm_data_ext_boot_block): + """Create a certificate + + Args: + cert_fname (str): Filename of certificate to create + input_fname (str): Filename containing data to sign + key_fname (str): Filename of .pem file + sw_rev (int): Software revision + config_fname (str): Filename to write fconfig into + req_dist_name_dict (dict): Dictionary containing key-value pairs of + req_distinguished_name section extensions, must contain extensions for + C, ST, L, O, OU, CN and emailAddress + cert_type (int): Certification type + bootcore (int): Booting core + load_addr (int): Load address of image + sha (int): Hash function + + Returns: + str: Tool output + """ + indata = tools.read_file(input_fname) + hashval = hashlib.sha512(indata).hexdigest() + sha_type = SHA_OIDS[sha] + with open(config_fname, 'w', encoding='utf-8') as outf: + print(f''' +[ req ] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no +dirstring_type = nobmp + +[ req_distinguished_name ] +C = {req_dist_name_dict['C']} +ST = {req_dist_name_dict['ST']} +L = {req_dist_name_dict['L']} +O = {req_dist_name_dict['O']} +OU = {req_dist_name_dict['OU']} +CN = {req_dist_name_dict['CN']} +emailAddress = {req_dist_name_dict['emailAddress']} + +[ v3_ca ] +basicConstraints = CA:true +1.3.6.1.4.1.294.1.3=ASN1:SEQUENCE:swrv +1.3.6.1.4.1.294.1.9=ASN1:SEQUENCE:ext_boot_info + +[swrv] +swrv=INTEGER:{sw_rev} + +[ext_boot_info] +extImgSize=INTEGER:{total_size} +numComp=INTEGER:{num_comps} +sbl=SEQUENCE:sbl +sysfw=SEQUENCE:sysfw +sysfw_data=SEQUENCE:sysfw_data +{sysfw_inner_cert_ext_boot_sequence_string} +{dm_data_ext_boot_sequence_string} + +[sbl] +compType = INTEGER:1 +bootCore = INTEGER:16 +compOpts = INTEGER:0 +destAddr = FORMAT:HEX,OCT:{load_addr:08x} +compSize = INTEGER:{imagesize_sbl} +shaType = OID:{sha_type} +shaValue = FORMAT:HEX,OCT:{hashval_sbl} + +[sysfw] +compType = INTEGER:2 +bootCore = INTEGER:0 +compOpts = INTEGER:0 +destAddr = FORMAT:HEX,OCT:{load_addr_sysfw:08x} +compSize = INTEGER:{imagesize_sysfw} +shaType = OID:{sha_type} +shaValue = FORMAT:HEX,OCT:{hashval_sysfw} + +[sysfw_data] +compType = INTEGER:18 +bootCore = INTEGER:0 +compOpts = INTEGER:0 +destAddr = FORMAT:HEX,OCT:{load_addr_sysfw_data:08x} +compSize = INTEGER:{imagesize_sysfw_data} +shaType = OID:{sha_type} +shaValue = FORMAT:HEX,OCT:{hashval_sysfw_data} + +{sysfw_inner_cert_ext_boot_block} + +{dm_data_ext_boot_block} + ''', file=outf) + args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', + '-outform', 'DER', '-out', cert_fname, '-config', config_fname, + '-sha512'] + return self.run_cmd(*args) + def fetch(self, method): """Fetch handler for openssl diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b55f424620..1621ff30ca 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1664,6 +1664,119 @@ by setting the size of the entry to something larger than the text. +.. _etype_ti_board_config: + +Entry: ti-board-config: An entry containing a TI schema validated board config binary +------------------------------------------------------------------------------------- + +This etype supports generation of two kinds of board configuration +binaries: singular board config binary as well as combined board config +binary. + +Properties / Entry arguments: + - config-file: File containing board configuration data in YAML + - schema-file: File containing board configuration YAML schema against + which the config file is validated + +Output files: + - board config binary: File containing board configuration binary + +These above parameters are used only when the generated binary is +intended to be a single board configuration binary. Example:: + + my-ti-board-config { + ti-board-config { + config = "board-config.yaml"; + schema = "schema.yaml"; + }; + }; + +To generate a combined board configuration binary, we pack the +needed individual binaries into a ti-board-config binary. In this case, +the available supported subnode names are board-cfg, pm-cfg, sec-cfg and +rm-cfg. The final binary is prepended with a header containing details about +the included board config binaries. Example:: + + my-combined-ti-board-config { + ti-board-config { + board-cfg { + config = "board-cfg.yaml"; + schema = "schema.yaml"; + }; + sec-cfg { + config = "sec-cfg.yaml"; + schema = "schema.yaml"; + }; + } + } + + + +.. _etype_ti_secure: + +Entry: ti-secure: Entry containing a TI x509 certificate binary +--------------------------------------------------------------- + +Properties / Entry arguments: + - content: List of phandles to entries to sign + - keyfile: Filename of file containing key to sign binary with + - sha: Hash function to be used for signing + +Output files: + - input.<unique_name> - input file passed to openssl + - config.<unique_name> - input file generated for openssl (which is + used as the config file) + - cert.<unique_name> - output file generated by openssl (which is + used as the entry contents) + +openssl signs the provided data, using the TI templated config file and +writes the signature in this entry. This allows verification that the +data is genuine. + + + +.. _etype_ti_secure_rom: + +Entry: ti-secure-rom: Entry containing a TI x509 certificate binary for images booted by ROM +-------------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - keyfile: Filename of file containing key to sign binary with + - combined: boolean if device follows combined boot flow + - countersign: boolean if device contains countersigned system firmware + - load: load address of SPL + - sw-rev: software revision + - sha: Hash function to be used for signing + - core: core on which bootloader runs, valid cores are 'secure' and 'public' + - content: phandle of SPL in case of legacy bootflow or phandles of component binaries + in case of combined bootflow + +The following properties are only for generating a combined bootflow binary: + - sysfw-inner-cert: boolean if binary contains sysfw inner certificate + - dm-data: boolean if binary contains dm-data binary + - content-sbl: phandle of SPL binary + - content-sysfw: phandle of sysfw binary + - content-sysfw-data: phandle of sysfw-data or tifs-data binary + - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary + - content-dm-data (optional): phandle of dm-data binary + - load-sysfw: load address of sysfw binary + - load-sysfw-data: load address of sysfw-data or tifs-data binary + - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary + - load-dm-data (optional): load address of dm-data binary + +Output files: + - input.<unique_name> - input file passed to openssl + - config.<unique_name> - input file generated for openssl (which is + used as the config file) + - cert.<unique_name> - output file generated by openssl (which is + used as the entry contents) + +openssl signs the provided data, using the TI templated config file and +writes the signature in this entry. This allows verification that the +data is genuine. + + + .. _etype_u_boot: Entry: u-boot: U-Boot flat binary diff --git a/tools/binman/etype/ti_board_config.py b/tools/binman/etype/ti_board_config.py new file mode 100644 index 0000000000..94f894c281 --- /dev/null +++ b/tools/binman/etype/ti_board_config.py @@ -0,0 +1,259 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ +# Written by Neha Malcom Francis <n-francis@ti.com> +# +# Entry-type module for generating schema validated TI board +# configuration binary +# + +import os +import struct +import yaml + +from collections import OrderedDict +from jsonschema import validate +from shutil import copyfileobj + +from binman.entry import Entry +from binman.etype.section import Entry_section +from dtoc import fdt_util +from u_boot_pylib import tools + +BOARDCFG = 0xB +BOARDCFG_SEC = 0xD +BOARDCFG_PM = 0xE +BOARDCFG_RM = 0xC +BOARDCFG_NUM_ELEMS = 4 + +class Entry_ti_board_config(Entry_section): + """An entry containing a TI schema validated board config binary + + This etype supports generation of two kinds of board configuration + binaries: singular board config binary as well as combined board config + binary. + + Properties / Entry arguments: + - config-file: File containing board configuration data in YAML + - schema-file: File containing board configuration YAML schema against + which the config file is validated + + Output files: + - board config binary: File containing board configuration binary + + These above parameters are used only when the generated binary is + intended to be a single board configuration binary. Example:: + + my-ti-board-config { + ti-board-config { + config = "board-config.yaml"; + schema = "schema.yaml"; + }; + }; + + To generate a combined board configuration binary, we pack the + needed individual binaries into a ti-board-config binary. In this case, + the available supported subnode names are board-cfg, pm-cfg, sec-cfg and + rm-cfg. The final binary is prepended with a header containing details about + the included board config binaries. Example:: + + my-combined-ti-board-config { + ti-board-config { + board-cfg { + config = "board-cfg.yaml"; + schema = "schema.yaml"; + }; + sec-cfg { + config = "sec-cfg.yaml"; + schema = "schema.yaml"; + }; + } + } + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self._config = None + self._schema = None + self._entries = OrderedDict() + self._num_elems = BOARDCFG_NUM_ELEMS + self._fmt = '<HHHBB' + self._index = 0 + self._binary_offset = 0 + self._sw_rev = 1 + self._devgrp = 0 + + def ReadNode(self): + super().ReadNode() + self._config = fdt_util.GetString(self._node, 'config') + self._schema = fdt_util.GetString(self._node, 'schema') + # Depending on whether config file is present in node, we determine + # whether it is a combined board config binary or not + if self._config is None: + self.ReadEntries() + else: + self._config_file = tools.get_input_filename(self._config) + self._schema_file = tools.get_input_filename(self._schema) + + def ReadEntries(self): + """Read the subnodes to find out what should go in this image + """ + for node in self._node.subnodes: + if 'type' not in node.props: + entry = Entry.Create(self, node, 'ti-board-config') + entry.ReadNode() + cfg_data = entry.BuildSectionData(True) + entry._cfg_data = cfg_data + self._entries[entry.name] = entry + self._num_elems = len(self._node.subnodes) + + def _convert_to_byte_chunk(self, val, data_type): + """Convert value into byte array + + Args: + val: value to convert into byte array + data_type: data type used in schema, supported data types are u8, + u16 and u32 + + Returns: + array of bytes representing value + """ + size = 0 + if (data_type == '#/definitions/u8'): + size = 1 + elif (data_type == '#/definitions/u16'): + size = 2 + else: + size = 4 + if type(val) == int: + br = val.to_bytes(size, byteorder='little') + return br + + def _compile_yaml(self, schema_yaml, file_yaml): + """Convert YAML file into byte array based on YAML schema + + Args: + schema_yaml: file containing YAML schema + file_yaml: file containing config to compile + + Returns: + array of bytes repesenting YAML file against YAML schema + """ + br = bytearray() + for key, node in file_yaml.items(): + node_schema = schema_yaml['properties'][key] + node_type = node_schema.get('type') + if not 'type' in node_schema: + br += self._convert_to_byte_chunk(node, + node_schema.get('$ref')) + elif node_type == 'object': + br += self._compile_yaml(node_schema, node) + elif node_type == 'array': + for item in node: + if not isinstance(item, dict): + br += self._convert_to_byte_chunk( + item, schema_yaml['properties'][key]['items']['$ref']) + else: + br += self._compile_yaml(node_schema.get('items'), item) + return br + + def _generate_binaries(self): + """Generate config binary artifacts from the loaded YAML configuration file + + Returns: + byte array containing config binary artifacts + or None if generation fails + """ + cfg_binary = bytearray() + for key, node in self.file_yaml.items(): + node_schema = self.schema_yaml['properties'][key] + br = self._compile_yaml(node_schema, node) + cfg_binary += br + return cfg_binary + + def _add_boardcfg(self, bcfgtype, bcfgdata): + """Add board config to combined board config binary + + Args: + bcfgtype (int): board config type + bcfgdata (byte array): board config data + """ + size = len(bcfgdata) + desc = struct.pack(self._fmt, bcfgtype, + self._binary_offset, size, self._devgrp, 0) + with open(self.descfile, 'ab+') as desc_fh: + desc_fh.write(desc) + with open(self.bcfgfile, 'ab+') as bcfg_fh: + bcfg_fh.write(bcfgdata) + self._binary_offset += size + self._index += 1 + + def _finalize(self): + """Generate final combined board config binary + + Returns: + byte array containing combined board config data + or None if unable to generate + """ + with open(self.descfile, 'rb') as desc_fh: + with open(self.bcfgfile, 'rb') as bcfg_fh: + with open(self.fh_file, 'ab+') as fh: + copyfileobj(desc_fh, fh) + copyfileobj(bcfg_fh, fh) + data = tools.read_file(self.fh_file) + return data + + def BuildSectionData(self, required): + if self._config is None: + self._binary_offset = 0 + uniq = self.GetUniqueName() + self.fh_file = tools.get_output_filename('fh.%s' % uniq) + self.descfile = tools.get_output_filename('desc.%s' % uniq) + self.bcfgfile = tools.get_output_filename('bcfg.%s' % uniq) + + # when binman runs again make sure we start clean + if os.path.exists(self.fh_file): + os.remove(self.fh_file) + if os.path.exists(self.descfile): + os.remove(self.descfile) + if os.path.exists(self.bcfgfile): + os.remove(self.bcfgfile) + + with open(self.fh_file, 'wb') as f: + t_bytes = f.write(struct.pack( + '<BB', self._num_elems, self._sw_rev)) + self._binary_offset += t_bytes + self._binary_offset += self._num_elems * struct.calcsize(self._fmt) + + if 'board-cfg' in self._entries: + self._add_boardcfg(BOARDCFG, self._entries['board-cfg']._cfg_data) + + if 'sec-cfg' in self._entries: + self._add_boardcfg(BOARDCFG_SEC, self._entries['sec-cfg']._cfg_data) + + if 'pm-cfg' in self._entries: + self._add_boardcfg(BOARDCFG_PM, self._entries['pm-cfg']._cfg_data) + + if 'rm-cfg' in self._entries: + self._add_boardcfg(BOARDCFG_RM, self._entries['rm-cfg']._cfg_data) + + data = self._finalize() + return data + + else: + with open(self._config_file, 'r') as f: + self.file_yaml = yaml.safe_load(f) + with open(self._schema_file, 'r') as sch: + self.schema_yaml = yaml.safe_load(sch) + + try: + validate(self.file_yaml, self.schema_yaml) + except Exception as e: + self.Raise(f"Schema validation error: {e}") + + data = self._generate_binaries() + return data + + def SetImagePos(self, image_pos): + Entry.SetImagePos(self, image_pos) + + def CheckEntries(self): + Entry.CheckEntries(self) diff --git a/tools/binman/etype/ti_secure.py b/tools/binman/etype/ti_secure.py new file mode 100644 index 0000000000..d939dce571 --- /dev/null +++ b/tools/binman/etype/ti_secure.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ +# Written by Neha Malcom Francis <n-francis@ti.com> +# + +# Support for generation of TI secured binary blobs + +from binman.entry import EntryArg +from binman.etype.x509_cert import Entry_x509_cert + +from dtoc import fdt_util + +class Entry_ti_secure(Entry_x509_cert): + """Entry containing a TI x509 certificate binary + + Properties / Entry arguments: + - content: List of phandles to entries to sign + - keyfile: Filename of file containing key to sign binary with + - sha: Hash function to be used for signing + + Output files: + - input.<unique_name> - input file passed to openssl + - config.<unique_name> - input file generated for openssl (which is + used as the config file) + - cert.<unique_name> - output file generated by openssl (which is + used as the entry contents) + + openssl signs the provided data, using the TI templated config file and + writes the signature in this entry. This allows verification that the + data is genuine. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.openssl = None + + def ReadNode(self): + super().ReadNode() + self.key_fname = self.GetEntryArgsOrProps([ + EntryArg('keyfile', str)], required=True)[0] + self.sha = fdt_util.GetInt(self._node, 'sha', 512) + self.req_dist_name = {'C': 'US', + 'ST': 'TX', + 'L': 'Dallas', + 'O': 'Texas Instruments Incorporated', + 'OU': 'Processors', + 'CN': 'TI Support', + 'emailAddress': 'support@ti.com'} + + def GetCertificate(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the certificate binary for the + provided data + """ + return super().GetCertificate(required=required, type='sysfw') + + def ObtainContents(self): + data = self.data + if data is None: + data = self.GetCertificate(False) + if data is None: + return False + self.SetContents(data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + data = self.data + return self.ProcessContentsUpdate(data) + + def AddBintools(self, btools): + super().AddBintools(btools) + self.openssl = self.AddBintool(btools, 'openssl') diff --git a/tools/binman/etype/ti_secure_rom.py b/tools/binman/etype/ti_secure_rom.py new file mode 100644 index 0000000000..9a7ac9e9e0 --- /dev/null +++ b/tools/binman/etype/ti_secure_rom.py @@ -0,0 +1,249 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ +# Written by Neha Malcom Francis <n-francis@ti.com> +# + +# Support for generation of TI secured bootloaders booted by ROM + +from binman.entry import EntryArg +from binman.etype.x509_cert import Entry_x509_cert + +import hashlib + +from dtoc import fdt_util +from u_boot_pylib import tools + +VALID_SHAS = [256, 384, 512, 224] +SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1', + 384:'2.16.840.1.101.3.4.2.2', + 512:'2.16.840.1.101.3.4.2.3', + 224:'2.16.840.1.101.3.4.2.4'} + +class Entry_ti_secure_rom(Entry_x509_cert): + """Entry containing a TI x509 certificate binary for images booted by ROM + + Properties / Entry arguments: + - keyfile: Filename of file containing key to sign binary with + - combined: boolean if device follows combined boot flow + - countersign: boolean if device contains countersigned system firmware + - load: load address of SPL + - sw-rev: software revision + - sha: Hash function to be used for signing + - core: core on which bootloader runs, valid cores are 'secure' and 'public' + - content: phandle of SPL in case of legacy bootflow or phandles of component binaries + in case of combined bootflow + + The following properties are only for generating a combined bootflow binary: + - sysfw-inner-cert: boolean if binary contains sysfw inner certificate + - dm-data: boolean if binary contains dm-data binary + - content-sbl: phandle of SPL binary + - content-sysfw: phandle of sysfw binary + - content-sysfw-data: phandle of sysfw-data or tifs-data binary + - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary + - content-dm-data (optional): phandle of dm-data binary + - load-sysfw: load address of sysfw binary + - load-sysfw-data: load address of sysfw-data or tifs-data binary + - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary + - load-dm-data (optional): load address of dm-data binary + + Output files: + - input.<unique_name> - input file passed to openssl + - config.<unique_name> - input file generated for openssl (which is + used as the config file) + - cert.<unique_name> - output file generated by openssl (which is + used as the entry contents) + + openssl signs the provided data, using the TI templated config file and + writes the signature in this entry. This allows verification that the + data is genuine. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.openssl = None + + def ReadNode(self): + super().ReadNode() + self.combined = fdt_util.GetBool(self._node, 'combined', False) + self.countersign = fdt_util.GetBool(self._node, 'countersign', False) + self.load_addr = fdt_util.GetInt(self._node, 'load', 0x00000000) + self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1) + self.sha = fdt_util.GetInt(self._node, 'sha', 512) + self.core = fdt_util.GetString(self._node, 'core', 'secure') + self.key_fname = self.GetEntryArgsOrProps([ + EntryArg('keyfile', str)], required=True)[0] + if self.combined: + self.sysfw_inner_cert = fdt_util.GetBool(self._node, 'sysfw-inner-cert', False) + self.load_addr_sysfw = fdt_util.GetInt(self._node, 'load-sysfw', 0x00000000) + self.load_addr_sysfw_data = fdt_util.GetInt(self._node, 'load-sysfw-data', 0x00000000) + self.dm_data = fdt_util.GetBool(self._node, 'dm-data', False) + if self.dm_data: + self.load_addr_dm_data = fdt_util.GetInt(self._node, 'load-dm-data', 0x00000000) + self.req_dist_name = {'C': 'US', + 'ST': 'TX', + 'L': 'Dallas', + 'O': 'Texas Instruments Incorporated', + 'OU': 'Processors', + 'CN': 'TI Support', + 'emailAddress': 'support@ti.com'} + + def NonCombinedGetCertificate(self, required): + """Generate certificate for legacy boot flow + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the certificate binary for the + provided data + """ + if self.core == 'secure': + if self.countersign: + self.cert_type = 3 + else: + self.cert_type = 2 + self.bootcore = 0 + self.bootcore_opts = 32 + else: + self.cert_type = 1 + self.bootcore = 16 + self.bootcore_opts = 0 + return super().GetCertificate(required=required, type='rom') + + def CombinedGetCertificate(self, required): + """Generate certificate for combined boot flow + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the certificate binary for the + provided data + """ + uniq = self.GetUniqueName() + + self.num_comps = 3 + self.sha_type = SHA_OIDS[self.sha] + + # sbl + self.content = fdt_util.GetPhandleList(self._node, 'content-sbl') + input_data_sbl = self.GetContents(required) + if input_data_sbl is None: + return None + + input_fname_sbl = tools.get_output_filename('input.%s' % uniq) + tools.write_file(input_fname_sbl, input_data_sbl) + + indata_sbl = tools.read_file(input_fname_sbl) + self.hashval_sbl = hashlib.sha512(indata_sbl).hexdigest() + self.imagesize_sbl = len(indata_sbl) + + # sysfw + self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw') + input_data_sysfw = self.GetContents(required) + + input_fname_sysfw = tools.get_output_filename('input.%s' % uniq) + tools.write_file(input_fname_sysfw, input_data_sysfw) + + indata_sysfw = tools.read_file(input_fname_sysfw) + self.hashval_sysfw = hashlib.sha512(indata_sysfw).hexdigest() + self.imagesize_sysfw = len(indata_sysfw) + + # sysfw data + self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-data') + input_data_sysfw_data = self.GetContents(required) + + input_fname_sysfw_data = tools.get_output_filename('input.%s' % uniq) + tools.write_file(input_fname_sysfw_data, input_data_sysfw_data) + + indata_sysfw_data = tools.read_file(input_fname_sysfw_data) + self.hashval_sysfw_data = hashlib.sha512(indata_sysfw_data).hexdigest() + self.imagesize_sysfw_data = len(indata_sysfw_data) + + # sysfw inner cert + self.sysfw_inner_cert_ext_boot_block = "" + self.sysfw_inner_cert_ext_boot_sequence_string = "" + imagesize_sysfw_inner_cert = 0 + if self.sysfw_inner_cert: + self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert') + input_data_sysfw_inner_cert = self.GetContents(required) + + input_fname_sysfw_inner_cert = tools.get_output_filename('input.%s' % uniq) + tools.write_file(input_fname_sysfw_inner_cert, input_data_sysfw_inner_cert) + + indata_sysfw_inner_cert = tools.read_file(input_fname_sysfw_inner_cert) + hashval_sysfw_inner_cert = hashlib.sha512(indata_sysfw_inner_cert).hexdigest() + imagesize_sysfw_inner_cert = len(indata_sysfw_inner_cert) + self.num_comps += 1 + self.sysfw_inner_cert_ext_boot_sequence_string = "sysfw_inner_cert=SEQUENCE:sysfw_inner_cert" + self.sysfw_inner_cert_ext_boot_block = f"""[sysfw_inner_cert] +compType = INTEGER:3 +bootCore = INTEGER:0 +compOpts = INTEGER:0 +destAddr = FORMAT:HEX,OCT:00000000 +compSize = INTEGER:{imagesize_sysfw_inner_cert} +shaType = OID:{self.sha_type} +shaValue = FORMAT:HEX,OCT:{hashval_sysfw_inner_cert}""" + + # dm data + self.dm_data_ext_boot_sequence_string = "" + self.dm_data_ext_boot_block = "" + imagesize_dm_data = 0 + if self.dm_data: + self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data') + input_data_dm_data = self.GetContents(required) + + input_fname_dm_data = tools.get_output_filename('input.%s' % uniq) + tools.write_file(input_fname_dm_data, input_data_dm_data) + + indata_dm_data = tools.read_file(input_fname_dm_data) + hashval_dm_data = hashlib.sha512(indata_dm_data).hexdigest() + imagesize_dm_data = len(indata_dm_data) + self.num_comps += 1 + self.dm_data_ext_boot_sequence_string = "dm_data=SEQUENCE:dm_data" + self.dm_data_ext_boot_block = f"""[dm_data] +compType = INTEGER:17 +bootCore = INTEGER:16 +compOpts = INTEGER:0 +destAddr = FORMAT:HEX,OCT:{self.load_addr_dm_data:08x} +compSize = INTEGER:{imagesize_dm_data} +shaType = OID:{self.sha_type} +shaValue = FORMAT:HEX,OCT:{hashval_dm_data}""" + + self.total_size = self.imagesize_sbl + self.imagesize_sysfw + self.imagesize_sysfw_data + imagesize_sysfw_inner_cert + imagesize_dm_data + return super().GetCertificate(required=required, type='rom-combined') + + def GetCertificate(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the certificate binary for the + provided data + """ + if self.combined: + return self.CombinedGetCertificate(required) + else: + return self.NonCombinedGetCertificate(required) + + def ObtainContents(self): + data = self.data + if data is None: + data = self.GetCertificate(False) + if data is None: + return False + self.SetContents(data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + data = self.data + return self.ProcessContentsUpdate(data) + + def AddBintools(self, btools): + super().AddBintools(btools) + self.openssl = self.AddBintool(btools, 'openssl') diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py index f80a6ec2d1..d028cfe38c 100644 --- a/tools/binman/etype/x509_cert.py +++ b/tools/binman/etype/x509_cert.py @@ -31,6 +31,26 @@ class Entry_x509_cert(Entry_collection): def __init__(self, section, etype, node): super().__init__(section, etype, node) self.openssl = None + self.req_dist_name = None + self.cert_type = None + self.bootcore = None + self.bootcore_opts = None + self.load_addr = None + self.sha = None + self.total_size = None + self.num_comps = None + self.sysfw_inner_cert_ext_boot_sequence_string = None + self.dm_data_ext_boot_sequence_string = None + self.imagesize_sbl = None + self.hashval_sbl = None + self.load_addr_sysfw = None + self.imagesize_sysfw = None + self.hashval_sysfw = None + self.load_addr_sysfw_data = None + self.imagesize_sysfw_data = None + self.hashval_sysfw_data = None + self.sysfw_inner_cert_ext_boot_block = None + self.dm_data_ext_boot_block = None def ReadNode(self): super().ReadNode() @@ -38,13 +58,16 @@ class Entry_x509_cert(Entry_collection): self._cert_rev = fdt_util.GetInt(self._node, 'cert-revision-int', 0) self.key_fname = self.GetEntryArgsOrProps([ EntryArg('keyfile', str)], required=True)[0] + self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1) - def GetCertificate(self, required): + def GetCertificate(self, required, type='generic'): """Get the contents of this entry Args: required: True if the data must be present, False if it is OK to return None + type: Type of x509 certificate to generate, current supported ones are + 'generic', 'sysfw', 'rom' Returns: bytes content of the entry, which is the signed vblock for the @@ -60,13 +83,61 @@ class Entry_x509_cert(Entry_collection): input_fname = tools.get_output_filename('input.%s' % uniq) config_fname = tools.get_output_filename('config.%s' % uniq) tools.write_file(input_fname, input_data) - stdout = self.openssl.x509_cert( - cert_fname=output_fname, - input_fname=input_fname, - key_fname=self.key_fname, - cn=self._cert_ca, - revision=self._cert_rev, - config_fname=config_fname) + if type == 'generic': + stdout = self.openssl.x509_cert( + cert_fname=output_fname, + input_fname=input_fname, + key_fname=self.key_fname, + cn=self._cert_ca, + revision=self._cert_rev, + config_fname=config_fname) + elif type == 'sysfw': + stdout = self.openssl.x509_cert_sysfw( + cert_fname=output_fname, + input_fname=input_fname, + key_fname=self.key_fname, + config_fname=config_fname, + sw_rev=self.sw_rev, + req_dist_name_dict=self.req_dist_name) + elif type == 'rom': + stdout = self.openssl.x509_cert_rom( + cert_fname=output_fname, + input_fname=input_fname, + key_fname=self.key_fname, + config_fname=config_fname, + sw_rev=self.sw_rev, + req_dist_name_dict=self.req_dist_name, + cert_type=self.cert_type, + bootcore=self.bootcore, + bootcore_opts=self.bootcore_opts, + load_addr=self.load_addr, + sha=self.sha + ) + elif type == 'rom-combined': + stdout = self.openssl.x509_cert_rom_combined( + cert_fname=output_fname, + input_fname=input_fname, + key_fname=self.key_fname, + config_fname=config_fname, + sw_rev=self.sw_rev, + req_dist_name_dict=self.req_dist_name, + load_addr=self.load_addr, + sha=self.sha, + total_size=self.total_size, + num_comps=self.num_comps, + sysfw_inner_cert_ext_boot_sequence_string=self.sysfw_inner_cert_ext_boot_sequence_string, + dm_data_ext_boot_sequence_string=self.dm_data_ext_boot_sequence_string, + imagesize_sbl=self.imagesize_sbl, + hashval_sbl=self.hashval_sbl, + load_addr_sysfw=self.load_addr_sysfw, + imagesize_sysfw=self.imagesize_sysfw, + hashval_sysfw=self.hashval_sysfw, + load_addr_sysfw_data=self.load_addr_sysfw_data, + imagesize_sysfw_data=self.imagesize_sysfw_data, + hashval_sysfw_data=self.hashval_sysfw_data, + sysfw_inner_cert_ext_boot_block=self.sysfw_inner_cert_ext_boot_block, + dm_data_ext_boot_block=self.dm_data_ext_boot_block + ) if stdout is not None: data = tools.read_file(output_fname) else: diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index e53181afb7..3e8091e832 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -97,6 +97,8 @@ ENV_DATA = b'var1=1\nvar2="2"' PRE_LOAD_MAGIC = b'UBSH' PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big') PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big') +TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +TI_UNSECURE_DATA = b'unsecuredata' # Subdirectory of the input dir to use to put test FDTs TEST_FDT_SUBDIR = 'fdts' @@ -199,6 +201,9 @@ class TestFunctional(unittest.TestCase): shutil.copytree(cls.TestFile('files'), os.path.join(cls._indir, 'files')) + shutil.copytree(cls.TestFile('yaml'), + os.path.join(cls._indir, 'yaml')) + TestFunctional._MakeInputFile('compress', COMPRESS_DATA) TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG) TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) @@ -207,6 +212,7 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) TestFunctional._MakeInputFile('scp.bin', SCP_DATA) TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA) + TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA) # Add a few .dtb files for testing TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, @@ -347,7 +353,7 @@ class TestFunctional(unittest.TestCase): use_expanded=False, verbosity=None, allow_missing=False, allow_fake_blobs=False, extra_indirs=None, threads=None, test_section_timeout=False, update_fdt_in_elf=None, - force_missing_bintools='', ignore_missing=False): + force_missing_bintools='', ignore_missing=False, output_dir=None): """Run binman with a given test file Args: @@ -378,6 +384,7 @@ class TestFunctional(unittest.TestCase): update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx force_missing_tools (str): comma-separated list of bintools to regard as missing + output_dir: Specific output directory to use for image using -O Returns: int return code, 0 on success @@ -424,6 +431,8 @@ class TestFunctional(unittest.TestCase): if extra_indirs: for indir in extra_indirs: args += ['-I', indir] + if output_dir: + args += ['-O', output_dir] return self._DoBinman(*args) def _SetupDtb(self, fname, outfile='u-boot.dtb'): @@ -6168,7 +6177,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap str(e.exception)) def testSymlink(self): - """Test that image files can be named""" + """Test that image files can be symlinked""" retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True) self.assertEqual(0, retcode) image = control.images['test_image'] @@ -6177,6 +6186,17 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertTrue(os.path.islink(sname)) self.assertEqual(os.readlink(sname), fname) + def testSymlinkOverwrite(self): + """Test that symlinked images can be overwritten""" + testdir = TestFunctional._MakeInputDir('symlinktest') + self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir) + # build the same image again in the same directory so that existing symlink is present + self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir) + fname = tools.get_output_filename('test_image.bin') + sname = tools.get_output_filename('symlink_to_test.bin') + self.assertTrue(os.path.islink(sname)) + self.assertEqual(os.readlink(sname), fname) + def testSymbolsElf(self): """Test binman can assign symbols embedded in an ELF file""" if not elf.ELF_TOOLS: @@ -6884,6 +6904,72 @@ fdt fdtmap Extract the devicetree blob from the fdtmap # Move to next spl_data = content[:0x18] + def testTIBoardConfig(self): + """Test that a schema validated board config file can be generated""" + data = self._DoReadFile('277_ti_board_cfg.dts') + self.assertEqual(TI_BOARD_CONFIG_DATA, data) + + def testTIBoardConfigCombined(self): + """Test that a schema validated combined board config file can be generated""" + data = self._DoReadFile('278_ti_board_cfg_combined.dts') + configlen_noheader = TI_BOARD_CONFIG_DATA * 4 + self.assertGreater(data, configlen_noheader) + + def testTIBoardConfigNoDataType(self): + """Test that error is thrown when data type is not supported""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('279_ti_board_cfg_no_type.dts') + self.assertIn("Schema validation error", str(e.exception)) + + def testPackTiSecure(self): + """Test that an image with a TI secured binary can be created""" + keyfile = self.TestFile('key.key') + entry_args = { + 'keyfile': keyfile, + } + data = self._DoReadFileDtb('279_ti_secure.dts', + entry_args=entry_args)[0] + self.assertGreater(len(data), len(TI_UNSECURE_DATA)) + + def testPackTiSecureMissingTool(self): + """Test that an image with a TI secured binary (non-functional) can be created + when openssl is missing""" + keyfile = self.TestFile('key.key') + entry_args = { + 'keyfile': keyfile, + } + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('279_ti_secure.dts', + force_missing_bintools='openssl', + entry_args=entry_args) + err = stderr.getvalue() + self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") + + def testPackTiSecureROM(self): + """Test that a ROM image with a TI secured binary can be created""" + keyfile = self.TestFile('key.key') + entry_args = { + 'keyfile': keyfile, + } + data = self._DoReadFileDtb('280_ti_secure_rom.dts', + entry_args=entry_args)[0] + data_a = self._DoReadFileDtb('288_ti_secure_rom_a.dts', + entry_args=entry_args)[0] + data_b = self._DoReadFileDtb('289_ti_secure_rom_b.dts', + entry_args=entry_args)[0] + self.assertGreater(len(data), len(TI_UNSECURE_DATA)) + self.assertGreater(len(data_a), len(TI_UNSECURE_DATA)) + self.assertGreater(len(data_b), len(TI_UNSECURE_DATA)) + + def testPackTiSecureROMCombined(self): + """Test that a ROM image with a TI secured binary can be created""" + keyfile = self.TestFile('key.key') + entry_args = { + 'keyfile': keyfile, + } + data = self._DoReadFileDtb('281_ti_secure_rom_combined.dts', + entry_args=entry_args)[0] + self.assertGreater(len(data), len(TI_UNSECURE_DATA)) if __name__ == "__main__": unittest.main() diff --git a/tools/binman/image.py b/tools/binman/image.py index 8ebf71d61a..e77b5d0d97 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -182,6 +182,8 @@ class Image(section.Entry_section): # Create symlink to file if symlink given if self._symlink is not None: sname = tools.get_output_filename(self._symlink) + if os.path.islink(sname): + os.remove(sname) os.symlink(fname, sname) def WriteMap(self): diff --git a/tools/binman/test/277_ti_board_cfg.dts b/tools/binman/test/277_ti_board_cfg.dts new file mode 100644 index 0000000000..cda024c1b8 --- /dev/null +++ b/tools/binman/test/277_ti_board_cfg.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-board-config { + config = "yaml/config.yaml"; + schema = "yaml/schema.yaml"; + }; + }; +}; diff --git a/tools/binman/test/278_ti_board_cfg_combined.dts b/tools/binman/test/278_ti_board_cfg_combined.dts new file mode 100644 index 0000000000..95ef449cbf --- /dev/null +++ b/tools/binman/test/278_ti_board_cfg_combined.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + ti-board-config { + board-cfg { + config = "yaml/config.yaml"; + schema = "yaml/schema.yaml"; + }; + sec-cfg { + config = "yaml/config.yaml"; + schema = "yaml/schema.yaml"; + }; + rm-cfg { + config = "yaml/config.yaml"; + schema = "yaml/schema.yaml"; + }; + pm-cfg { + config = "yaml/config.yaml"; + schema = "yaml/schema.yaml"; + }; + }; + }; +}; diff --git a/tools/binman/test/279_ti_board_cfg_no_type.dts b/tools/binman/test/279_ti_board_cfg_no_type.dts new file mode 100644 index 0000000000..584b7acc5a --- /dev/null +++ b/tools/binman/test/279_ti_board_cfg_no_type.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + ti-board-config { + config = "yaml/config.yaml"; + schema = "yaml/schema_notype.yaml"; + }; + }; +}; diff --git a/tools/binman/test/279_ti_secure.dts b/tools/binman/test/279_ti_secure.dts new file mode 100644 index 0000000000..941d0ab4ca --- /dev/null +++ b/tools/binman/test/279_ti_secure.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-secure { + content = <&unsecure_binary>; + }; + unsecure_binary: blob-ext { + filename = "ti_unsecure.bin"; + }; + }; +}; diff --git a/tools/binman/test/280_ti_secure_rom.dts b/tools/binman/test/280_ti_secure_rom.dts new file mode 100644 index 0000000000..d1313769f4 --- /dev/null +++ b/tools/binman/test/280_ti_secure_rom.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-secure-rom { + content = <&unsecure_binary>; + }; + unsecure_binary: blob-ext { + filename = "ti_unsecure.bin"; + }; + }; +}; diff --git a/tools/binman/test/281_ti_secure_rom_combined.dts b/tools/binman/test/281_ti_secure_rom_combined.dts new file mode 100644 index 0000000000..bf872739bc --- /dev/null +++ b/tools/binman/test/281_ti_secure_rom_combined.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-secure-rom { + content = <&unsecure_binary>; + content-sbl = <&unsecure_binary>; + content-sysfw = <&unsecure_binary>; + content-sysfw-data = <&unsecure_binary>; + content-sysfw-inner-cert = <&unsecure_binary>; + content-dm-data = <&unsecure_binary>; + combined; + sysfw-inner-cert; + dm-data; + }; + unsecure_binary: blob-ext { + filename = "ti_unsecure.bin"; + }; + }; +}; diff --git a/tools/binman/test/288_ti_secure_rom_a.dts b/tools/binman/test/288_ti_secure_rom_a.dts new file mode 100644 index 0000000000..887138f0e4 --- /dev/null +++ b/tools/binman/test/288_ti_secure_rom_a.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-secure-rom { + content = <&unsecure_binary>; + core = "secure"; + countersign; + }; + unsecure_binary: blob-ext { + filename = "ti_unsecure.bin"; + }; + }; +}; diff --git a/tools/binman/test/289_ti_secure_rom_b.dts b/tools/binman/test/289_ti_secure_rom_b.dts new file mode 100644 index 0000000000..c6d6182158 --- /dev/null +++ b/tools/binman/test/289_ti_secure_rom_b.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ti-secure-rom { + content = <&unsecure_binary>; + core = "public"; + }; + unsecure_binary: blob-ext { + filename = "ti_unsecure.bin"; + }; + }; +}; diff --git a/tools/binman/test/yaml/config.yaml b/tools/binman/test/yaml/config.yaml new file mode 100644 index 0000000000..5f799a6e3a --- /dev/null +++ b/tools/binman/test/yaml/config.yaml @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Test config +# +--- + +main-branch: + obj: + a: 0x0 + b: 0 + arr: [0, 0, 0, 0] + another-arr: + - #1 + c: 0 + d: 0 + - #2 + c: 0 + d: 0 diff --git a/tools/binman/test/yaml/schema.yaml b/tools/binman/test/yaml/schema.yaml new file mode 100644 index 0000000000..8aa03f3c8e --- /dev/null +++ b/tools/binman/test/yaml/schema.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Test schema +# +--- + +definitions: + u8: + type: integer + minimum: 0 + maximum: 0xff + u16: + type: integer + minimum: 0 + maximum: 0xffff + u32: + type: integer + minimum: 0 + maximum: 0xffffffff + +type: object +properties: + main-branch: + type: object + properties: + obj: + type: object + properties: + a: + $ref: "#/definitions/u32" + b: + $ref: "#/definitions/u16" + arr: + type: array + minItems: 4 + maxItems: 4 + items: + $ref: "#/definitions/u8" + another-arr: + type: array + minItems: 2 + maxItems: 2 + items: + type: object + properties: + c: + $ref: "#/definitions/u8" + d: + $ref: "#/definitions/u8" diff --git a/tools/binman/test/yaml/schema_notype.yaml b/tools/binman/test/yaml/schema_notype.yaml new file mode 100644 index 0000000000..6b4d98ffa1 --- /dev/null +++ b/tools/binman/test/yaml/schema_notype.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Test schema +# +--- + +definitions: + u8: + type: integer + minimum: 0 + maximum: 0xff + u16: + type: integer + minimum: 0 + maximum: 0xffff + u32: + type: integer + minimum: 0 + maximum: 0xffffffff + +type: object +properties: + main-branch: + type: object + properties: + obj: + type: object + properties: + a: + $ref: "#/definitions/u4" + b: + $ref: "#/definitions/u16" + arr: + type: array + minItems: 4 + maxItems: 4 + items: + $ref: "#/definitions/u8" diff --git a/tools/buildman/requirements.txt b/tools/buildman/requirements.txt new file mode 100644 index 0000000000..a1efcb9d4b --- /dev/null +++ b/tools/buildman/requirements.txt @@ -0,0 +1,2 @@ +jsonschema==4.17.3 +pyyaml==6.0 diff --git a/tools/k3_fit_atf.sh b/tools/k3_fit_atf.sh deleted file mode 100755 index 7bc07ad074..0000000000 --- a/tools/k3_fit_atf.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0+ -# -# script to generate FIT image source for K3 Family boards with -# ATF, OPTEE, SPL and multiple device trees (given on the command line). -# Inspired from board/sunxi/mksunxi_fit_atf.sh -# -# usage: $0 <atf_load_addr> <dt_name> [<dt_name> [<dt_name] ...] - -[ -z "$ATF" ] && ATF="bl31.bin" - -if [ ! -f $ATF ]; then - echo "WARNING ATF file $ATF NOT found, resulting binary is non-functional" >&2 - ATF=/dev/null -fi - -[ -z "$TEE" ] && TEE="bl32.bin" - -if [ ! -f $TEE ]; then - echo "WARNING OPTEE file $TEE NOT found, resulting might be non-functional" >&2 - TEE=/dev/null -fi - -[ -z "$DM" ] && DM="dm.bin" - -if [ ! -e $DM ]; then - echo "WARNING DM file $DM NOT found, resulting might be non-functional" >&2 - DM=/dev/null -fi - -if [ ! -z "$IS_HS" ]; then - HS_APPEND=_HS -fi - -cat << __HEADER_EOF -/dts-v1/; - -/ { - description = "Configuration to load ATF and SPL"; - #address-cells = <1>; - - images { - atf { - description = "ARM Trusted Firmware"; - data = /incbin/("$ATF"); - type = "firmware"; - arch = "arm64"; - compression = "none"; - os = "arm-trusted-firmware"; - load = <$1>; - entry = <$1>; - }; - tee { - description = "OPTEE"; - data = /incbin/("$TEE"); - type = "tee"; - arch = "arm64"; - compression = "none"; - os = "tee"; - load = <0x9e800000>; - entry = <0x9e800000>; - }; - dm { - description = "DM binary"; - data = /incbin/("$DM"); - type = "firmware"; - arch = "arm32"; - compression = "none"; - os = "DM"; - load = <0x89000000>; - entry = <0x89000000>; - }; - spl { - description = "SPL (64-bit)"; - data = /incbin/("spl/u-boot-spl-nodtb.bin$HS_APPEND"); - type = "standalone"; - os = "U-Boot"; - arch = "arm64"; - compression = "none"; - load = <0x80080000>; - entry = <0x80080000>; - }; -__HEADER_EOF - -# shift through ATF load address in the command line arguments -shift - -for dtname in $* -do - cat << __FDT_IMAGE_EOF - $(basename $dtname) { - description = "$(basename $dtname .dtb)"; - data = /incbin/("$dtname$HS_APPEND"); - type = "flat_dt"; - arch = "arm"; - compression = "none"; - }; -__FDT_IMAGE_EOF -done - -cat << __CONF_HEADER_EOF - }; - configurations { - default = "$(basename $1)"; - -__CONF_HEADER_EOF - -for dtname in $* -do - cat << __CONF_SECTION_EOF - $(basename $dtname) { - description = "$(basename $dtname .dtb)"; - firmware = "atf"; - loadables = "tee", "dm", "spl"; - fdt = "$(basename $dtname)"; - }; -__CONF_SECTION_EOF -done - -cat << __ITS_EOF - }; -}; -__ITS_EOF diff --git a/tools/k3_gen_x509_cert.sh b/tools/k3_gen_x509_cert.sh deleted file mode 100755 index d9cde07417..0000000000 --- a/tools/k3_gen_x509_cert.sh +++ /dev/null @@ -1,262 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause -# -# Script to add K3 specific x509 cetificate to a binary. -# - -# Variables -OUTPUT=tiboot3.bin -TEMP_X509=x509-temp.cert -CERT=certificate.bin -RAND_KEY=eckey.pem -LOADADDR=0x41c00000 -BOOTCORE_OPTS=0 -BOOTCORE=16 -DEBUG_TYPE=0 -SWRV=1 - -gen_degen_template() { -cat << 'EOF' > degen-template.txt - -asn1=SEQUENCE:rsa_key - -[rsa_key] -version=INTEGER:0 -modulus=INTEGER:0xDEGEN_MODULUS -pubExp=INTEGER:1 -privExp=INTEGER:1 -p=INTEGER:0xDEGEN_P -q=INTEGER:0xDEGEN_Q -e1=INTEGER:1 -e2=INTEGER:1 -coeff=INTEGER:0xDEGEN_COEFF -EOF -} - -# Generate x509 Template -gen_template() { -cat << 'EOF' > x509-template.txt - [ req ] - distinguished_name = req_distinguished_name - x509_extensions = v3_ca - prompt = no - dirstring_type = nobmp - - [ req_distinguished_name ] - C = US - ST = TX - L = Dallas - O = Texas Instruments Incorporated - OU = Processors - CN = TI support - emailAddress = support@ti.com - - [ v3_ca ] - basicConstraints = CA:true - 1.3.6.1.4.1.294.1.1 = ASN1:SEQUENCE:boot_seq - 1.3.6.1.4.1.294.1.2 = ASN1:SEQUENCE:image_integrity - 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv -# 1.3.6.1.4.1.294.1.4 = ASN1:SEQUENCE:encryption - 1.3.6.1.4.1.294.1.8 = ASN1:SEQUENCE:debug - - [ boot_seq ] - certType = INTEGER:TEST_CERT_TYPE - bootCore = INTEGER:TEST_BOOT_CORE - bootCoreOpts = INTEGER:TEST_BOOT_CORE_OPTS - destAddr = FORMAT:HEX,OCT:TEST_BOOT_ADDR - imageSize = INTEGER:TEST_IMAGE_LENGTH - - [ image_integrity ] - shaType = OID:2.16.840.1.101.3.4.2.3 - shaValue = FORMAT:HEX,OCT:TEST_IMAGE_SHA_VAL - - [ swrv ] - swrv = INTEGER:TEST_SWRV - -# [ encryption ] -# initalVector = FORMAT:HEX,OCT:TEST_IMAGE_ENC_IV -# randomString = FORMAT:HEX,OCT:TEST_IMAGE_ENC_RS -# iterationCnt = INTEGER:TEST_IMAGE_KEY_DERIVE_INDEX -# salt = FORMAT:HEX,OCT:TEST_IMAGE_KEY_DERIVE_SALT - - [ debug ] - debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 - debugType = INTEGER:TEST_DEBUG_TYPE - coreDbgEn = INTEGER:0 - coreDbgSecEn = INTEGER:0 -EOF -} - -parse_key() { - sed '/ /s/://g' key.txt | \ - awk '!/ / {printf("\n%s\n", $0)}; / / {printf("%s", $0)}' | \ - sed 's/ //g' | \ - awk "/$1:/{getline; print}" -} - -gen_degen_key() { -# Generate a 4096 bit RSA Key - openssl genrsa -out key.pem 1024 >>/dev/null 2>&1 - openssl rsa -in key.pem -text -out key.txt >>/dev/null 2>&1 - DEGEN_MODULUS=$( parse_key 'modulus' ) - DEGEN_P=$( parse_key 'prime1' ) - DEGEN_Q=$( parse_key 'prime2' ) - DEGEN_COEFF=$( parse_key 'coefficient' ) - gen_degen_template - - sed -e "s/DEGEN_MODULUS/$DEGEN_MODULUS/"\ - -e "s/DEGEN_P/$DEGEN_P/" \ - -e "s/DEGEN_Q/$DEGEN_Q/" \ - -e "s/DEGEN_COEFF/$DEGEN_COEFF/" \ - degen-template.txt > degenerateKey.txt - - openssl asn1parse -genconf degenerateKey.txt -out degenerateKey.der >>/dev/null 2>&1 - openssl rsa -in degenerateKey.der -inform DER -outform PEM -out $RAND_KEY >>/dev/null 2>&1 - KEY=$RAND_KEY - rm key.pem key.txt degen-template.txt degenerateKey.txt degenerateKey.der -} - -declare -A options_help -usage() { - if [ -n "$*" ]; then - echo "ERROR: $*" - fi - echo -n "Usage: $0 " - for option in "${!options_help[@]}" - do - arg=`echo ${options_help[$option]}|cut -d ':' -f1` - if [ -n "$arg" ]; then - arg=" $arg" - fi - echo -n "[-$option$arg] " - done - echo - echo -e "\nWhere:" - for option in "${!options_help[@]}" - do - arg=`echo ${options_help[$option]}|cut -d ':' -f1` - txt=`echo ${options_help[$option]}|cut -d ':' -f2` - tb="\t\t\t" - if [ -n "$arg" ]; then - arg=" $arg" - tb="\t" - fi - echo -e " -$option$arg:$tb$txt" - done - echo - echo "Examples of usage:-" - echo "# Example of signing the SYSFW binary with rsa degenerate key" - echo " $0 -c 0 -b ti-sci-firmware-am6x.bin -o sysfw.bin -l 0x40000" - echo "# Example of signing the SPL binary with rsa degenerate key" - echo " $0 -c 16 -b spl/u-boot-spl.bin -o tiboot3.bin -l 0x41c00000" -} - -options_help[b]="bin_file:Bin file that needs to be signed" -options_help[k]="key_file:file with key inside it. If not provided script generates a rsa degenerate key." -options_help[o]="output_file:Name of the final output file. default to $OUTPUT" -options_help[c]="core_id:target core id on which the image would be running. Default to $BOOTCORE" -options_help[l]="loadaddr: Target load address of the binary in hex. Default to $LOADADDR" -options_help[d]="debug_type: Debug type, set to 4 to enable early JTAG. Default to $DEBUG_TYPE" -options_help[r]="SWRV: Software Rev for X509 certificate" - -while getopts "b:k:o:c:l:d:h:r:" opt -do - case $opt in - b) - BIN=$OPTARG - ;; - k) - KEY=$OPTARG - ;; - o) - OUTPUT=$OPTARG - ;; - l) - LOADADDR=$OPTARG - ;; - c) - BOOTCORE=$OPTARG - ;; - d) - DEBUG_TYPE=$OPTARG - ;; - r) - SWRV=$OPTARG - ;; - h) - usage - exit 0 - ;; - \?) - usage "Invalid Option '-$OPTARG'" - exit 1 - ;; - :) - usage "Option '-$OPTARG' Needs an argument." - exit 1 - ;; - esac -done - -if [ "$#" -eq 0 ]; then - usage "Arguments missing" - exit 1 -fi - -if [ -z "$BIN" ]; then - usage "Bin file missing in arguments" - exit 1 -fi - -# Generate rsa degenerate key if user doesn't provide a key -if [ -z "$KEY" ]; then - gen_degen_key -fi - -if [ $BOOTCORE == 0 ]; then # BOOTCORE M3, loaded by ROM - CERTTYPE=2 -elif [ $BOOTCORE == 16 ]; then # BOOTCORE R5, loaded by ROM - CERTTYPE=1 -else # Non BOOTCORE, loaded by SYSFW - BOOTCORE_OPTS_VER=$(printf "%01x" 1) - # Add input args option for SET and CLR flags. - BOOTCORE_OPTS_SETFLAG=$(printf "%08x" 0) - BOOTCORE_OPTS_CLRFLAG=$(printf "%08x" 0x100) # Clear FLAG_ARMV8_AARCH32 - BOOTCORE_OPTS="0x$BOOTCORE_OPTS_VER$BOOTCORE_OPTS_SETFLAG$BOOTCORE_OPTS_CLRFLAG" - # Set the cert type to zero. - # We are not using public/private key store now - CERTTYPE=$(printf "0x%08x" 0) -fi - -SHA_VAL=`openssl dgst -sha512 -hex $BIN | sed -e "s/^.*= //g"` -BIN_SIZE=`cat $BIN | wc -c` -ADDR=`printf "%08x" $LOADADDR` - -gen_cert() { - #echo "Certificate being generated :" - #echo " LOADADDR = 0x$ADDR" - #echo " IMAGE_SIZE = $BIN_SIZE" - #echo " CERT_TYPE = $CERTTYPE" - #echo " DEBUG_TYPE = $DEBUG_TYPE" - #echo " SWRV = $SWRV" - sed -e "s/TEST_IMAGE_LENGTH/$BIN_SIZE/" \ - -e "s/TEST_IMAGE_SHA_VAL/$SHA_VAL/" \ - -e "s/TEST_CERT_TYPE/$CERTTYPE/" \ - -e "s/TEST_BOOT_CORE_OPTS/$BOOTCORE_OPTS/" \ - -e "s/TEST_BOOT_CORE/$BOOTCORE/" \ - -e "s/TEST_BOOT_ADDR/$ADDR/" \ - -e "s/TEST_DEBUG_TYPE/$DEBUG_TYPE/" \ - -e "s/TEST_SWRV/$SWRV/" \ - x509-template.txt > $TEMP_X509 - openssl req -new -x509 -key $KEY -nodes -outform DER -out $CERT -config $TEMP_X509 -sha512 -} - -gen_template -gen_cert -cat $CERT $BIN > $OUTPUT - -# Remove all intermediate files -rm $TEMP_X509 $CERT x509-template.txt -if [ "$KEY" == "$RAND_KEY" ]; then - rm $RAND_KEY -fi |