1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ 3# Written by Neha Malcom Francis <n-francis@ti.com> 4# 5 6# Support for generation of TI secured binary blobs 7 8from binman.entry import EntryArg 9from binman.etype.x509_cert import Entry_x509_cert 10from dataclasses import dataclass 11 12from dtoc import fdt_util 13 14@dataclass 15class Firewall(): 16 id: int 17 region: int 18 control : int 19 permissions: list 20 start_address: str 21 end_address: str 22 23 def ensure_props(self, etype, name): 24 missing_props = [] 25 for key, val in self.__dict__.items(): 26 if val is None: 27 missing_props += [key] 28 29 if len(missing_props): 30 etype.Raise(f"Subnode '{name}' is missing properties: {','.join(missing_props)}") 31 32 def get_certificate(self) -> str: 33 unique_identifier = f"{self.id}{self.region}" 34 cert = f""" 35firewallID{unique_identifier} = INTEGER:{self.id} 36region{unique_identifier} = INTEGER:{self.region} 37control{unique_identifier} = INTEGER:{hex(self.control)} 38nPermissionRegs{unique_identifier} = INTEGER:{len(self.permissions)} 39""" 40 for index, permission in enumerate(self.permissions): 41 cert += f"""permissions{unique_identifier}{index} = INTEGER:{hex(permission)} 42""" 43 cert += f"""startAddress{unique_identifier} = FORMAT:HEX,OCT:{self.start_address:02x} 44endAddress{unique_identifier} = FORMAT:HEX,OCT:{self.end_address:02x} 45""" 46 return cert 47 48class Entry_ti_secure(Entry_x509_cert): 49 """Entry containing a TI x509 certificate binary 50 51 Properties / Entry arguments: 52 - content: List of phandles to entries to sign 53 - keyfile: Filename of file containing key to sign binary with 54 - sha: Hash function to be used for signing 55 - auth-in-place: This is an integer field that contains two pieces 56 of information: 57 58 - Lower Byte - Remains 0x02 as per our use case 59 ( 0x02: Move the authenticated binary back to the header ) 60 - Upper Byte - The Host ID of the core owning the firewall 61 62 Output files: 63 - input.<unique_name> - input file passed to openssl 64 - config.<unique_name> - input file generated for openssl (which is 65 used as the config file) 66 - cert.<unique_name> - output file generated by openssl (which is 67 used as the entry contents) 68 69 Depending on auth-in-place information in the inputs, we read the 70 firewall nodes that describe the configurations of firewall that TIFS 71 will be doing after reading the certificate. 72 73 The syntax of the firewall nodes are as such:: 74 75 firewall-257-0 { 76 id = <257>; /* The ID of the firewall being configured */ 77 region = <0>; /* Region number to configure */ 78 79 control = /* The control register */ 80 <(FWCTRL_EN | FWCTRL_LOCK | FWCTRL_BG | FWCTRL_CACHE)>; 81 82 permissions = /* The permission registers */ 83 <((FWPRIVID_ALL << FWPRIVID_SHIFT) | 84 FWPERM_SECURE_PRIV_RWCD | 85 FWPERM_SECURE_USER_RWCD | 86 FWPERM_NON_SECURE_PRIV_RWCD | 87 FWPERM_NON_SECURE_USER_RWCD)>; 88 89 /* More defines can be found in k3-security.h */ 90 91 start_address = /* The Start Address of the firewall */ 92 <0x0 0x0>; 93 end_address = /* The End Address of the firewall */ 94 <0xff 0xffffffff>; 95 }; 96 97 98 openssl signs the provided data, using the TI templated config file and 99 writes the signature in this entry. This allows verification that the 100 data is genuine. 101 """ 102 def __init__(self, section, etype, node): 103 super().__init__(section, etype, node) 104 self.openssl = None 105 self.firewall_cert_data: dict = { 106 'auth_in_place': 0x02, 107 'num_firewalls': 0, 108 'certificate': '', 109 } 110 111 def ReadNode(self): 112 super().ReadNode() 113 self.key_fname = self.GetEntryArgsOrProps([ 114 EntryArg('keyfile', str)], required=True)[0] 115 auth_in_place = fdt_util.GetInt(self._node, 'auth-in-place') 116 if auth_in_place: 117 self.firewall_cert_data['auth_in_place'] = auth_in_place 118 self.ReadFirewallNode() 119 self.sha = fdt_util.GetInt(self._node, 'sha', 512) 120 self.req_dist_name = {'C': 'US', 121 'ST': 'TX', 122 'L': 'Dallas', 123 'O': 'Texas Instruments Incorporated', 124 'OU': 'Processors', 125 'CN': 'TI Support', 126 'emailAddress': 'support@ti.com'} 127 self.debug = fdt_util.GetBool(self._node, 'debug', False) 128 129 def ReadFirewallNode(self): 130 self.firewall_cert_data['certificate'] = "" 131 self.firewall_cert_data['num_firewalls'] = 0 132 for node in self._node.subnodes: 133 if 'firewall' in node.name: 134 firewall = Firewall( 135 fdt_util.GetInt(node, 'id'), 136 fdt_util.GetInt(node, 'region'), 137 fdt_util.GetInt(node, 'control'), 138 fdt_util.GetPhandleList(node, 'permissions'), 139 fdt_util.GetInt64(node, 'start_address'), 140 fdt_util.GetInt64(node, 'end_address'), 141 ) 142 firewall.ensure_props(self, node.name) 143 self.firewall_cert_data['num_firewalls'] += 1 144 self.firewall_cert_data['certificate'] += firewall.get_certificate() 145 146 def GetCertificate(self, required): 147 """Get the contents of this entry 148 149 Args: 150 required: True if the data must be present, False if it is OK to 151 return None 152 153 Returns: 154 bytes content of the entry, which is the certificate binary for the 155 provided data 156 """ 157 return super().GetCertificate(required=required, type='sysfw') 158 159 def ObtainContents(self): 160 data = self.data 161 if data is None: 162 data = self.GetCertificate(False) 163 if data is None: 164 return False 165 self.SetContents(data) 166 return True 167 168 def ProcessContents(self): 169 # The blob may have changed due to WriteSymbols() 170 data = self.data 171 return self.ProcessContentsUpdate(data) 172 173 def AddBintools(self, btools): 174 super().AddBintools(btools) 175 self.openssl = self.AddBintool(btools, 'openssl') 176