77"""
88
99from logging import getLogger
10- from typing import TYPE_CHECKING , TextIO
10+ from typing import NamedTuple , TYPE_CHECKING , TextIO
11+
12+ from ..util .misc import redent
13+ from .partition import OtpPartition
1114
1215if TYPE_CHECKING :
1316 from .map import OtpMap
1417
1518
19+ class OtpSlotDescriptor (NamedTuple ):
20+ """A location descriptor in OTP, either a whole partition or an item.
21+
22+ It has no other purpose than storing intermediate info as a container.
23+ """
24+
25+ name : str
26+ """Name of the slot."""
27+
28+ offset : str
29+ """Offset in bytes."""
30+
31+ size : str
32+ """Size in bytes."""
33+
34+ gen : bool = False
35+ """Whether this slot is generated by the script or defined."""
36+
37+ part : bool = False
38+ """Whether the slot defines the whole partition or a single item."""
39+
40+
1641class OtpPartitionDesc :
1742 """OTP Partition descriptor generator."""
1843
@@ -39,9 +64,18 @@ def __init__(self, otpmap: 'OtpMap'):
3964 self ._log = getLogger ('otp.partdesc' )
4065 self ._otpmap = otpmap
4166
42- def save (self , hjname : str , scriptname : str , cfp : TextIO ) -> None :
43- """Generate a C file with a static description for the partitions."""
67+ def save (self , kind : str , hjname : str , scriptname : str , cfp : TextIO ) \
68+ -> None :
69+ """Generate a source file with a static description for the partitions.
70+
71+ :param kind: kind of generation output
72+ :param hjname: the name of the input HJSON configuration file
73+ :param scriptname: the name of the script that generates this output
74+ :param cfp: the output text stream
75+ """
4476 # pylint: disable=f-string-without-interpolation
77+ if kind != 'qemu' :
78+ raise NotImplementedError (f'No support for { kind } ' )
4579 attrs = {n : getattr (self , f'_convert_to_{ k } ' ) if k else lambda x : x
4680 for n , k in self .ATTRS .items () if k is not None }
4781 print (f'/* Generated from { hjname } with { scriptname } */' , file = cfp )
@@ -126,48 +160,190 @@ def __init__(self, otpmap: 'OtpMap'):
126160 self ._log = getLogger ('otp.reg' )
127161 self ._otpmap = otpmap
128162
129- def save (self , hjname : str , scriptname : str , cfp : TextIO ) -> None :
130- """Generate a C file with register definition for the partitions."""
131- reg_offsets = []
132- reg_sizes = []
133- part_names = []
163+ def save (self , kind : str , hjname : str , scriptname : str , cfp : TextIO ) \
164+ -> None :
165+ """Generate a source file with register definition for the partitions.
166+
167+ :param kind: kind of generation output
168+ :param hjname: the name of the input HJSON configuration file
169+ :param scriptname: the name of the script that generates this output
170+ :param cfp: the output text stream
171+ """
172+ try :
173+ save = getattr (self , f'_save_{ kind .lower ()} ' )
174+ except AttributeError as exc :
175+ raise NotImplementedError (f'No support for { kind } ' ) from exc
176+ slots : list [OtpSlotDescriptor ] = []
134177 for part in self ._otpmap .enumerate_partitions ():
135- part_names .append (f'OTP_PART_{ part .name } ' )
136178 offset = part .offset
137- reg_sizes .append ((f'{ part .name } _SIZE' , part .size ))
179+ slots .append (OtpSlotDescriptor (part .name , offset , part .size , True ,
180+ True ))
138181 for itname , itdict in part .items .items ():
139182 size = itdict ['size' ]
140183 if not itname .startswith (f'{ part .name } _' ):
141184 name = f'{ part .name } _{ itname } ' .upper ()
142185 else :
143186 name = itname
144- reg_offsets .append ((name , offset ))
145- reg_sizes .append ((f'{ name } _SIZE' , size ))
187+ slots .append (OtpSlotDescriptor (name , offset , size ))
146188 offset += size
189+ zeroizable = getattr (part , 'zeroizable' , False )
190+ digest = any (getattr (part , f'{ k } w_digest' , False ) for k in 'sh' )
191+ if digest :
192+ offset = part .offset + part .size - OtpPartition .DIGEST_SIZE
193+ if zeroizable :
194+ offset -= OtpPartition .ZER_SIZE
195+ slots .append (OtpSlotDescriptor (f'{ part .name } _DIGEST' , offset ,
196+ OtpPartition .DIGEST_SIZE , True ))
197+ if zeroizable :
198+ offset = part .offset + part .size - OtpPartition .DIGEST_SIZE
199+ slots .append (OtpSlotDescriptor (f'{ part .name } _ZER' , offset ,
200+ OtpPartition .ZER_SIZE , True ))
201+
202+ save (hjname , scriptname , cfp , slots )
203+
204+ def _save_qemu (self , hjname : str , scriptname : str , cfp : TextIO ,
205+ slots : list [OtpSlotDescriptor ]) -> None :
147206 print (f'/* Generated from { hjname } with { scriptname } */' )
148207 print (file = cfp )
149- print ('/* clang-format off */' , file = cfp )
150- for reg , off in reg_offsets :
151- print (f'REG32({ reg } , { off } u)' , file = cfp )
208+ for slot in slots :
209+ if slot .part :
210+ continue
211+ print (f'REG32({ slot .name } , { slot .offset } u)' , file = cfp )
152212 print (file = cfp )
153- regwidth = max (len (r [0 ]) for r in reg_sizes )
154- for reg , size in reg_sizes :
155- print (f'#define { reg :{regwidth }s} { size } u' , file = cfp )
213+
214+ regwidth = max (len (s .name ) for s in slots )
215+ regwidth += len ('_SIZE' )
216+ for slot in slots :
217+ if slot .gen and not slot .part :
218+ continue
219+ name = f'{ slot .name } _SIZE'
220+ print (f'#define { name :{regwidth }s} { slot .size } u' , file = cfp )
156221 print (file = cfp )
222+
223+ part_names = [slot .name for slot in slots if slot .part ]
157224 pcount = len (part_names )
225+ part_names = [f'OTP_PART_{ pn } ' for pn in part_names ]
158226 part_names .extend ((
159- '_OTP_PART_COUNT' ,
160- 'OTP_ENTRY_DAI = _OTP_PART_COUNT' ,
161- 'OTP_ENTRY_KDI' ,
162- '_OTP_ENTRY_COUNT' ))
227+ 'OTP_PART_COUNT' ,
228+ 'OTP_ENTRY_DAI = OTP_PART_COUNT, '
229+ '/* Fake partitions for error (...) */' ,
230+ 'OTP_ENTRY_KDI, /* Key derivation issue, not really OTP */' ,
231+ 'OTP_ENTRY_COUNT' ))
163232 print ('typedef enum {' , file = cfp )
164233 for pname in part_names :
165- print (f' { pname } , ' , file = cfp )
234+ print (f' { pname } { "," if "," not in pname else "" } ' , file = cfp )
166235 print ('} OtOTPPartitionType;' , file = cfp )
167236 print (file = cfp )
237+
168238 print ('static const char *PART_NAMES[] = {' , file = cfp )
239+ print (' /* clang-format off */' , file = cfp )
169240 for pname in part_names [:pcount ]:
170241 print (f' OTP_NAME_ENTRY({ pname } ),' , file = cfp )
242+ print (' /* fake partitions */' , file = cfp )
243+ print (' OTP_NAME_ENTRY(OTP_ENTRY_DAI),' , file = cfp )
244+ print (' OTP_NAME_ENTRY(OTP_ENTRY_KDI),' , file = cfp )
245+ print (' /* clang-format on */' , file = cfp )
171246 print ('};' , file = cfp )
172- print ('/* clang-format on */' , file = cfp )
247+ print (file = cfp )
248+
249+ cases : list [str ] = []
250+ for slot in slots :
251+ if slot .part :
252+ continue
253+ if slot .gen :
254+ cases .append (f'CASE_WIDE({ slot .name } );' )
255+ elif slot .size > 4 :
256+ cases .append (f'CASE_RANGE({ slot .name } );' )
257+ elif slot .size == 1 :
258+ cases .append (f'CASE_BYTE({ slot .name } );' )
259+ elif slot .size < 4 :
260+ cases .append (f'CASE_SUB({ slot .name } , { slot .size } u);' )
261+ else :
262+ cases .append (f'CASE_REG({ slot .name } );' )
263+
264+ code = '''
265+ static const char *ot_otp_swcfg_reg_name(unsigned swreg)
266+ {
267+ #define CASE_BYTE(_reg_) \\
268+ case A_##_reg_: \\
269+ return stringify(_reg_)
270+ #define CASE_SUB(_reg_, _sz_) \\
271+ case A_##_reg_...(A_##_reg_ + (_sz_)): \\
272+ return stringify(_reg_)
273+ #define CASE_REG(_reg_) \\
274+ case A_##_reg_...(A_##_reg_ + 3u): \\
275+ return stringify(_reg_)
276+ #define CASE_WIDE(_reg_) \\
277+ case A_##_reg_...(A_##_reg_ + 7u): \\
278+ return stringify(_reg_)
279+ #define CASE_RANGE(_reg_) \\
280+ case A_##_reg_...(A_##_reg_ + (_reg_##_SIZE) - 1u): \\
281+ return stringify(_reg_)
282+
283+ switch (swreg) {
284+ _CASES_
285+ default:
286+ return "<?>";
287+ }
288+
289+ #undef CASE_BYTE
290+ #undef CASE_SUB
291+ #undef CASE_REG
292+ #undef CASE_RANGE
293+ #undef CASE_DIGEST
294+ }
295+ '''
296+ code = redent (code )
297+ code = code .replace ('_CASES_' , '\n ' .join (cases ))
298+ print (redent (code ), '' , file = cfp )
299+
300+ def _save_bmtest (self , hjname : str , scriptname : str , cfp : TextIO ,
301+ slots : list [OtpSlotDescriptor ]) -> None :
302+ # pylint: disable=unused-argument
303+ print (f'// Generated from { hjname } with { scriptname } ' , file = cfp )
304+ print (file = cfp )
305+ rec_p2 = 1 << (len (slots ) - 1 ).bit_length ()
306+ print (f'#![recursion_limit = "{ rec_p2 } "]' , file = cfp )
307+ print (file = cfp )
308+ print ('#[derive(Clone, Copy, Debug, PartialEq)]' , file = cfp )
309+ print ('pub enum Partition {' , file = cfp )
310+ part_names = [slot .name for slot in slots if slot .part ]
311+ for pname in part_names :
312+ pname = pname .title ().replace ('_' , '' )
313+ print (f' { pname } ,' , file = cfp )
314+ print ('}' , file = cfp )
315+ print (file = cfp )
316+ print ('register_structs! {' , file = cfp )
317+ print (' pub OtpSwCfgRegs {' , file = cfp )
318+ slot = OtpSlotDescriptor ('' , 0 , 0 ) # default slot if none is defined
319+ end = 0
320+ rsv = 0
321+ for slot in slots :
322+ if slot .part :
323+ continue
324+ if slot .offset > end :
325+ missing = slot .offset - end
326+ count = missing // 4
327+ if count > 1 :
328+ print (f' (0x{ end :04x} => '
329+ f'_reserved{ rsv } : [ReadOnly<u32>; { count } ]),' ,
330+ file = cfp )
331+ else :
332+ width = missing * 8
333+ print (f' (0x{ end :04x} => '
334+ f'_reserved{ rsv } : ReadOnly<u{ width } >),' , file = cfp )
335+ rsv += 1
336+ if slot .size <= 4 :
337+ width = slot .size * 8
338+ print (f' (0x{ slot .offset :04x} => '
339+ f'{ slot .name .lower ()} : ReadOnly<u{ width } >),' , file = cfp )
340+ else :
341+ count = slot .size // 4
342+ print (f' (0x{ slot .offset :04x} => '
343+ f'{ slot .name .lower ()} : [ReadOnly<u32>; { count } ]),' ,
344+ file = cfp )
345+ end = slot .offset + slot .size
346+ print (f' (0x{ slot .offset + slot .size :04x} => @END),' , file = cfp )
347+ print (' }' , file = cfp )
348+ print ('}' , file = cfp )
173349 print (file = cfp )
0 commit comments