]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFsp2Pkg/Tools/FspDscBsf2Yaml.py
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / IntelFsp2Pkg / Tools / FspDscBsf2Yaml.py
1 #!/usr/bin/env python
2 ## @ FspDscBsf2Yaml.py
3 # This script convert DSC or BSF format file into YAML format
4 #
5 # Copyright(c) 2021, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
7 #
8 ##
9
10 import os
11 import re
12 import sys
13
14 from collections import OrderedDict
15 from datetime import date
16
17 from FspGenCfgData import CFspBsf2Dsc, CGenCfgData
18
19 __copyright_tmp__ = """## @file
20 #
21 # Slim Bootloader CFGDATA %s File.
22 #
23 # Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
24 # SPDX-License-Identifier: BSD-2-Clause-Patent
25 #
26 ##
27 """
28
29
30 class CFspDsc2Yaml():
31
32 def __init__(self):
33 self._Hdr_key_list = ['EMBED', 'STRUCT']
34 self._Bsf_key_list = ['NAME', 'HELP', 'TYPE', 'PAGE', 'PAGES',
35 'OPTION', 'CONDITION', 'ORDER', 'MARKER',
36 'SUBT', 'FIELD', 'FIND']
37 self.gen_cfg_data = None
38 self.cfg_reg_exp = re.compile(
39 "^([_a-zA-Z0-9$\\(\\)]+)\\s*\\|\\s*(0x[0-9A-F]+|\\*)"
40 "\\s*\\|\\s*(\\d+|0x[0-9a-fA-F]+)\\s*\\|\\s*(.+)")
41 self.bsf_reg_exp = re.compile("(%s):{(.+?)}(?:$|\\s+)"
42 % '|'.join(self._Bsf_key_list))
43 self.hdr_reg_exp = re.compile("(%s):{(.+?)}"
44 % '|'.join(self._Hdr_key_list))
45 self.prefix = ''
46 self.unused_idx = 0
47 self.offset = 0
48 self.base_offset = 0
49
50 def load_config_data_from_dsc(self, file_name):
51 """
52 Load and parse a DSC CFGDATA file.
53 """
54 gen_cfg_data = CGenCfgData('FSP')
55 if file_name.endswith('.dsc'):
56 if gen_cfg_data.ParseDscFile(file_name) != 0:
57 raise Exception('DSC file parsing error !')
58 if gen_cfg_data.CreateVarDict() != 0:
59 raise Exception('DSC variable creation error !')
60 else:
61 raise Exception('Unsupported file "%s" !' % file_name)
62 gen_cfg_data.UpdateDefaultValue()
63 self.gen_cfg_data = gen_cfg_data
64
65 def print_dsc_line(self):
66 """
67 Debug function to print all DSC lines.
68 """
69 for line in self.gen_cfg_data._DscLines:
70 print(line)
71
72 def format_value(self, field, text, indent=''):
73 """
74 Format a CFGDATA item into YAML format.
75 """
76 if (not text.startswith('!expand')) and (': ' in text):
77 tgt = ':' if field == 'option' else '- '
78 text = text.replace(': ', tgt)
79 lines = text.splitlines()
80 if len(lines) == 1 and field != 'help':
81 return text
82 else:
83 return '>\n ' + '\n '.join(
84 [indent + i.lstrip() for i in lines])
85
86 def reformat_pages(self, val):
87 # Convert XXX:YYY into XXX::YYY format for page definition
88 parts = val.split(',')
89 if len(parts) <= 1:
90 return val
91
92 new_val = []
93 for each in parts:
94 nodes = each.split(':')
95 if len(nodes) == 2:
96 each = '%s::%s' % (nodes[0], nodes[1])
97 new_val.append(each)
98 ret = ','.join(new_val)
99 return ret
100
101 def reformat_struct_value(self, utype, val):
102 # Convert DSC UINT16/32/64 array into new format by
103 # adding prefix 0:0[WDQ] to provide hint to the array format
104 if utype in ['UINT16', 'UINT32', 'UINT64']:
105 if val and val[0] == '{' and val[-1] == '}':
106 if utype == 'UINT16':
107 unit = 'W'
108 elif utype == 'UINT32':
109 unit = 'D'
110 else:
111 unit = 'Q'
112 val = '{ 0:0%s, %s }' % (unit, val[1:-1])
113 return val
114
115 def process_config(self, cfg):
116 if 'page' in cfg:
117 cfg['page'] = self.reformat_pages(cfg['page'])
118
119 if 'struct' in cfg:
120 cfg['value'] = self.reformat_struct_value(
121 cfg['struct'], cfg['value'])
122
123 def parse_dsc_line(self, dsc_line, config_dict, init_dict, include):
124 """
125 Parse a line in DSC and update the config dictionary accordingly.
126 """
127 init_dict.clear()
128 match = re.match('g(CfgData|\\w+FspPkgTokenSpaceGuid)\\.(.+)',
129 dsc_line)
130 if match:
131 match = self.cfg_reg_exp.match(match.group(2))
132 if not match:
133 return False
134 config_dict['cname'] = self.prefix + match.group(1)
135 value = match.group(4).strip()
136 length = match.group(3).strip()
137 config_dict['length'] = length
138 config_dict['value'] = value
139 if match.group(2) == '*':
140 self.offset += int(length, 0)
141 else:
142 org_offset = int(match.group(2), 0)
143 if org_offset == 0:
144 self.base_offset = self.offset
145 offset = org_offset + self.base_offset
146 if self.offset != offset:
147 if offset > self.offset:
148 init_dict['padding'] = offset - self.offset
149 self.offset = offset + int(length, 0)
150 return True
151
152 match = re.match("^\\s*#\\s+!([<>])\\s+include\\s+(.+)", dsc_line)
153 if match and len(config_dict) == 0:
154 # !include should not be inside a config field
155 # if so, do not convert include into YAML
156 init_dict = dict(config_dict)
157 config_dict.clear()
158 config_dict['cname'] = '$ACTION'
159 if match.group(1) == '<':
160 config_dict['include'] = match.group(2)
161 else:
162 config_dict['include'] = ''
163 return True
164
165 match = re.match("^\\s*#\\s+(!BSF|!HDR)\\s+(.+)", dsc_line)
166 if not match:
167 return False
168
169 remaining = match.group(2)
170 if match.group(1) == '!BSF':
171 result = self.bsf_reg_exp.findall(remaining)
172 if not result:
173 return False
174
175 for each in result:
176 key = each[0].lower()
177 val = each[1]
178 if key == 'field':
179 name = each[1]
180 if ':' not in name:
181 raise Exception('Incorrect bit field format !')
182 parts = name.split(':')
183 config_dict['length'] = parts[1]
184 config_dict['cname'] = '@' + parts[0]
185 return True
186 elif key in ['pages', 'page', 'find']:
187 init_dict = dict(config_dict)
188 config_dict.clear()
189 config_dict['cname'] = '$ACTION'
190 if key == 'find':
191 config_dict['find'] = val
192 else:
193 config_dict['page'] = val
194 return True
195 elif key == 'subt':
196 config_dict.clear()
197 parts = each[1].split(':')
198 tmp_name = parts[0][:-5]
199 if tmp_name == 'CFGHDR':
200 cfg_tag = '_$FFF_'
201 sval = '!expand { %s_TMPL : [ ' % \
202 tmp_name + '%s, %s, ' % (parts[1], cfg_tag) + \
203 ', '.join(parts[2:]) + ' ] }'
204 else:
205 sval = '!expand { %s_TMPL : [ ' % \
206 tmp_name + ', '.join(parts[1:]) + ' ] }'
207 config_dict.clear()
208 config_dict['cname'] = tmp_name
209 config_dict['expand'] = sval
210 return True
211 else:
212 if key in ['name', 'help', 'option'] and \
213 val.startswith('+'):
214 val = config_dict[key] + '\n' + val[1:]
215 if val.strip() == '':
216 val = "''"
217 config_dict[key] = val
218
219 else:
220 match = self.hdr_reg_exp.match(remaining)
221 if not match:
222 return False
223 key = match.group(1)
224 remaining = match.group(2)
225 if key == 'EMBED':
226 parts = remaining.split(':')
227 names = parts[0].split(',')
228 if parts[-1] == 'END':
229 prefix = '>'
230 else:
231 prefix = '<'
232 skip = False
233 if parts[1].startswith('TAG_'):
234 tag_txt = '%s:%s' % (names[0], parts[1])
235 else:
236 tag_txt = names[0]
237 if parts[2] in ['START', 'END']:
238 if names[0] == 'PCIE_RP_PIN_CTRL[]':
239 skip = True
240 else:
241 tag_txt = '%s:%s' % (names[0], parts[1])
242 if not skip:
243 config_dict.clear()
244 config_dict['cname'] = prefix + tag_txt
245 return True
246
247 if key == 'STRUCT':
248 text = remaining.strip()
249 config_dict[key.lower()] = text
250
251 return False
252
253 def process_template_lines(self, lines):
254 """
255 Process a line in DSC template section.
256 """
257 template_name = ''
258 bsf_temp_dict = OrderedDict()
259 temp_file_dict = OrderedDict()
260 include_file = ['.']
261
262 for line in lines:
263 match = re.match("^\\s*#\\s+!([<>])\\s+include\\s+(.+)", line)
264 if match:
265 if match.group(1) == '<':
266 include_file.append(match.group(2))
267 else:
268 include_file.pop()
269
270 match = re.match(
271 "^\\s*#\\s+(!BSF)\\s+DEFT:{(.+?):(START|END)}", line)
272 if match:
273 if match.group(3) == 'START' and not template_name:
274 template_name = match.group(2).strip()
275 temp_file_dict[template_name] = list(include_file)
276 bsf_temp_dict[template_name] = []
277 if match.group(3) == 'END' and \
278 (template_name == match.group(2).strip()) and \
279 template_name:
280 template_name = ''
281 else:
282 if template_name:
283 bsf_temp_dict[template_name].append(line)
284 return bsf_temp_dict, temp_file_dict
285
286 def process_option_lines(self, lines):
287 """
288 Process a line in DSC config section.
289 """
290 cfgs = []
291 struct_end = False
292 config_dict = dict()
293 init_dict = dict()
294 include = ['']
295 for line in lines:
296 ret = self.parse_dsc_line(line, config_dict, init_dict, include)
297 if ret:
298 if 'padding' in init_dict:
299 num = init_dict['padding']
300 init_dict.clear()
301 padding_dict = {}
302 cfgs.append(padding_dict)
303 padding_dict['cname'] = 'UnusedUpdSpace%d' % \
304 self.unused_idx
305 padding_dict['length'] = '0x%x' % num
306 padding_dict['value'] = '{ 0 }'
307 self.unused_idx += 1
308
309 if cfgs and cfgs[-1]['cname'][0] != '@' and \
310 config_dict['cname'][0] == '@':
311 # it is a bit field, mark the previous one as virtual
312 cname = cfgs[-1]['cname']
313 new_cfg = dict(cfgs[-1])
314 new_cfg['cname'] = '@$STRUCT'
315 cfgs[-1].clear()
316 cfgs[-1]['cname'] = cname
317 cfgs.append(new_cfg)
318
319 if cfgs and cfgs[-1]['cname'] == 'CFGHDR' and \
320 config_dict['cname'][0] == '<':
321 # swap CfgHeader and the CFG_DATA order
322 if ':' in config_dict['cname']:
323 # replace the real TAG for CFG_DATA
324 cfgs[-1]['expand'] = cfgs[-1]['expand'].replace(
325 '_$FFF_', '0x%s' %
326 config_dict['cname'].split(':')[1][4:])
327 cfgs.insert(-1, config_dict)
328 else:
329 self.process_config(config_dict)
330 if struct_end:
331 struct_end = False
332 cfgs.insert(-1, config_dict)
333 else:
334 cfgs.append(config_dict)
335 if config_dict['cname'][0] == '>':
336 struct_end = True
337
338 config_dict = dict(init_dict)
339 return cfgs
340
341 def variable_fixup(self, each):
342 """
343 Fix up some variable definitions for SBL.
344 """
345 key = each
346 val = self.gen_cfg_data._MacroDict[each]
347 return key, val
348
349 def template_fixup(self, tmp_name, tmp_list):
350 """
351 Fix up some special config templates for SBL
352 """
353 return
354
355 def config_fixup(self, cfg_list):
356 """
357 Fix up some special config items for SBL.
358 """
359
360 # Insert FSPT_UPD/FSPM_UPD/FSPS_UPD tag so as to create C strcture
361 idxs = []
362 for idx, cfg in enumerate(cfg_list):
363 if cfg['cname'].startswith('<FSP_UPD_HEADER'):
364 idxs.append(idx)
365
366 if len(idxs) != 3:
367 return
368
369 # Handle insert backwards so that the index does not change in the loop
370 fsp_comp = 'SMT'
371 idx_comp = 0
372 for idx in idxs[::-1]:
373 # Add current FSP?_UPD start tag
374 cfgfig_dict = {}
375 cfgfig_dict['cname'] = '<FSP%s_UPD' % fsp_comp[idx_comp]
376 cfg_list.insert(idx, cfgfig_dict)
377 if idx_comp < 2:
378 # Add previous FSP?_UPD end tag
379 cfgfig_dict = {}
380 cfgfig_dict['cname'] = '>FSP%s_UPD' % fsp_comp[idx_comp + 1]
381 cfg_list.insert(idx, cfgfig_dict)
382 idx_comp += 1
383
384 # Add final FSPS_UPD end tag
385 cfgfig_dict = {}
386 cfgfig_dict['cname'] = '>FSP%s_UPD' % fsp_comp[0]
387 cfg_list.append(cfgfig_dict)
388
389 return
390
391 def get_section_range(self, section_name):
392 """
393 Extract line number range from config file for a given section name.
394 """
395 start = -1
396 end = -1
397 for idx, line in enumerate(self.gen_cfg_data._DscLines):
398 if start < 0 and line.startswith('[%s]' % section_name):
399 start = idx
400 elif start >= 0 and line.startswith('['):
401 end = idx
402 break
403 if start == -1:
404 start = 0
405 if end == -1:
406 end = len(self.gen_cfg_data._DscLines)
407 return start, end
408
409 def normalize_file_name(self, file, is_temp=False):
410 """
411 Normalize file name convention so that it is consistent.
412 """
413 if file.endswith('.dsc'):
414 file = file[:-4] + '.yaml'
415 dir_name = os.path.dirname(file)
416 base_name = os.path.basename(file)
417 if is_temp:
418 if 'Template_' not in file:
419 base_name = base_name.replace('Template', 'Template_')
420 else:
421 if 'CfgData_' not in file:
422 base_name = base_name.replace('CfgData', 'CfgData_')
423 if dir_name:
424 path = dir_name + '/' + base_name
425 else:
426 path = base_name
427 return path
428
429 def output_variable(self):
430 """
431 Output variable block into a line list.
432 """
433 lines = []
434 for each in self.gen_cfg_data._MacroDict:
435 key, value = self.variable_fixup(each)
436 lines.append('%-30s : %s' % (key, value))
437 return lines
438
439 def output_template(self):
440 """
441 Output template block into a line list.
442 """
443 self.offset = 0
444 self.base_offset = 0
445 start, end = self.get_section_range('PcdsDynamicVpd.Tmp')
446 bsf_temp_dict, temp_file_dict = self.process_template_lines(
447 self.gen_cfg_data._DscLines[start:end])
448 template_dict = dict()
449 lines = []
450 file_lines = {}
451 last_file = '.'
452 file_lines[last_file] = []
453
454 for tmp_name in temp_file_dict:
455 temp_file_dict[tmp_name][-1] = self.normalize_file_name(
456 temp_file_dict[tmp_name][-1], True)
457 if len(temp_file_dict[tmp_name]) > 1:
458 temp_file_dict[tmp_name][-2] = self.normalize_file_name(
459 temp_file_dict[tmp_name][-2], True)
460
461 for tmp_name in bsf_temp_dict:
462 file = temp_file_dict[tmp_name][-1]
463 if last_file != file and len(temp_file_dict[tmp_name]) > 1:
464 inc_file = temp_file_dict[tmp_name][-2]
465 file_lines[inc_file].extend(
466 ['', '- !include %s' % temp_file_dict[tmp_name][-1], ''])
467 last_file = file
468 if file not in file_lines:
469 file_lines[file] = []
470 lines = file_lines[file]
471 text = bsf_temp_dict[tmp_name]
472 tmp_list = self.process_option_lines(text)
473 self.template_fixup(tmp_name, tmp_list)
474 template_dict[tmp_name] = tmp_list
475 lines.append('%s: >' % tmp_name)
476 lines.extend(self.output_dict(tmp_list, False)['.'])
477 lines.append('\n')
478 return file_lines
479
480 def output_config(self):
481 """
482 Output config block into a line list.
483 """
484 self.offset = 0
485 self.base_offset = 0
486 start, end = self.get_section_range('PcdsDynamicVpd.Upd')
487 cfgs = self.process_option_lines(
488 self.gen_cfg_data._DscLines[start:end])
489 self.config_fixup(cfgs)
490 file_lines = self.output_dict(cfgs, True)
491 return file_lines
492
493 def output_dict(self, cfgs, is_configs):
494 """
495 Output one config item into a line list.
496 """
497 file_lines = {}
498 level = 0
499 file = '.'
500 for each in cfgs:
501 if 'length' in each:
502 if not each['length'].endswith('b') and int(each['length'],
503 0) == 0:
504 continue
505
506 if 'include' in each:
507 if each['include']:
508 each['include'] = self.normalize_file_name(
509 each['include'])
510 file_lines[file].extend(
511 ['', '- !include %s' % each['include'], ''])
512 file = each['include']
513 else:
514 file = '.'
515 continue
516
517 if file not in file_lines:
518 file_lines[file] = []
519
520 lines = file_lines[file]
521 name = each['cname']
522
523 prefix = name[0]
524 if prefix == '<':
525 level += 1
526
527 padding = ' ' * level
528 if prefix not in '<>@':
529 padding += ' '
530 else:
531 name = name[1:]
532 if prefix == '@':
533 padding += ' '
534
535 if ':' in name:
536 parts = name.split(':')
537 name = parts[0]
538
539 padding = padding[2:] if is_configs else padding
540
541 if prefix != '>':
542 if 'expand' in each:
543 lines.append('%s- %s' % (padding, each['expand']))
544 else:
545 lines.append('%s- %-12s :' % (padding, name))
546
547 for field in each:
548 if field in ['cname', 'expand', 'include']:
549 continue
550 value_str = self.format_value(
551 field, each[field], padding + ' ' * 16)
552 full_line = ' %s %-12s : %s' % (padding, field, value_str)
553 lines.extend(full_line.splitlines())
554
555 if prefix == '>':
556 level -= 1
557 if level == 0:
558 lines.append('')
559
560 return file_lines
561
562
563 def bsf_to_dsc(bsf_file, dsc_file):
564 fsp_dsc = CFspBsf2Dsc(bsf_file)
565 dsc_lines = fsp_dsc.get_dsc_lines()
566 fd = open(dsc_file, 'w')
567 fd.write('\n'.join(dsc_lines))
568 fd.close()
569 return
570
571
572 def dsc_to_yaml(dsc_file, yaml_file):
573 dsc2yaml = CFspDsc2Yaml()
574 dsc2yaml.load_config_data_from_dsc(dsc_file)
575
576 cfgs = {}
577 for cfg in ['Template', 'Option']:
578 if cfg == 'Template':
579 file_lines = dsc2yaml.output_template()
580 else:
581 file_lines = dsc2yaml.output_config()
582 for file in file_lines:
583 lines = file_lines[file]
584 if file == '.':
585 cfgs[cfg] = lines
586 else:
587 if ('/' in file or '\\' in file):
588 continue
589 file = os.path.basename(file)
590 out_dir = os.path.dirname(file)
591 fo = open(os.path.join(out_dir, file), 'w')
592 fo.write(__copyright_tmp__ % (
593 cfg, date.today().year) + '\n\n')
594 for line in lines:
595 fo.write(line + '\n')
596 fo.close()
597
598 variables = dsc2yaml.output_variable()
599 fo = open(yaml_file, 'w')
600 fo.write(__copyright_tmp__ % ('Default', date.today().year))
601 if len(variables) > 0:
602 fo.write('\n\nvariable:\n')
603 for line in variables:
604 fo.write(' ' + line + '\n')
605
606 fo.write('\n\ntemplate:\n')
607 for line in cfgs['Template']:
608 fo.write(' ' + line + '\n')
609
610 fo.write('\n\nconfigs:\n')
611 for line in cfgs['Option']:
612 fo.write(' ' + line + '\n')
613
614 fo.close()
615
616
617 def get_fsp_name_from_path(bsf_file):
618 name = ''
619 parts = bsf_file.split(os.sep)
620 for part in parts:
621 if part.endswith('FspBinPkg'):
622 name = part[:-9]
623 break
624 if not name:
625 raise Exception('Could not get FSP name from file path!')
626 return name
627
628
629 def usage():
630 print('\n'.join([
631 "FspDscBsf2Yaml Version 0.10",
632 "Usage:",
633 " FspDscBsf2Yaml BsfFile|DscFile YamlFile"
634 ]))
635
636
637 def main():
638 #
639 # Parse the options and args
640 #
641 argc = len(sys.argv)
642 if argc < 3:
643 usage()
644 return 1
645
646 bsf_file = sys.argv[1]
647 yaml_file = sys.argv[2]
648 if os.path.isdir(yaml_file):
649 yaml_file = os.path.join(
650 yaml_file, get_fsp_name_from_path(bsf_file) + '.yaml')
651
652 if bsf_file.endswith('.dsc'):
653 dsc_file = bsf_file
654 bsf_file = ''
655 else:
656 dsc_file = os.path.splitext(yaml_file)[0] + '.dsc'
657 bsf_to_dsc(bsf_file, dsc_file)
658
659 dsc_to_yaml(dsc_file, yaml_file)
660
661 print("'%s' was created successfully!" % yaml_file)
662
663 return 0
664
665
666 if __name__ == '__main__':
667 sys.exit(main())