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