]>
git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/ConvertFceToStructurePcd.py
3 # Firmware Configuration Editor (FCE) from https://firmware.intel.com/develop
4 # can parse BIOS image and generate Firmware Configuration file.
5 # This script bases on Firmware Configuration file, and generate the structure
6 # PCD setting in DEC/DSC/INF files.
8 # Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
9 # SPDX-License-Identifier: BSD-2-Clause-Patent
13 ConvertFceToStructurePcd
22 # Globals for help information
24 __prog__
= 'ConvertFceToStructurePcd'
25 __version__
= '%s Version %s' % (__prog__
, '0.1 ')
26 __copyright__
= 'Copyright (c) 2018, Intel Corporation. All rights reserved.'
27 __description__
= 'Generate Structure PCD in DEC/DSC/INF based on Firmware Configuration.\n'
30 dscstatement
='''[Defines]
31 VPD_TOOL_GUID = 8C3D856A-9BE6-468E-850A-24F7A8D38E08
34 0|DEFAULT # The entry: 0|DEFAULT is reserved and always required.
37 0|STANDARD # UEFI Standard default 0|STANDARD is reserved.
38 1|MANUFACTURING # UEFI Manufacturing default 1|MANUFACTURING is reserved.
40 [PcdsDynamicExVpd.common.DEFAULT]
41 gEfiMdeModulePkgTokenSpaceGuid.PcdNvStoreDefaultValueBuffer|*
44 decstatement
= '''[Guids]
45 gStructPcdTokenSpaceGuid = {0x3f1406f4, 0x2b, 0x487a, {0x8b, 0x69, 0x74, 0x29, 0x1b, 0x36, 0x16, 0xf4}}
47 [PcdsFixedAtBuild,PcdsPatchableInModule,PcdsDynamic,PcdsDynamicEx]
50 infstatement
= '''[Pcd]
53 SECTION
='PcdsDynamicHii'
54 PCD_NAME
='gStructPcdTokenSpaceGuid.Pcd'
60 class parser_lst(object):
62 def __init__(self
,filelist
):
63 self
._ignore
=['BOOLEAN', 'UINT8', 'UINT16', 'UINT32', 'UINT64']
65 self
.text
=self
.megre_lst()[0]
66 self
.content
=self
.megre_lst()[1]
71 for file in self
.file:
72 with
open(file,'r') as f
:
76 return alltext
,content
78 def struct_lst(self
):#{struct:lst file}
80 name_format
= re
.compile(r
'(?<!typedef)\s+struct (\w+) {.*?;', re
.S
)
81 for i
in list(self
.content
.keys()):
82 structs
= name_format
.findall(self
.content
[i
])
85 if j
not in self
._ignore
:
91 def struct(self
):#struct:{offset:name}
92 unit_num
= re
.compile('(\d+)')
93 offset1_re
= re
.compile('(\d+)\[')
94 pcdname_num_re
= re
.compile('\w+\[(\S+)\]')
95 pcdname_re
= re
.compile('\](.*)\<')
96 pcdname2_re
= re
.compile('(\w+)\[')
97 uint_re
= re
.compile('\<(\S+)\>')
98 name_format
= re
.compile(r
'(?<!typedef)\s+struct (\w+) {.*?;', re
.S
)
99 name
=name_format
.findall(self
.text
)
103 tmp_n
= [n
for n
in name
if n
not in self
._ignore
]
104 name
= list(set(tmp_n
))
105 name
.sort(key
= tmp_n
.index
)
107 #name=list(set(name).difference(set(self._ignore)))
109 s_re
= re
.compile(r
'struct %s :(.*?)};'% struct
, re
.S
)
110 content
= s_re
.search(self
.text
)
113 text
= content
.group().split('+')
114 for line
in text
[1:]:
115 offset
= offset1_re
.findall(line
)
116 t_name
= pcdname_re
.findall(line
)
117 uint
= uint_re
.findall(line
)
122 t_name
= t_name
[0].strip()
123 if (' ' in t_name
) or ("=" in t_name
) or (";" in t_name
) or("\\" in name
) or (t_name
==''):
124 WARNING
.append("Warning:Invalid Pcd name '%s' for Offset %s in struct %s" % (t_name
,offset
, struct
))
127 if uint
in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
128 offset
= int(offset
, 10)
129 tmp_name
= pcdname2_re
.findall(t_name
)[0] + '[0]'
130 tmp_dict
[offset
] = tmp_name
131 pcdname_num
= int(pcdname_num_re
.findall(t_name
)[0],10)
132 uint
= int(unit_num
.findall(uint
)[0],10)
134 for i
in range(1, pcdname_num
):
136 tmp_name
= pcdname2_re
.findall(t_name
)[0] + '[%s]' % i
137 tmp_dict
[offset
] = tmp_name
139 tmp_name
= pcdname2_re
.findall(t_name
)[0]
140 pcdname_num
= pcdname_num_re
.findall(t_name
)[0]
141 line
= [offset
,tmp_name
,pcdname_num
,uint
]
145 if uint
not in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
146 line
= [offset
, t_name
, 0, uint
]
150 offset
= int(offset
,10)
151 tmp_dict
[offset
] = t_name
152 info
[struct
] = tmp_dict
153 if len(unparse
) != 0:
155 if u
[3] in list(info
.keys()):
156 unpar
= self
.nameISstruct(u
,info
[u
[3]])
157 info
[u
[4]]= dict(list(info
[u
[4]].items())+list(unpar
[u
[4]].items()))
159 print("ERROR: No struct name found in %s" % self
.file)
160 ERRORMSG
.append("ERROR: No struct name found in %s" % self
.file)
164 def nameISstruct(self
,line
,key_dict
):
167 s_re
= re
.compile(r
'struct %s :(.*?)};' % line
[3], re
.S
)
168 size_re
= re
.compile(r
'mTotalSize \[(\S+)\]')
169 content
= s_re
.search(self
.text
)
171 s_size
= size_re
.findall(content
.group())[0]
174 print("ERROR: Struct %s not define mTotalSize in lst file" %line
[3])
175 ERRORMSG
.append("ERROR: Struct %s not define mTotalSize in lst file" %line
[3])
176 size
= int(line
[0], 10)
178 for j
in range(0, int(line
[2], 10)):
179 for k
in list(key_dict
.keys()):
181 name
='%s.%s' %((line
[1]+'[%s]'%j
),key_dict
[k
])
183 size
= int(s_size
,16)+size
185 for k
in list(key_dict
.keys()):
187 name
= '%s.%s' % (line
[1], key_dict
[k
])
189 dict2
[line
[4]] = dict
192 def efivarstore_parser(self
):
193 efivarstore_format
= re
.compile(r
'efivarstore.*?;', re
.S
)
194 struct_re
= re
.compile(r
'efivarstore(.*?),',re
.S
)
195 name_re
= re
.compile(r
'name=(\w+)')
197 efitxt
= efivarstore_format
.findall(self
.text
)
199 struct
= struct_re
.findall(i
.replace(' ',''))
200 name
= name_re
.findall(i
.replace(' ',''))
202 efivarstore_dict
[name
[0]]=struct
[0]
204 print("ERROR: Can't find Struct or name in lst file, please check have this format:efivarstore XXXX, name=xxxx")
205 ERRORMSG
.append("ERROR: Can't find Struct or name in lst file, please check have this format:efivarstore XXXX, name=xxxx")
206 return efivarstore_dict
208 class Config(object):
210 def __init__(self
,Config
):
213 #Parser .config file,return list[offset,name,guid,value,help]
214 def config_parser(self
):
215 ids_re
=re
.compile('_ID:(\d+)',re
.S
)
216 id_re
= re
.compile('\s+')
219 with
open(self
.config
, 'r') as text
:
221 if 'DEFAULT_ID:' in read
:
222 all_txt
= read
.split('FCEKEY DEFAULT')
223 for i
in all_txt
[1:]:
224 part
= [] #save all infomation for DEFAULT_ID
226 ids
= ids_re
.findall(i
.replace(' ',''))
231 section
= i
.split('\nQ') #split with '\nQ ' to get every block
232 part
+=self
.section_parser(section
)
233 info_dict
[str_id
] = self
.section_parser(section
)
240 section
= read
.split('\nQ')
241 part
+=self
.section_parser(section
)
242 info_dict
[str_id
] = self
.section_parser(section
)
246 def eval_id(self
,id):
248 default_id
=id[0:len(id)//2]
249 platform_id
=id[len(id)//2:]
251 for i
in range(len(default_id
)):
252 text
+="%s.common.%s.%s,"%(SECTION
,self
.id_name(platform_id
[i
],'PLATFORM'),self
.id_name(default_id
[i
],'DEFAULT'))
253 return '\n[%s]\n'%text
[:-1]
255 def id_name(self
,ID
, flag
):
256 platform_dict
= {'0': 'DEFAULT'}
257 default_dict
= {'0': 'STANDARD', '1': 'MANUFACTURING'}
258 if flag
== "PLATFORM":
260 value
= platform_dict
[ID
]
262 value
= 'SKUID%s' % ID
263 elif flag
== 'DEFAULT':
265 value
= default_dict
[ID
]
267 value
= 'DEFAULTID%s' % ID
272 def section_parser(self
,section
):
273 offset_re
= re
.compile(r
'offset=(\w+)')
274 name_re
= re
.compile(r
'name=(\S+)')
275 guid_re
= re
.compile(r
'guid=(\S+)')
276 # help_re = re.compile(r'help = (.*)')
277 attribute_re
=re
.compile(r
'attribute=(\w+)')
278 value_re
= re
.compile(r
'(//.*)')
280 for x
in section
[1:]:
281 line
=x
.split('\n')[0]
282 comment_list
= value_re
.findall(line
) # the string \\... in "Q...." line
283 comment_list
[0] = comment_list
[0].replace('//', '')
284 comment
= comment_list
[0].strip()
285 line
=value_re
.sub('',line
) #delete \\... in "Q...." line
286 list1
=line
.split(' ')
287 value
=self
.value_parser(list1
)
288 offset
= offset_re
.findall(x
.replace(' ',''))
289 name
= name_re
.findall(x
.replace(' ',''))
290 guid
= guid_re
.findall(x
.replace(' ',''))
291 attribute
=attribute_re
.findall(x
.replace(' ',''))
292 if offset
and name
and guid
and value
and attribute
:
293 if attribute
[0] in ['0x3','0x7']:
294 offset
= int(offset
[0], 16)
295 #help = help_re.findall(x)
296 text
= offset
, name
[0], guid
[0], value
, attribute
[0], comment
300 def value_parser(self
, list1
):
301 list1
= [t
for t
in list1
if t
!= ''] # remove '' form list
302 first_num
= int(list1
[0], 16)
303 if list1
[first_num
+ 1] == 'STRING': # parser STRING
304 if list1
[-1] == '""':
307 value
= 'L%s' % list1
[-1]
308 elif list1
[first_num
+ 1] == 'ORDERED_LIST': # parser ORDERED_LIST
309 value_total
= int(list1
[first_num
+ 2])
310 list2
= list1
[-value_total
:]
314 if len(i
) % 2 == 0 and len(i
) != 2:
315 for m
in range(0, len(i
) // 2):
316 tmp
.append('0x%02x' % (int('0x%s' % i
, 16) >> m
* 8 & 0xff))
318 tmp
.append('0x%s' % i
)
321 value
= '{%s}' % line
[:-1]
323 value
= "0x%01x" % int(list1
[-1], 16)
327 #parser Guid file, get guid name form guid value
330 def __init__(self
,path
):
332 self
.guidfile
= self
.gfile()
333 self
.guiddict
= self
.guid_dict()
336 for root
, dir, file in os
.walk(self
.path
, topdown
=True, followlinks
=False):
338 gfile
= os
.path
.join(root
,'Fv','Guid.xref')
339 if os
.path
.isfile(gfile
):
342 print("ERROR: Guid.xref file not found")
343 ERRORMSG
.append("ERROR: Guid.xref file not found")
348 with
open(self
.guidfile
,'r') as file:
349 lines
= file.readlines()
351 for line
in guidinfo
:
352 list=line
.strip().split(' ')
355 guiddict
[list[0].upper()]=list[1]
356 elif list[0] != ''and len(list)==1:
357 print("Error: line %s can't be parser in %s"%(line
.strip(),self
.guidfile
))
358 ERRORMSG
.append("Error: line %s can't be parser in %s"%(line
.strip(),self
.guidfile
))
360 print("ERROR: No data in %s" %self
.guidfile
)
361 ERRORMSG
.append("ERROR: No data in %s" %self
.guidfile
)
364 def guid_parser(self
,guid
):
365 if guid
.upper() in self
.guiddict
:
366 return self
.guiddict
[guid
.upper()]
368 print("ERROR: GUID %s not found in file %s"%(guid
, self
.guidfile
))
369 ERRORMSG
.append("ERROR: GUID %s not found in file %s"%(guid
, self
.guidfile
))
374 def __init__(self
,path
):
376 self
.rootdir
=self
.get_root_dir()
379 for path
in self
.rootdir
:
380 for o_root
, o_dir
, o_file
in os
.walk(os
.path
.join(path
, "OUTPUT"), topdown
=True, followlinks
=False):
382 if os
.path
.splitext(INF
)[1] == '.inf':
383 for l_root
, l_dir
, l_file
in os
.walk(os
.path
.join(path
, "DEBUG"), topdown
=True,
386 if os
.path
.splitext(LST
)[1] == '.lst':
387 self
.lstinf
[os
.path
.join(l_root
, LST
)] = os
.path
.join(o_root
, INF
)
388 self
.usefuldir
.add(path
)
390 def get_root_dir(self
):
392 for root
,dir,file in os
.walk(self
.path
,topdown
=True,followlinks
=False):
394 updir
=root
.split("OUTPUT",1)[0]
395 rootdir
.append(updir
)
396 rootdir
=list(set(rootdir
))
404 package_re
=re
.compile(r
'Packages\.\w+]\n(.*)',re
.S
)
405 for i
in list(self
.lstinf
.values()):
406 with
open(i
,'r') as inf
:
408 section
=read
.split('[')
410 p
=package_re
.findall(j
)
412 package
[i
]=p
[0].rstrip()
415 def header(self
,struct
):
417 head_re
= re
.compile('typedef.*} %s;[\n]+(.*)(?:typedef|formset)'%struct
,re
.M|re
.S
)
418 head_re2
= re
.compile(r
'#line[\s\d]+"(\S+h)"')
419 for i
in list(self
.lstinf
.keys()):
420 with
open(i
,'r') as lst
:
422 h
= head_re
.findall(read
)
424 head
=head_re2
.findall(h
[0])
426 format
= head
[0].replace('\\\\','/').replace('\\','/')
427 name
=format
.split('/')[-1]
428 head
= self
.headerfileset
.get(name
)
430 head
= head
.replace('\\','/')
431 header
[struct
] = head
434 def headerfileset(self
):
436 for root
,dirs
,files
in os
.walk(self
.path
):
438 if os
.path
.basename(file) == 'deps.txt':
439 with
open(os
.path
.join(root
,file),"r") as fr
:
440 for line
in fr
.readlines():
441 headerset
[os
.path
.basename(line
).strip()] = line
.strip()
444 def makefile(self
,filename
):
445 re_format
= re
.compile(r
'DEBUG_DIR.*(?:\S+Pkg)\\(.*\\%s)'%filename
)
446 for i
in self
.usefuldir
:
447 with
open(os
.path
.join(i
,'Makefile'),'r') as make
:
449 dir = re_format
.findall(read
)
454 class mainprocess(object):
456 def __init__(self
,InputPath
,Config
,OutputPath
):
457 self
.init
= 0xFCD00000
458 self
.inputpath
= os
.path
.abspath(InputPath
)
459 self
.outputpath
= os
.path
.abspath(OutputPath
)
460 self
.LST
= PATH(self
.inputpath
)
461 self
.lst_dict
= self
.LST
.lst_inf()
463 self
.attribute_dict
= {'0x3': 'NV, BS', '0x7': 'NV, BS, RT'}
464 self
.guid
= GUID(self
.inputpath
)
468 conf
=Config(self
.Config
)
469 config_dict
=conf
.config_parser() #get {'0_0':[offset,name,guid,value,attribute]...,'1_0':....}
470 lst
=parser_lst(list(self
.lst_dict
.keys()))
471 efi_dict
=lst
.efivarstore_parser() #get {name:struct} form lst file
472 keys
=sorted(config_dict
.keys())
473 all_struct
=lst
.struct()
474 stru_lst
=lst
.struct_lst()
480 tmp
= self
.LST
.header(i
)
481 self
.header
.update(tmp
)
483 tmp_id
=[id_key
] #['0_0',[(struct,[name...]),(struct,[name...])]]
484 tmp_info
={} #{name:struct}
485 for section
in config_dict
[id_key
]:
486 c_offset
,c_name
,c_guid
,c_value
,c_attribute
,c_comment
= section
487 if c_name
in efi_dict
:
488 struct
= efi_dict
[c_name
]
489 title
='%s%s|L"%s"|%s|0x00||%s\n'%(PCD_NAME
,c_name
,c_name
,self
.guid
.guid_parser(c_guid
),self
.attribute_dict
[c_attribute
])
490 if struct
in all_struct
:
491 lstfile
= stru_lst
[struct
]
492 struct_dict
=all_struct
[struct
]
494 title2
= '%s%s|{0}|%s|0xFCD00000{\n <HeaderFiles>\n %s\n <Packages>\n%s\n}\n' % (PCD_NAME
, c_name
, struct
, self
.header
[struct
], self
.LST
.package()[self
.lst_dict
[lstfile
]])
496 WARNING
.append("Warning: No <HeaderFiles> for struct %s"%struct
)
497 title2
= '%s%s|{0}|%s|0xFCD00000{\n <HeaderFiles>\n %s\n <Packages>\n%s\n}\n' % (PCD_NAME
, c_name
, struct
, '', self
.LST
.package()[self
.lst_dict
[lstfile
]])
498 header_list
.append(title2
)
499 elif struct
not in lst
._ignore
:
501 print("ERROR: Struct %s can't found in lst file" %struct
)
502 ERRORMSG
.append("ERROR: Struct %s can't found in lst file" %struct
)
503 if c_offset
in struct_dict
:
504 offset_name
=struct_dict
[c_offset
]
505 info
= "%s%s.%s|%s\n"%(PCD_NAME
,c_name
,offset_name
,c_value
)
506 blank_length
= Max_Pcd_Len
- len(info
)
507 if blank_length
<= 0:
508 info_comment
= "%s%s.%s|%s%s# %s\n"%(PCD_NAME
,c_name
,offset_name
,c_value
," ",c_comment
)
510 info_comment
= "%s%s.%s|%s%s# %s\n"%(PCD_NAME
,c_name
,offset_name
,c_value
,blank_length
*" ",c_comment
)
511 inf
= "%s%s\n"%(PCD_NAME
,c_name
)
513 tmp_info
[info_comment
]=title
515 print("ERROR: Can't find offset %s with struct name %s"%(c_offset
,struct
))
516 ERRORMSG
.append("ERROR: Can't find offset %s with name %s"%(c_offset
,struct
))
518 print("ERROR: Can't find name %s in lst file"%(c_name))
519 ERRORMSG
.append("ERROR: Can't find name %s in lst file"%(c_name))
520 tmp_id
.append(list(self
.reverse_dict(tmp_info
).items()))
521 id,tmp_title_list
,tmp_info_list
= self
.read_list(tmp_id
)
522 title_list
+=tmp_title_list
523 info_list
.append(tmp_info_list
)
524 inf_list
= self
.del_repeat(inf_list
)
525 header_list
= self
.plus(self
.del_repeat(header_list
))
526 title_all
=list(set(title_list
))
527 info_list
= self
.remove_bracket(self
.del_repeat(info_list
))
528 for i
in range(len(info_list
)-1,-1,-1):
529 if len(info_list
[i
]) == 0:
530 info_list
.remove(info_list
[i
])
531 for i
in (inf_list
, title_all
, header_list
):
533 return keys
,title_all
,info_list
,header_list
,inf_list
535 def remove_bracket(self
,List
):
539 if (('L"' in j
) and ("[" in j
)) or (tmp
[1].strip() == '{0x0, 0x0}'):
540 tmp
[0] = tmp
[0][:tmp
[0].index('[')]
541 List
[List
.index(i
)][i
.index(j
)] = "|".join(tmp
)
543 List
[List
.index(i
)][i
.index(j
)] = j
545 if type(i
) == type([0,0]):
552 if not os
.path
.isdir(self
.outputpath
):
553 os
.makedirs(self
.outputpath
)
554 decwrite
= write2file(os
.path
.join(self
.outputpath
,'StructurePcd.dec'))
555 dscwrite
= write2file(os
.path
.join(self
.outputpath
,'StructurePcd.dsc'))
556 infwrite
= write2file(os
.path
.join(self
.outputpath
, 'StructurePcd.inf'))
557 conf
= Config(self
.Config
)
558 ids
,title
,info
,header
,inf
=self
.main()
559 decwrite
.add2file(decstatement
)
560 decwrite
.add2file(header
)
561 infwrite
.add2file(infstatement
)
562 infwrite
.add2file(inf
)
563 dscwrite
.add2file(dscstatement
)
565 dscwrite
.add2file(conf
.eval_id(id))
567 dscwrite
.add2file(title
)
570 dscwrite
.add2file(info
)
573 dscwrite
.add2file(info
[0])
576 dscwrite
.add2file(info
[1])
578 def del_repeat(self
,List
):
579 if len(List
) == 1 or len(List
) == 0:
582 if type(List
[0]) != type('xxx'):
584 for i
in range(len(List
)):
586 alist
.append(List
[0])
591 alist
.append(self
.__del
(list(set(plist
)), List
[i
]))
594 return list(set(List
))
597 def __del(self
,list1
,list2
):
598 return list(set(list2
).difference(set(list1
)))
600 def reverse_dict(self
,dict):
602 for i
in list(dict.items()):
603 if i
[1] not in list(data
.keys()):
606 data
[i
[1]].append(i
[0])
609 def read_list(self
,list):
613 title_list
.append(i
[0])
616 return list[0],title_list
,info_list
621 if type(i
) != type([0]):
623 num
= "0x%01x" % self
.init
624 j
=i
.replace('0xFCD00000',num
.upper())
628 class write2file(object):
630 def __init__(self
,Output
):
633 if os
.path
.exists(self
.output
):
634 os
.remove(self
.output
)
636 def add2file(self
,content
):
638 with
open(self
.output
,'a+') as file:
639 file.write(self
.__gen
(content
))
641 def __gen(self
,content
):
642 if type(content
) == type(''):
644 elif type(content
) == type([0,0])or type(content
) == type((0,0)):
645 return self
.__readlist
(content
)
646 elif type(content
) == type({0:0}):
647 return self
.__readdict
(content
)
649 def __readlist(self
,list):
651 if type(i
) == type([0,0])or type(i
) == type((0,0)):
653 elif type(i
) == type('') :
657 def __readdict(self
,dict):
658 content
=list(dict.items())
659 return self
.__readlist
(content
)
662 return datetime
.datetime
.now()
664 def dtime(start
,end
,id=None):
667 print("%s time:%s" % (id,str(end
- start
)))
669 print("Total time:%s" %str
(end
-start
)[:-7])
674 parser
= argparse
.ArgumentParser(prog
= __prog__
,
675 description
= __description__
+ __copyright__
,
676 conflict_handler
= 'resolve')
677 parser
.add_argument('-v', '--version', action
= 'version',version
= __version__
, help="show program's version number and exit")
678 parser
.add_argument('-p', '--path', metavar
='PATH', dest
='path', help="platform build output directory")
679 parser
.add_argument('-c', '--config',metavar
='FILENAME', dest
='config', help="firmware configuration file")
680 parser
.add_argument('-o', '--outputdir', metavar
='PATH', dest
='output', help="output directoy")
681 options
= parser
.parse_args()
685 run
= mainprocess(options
.path
, options
.config
, options
.output
)
689 warning
= list(set(WARNING
))
693 ERROR
= list(set(ERRORMSG
))
694 with
open("ERROR.log", 'w+') as error
:
696 error
.write(i
+ '\n')
697 print("Some error find, error log in ERROR.log")
698 print('Finished, Output files in directory %s'%os.path
.abspath(options
.output
))
700 print('Error command, no output path, use -h for help')
702 print('Error command, no build path input, use -h for help')
704 print('Error command, no output file, use -h for help')
708 if __name__
== '__main__':