]> git.proxmox.com Git - mirror_edk2.git/blame - IntelFsp2Pkg/Tools/ConfigEditor/ConfigEditor.py
IntelFsp2Pkg: Add search function for Config Editor
[mirror_edk2.git] / IntelFsp2Pkg / Tools / ConfigEditor / ConfigEditor.py
CommitLineData
580b1120
LTL
1# @ ConfigEditor.py\r
2#\r
3# Copyright(c) 2018 - 2021, Intel Corporation. All rights reserved.<BR>\r
4# SPDX-License-Identifier: BSD-2-Clause-Patent\r
5#\r
6##\r
7\r
8import os\r
9import sys\r
10import marshal\r
11import tkinter\r
12import tkinter.ttk as ttk\r
13import tkinter.messagebox as messagebox\r
14import tkinter.filedialog as filedialog\r
15\r
16from pathlib import Path\r
17from GenYamlCfg import CGenYamlCfg, bytes_to_value, \\r
18 bytes_to_bracket_str, value_to_bytes, array_str_to_value\r
19from ctypes import sizeof, Structure, ARRAY, c_uint8, c_uint64, c_char, \\r
20 c_uint32, c_uint16\r
21from functools import reduce\r
22\r
23sys.path.insert(0, '..')\r
24from FspDscBsf2Yaml import bsf_to_dsc, dsc_to_yaml # noqa\r
25\r
26\r
27sys.dont_write_bytecode = True\r
28\r
29\r
30class create_tool_tip(object):\r
31 '''\r
32 create a tooltip for a given widget\r
33 '''\r
34 in_progress = False\r
35\r
36 def __init__(self, widget, text=''):\r
37 self.top_win = None\r
38 self.widget = widget\r
39 self.text = text\r
40 self.widget.bind("<Enter>", self.enter)\r
41 self.widget.bind("<Leave>", self.leave)\r
42\r
43 def enter(self, event=None):\r
44 if self.in_progress:\r
45 return\r
46 if self.widget.winfo_class() == 'Treeview':\r
47 # Only show help when cursor is on row header.\r
48 rowid = self.widget.identify_row(event.y)\r
49 if rowid != '':\r
50 return\r
51 else:\r
52 x, y, cx, cy = self.widget.bbox("insert")\r
53\r
54 cursor = self.widget.winfo_pointerxy()\r
55 x = self.widget.winfo_rootx() + 35\r
56 y = self.widget.winfo_rooty() + 20\r
57 if cursor[1] > y and cursor[1] < y + 20:\r
58 y += 20\r
59\r
60 # creates a toplevel window\r
61 self.top_win = tkinter.Toplevel(self.widget)\r
62 # Leaves only the label and removes the app window\r
63 self.top_win.wm_overrideredirect(True)\r
64 self.top_win.wm_geometry("+%d+%d" % (x, y))\r
65 label = tkinter.Message(self.top_win,\r
66 text=self.text,\r
67 justify='left',\r
68 background='bisque',\r
69 relief='solid',\r
70 borderwidth=1,\r
71 font=("times", "10", "normal"))\r
72 label.pack(ipadx=1)\r
73 self.in_progress = True\r
74\r
75 def leave(self, event=None):\r
76 if self.top_win:\r
77 self.top_win.destroy()\r
78 self.in_progress = False\r
79\r
80\r
81class validating_entry(tkinter.Entry):\r
82 def __init__(self, master, **kw):\r
83 tkinter.Entry.__init__(*(self, master), **kw)\r
84 self.parent = master\r
85 self.old_value = ''\r
86 self.last_value = ''\r
87 self.variable = tkinter.StringVar()\r
88 self.variable.trace("w", self.callback)\r
89 self.config(textvariable=self.variable)\r
90 self.config({"background": "#c0c0c0"})\r
91 self.bind("<Return>", self.move_next)\r
92 self.bind("<Tab>", self.move_next)\r
93 self.bind("<Escape>", self.cancel)\r
94 for each in ['BackSpace', 'Delete']:\r
95 self.bind("<%s>" % each, self.ignore)\r
96 self.display(None)\r
97\r
98 def ignore(self, even):\r
99 return "break"\r
100\r
101 def move_next(self, event):\r
102 if self.row < 0:\r
103 return\r
104 row, col = self.row, self.col\r
105 txt, row_id, col_id = self.parent.get_next_cell(row, col)\r
106 self.display(txt, row_id, col_id)\r
107 return "break"\r
108\r
109 def cancel(self, event):\r
110 self.variable.set(self.old_value)\r
111 self.display(None)\r
112\r
113 def display(self, txt, row_id='', col_id=''):\r
114 if txt is None:\r
115 self.row = -1\r
116 self.col = -1\r
117 self.place_forget()\r
118 else:\r
119 row = int('0x' + row_id[1:], 0) - 1\r
120 col = int(col_id[1:]) - 1\r
121 self.row = row\r
122 self.col = col\r
123 self.old_value = txt\r
124 self.last_value = txt\r
125 x, y, width, height = self.parent.bbox(row_id, col)\r
126 self.place(x=x, y=y, w=width)\r
127 self.variable.set(txt)\r
128 self.focus_set()\r
129 self.icursor(0)\r
130\r
131 def callback(self, *Args):\r
132 cur_val = self.variable.get()\r
133 new_val = self.validate(cur_val)\r
134 if new_val is not None and self.row >= 0:\r
135 self.last_value = new_val\r
136 self.parent.set_cell(self.row, self.col, new_val)\r
137 self.variable.set(self.last_value)\r
138\r
139 def validate(self, value):\r
140 if len(value) > 0:\r
141 try:\r
142 int(value, 16)\r
143 except Exception:\r
144 return None\r
145\r
146 # Normalize the cell format\r
147 self.update()\r
148 cell_width = self.winfo_width()\r
149 max_len = custom_table.to_byte_length(cell_width) * 2\r
150 cur_pos = self.index("insert")\r
151 if cur_pos == max_len + 1:\r
152 value = value[-max_len:]\r
153 else:\r
154 value = value[:max_len]\r
155 if value == '':\r
156 value = '0'\r
157 fmt = '%%0%dX' % max_len\r
158 return fmt % int(value, 16)\r
159\r
160\r
161class custom_table(ttk.Treeview):\r
162 _Padding = 20\r
163 _Char_width = 6\r
164\r
165 def __init__(self, parent, col_hdr, bins):\r
166 cols = len(col_hdr)\r
167\r
168 col_byte_len = []\r
169 for col in range(cols): # Columns\r
170 col_byte_len.append(int(col_hdr[col].split(':')[1]))\r
171\r
172 byte_len = sum(col_byte_len)\r
173 rows = (len(bins) + byte_len - 1) // byte_len\r
174\r
175 self.rows = rows\r
176 self.cols = cols\r
177 self.col_byte_len = col_byte_len\r
178 self.col_hdr = col_hdr\r
179\r
180 self.size = len(bins)\r
181 self.last_dir = ''\r
182\r
183 style = ttk.Style()\r
184 style.configure("Custom.Treeview.Heading",\r
185 font=('calibri', 10, 'bold'),\r
186 foreground="blue")\r
187 ttk.Treeview.__init__(self, parent, height=rows,\r
188 columns=[''] + col_hdr, show='headings',\r
189 style="Custom.Treeview",\r
190 selectmode='none')\r
191 self.bind("<Button-1>", self.click)\r
192 self.bind("<FocusOut>", self.focus_out)\r
193 self.entry = validating_entry(self, width=4, justify=tkinter.CENTER)\r
194\r
195 self.heading(0, text='LOAD')\r
196 self.column(0, width=60, stretch=0, anchor=tkinter.CENTER)\r
197\r
198 for col in range(cols): # Columns\r
199 text = col_hdr[col].split(':')[0]\r
200 byte_len = int(col_hdr[col].split(':')[1])\r
201 self.heading(col+1, text=text)\r
202 self.column(col+1, width=self.to_cell_width(byte_len),\r
203 stretch=0, anchor=tkinter.CENTER)\r
204 idx = 0\r
205 for row in range(rows): # Rows\r
206 text = '%04X' % (row * len(col_hdr))\r
207 vals = ['%04X:' % (cols * row)]\r
208 for col in range(cols): # Columns\r
209 if idx >= len(bins):\r
210 break\r
211 byte_len = int(col_hdr[col].split(':')[1])\r
212 value = bytes_to_value(bins[idx:idx+byte_len])\r
213 hex = ("%%0%dX" % (byte_len * 2)) % value\r
214 vals.append(hex)\r
215 idx += byte_len\r
216 self.insert('', 'end', values=tuple(vals))\r
217 if idx >= len(bins):\r
218 break\r
219\r
220 @staticmethod\r
221 def to_cell_width(byte_len):\r
222 return byte_len * 2 * custom_table._Char_width + custom_table._Padding\r
223\r
224 @staticmethod\r
225 def to_byte_length(cell_width):\r
226 return(cell_width - custom_table._Padding) \\r
227 // (2 * custom_table._Char_width)\r
228\r
229 def focus_out(self, event):\r
230 self.entry.display(None)\r
231\r
232 def refresh_bin(self, bins):\r
233 if not bins:\r
234 return\r
235\r
236 # Reload binary into widget\r
237 bin_len = len(bins)\r
238 for row in range(self.rows):\r
239 iid = self.get_children()[row]\r
240 for col in range(self.cols):\r
241 idx = row * sum(self.col_byte_len) + \\r
242 sum(self.col_byte_len[:col])\r
243 byte_len = self.col_byte_len[col]\r
244 if idx + byte_len <= self.size:\r
245 byte_len = int(self.col_hdr[col].split(':')[1])\r
246 if idx + byte_len > bin_len:\r
247 val = 0\r
248 else:\r
249 val = bytes_to_value(bins[idx:idx+byte_len])\r
250 hex_val = ("%%0%dX" % (byte_len * 2)) % val\r
251 self.set(iid, col + 1, hex_val)\r
252\r
253 def get_cell(self, row, col):\r
254 iid = self.get_children()[row]\r
255 txt = self.item(iid, 'values')[col]\r
256 return txt\r
257\r
258 def get_next_cell(self, row, col):\r
259 rows = self.get_children()\r
260 col += 1\r
261 if col > self.cols:\r
262 col = 1\r
263 row += 1\r
264 cnt = row * sum(self.col_byte_len) + sum(self.col_byte_len[:col])\r
265 if cnt > self.size:\r
266 # Reached the last cell, so roll back to beginning\r
267 row = 0\r
268 col = 1\r
269\r
270 txt = self.get_cell(row, col)\r
271 row_id = rows[row]\r
272 col_id = '#%d' % (col + 1)\r
273 return(txt, row_id, col_id)\r
274\r
275 def set_cell(self, row, col, val):\r
276 iid = self.get_children()[row]\r
277 self.set(iid, col, val)\r
278\r
279 def load_bin(self):\r
280 # Load binary from file\r
281 path = filedialog.askopenfilename(\r
282 initialdir=self.last_dir,\r
283 title="Load binary file",\r
284 filetypes=(("Binary files", "*.bin"), (\r
285 "binary files", "*.bin")))\r
286 if path:\r
287 self.last_dir = os.path.dirname(path)\r
288 fd = open(path, 'rb')\r
289 bins = bytearray(fd.read())[:self.size]\r
290 fd.close()\r
291 bins.extend(b'\x00' * (self.size - len(bins)))\r
292 return bins\r
293\r
294 return None\r
295\r
296 def click(self, event):\r
297 row_id = self.identify_row(event.y)\r
298 col_id = self.identify_column(event.x)\r
299 if row_id == '' and col_id == '#1':\r
300 # Clicked on "LOAD" cell\r
301 bins = self.load_bin()\r
302 self.refresh_bin(bins)\r
303 return\r
304\r
305 if col_id == '#1':\r
306 # Clicked on column 1(Offset column)\r
307 return\r
308\r
309 item = self.identify('item', event.x, event.y)\r
310 if not item or not col_id:\r
311 # Not clicked on valid cell\r
312 return\r
313\r
314 # Clicked cell\r
315 row = int('0x' + row_id[1:], 0) - 1\r
316 col = int(col_id[1:]) - 1\r
317 if row * self.cols + col > self.size:\r
318 return\r
319\r
320 vals = self.item(item, 'values')\r
321 if col < len(vals):\r
322 txt = self.item(item, 'values')[col]\r
323 self.entry.display(txt, row_id, col_id)\r
324\r
325 def get(self):\r
326 bins = bytearray()\r
327 row_ids = self.get_children()\r
328 for row_id in row_ids:\r
329 row = int('0x' + row_id[1:], 0) - 1\r
330 for col in range(self.cols):\r
331 idx = row * sum(self.col_byte_len) + \\r
332 sum(self.col_byte_len[:col])\r
333 byte_len = self.col_byte_len[col]\r
334 if idx + byte_len > self.size:\r
335 break\r
336 hex = self.item(row_id, 'values')[col + 1]\r
337 values = value_to_bytes(int(hex, 16)\r
338 & ((1 << byte_len * 8) - 1), byte_len)\r
339 bins.extend(values)\r
340 return bins\r
341\r
342\r
343class c_uint24(Structure):\r
344 """Little-Endian 24-bit Unsigned Integer"""\r
345 _pack_ = 1\r
346 _fields_ = [('Data', (c_uint8 * 3))]\r
347\r
348 def __init__(self, val=0):\r
349 self.set_value(val)\r
350\r
351 def __str__(self, indent=0):\r
352 return '0x%.6x' % self.value\r
353\r
354 def __int__(self):\r
355 return self.get_value()\r
356\r
357 def set_value(self, val):\r
358 self.Data[0:3] = Val2Bytes(val, 3)\r
359\r
360 def get_value(self):\r
361 return Bytes2Val(self.Data[0:3])\r
362\r
363 value = property(get_value, set_value)\r
364\r
365\r
366class EFI_FIRMWARE_VOLUME_HEADER(Structure):\r
367 _fields_ = [\r
368 ('ZeroVector', ARRAY(c_uint8, 16)),\r
369 ('FileSystemGuid', ARRAY(c_uint8, 16)),\r
370 ('FvLength', c_uint64),\r
371 ('Signature', ARRAY(c_char, 4)),\r
372 ('Attributes', c_uint32),\r
373 ('HeaderLength', c_uint16),\r
374 ('Checksum', c_uint16),\r
375 ('ExtHeaderOffset', c_uint16),\r
376 ('Reserved', c_uint8),\r
377 ('Revision', c_uint8)\r
378 ]\r
379\r
380\r
381class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):\r
382 _fields_ = [\r
383 ('FvName', ARRAY(c_uint8, 16)),\r
384 ('ExtHeaderSize', c_uint32)\r
385 ]\r
386\r
387\r
388class EFI_FFS_INTEGRITY_CHECK(Structure):\r
389 _fields_ = [\r
390 ('Header', c_uint8),\r
391 ('File', c_uint8)\r
392 ]\r
393\r
394\r
395class EFI_FFS_FILE_HEADER(Structure):\r
396 _fields_ = [\r
397 ('Name', ARRAY(c_uint8, 16)),\r
398 ('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),\r
399 ('Type', c_uint8),\r
400 ('Attributes', c_uint8),\r
401 ('Size', c_uint24),\r
402 ('State', c_uint8)\r
403 ]\r
404\r
405\r
406class EFI_COMMON_SECTION_HEADER(Structure):\r
407 _fields_ = [\r
408 ('Size', c_uint24),\r
409 ('Type', c_uint8)\r
410 ]\r
411\r
412\r
413class EFI_SECTION_TYPE:\r
414 """Enumeration of all valid firmware file section types."""\r
415 ALL = 0x00\r
416 COMPRESSION = 0x01\r
417 GUID_DEFINED = 0x02\r
418 DISPOSABLE = 0x03\r
419 PE32 = 0x10\r
420 PIC = 0x11\r
421 TE = 0x12\r
422 DXE_DEPEX = 0x13\r
423 VERSION = 0x14\r
424 USER_INTERFACE = 0x15\r
425 COMPATIBILITY16 = 0x16\r
426 FIRMWARE_VOLUME_IMAGE = 0x17\r
427 FREEFORM_SUBTYPE_GUID = 0x18\r
428 RAW = 0x19\r
429 PEI_DEPEX = 0x1b\r
430 SMM_DEPEX = 0x1c\r
431\r
432\r
433class FSP_COMMON_HEADER(Structure):\r
434 _fields_ = [\r
435 ('Signature', ARRAY(c_char, 4)),\r
436 ('HeaderLength', c_uint32)\r
437 ]\r
438\r
439\r
440class FSP_INFORMATION_HEADER(Structure):\r
441 _fields_ = [\r
442 ('Signature', ARRAY(c_char, 4)),\r
443 ('HeaderLength', c_uint32),\r
444 ('Reserved1', c_uint16),\r
445 ('SpecVersion', c_uint8),\r
446 ('HeaderRevision', c_uint8),\r
447 ('ImageRevision', c_uint32),\r
448 ('ImageId', ARRAY(c_char, 8)),\r
449 ('ImageSize', c_uint32),\r
450 ('ImageBase', c_uint32),\r
451 ('ImageAttribute', c_uint16),\r
452 ('ComponentAttribute', c_uint16),\r
453 ('CfgRegionOffset', c_uint32),\r
454 ('CfgRegionSize', c_uint32),\r
455 ('Reserved2', c_uint32),\r
456 ('TempRamInitEntryOffset', c_uint32),\r
457 ('Reserved3', c_uint32),\r
458 ('NotifyPhaseEntryOffset', c_uint32),\r
459 ('FspMemoryInitEntryOffset', c_uint32),\r
460 ('TempRamExitEntryOffset', c_uint32),\r
461 ('FspSiliconInitEntryOffset', c_uint32)\r
462 ]\r
463\r
464\r
465class FSP_EXTENDED_HEADER(Structure):\r
466 _fields_ = [\r
467 ('Signature', ARRAY(c_char, 4)),\r
468 ('HeaderLength', c_uint32),\r
469 ('Revision', c_uint8),\r
470 ('Reserved', c_uint8),\r
471 ('FspProducerId', ARRAY(c_char, 6)),\r
472 ('FspProducerRevision', c_uint32),\r
473 ('FspProducerDataSize', c_uint32)\r
474 ]\r
475\r
476\r
477class FSP_PATCH_TABLE(Structure):\r
478 _fields_ = [\r
479 ('Signature', ARRAY(c_char, 4)),\r
480 ('HeaderLength', c_uint16),\r
481 ('HeaderRevision', c_uint8),\r
482 ('Reserved', c_uint8),\r
483 ('PatchEntryNum', c_uint32)\r
484 ]\r
485\r
486\r
487class Section:\r
488 def __init__(self, offset, secdata):\r
489 self.SecHdr = EFI_COMMON_SECTION_HEADER.from_buffer(secdata, 0)\r
490 self.SecData = secdata[0:int(self.SecHdr.Size)]\r
491 self.Offset = offset\r
492\r
493\r
494def AlignPtr(offset, alignment=8):\r
495 return (offset + alignment - 1) & ~(alignment - 1)\r
496\r
497\r
498def Bytes2Val(bytes):\r
499 return reduce(lambda x, y: (x << 8) | y, bytes[:: -1])\r
500\r
501\r
502def Val2Bytes(value, blen):\r
503 return [(value >> (i*8) & 0xff) for i in range(blen)]\r
504\r
505\r
506class FirmwareFile:\r
507 def __init__(self, offset, filedata):\r
508 self.FfsHdr = EFI_FFS_FILE_HEADER.from_buffer(filedata, 0)\r
509 self.FfsData = filedata[0:int(self.FfsHdr.Size)]\r
510 self.Offset = offset\r
511 self.SecList = []\r
512\r
513 def ParseFfs(self):\r
514 ffssize = len(self.FfsData)\r
515 offset = sizeof(self.FfsHdr)\r
516 if self.FfsHdr.Name != '\xff' * 16:\r
517 while offset < (ffssize - sizeof(EFI_COMMON_SECTION_HEADER)):\r
518 sechdr = EFI_COMMON_SECTION_HEADER.from_buffer(\r
519 self.FfsData, offset)\r
520 sec = Section(\r
521 offset, self.FfsData[offset:offset + int(sechdr.Size)])\r
522 self.SecList.append(sec)\r
523 offset += int(sechdr.Size)\r
524 offset = AlignPtr(offset, 4)\r
525\r
526\r
527class FirmwareVolume:\r
528 def __init__(self, offset, fvdata):\r
529 self.FvHdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer(fvdata, 0)\r
530 self.FvData = fvdata[0: self.FvHdr.FvLength]\r
531 self.Offset = offset\r
532 if self.FvHdr.ExtHeaderOffset > 0:\r
533 self.FvExtHdr = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer(\r
534 self.FvData, self.FvHdr.ExtHeaderOffset)\r
535 else:\r
536 self.FvExtHdr = None\r
537 self.FfsList = []\r
538\r
539 def ParseFv(self):\r
540 fvsize = len(self.FvData)\r
541 if self.FvExtHdr:\r
542 offset = self.FvHdr.ExtHeaderOffset + self.FvExtHdr.ExtHeaderSize\r
543 else:\r
544 offset = self.FvHdr.HeaderLength\r
545 offset = AlignPtr(offset)\r
546 while offset < (fvsize - sizeof(EFI_FFS_FILE_HEADER)):\r
547 ffshdr = EFI_FFS_FILE_HEADER.from_buffer(self.FvData, offset)\r
548 if (ffshdr.Name == '\xff' * 16) and \\r
549 (int(ffshdr.Size) == 0xFFFFFF):\r
550 offset = fvsize\r
551 else:\r
552 ffs = FirmwareFile(\r
553 offset, self.FvData[offset:offset + int(ffshdr.Size)])\r
554 ffs.ParseFfs()\r
555 self.FfsList.append(ffs)\r
556 offset += int(ffshdr.Size)\r
557 offset = AlignPtr(offset)\r
558\r
559\r
560class FspImage:\r
561 def __init__(self, offset, fih, fihoff, patch):\r
562 self.Fih = fih\r
563 self.FihOffset = fihoff\r
564 self.Offset = offset\r
565 self.FvIdxList = []\r
566 self.Type = "XTMSXXXXOXXXXXXX"[(fih.ComponentAttribute >> 12) & 0x0F]\r
567 self.PatchList = patch\r
568 self.PatchList.append(fihoff + 0x1C)\r
569\r
570 def AppendFv(self, FvIdx):\r
571 self.FvIdxList.append(FvIdx)\r
572\r
573 def Patch(self, delta, fdbin):\r
574 count = 0\r
575 applied = 0\r
576 for idx, patch in enumerate(self.PatchList):\r
577 ptype = (patch >> 24) & 0x0F\r
578 if ptype not in [0x00, 0x0F]:\r
579 raise Exception('ERROR: Invalid patch type %d !' % ptype)\r
580 if patch & 0x80000000:\r
581 patch = self.Fih.ImageSize - (0x1000000 - (patch & 0xFFFFFF))\r
582 else:\r
583 patch = patch & 0xFFFFFF\r
584 if (patch < self.Fih.ImageSize) and \\r
585 (patch + sizeof(c_uint32) <= self.Fih.ImageSize):\r
586 offset = patch + self.Offset\r
587 value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)])\r
588 value += delta\r
589 fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(\r
590 value, sizeof(c_uint32))\r
591 applied += 1\r
592 count += 1\r
593 # Don't count the FSP base address patch entry appended at the end\r
594 if count != 0:\r
595 count -= 1\r
596 applied -= 1\r
597 return (count, applied)\r
598\r
599\r
600class FirmwareDevice:\r
601 def __init__(self, offset, FdData):\r
602 self.FvList = []\r
603 self.FspList = []\r
604 self.FspExtList = []\r
605 self.FihList = []\r
606 self.BuildList = []\r
607 self.OutputText = ""\r
608 self.Offset = 0\r
609 self.FdData = FdData\r
610\r
611 def ParseFd(self):\r
612 offset = 0\r
613 fdsize = len(self.FdData)\r
614 self.FvList = []\r
615 while offset < (fdsize - sizeof(EFI_FIRMWARE_VOLUME_HEADER)):\r
616 fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer(self.FdData, offset)\r
617 if b'_FVH' != fvh.Signature:\r
618 raise Exception("ERROR: Invalid FV header !")\r
619 fv = FirmwareVolume(\r
620 offset, self.FdData[offset:offset + fvh.FvLength])\r
621 fv.ParseFv()\r
622 self.FvList.append(fv)\r
623 offset += fv.FvHdr.FvLength\r
624\r
625 def CheckFsp(self):\r
626 if len(self.FspList) == 0:\r
627 return\r
628\r
629 fih = None\r
630 for fsp in self.FspList:\r
631 if not fih:\r
632 fih = fsp.Fih\r
633 else:\r
634 newfih = fsp.Fih\r
635 if (newfih.ImageId != fih.ImageId) or \\r
636 (newfih.ImageRevision != fih.ImageRevision):\r
637 raise Exception(\r
638 "ERROR: Inconsistent FSP ImageId or "\r
639 "ImageRevision detected !")\r
640\r
641 def ParseFsp(self):\r
642 flen = 0\r
643 for idx, fv in enumerate(self.FvList):\r
644 # Check if this FV contains FSP header\r
645 if flen == 0:\r
646 if len(fv.FfsList) == 0:\r
647 continue\r
648 ffs = fv.FfsList[0]\r
649 if len(ffs.SecList) == 0:\r
650 continue\r
651 sec = ffs.SecList[0]\r
652 if sec.SecHdr.Type != EFI_SECTION_TYPE.RAW:\r
653 continue\r
654 fihoffset = ffs.Offset + sec.Offset + sizeof(sec.SecHdr)\r
655 fspoffset = fv.Offset\r
656 offset = fspoffset + fihoffset\r
657 fih = FSP_INFORMATION_HEADER.from_buffer(self.FdData, offset)\r
658 self.FihList.append(fih)\r
659 if b'FSPH' != fih.Signature:\r
660 continue\r
661\r
662 offset += fih.HeaderLength\r
663\r
664 offset = AlignPtr(offset, 2)\r
665 Extfih = FSP_EXTENDED_HEADER.from_buffer(self.FdData, offset)\r
666 self.FspExtList.append(Extfih)\r
667 offset = AlignPtr(offset, 4)\r
668 plist = []\r
669 while True:\r
670 fch = FSP_COMMON_HEADER.from_buffer(self.FdData, offset)\r
671 if b'FSPP' != fch.Signature:\r
672 offset += fch.HeaderLength\r
673 offset = AlignPtr(offset, 4)\r
674 else:\r
675 fspp = FSP_PATCH_TABLE.from_buffer(\r
676 self.FdData, offset)\r
677 offset += sizeof(fspp)\r
678 start_offset = offset + 32\r
679 end_offset = offset + 32\r
680 while True:\r
681 end_offset += 1\r
682 if(self.FdData[\r
683 end_offset: end_offset + 1] == b'\xff'):\r
684 break\r
685 self.BuildList.append(\r
686 self.FdData[start_offset:end_offset])\r
687 pdata = (c_uint32 * fspp.PatchEntryNum).from_buffer(\r
688 self.FdData, offset)\r
689 plist = list(pdata)\r
690 break\r
691\r
692 fsp = FspImage(fspoffset, fih, fihoffset, plist)\r
693 fsp.AppendFv(idx)\r
694 self.FspList.append(fsp)\r
695 flen = fsp.Fih.ImageSize - fv.FvHdr.FvLength\r
696 else:\r
697 fsp.AppendFv(idx)\r
698 flen -= fv.FvHdr.FvLength\r
699 if flen < 0:\r
700 raise Exception("ERROR: Incorrect FV size in image !")\r
701 self.CheckFsp()\r
702\r
703 def OutputFsp(self):\r
704 def copy_text_to_clipboard():\r
705 window.clipboard_clear()\r
706 window.clipboard_append(self.OutputText)\r
707\r
708 window = tkinter.Tk()\r
709 window.title("Fsp Headers")\r
710 window.resizable(0, 0)\r
711 # Window Size\r
712 window.geometry("300x400+350+150")\r
713 frame = tkinter.Frame(window)\r
714 frame.pack(side=tkinter.BOTTOM)\r
715 # Vertical (y) Scroll Bar\r
716 scroll = tkinter.Scrollbar(window)\r
717 scroll.pack(side=tkinter.RIGHT, fill=tkinter.Y)\r
718 text = tkinter.Text(window,\r
719 wrap=tkinter.NONE, yscrollcommand=scroll.set)\r
720 i = 0\r
721 self.OutputText = self.OutputText + "Fsp Header Details \n\n"\r
722 while i < len(self.FihList):\r
723 try:\r
724 self.OutputText += str(self.BuildList[i].decode()) + "\n"\r
725 except Exception:\r
726 self.OutputText += "No description found\n"\r
727 self.OutputText += "FSP Header :\n "\r
728 self.OutputText += "Signature : " + \\r
729 str(self.FihList[i].Signature.decode('utf-8')) + "\n "\r
730 self.OutputText += "Header Length : " + \\r
731 str(hex(self.FihList[i].HeaderLength)) + "\n "\r
732 self.OutputText += "Header Revision : " + \\r
733 str(hex(self.FihList[i].HeaderRevision)) + "\n "\r
734 self.OutputText += "Spec Version : " + \\r
735 str(hex(self.FihList[i].SpecVersion)) + "\n "\r
736 self.OutputText += "Image Revision : " + \\r
737 str(hex(self.FihList[i].ImageRevision)) + "\n "\r
738 self.OutputText += "Image Id : " + \\r
739 str(self.FihList[i].ImageId.decode('utf-8')) + "\n "\r
740 self.OutputText += "Image Size : " + \\r
741 str(hex(self.FihList[i].ImageSize)) + "\n "\r
742 self.OutputText += "Image Base : " + \\r
743 str(hex(self.FihList[i].ImageBase)) + "\n "\r
744 self.OutputText += "Image Attribute : " + \\r
745 str(hex(self.FihList[i].ImageAttribute)) + "\n "\r
746 self.OutputText += "Cfg Region Offset : " + \\r
747 str(hex(self.FihList[i].CfgRegionOffset)) + "\n "\r
748 self.OutputText += "Cfg Region Size : " + \\r
749 str(hex(self.FihList[i].CfgRegionSize)) + "\n "\r
750 self.OutputText += "API Entry Num : " + \\r
751 str(hex(self.FihList[i].Reserved2)) + "\n "\r
752 self.OutputText += "Temp Ram Init Entry : " + \\r
753 str(hex(self.FihList[i].TempRamInitEntryOffset)) + "\n "\r
754 self.OutputText += "FSP Init Entry : " + \\r
755 str(hex(self.FihList[i].Reserved3)) + "\n "\r
756 self.OutputText += "Notify Phase Entry : " + \\r
757 str(hex(self.FihList[i].NotifyPhaseEntryOffset)) + "\n "\r
758 self.OutputText += "Fsp Memory Init Entry : " + \\r
759 str(hex(self.FihList[i].FspMemoryInitEntryOffset)) + "\n "\r
760 self.OutputText += "Temp Ram Exit Entry : " + \\r
761 str(hex(self.FihList[i].TempRamExitEntryOffset)) + "\n "\r
762 self.OutputText += "Fsp Silicon Init Entry : " + \\r
763 str(hex(self.FihList[i].FspSiliconInitEntryOffset)) + "\n\n"\r
764 self.OutputText += "FSP Extended Header:\n "\r
765 self.OutputText += "Signature : " + \\r
766 str(self.FspExtList[i].Signature.decode('utf-8')) + "\n "\r
767 self.OutputText += "Header Length : " + \\r
768 str(hex(self.FspExtList[i].HeaderLength)) + "\n "\r
769 self.OutputText += "Header Revision : " + \\r
770 str(hex(self.FspExtList[i].Revision)) + "\n "\r
771 self.OutputText += "Fsp Producer Id : " + \\r
772 str(self.FspExtList[i].FspProducerId.decode('utf-8')) + "\n "\r
773 self.OutputText += "FspProducerRevision : " + \\r
774 str(hex(self.FspExtList[i].FspProducerRevision)) + "\n\n"\r
775 i += 1\r
776 text.insert(tkinter.INSERT, self.OutputText)\r
777 text.pack()\r
778 # Configure the scrollbars\r
779 scroll.config(command=text.yview)\r
780 copy_button = tkinter.Button(\r
781 window, text="Copy to Clipboard", command=copy_text_to_clipboard)\r
782 copy_button.pack(in_=frame, side=tkinter.LEFT, padx=20, pady=10)\r
783 exit_button = tkinter.Button(\r
784 window, text="Close", command=window.destroy)\r
785 exit_button.pack(in_=frame, side=tkinter.RIGHT, padx=20, pady=10)\r
786 window.mainloop()\r
787\r
788\r
789class state:\r
790 def __init__(self):\r
791 self.state = False\r
792\r
793 def set(self, value):\r
794 self.state = value\r
795\r
796 def get(self):\r
797 return self.state\r
798\r
799\r
800class application(tkinter.Frame):\r
801 def __init__(self, master=None):\r
802 root = master\r
803\r
804 self.debug = True\r
805 self.mode = 'FSP'\r
806 self.last_dir = '.'\r
807 self.page_id = ''\r
808 self.page_list = {}\r
809 self.conf_list = {}\r
810 self.cfg_data_obj = None\r
811 self.org_cfg_data_bin = None\r
812 self.in_left = state()\r
813 self.in_right = state()\r
cac83b6f
LTL
814 self.search_text = ''\r
815 self.binseg_dict = {}\r
580b1120
LTL
816\r
817 # Check if current directory contains a file with a .yaml extension\r
818 # if not default self.last_dir to a Platform directory where it is\r
819 # easier to locate *BoardPkg\CfgData\*Def.yaml files\r
820 self.last_dir = '.'\r
821 if not any(fname.endswith('.yaml') for fname in os.listdir('.')):\r
822 platform_path = Path(os.path.realpath(__file__)).parents[2].\\r
823 joinpath('Platform')\r
824 if platform_path.exists():\r
825 self.last_dir = platform_path\r
826\r
827 tkinter.Frame.__init__(self, master, borderwidth=2)\r
828\r
829 self.menu_string = [\r
830 'Save Config Data to Binary', 'Load Config Data from Binary',\r
831 'Show Binary Information',\r
832 'Load Config Changes from Delta File',\r
833 'Save Config Changes to Delta File',\r
834 'Save Full Config Data to Delta File',\r
835 'Open Config BSF file'\r
836 ]\r
837\r
838 root.geometry("1200x800")\r
839\r
cac83b6f
LTL
840 # Search string\r
841 fram = tkinter.Frame(root)\r
842 # adding label to search box\r
843 tkinter.Label(fram, text='Text to find:').pack(side=tkinter.LEFT)\r
844 # adding of single line text box\r
845 self.edit = tkinter.Entry(fram, width=30)\r
846 # positioning of text box\r
847 self.edit.pack(\r
848 side=tkinter.LEFT, fill=tkinter.BOTH, expand=1, padx=(4, 4))\r
849 # setting focus\r
850 self.edit.focus_set()\r
851 # adding of search button\r
852 butt = tkinter.Button(fram, text='Search', relief=tkinter.GROOVE,\r
853 command=self.search_bar)\r
854 butt.pack(side=tkinter.RIGHT, padx=(4, 4))\r
855 fram.pack(side=tkinter.TOP, anchor=tkinter.SE)\r
856\r
580b1120
LTL
857 paned = ttk.Panedwindow(root, orient=tkinter.HORIZONTAL)\r
858 paned.pack(fill=tkinter.BOTH, expand=True, padx=(4, 4))\r
859\r
860 status = tkinter.Label(master, text="", bd=1, relief=tkinter.SUNKEN,\r
861 anchor=tkinter.W)\r
862 status.pack(side=tkinter.BOTTOM, fill=tkinter.X)\r
863\r
864 frame_left = ttk.Frame(paned, height=800, relief="groove")\r
865\r
866 self.left = ttk.Treeview(frame_left, show="tree")\r
867\r
868 # Set up tree HScroller\r
869 pady = (10, 10)\r
870 self.tree_scroll = ttk.Scrollbar(frame_left,\r
871 orient="vertical",\r
872 command=self.left.yview)\r
873 self.left.configure(yscrollcommand=self.tree_scroll.set)\r
874 self.left.bind("<<TreeviewSelect>>", self.on_config_page_select_change)\r
875 self.left.bind("<Enter>", lambda e: self.in_left.set(True))\r
876 self.left.bind("<Leave>", lambda e: self.in_left.set(False))\r
877 self.left.bind("<MouseWheel>", self.on_tree_scroll)\r
878\r
879 self.left.pack(side='left',\r
880 fill=tkinter.BOTH,\r
881 expand=True,\r
882 padx=(5, 0),\r
883 pady=pady)\r
884 self.tree_scroll.pack(side='right', fill=tkinter.Y,\r
885 pady=pady, padx=(0, 5))\r
886\r
887 frame_right = ttk.Frame(paned, relief="groove")\r
888 self.frame_right = frame_right\r
889\r
890 self.conf_canvas = tkinter.Canvas(frame_right, highlightthickness=0)\r
891 self.page_scroll = ttk.Scrollbar(frame_right,\r
892 orient="vertical",\r
893 command=self.conf_canvas.yview)\r
894 self.right_grid = ttk.Frame(self.conf_canvas)\r
895 self.conf_canvas.configure(yscrollcommand=self.page_scroll.set)\r
896 self.conf_canvas.pack(side='left',\r
897 fill=tkinter.BOTH,\r
898 expand=True,\r
899 pady=pady,\r
900 padx=(5, 0))\r
901 self.page_scroll.pack(side='right', fill=tkinter.Y,\r
902 pady=pady, padx=(0, 5))\r
903 self.conf_canvas.create_window(0, 0, window=self.right_grid,\r
904 anchor='nw')\r
905 self.conf_canvas.bind('<Enter>', lambda e: self.in_right.set(True))\r
906 self.conf_canvas.bind('<Leave>', lambda e: self.in_right.set(False))\r
907 self.conf_canvas.bind("<Configure>", self.on_canvas_configure)\r
908 self.conf_canvas.bind_all("<MouseWheel>", self.on_page_scroll)\r
909\r
910 paned.add(frame_left, weight=2)\r
911 paned.add(frame_right, weight=10)\r
912\r
913 style = ttk.Style()\r
914 style.layout("Treeview", [('Treeview.treearea', {'sticky': 'nswe'})])\r
915\r
916 menubar = tkinter.Menu(root)\r
917 file_menu = tkinter.Menu(menubar, tearoff=0)\r
918 file_menu.add_command(label="Open Config YAML file",\r
919 command=self.load_from_yaml)\r
920 file_menu.add_command(label=self.menu_string[6],\r
921 command=self.load_from_bsf_file)\r
922 file_menu.add_command(label=self.menu_string[2],\r
923 command=self.load_from_fd)\r
924 file_menu.add_command(label=self.menu_string[0],\r
925 command=self.save_to_bin,\r
926 state='disabled')\r
927 file_menu.add_command(label=self.menu_string[1],\r
928 command=self.load_from_bin,\r
929 state='disabled')\r
930 file_menu.add_command(label=self.menu_string[3],\r
931 command=self.load_from_delta,\r
932 state='disabled')\r
933 file_menu.add_command(label=self.menu_string[4],\r
934 command=self.save_to_delta,\r
935 state='disabled')\r
936 file_menu.add_command(label=self.menu_string[5],\r
937 command=self.save_full_to_delta,\r
938 state='disabled')\r
939 file_menu.add_command(label="About", command=self.about)\r
940 menubar.add_cascade(label="File", menu=file_menu)\r
941 self.file_menu = file_menu\r
942\r
943 root.config(menu=menubar)\r
944\r
945 if len(sys.argv) > 1:\r
946 path = sys.argv[1]\r
947 if not path.endswith('.yaml') and not path.endswith('.pkl'):\r
948 messagebox.showerror('LOADING ERROR',\r
949 "Unsupported file '%s' !" % path)\r
950 return\r
951 else:\r
952 self.load_cfg_file(path)\r
953\r
954 if len(sys.argv) > 2:\r
955 path = sys.argv[2]\r
956 if path.endswith('.dlt'):\r
957 self.load_delta_file(path)\r
958 elif path.endswith('.bin'):\r
959 self.load_bin_file(path)\r
960 else:\r
961 messagebox.showerror('LOADING ERROR',\r
962 "Unsupported file '%s' !" % path)\r
963 return\r
964\r
cac83b6f
LTL
965 def search_bar(self):\r
966 # get data from text box\r
967 self.search_text = self.edit.get()\r
968 # Clear the page and update it according to search value\r
969 self.refresh_config_data_page()\r
970\r
580b1120
LTL
971 def set_object_name(self, widget, name):\r
972 self.conf_list[id(widget)] = name\r
973\r
974 def get_object_name(self, widget):\r
975 if id(widget) in self.conf_list:\r
976 return self.conf_list[id(widget)]\r
977 else:\r
978 return None\r
979\r
980 def limit_entry_size(self, variable, limit):\r
981 value = variable.get()\r
982 if len(value) > limit:\r
983 variable.set(value[:limit])\r
984\r
985 def on_canvas_configure(self, event):\r
986 self.right_grid.grid_columnconfigure(0, minsize=event.width)\r
987\r
988 def on_tree_scroll(self, event):\r
989 if not self.in_left.get() and self.in_right.get():\r
990 # This prevents scroll event from being handled by both left and\r
991 # right frame at the same time.\r
992 self.on_page_scroll(event)\r
993 return 'break'\r
994\r
995 def on_page_scroll(self, event):\r
996 if self.in_right.get():\r
997 # Only scroll when it is in active area\r
998 min, max = self.page_scroll.get()\r
999 if not((min == 0.0) and (max == 1.0)):\r
1000 self.conf_canvas.yview_scroll(-1 * int(event.delta / 120),\r
1001 'units')\r
1002\r
1003 def update_visibility_for_widget(self, widget, args):\r
580b1120
LTL
1004 visible = True\r
1005 item = self.get_config_data_item_from_widget(widget, True)\r
1006 if item is None:\r
1007 return visible\r
1008 elif not item:\r
1009 return visible\r
cac83b6f
LTL
1010 if self.cfg_data_obj.binseg_dict:\r
1011 str_split = item['path'].split('.')\r
1012 if self.cfg_data_obj.binseg_dict[str_split[-2]] == -1:\r
1013 visible = False\r
1014 widget.grid_remove()\r
1015 return visible\r
580b1120
LTL
1016 result = 1\r
1017 if item['condition']:\r
1018 result = self.evaluate_condition(item)\r
1019 if result == 2:\r
1020 # Gray\r
1021 widget.configure(state='disabled')\r
1022 elif result == 0:\r
1023 # Hide\r
1024 visible = False\r
1025 widget.grid_remove()\r
1026 else:\r
1027 # Show\r
1028 widget.grid()\r
1029 widget.configure(state='normal')\r
1030\r
cac83b6f
LTL
1031 if visible and self.search_text != '':\r
1032 name = item['name']\r
1033 if name.lower().find(self.search_text.lower()) == -1:\r
1034 visible = False\r
1035 widget.grid_remove()\r
1036\r
580b1120
LTL
1037 return visible\r
1038\r
1039 def update_widgets_visibility_on_page(self):\r
1040 self.walk_widgets_in_layout(self.right_grid,\r
1041 self.update_visibility_for_widget)\r
1042\r
1043 def combo_select_changed(self, event):\r
1044 self.update_config_data_from_widget(event.widget, None)\r
1045 self.update_widgets_visibility_on_page()\r
1046\r
1047 def edit_num_finished(self, event):\r
1048 widget = event.widget\r
1049 item = self.get_config_data_item_from_widget(widget)\r
1050 if not item:\r
1051 return\r
1052 parts = item['type'].split(',')\r
1053 if len(parts) > 3:\r
1054 min = parts[2].lstrip()[1:]\r
1055 max = parts[3].rstrip()[:-1]\r
1056 min_val = array_str_to_value(min)\r
1057 max_val = array_str_to_value(max)\r
1058 text = widget.get()\r
1059 if ',' in text:\r
1060 text = '{ %s }' % text\r
1061 try:\r
1062 value = array_str_to_value(text)\r
1063 if value < min_val or value > max_val:\r
1064 raise Exception('Invalid input!')\r
1065 self.set_config_item_value(item, text)\r
1066 except Exception:\r
1067 pass\r
1068\r
1069 text = item['value'].strip('{').strip('}').strip()\r
1070 widget.delete(0, tkinter.END)\r
1071 widget.insert(0, text)\r
1072\r
1073 self.update_widgets_visibility_on_page()\r
1074\r
1075 def update_page_scroll_bar(self):\r
1076 # Update scrollbar\r
1077 self.frame_right.update()\r
1078 self.conf_canvas.config(scrollregion=self.conf_canvas.bbox("all"))\r
1079\r
1080 def on_config_page_select_change(self, event):\r
1081 self.update_config_data_on_page()\r
1082 sel = self.left.selection()\r
1083 if len(sel) > 0:\r
1084 page_id = sel[0]\r
1085 self.build_config_data_page(page_id)\r
1086 self.update_widgets_visibility_on_page()\r
1087 self.update_page_scroll_bar()\r
1088\r
1089 def walk_widgets_in_layout(self, parent, callback_function, args=None):\r
1090 for widget in parent.winfo_children():\r
1091 callback_function(widget, args)\r
1092\r
1093 def clear_widgets_inLayout(self, parent=None):\r
1094 if parent is None:\r
1095 parent = self.right_grid\r
1096\r
1097 for widget in parent.winfo_children():\r
1098 widget.destroy()\r
1099\r
1100 parent.grid_forget()\r
1101 self.conf_list.clear()\r
1102\r
1103 def build_config_page_tree(self, cfg_page, parent):\r
1104 for page in cfg_page['child']:\r
1105 page_id = next(iter(page))\r
1106 # Put CFG items into related page list\r
1107 self.page_list[page_id] = self.cfg_data_obj.get_cfg_list(page_id)\r
1108 self.page_list[page_id].sort(key=lambda x: x['order'])\r
1109 page_name = self.cfg_data_obj.get_page_title(page_id)\r
1110 child = self.left.insert(\r
1111 parent, 'end',\r
1112 iid=page_id, text=page_name,\r
1113 value=0)\r
1114 if len(page[page_id]) > 0:\r
1115 self.build_config_page_tree(page[page_id], child)\r
1116\r
1117 def is_config_data_loaded(self):\r
1118 return True if len(self.page_list) else False\r
1119\r
1120 def set_current_config_page(self, page_id):\r
1121 self.page_id = page_id\r
1122\r
1123 def get_current_config_page(self):\r
1124 return self.page_id\r
1125\r
1126 def get_current_config_data(self):\r
1127 page_id = self.get_current_config_page()\r
1128 if page_id in self.page_list:\r
1129 return self.page_list[page_id]\r
1130 else:\r
1131 return []\r
1132\r
1133 invalid_values = {}\r
1134\r
1135 def build_config_data_page(self, page_id):\r
1136 self.clear_widgets_inLayout()\r
1137 self.set_current_config_page(page_id)\r
1138 disp_list = []\r
1139 for item in self.get_current_config_data():\r
1140 disp_list.append(item)\r
1141 row = 0\r
1142 disp_list.sort(key=lambda x: x['order'])\r
1143 for item in disp_list:\r
1144 self.add_config_item(item, row)\r
1145 row += 2\r
1146 if self.invalid_values:\r
1147 string = 'The following contails invalid options/values \n\n'\r
1148 for i in self.invalid_values:\r
1149 string += i + ": " + str(self.invalid_values[i]) + "\n"\r
1150 reply = messagebox.showwarning('Warning!', string)\r
1151 if reply == 'ok':\r
1152 self.invalid_values.clear()\r
1153\r
1154 fsp_version = ''\r
1155\r
1156 def load_config_data(self, file_name):\r
1157 gen_cfg_data = CGenYamlCfg()\r
1158 if file_name.endswith('.pkl'):\r
1159 with open(file_name, "rb") as pkl_file:\r
1160 gen_cfg_data.__dict__ = marshal.load(pkl_file)\r
1161 gen_cfg_data.prepare_marshal(False)\r
1162 elif file_name.endswith('.yaml'):\r
1163 if gen_cfg_data.load_yaml(file_name) != 0:\r
1164 raise Exception(gen_cfg_data.get_last_error())\r
1165 else:\r
1166 raise Exception('Unsupported file "%s" !' % file_name)\r
1167 # checking fsp version\r
1168 if gen_cfg_data.detect_fsp():\r
1169 self.fsp_version = '2.X'\r
1170 else:\r
1171 self.fsp_version = '1.X'\r
cac83b6f 1172\r
580b1120
LTL
1173 return gen_cfg_data\r
1174\r
1175 def about(self):\r
1176 msg = 'Configuration Editor\n--------------------------------\n \\r
1177 Version 0.8\n2021'\r
1178 lines = msg.split('\n')\r
1179 width = 30\r
1180 text = []\r
1181 for line in lines:\r
1182 text.append(line.center(width, ' '))\r
1183 messagebox.showinfo('Config Editor', '\n'.join(text))\r
1184\r
1185 def update_last_dir(self, path):\r
1186 self.last_dir = os.path.dirname(path)\r
1187\r
1188 def get_open_file_name(self, ftype):\r
1189 if self.is_config_data_loaded():\r
1190 if ftype == 'dlt':\r
1191 question = ''\r
1192 elif ftype == 'bin':\r
1193 question = 'All configuration will be reloaded from BIN file, \\r
1194 continue ?'\r
1195 elif ftype == 'yaml':\r
1196 question = ''\r
1197 elif ftype == 'bsf':\r
1198 question = ''\r
1199 else:\r
1200 raise Exception('Unsupported file type !')\r
1201 if question:\r
1202 reply = messagebox.askquestion('', question, icon='warning')\r
1203 if reply == 'no':\r
1204 return None\r
1205\r
1206 if ftype == 'yaml':\r
1207 if self.mode == 'FSP':\r
1208 file_type = 'YAML'\r
1209 file_ext = 'yaml'\r
1210 else:\r
1211 file_type = 'YAML or PKL'\r
1212 file_ext = 'pkl *.yaml'\r
1213 else:\r
1214 file_type = ftype.upper()\r
1215 file_ext = ftype\r
1216\r
1217 path = filedialog.askopenfilename(\r
1218 initialdir=self.last_dir,\r
1219 title="Load file",\r
1220 filetypes=(("%s files" % file_type, "*.%s" % file_ext), (\r
1221 "all files", "*.*")))\r
1222 if path:\r
1223 self.update_last_dir(path)\r
1224 return path\r
1225 else:\r
1226 return None\r
1227\r
1228 def load_from_delta(self):\r
1229 path = self.get_open_file_name('dlt')\r
1230 if not path:\r
1231 return\r
1232 self.load_delta_file(path)\r
1233\r
1234 def load_delta_file(self, path):\r
1235 self.reload_config_data_from_bin(self.org_cfg_data_bin)\r
1236 try:\r
1237 self.cfg_data_obj.override_default_value(path)\r
1238 except Exception as e:\r
1239 messagebox.showerror('LOADING ERROR', str(e))\r
1240 return\r
1241 self.update_last_dir(path)\r
1242 self.refresh_config_data_page()\r
1243\r
1244 def load_from_bin(self):\r
1245 path = filedialog.askopenfilename(\r
1246 initialdir=self.last_dir,\r
1247 title="Load file",\r
1248 filetypes={("Binaries", "*.fv *.fd *.bin *.rom")})\r
1249 if not path:\r
1250 return\r
1251 self.load_bin_file(path)\r
1252\r
1253 def load_bin_file(self, path):\r
1254 with open(path, 'rb') as fd:\r
1255 bin_data = bytearray(fd.read())\r
1256 if len(bin_data) < len(self.org_cfg_data_bin):\r
1257 messagebox.showerror('Binary file size is smaller than what \\r
1258 YAML requires !')\r
1259 return\r
1260\r
1261 try:\r
1262 self.reload_config_data_from_bin(bin_data)\r
1263 except Exception as e:\r
1264 messagebox.showerror('LOADING ERROR', str(e))\r
1265 return\r
1266\r
1267 def load_from_bsf_file(self):\r
1268 path = self.get_open_file_name('bsf')\r
1269 if not path:\r
1270 return\r
1271 self.load_bsf_file(path)\r
1272\r
1273 def load_bsf_file(self, path):\r
1274 bsf_file = path\r
1275 dsc_file = os.path.splitext(bsf_file)[0] + '.dsc'\r
1276 yaml_file = os.path.splitext(bsf_file)[0] + '.yaml'\r
1277 bsf_to_dsc(bsf_file, dsc_file)\r
1278 dsc_to_yaml(dsc_file, yaml_file)\r
1279\r
1280 self.load_cfg_file(yaml_file)\r
1281 return\r
1282\r
1283 def load_from_fd(self):\r
1284 path = filedialog.askopenfilename(\r
1285 initialdir=self.last_dir,\r
1286 title="Load file",\r
1287 filetypes={("Binaries", "*.fv *.fd *.bin *.rom")})\r
1288 if not path:\r
1289 return\r
1290 self.load_fd_file(path)\r
1291\r
1292 def load_fd_file(self, path):\r
1293 with open(path, 'rb') as fd:\r
1294 bin_data = bytearray(fd.read())\r
1295\r
1296 fd = FirmwareDevice(0, bin_data)\r
1297 fd.ParseFd()\r
1298 fd.ParseFsp()\r
1299 fd.OutputFsp()\r
1300\r
1301 def load_cfg_file(self, path):\r
1302 # Save current values in widget and clear database\r
1303 self.clear_widgets_inLayout()\r
1304 self.left.delete(*self.left.get_children())\r
1305\r
1306 self.cfg_data_obj = self.load_config_data(path)\r
1307\r
1308 self.update_last_dir(path)\r
1309 self.org_cfg_data_bin = self.cfg_data_obj.generate_binary_array()\r
1310 self.build_config_page_tree(self.cfg_data_obj.get_cfg_page()['root'],\r
1311 '')\r
1312\r
1313 msg_string = 'Click YES if it is FULL FSP '\\r
1314 + self.fsp_version + ' Binary'\r
1315 reply = messagebox.askquestion('Form', msg_string)\r
1316 if reply == 'yes':\r
1317 self.load_from_bin()\r
1318\r
1319 for menu in self.menu_string:\r
1320 self.file_menu.entryconfig(menu, state="normal")\r
1321\r
1322 return 0\r
1323\r
1324 def load_from_yaml(self):\r
1325 path = self.get_open_file_name('yaml')\r
1326 if not path:\r
1327 return\r
1328\r
1329 self.load_cfg_file(path)\r
1330\r
1331 def get_save_file_name(self, extension):\r
1332 path = filedialog.asksaveasfilename(\r
1333 initialdir=self.last_dir,\r
1334 title="Save file",\r
1335 defaultextension=extension)\r
1336 if path:\r
1337 self.last_dir = os.path.dirname(path)\r
1338 return path\r
1339 else:\r
1340 return None\r
1341\r
1342 def save_delta_file(self, full=False):\r
1343 path = self.get_save_file_name(".dlt")\r
1344 if not path:\r
1345 return\r
1346\r
1347 self.update_config_data_on_page()\r
1348 new_data = self.cfg_data_obj.generate_binary_array()\r
1349 self.cfg_data_obj.generate_delta_file_from_bin(path,\r
1350 self.org_cfg_data_bin,\r
1351 new_data, full)\r
1352\r
1353 def save_to_delta(self):\r
1354 self.save_delta_file()\r
1355\r
1356 def save_full_to_delta(self):\r
1357 self.save_delta_file(True)\r
1358\r
1359 def save_to_bin(self):\r
1360 path = self.get_save_file_name(".bin")\r
1361 if not path:\r
1362 return\r
1363\r
1364 self.update_config_data_on_page()\r
1365 bins = self.cfg_data_obj.save_current_to_bin()\r
1366\r
1367 with open(path, 'wb') as fd:\r
1368 fd.write(bins)\r
1369\r
1370 def refresh_config_data_page(self):\r
1371 self.clear_widgets_inLayout()\r
1372 self.on_config_page_select_change(None)\r
1373\r
1374 def reload_config_data_from_bin(self, bin_dat):\r
1375 self.cfg_data_obj.load_default_from_bin(bin_dat)\r
1376 self.refresh_config_data_page()\r
1377\r
1378 def set_config_item_value(self, item, value_str):\r
1379 itype = item['type'].split(',')[0]\r
1380 if itype == "Table":\r
1381 new_value = value_str\r
1382 elif itype == "EditText":\r
1383 length = (self.cfg_data_obj.get_cfg_item_length(item) + 7) // 8\r
1384 new_value = value_str[:length]\r
1385 if item['value'].startswith("'"):\r
1386 new_value = "'%s'" % new_value\r
1387 else:\r
1388 try:\r
1389 new_value = self.cfg_data_obj.reformat_value_str(\r
1390 value_str,\r
1391 self.cfg_data_obj.get_cfg_item_length(item),\r
1392 item['value'])\r
1393 except Exception:\r
1394 print("WARNING: Failed to format value string '%s' for '%s' !"\r
1395 % (value_str, item['path']))\r
1396 new_value = item['value']\r
1397\r
1398 if item['value'] != new_value:\r
1399 if self.debug:\r
1400 print('Update %s from %s to %s !'\r
1401 % (item['cname'], item['value'], new_value))\r
1402 item['value'] = new_value\r
1403\r
1404 def get_config_data_item_from_widget(self, widget, label=False):\r
1405 name = self.get_object_name(widget)\r
1406 if not name or not len(self.page_list):\r
1407 return None\r
1408\r
1409 if name.startswith('LABEL_'):\r
1410 if label:\r
1411 path = name[6:]\r
1412 else:\r
1413 return None\r
1414 else:\r
1415 path = name\r
cac83b6f 1416\r
580b1120
LTL
1417 item = self.cfg_data_obj.get_item_by_path(path)\r
1418 return item\r
1419\r
1420 def update_config_data_from_widget(self, widget, args):\r
1421 item = self.get_config_data_item_from_widget(widget)\r
1422 if item is None:\r
1423 return\r
1424 elif not item:\r
1425 if isinstance(widget, tkinter.Label):\r
1426 return\r
1427 raise Exception('Failed to find "%s" !' %\r
1428 self.get_object_name(widget))\r
1429\r
1430 itype = item['type'].split(',')[0]\r
1431 if itype == "Combo":\r
1432 opt_list = self.cfg_data_obj.get_cfg_item_options(item)\r
1433 tmp_list = [opt[0] for opt in opt_list]\r
1434 idx = widget.current()\r
1435 if idx != -1:\r
1436 self.set_config_item_value(item, tmp_list[idx])\r
1437 elif itype in ["EditNum", "EditText"]:\r
1438 self.set_config_item_value(item, widget.get())\r
1439 elif itype in ["Table"]:\r
1440 new_value = bytes_to_bracket_str(widget.get())\r
1441 self.set_config_item_value(item, new_value)\r
1442\r
1443 def evaluate_condition(self, item):\r
1444 try:\r
1445 result = self.cfg_data_obj.evaluate_condition(item)\r
1446 except Exception:\r
1447 print("WARNING: Condition '%s' is invalid for '%s' !"\r
1448 % (item['condition'], item['path']))\r
1449 result = 1\r
1450 return result\r
1451\r
1452 def add_config_item(self, item, row):\r
1453 parent = self.right_grid\r
1454\r
1455 name = tkinter.Label(parent, text=item['name'], anchor="w")\r
1456\r
1457 parts = item['type'].split(',')\r
1458 itype = parts[0]\r
1459 widget = None\r
1460\r
1461 if itype == "Combo":\r
1462 # Build\r
1463 opt_list = self.cfg_data_obj.get_cfg_item_options(item)\r
1464 current_value = self.cfg_data_obj.get_cfg_item_value(item, False)\r
1465 option_list = []\r
1466 current = None\r
1467\r
1468 for idx, option in enumerate(opt_list):\r
1469 option_str = option[0]\r
1470 try:\r
1471 option_value = self.cfg_data_obj.get_value(\r
1472 option_str,\r
1473 len(option_str), False)\r
1474 except Exception:\r
1475 option_value = 0\r
1476 print('WARNING: Option "%s" has invalid format for "%s" !'\r
1477 % (option_str, item['path']))\r
1478 if option_value == current_value:\r
1479 current = idx\r
1480 option_list.append(option[1])\r
1481\r
1482 widget = ttk.Combobox(parent, value=option_list, state="readonly")\r
1483 widget.bind("<<ComboboxSelected>>", self.combo_select_changed)\r
1484 widget.unbind_class("TCombobox", "<MouseWheel>")\r
1485\r
1486 if current is None:\r
1487 print('WARNING: Value "%s" is an invalid option for "%s" !' %\r
1488 (current_value, item['path']))\r
1489 self.invalid_values[item['path']] = current_value\r
1490 else:\r
1491 widget.current(current)\r
1492\r
1493 elif itype in ["EditNum", "EditText"]:\r
1494 txt_val = tkinter.StringVar()\r
1495 widget = tkinter.Entry(parent, textvariable=txt_val)\r
1496 value = item['value'].strip("'")\r
1497 if itype in ["EditText"]:\r
1498 txt_val.trace(\r
1499 'w',\r
1500 lambda *args: self.limit_entry_size\r
1501 (txt_val, (self.cfg_data_obj.get_cfg_item_length(item)\r
1502 + 7) // 8))\r
1503 elif itype in ["EditNum"]:\r
1504 value = item['value'].strip("{").strip("}").strip()\r
1505 widget.bind("<FocusOut>", self.edit_num_finished)\r
1506 txt_val.set(value)\r
1507\r
1508 elif itype in ["Table"]:\r
1509 bins = self.cfg_data_obj.get_cfg_item_value(item, True)\r
1510 col_hdr = item['option'].split(',')\r
1511 widget = custom_table(parent, col_hdr, bins)\r
1512\r
1513 else:\r
1514 if itype and itype not in ["Reserved"]:\r
1515 print("WARNING: Type '%s' is invalid for '%s' !" %\r
1516 (itype, item['path']))\r
1517 self.invalid_values[item['path']] = itype\r
1518\r
1519 if widget:\r
1520 create_tool_tip(widget, item['help'])\r
1521 self.set_object_name(name, 'LABEL_' + item['path'])\r
1522 self.set_object_name(widget, item['path'])\r
1523 name.grid(row=row, column=0, padx=10, pady=5, sticky="nsew")\r
1524 widget.grid(row=row + 1, rowspan=1, column=0,\r
1525 padx=10, pady=5, sticky="nsew")\r
1526\r
1527 def update_config_data_on_page(self):\r
1528 self.walk_widgets_in_layout(self.right_grid,\r
1529 self.update_config_data_from_widget)\r
1530\r
1531\r
1532if __name__ == '__main__':\r
1533 root = tkinter.Tk()\r
1534 app = application(master=root)\r
1535 root.title("Config Editor")\r
1536 root.mainloop()\r