blob: 360278ce28909304e75ec7c92984c037d983c267 [file] [log] [blame]
Denis 'GNUtoo' Carikli40a2a9c2017-03-01 14:56:25 +01001#!/usr/bin/env python2
roxfan754a7d72012-09-13 01:11:35 +04002# extract microcode updates from binary BIOS files
3# v 0.1 2012/07/23
4# v 0.2 2012/07/23 added VIA Nano support (relaxed some checks)
5# Licensed as Public Domain
6
7import ctypes
8import struct
9import sys
10import array
11
12uint8_t = ctypes.c_ubyte
13char = ctypes.c_char
14uint32_t = ctypes.c_uint
15uint64_t = ctypes.c_uint64
16uint16_t = ctypes.c_ushort
17
18def get_struct(str_, off, struct):
19 s = struct()
20 slen = ctypes.sizeof(s)
21 bytes = str_[off:off+slen]
22 fit = min(len(bytes), slen)
23 ctypes.memmove(ctypes.addressof(s), bytes, fit)
24 return s
25
26def DwordAt(f, off):
27 return struct.unpack("<I", f[off:off+4])[0]
28
29# Intel microcode update header
30class IntelUcUpdateHeader(ctypes.LittleEndianStructure):
31 _fields_ = [
32 ("HeaderVersion", uint32_t), #
33 ("UpdateRevision", uint32_t), #
34 ("Date", uint32_t), #
35 ("ProcessorSignature", uint32_t), #
36 ("Checksum", uint32_t), #
37 ("LoaderRevision", uint32_t), #
38 ("ProcessorFlags", uint32_t), #
39 ("DataSize", uint32_t), #
40 ("TotalSize", uint32_t), #
41 ]
42
43def check_valid_mcode(f, off, maxoff, is_via = False):
44 hdr = get_struct(f, off, IntelUcUpdateHeader)
45 # on Intel, Total Size is always a multiple of 1024.
46 if not is_via and hdr.TotalSize & 0x3FF:
47 return
48
49 # For microcode updates with a data size field
50 # equal to 00000000H, the size of the microcode
51 # update is 2048 bytes.
52 if hdr.DataSize == 0:
53 check_len = 2048
54 else:
55 check_len = hdr.TotalSize
56 if check_len < 2048:
57 return False
58
59 # it should not run off the end of file or another update
60 if check_len + off > maxoff:
61 return
62
63 # update size must be a multiple of DWORD
64 if (hdr.DataSize & 3) or (hdr.TotalSize & 3):
65 return
66
67 # looks okay. let's check the checksum
68
69 mdata = f[off:off+check_len]
70 # make an array of DWORDs
71 arr = array.array("I", mdata)
72 # sum them
73 ck = sum(arr) & 0xFFFFFFFF
74 if ck == 0:
75 print "%08X: found a valid-looking update" % off
76 print ["Date: %02X/%02X/%4X", "Date: %02d/%02d/%4d"][is_via] % ((hdr.Date >> 24)&0xFF, (hdr.Date >> 16)&0xFF, hdr.Date & 0xFFFF)
77 print "Processor signature: %08X" % hdr.ProcessorSignature
78 if not is_via:
79 print "Processor flags: %08X" % hdr.ProcessorFlags
80 print "Length: %08X" % check_len
81 fname = "mcode_upd_%08X.bin" % off
82 print "Extracting to %s" % fname
83 open(fname, "wb").write(mdata)
84 return True
85 # nope
86 return False
87
88def find_ucode(f, is_via):
89 maxoff = len(f)
90 # minimal microcode update length is 2048 bytes
91 off = (maxoff-2048+8)&(~15)
92 # look for BCD date ddmmyyyy
93 # yyyy off-2
94 # dd off-1
95 # mm off
96 print "Scanning...\n%08X" % off
97 while off > 11:
98 # looks like a date?
99 m = ord(f[off])
100 if (1 <= m <= 0x12) and (1 <= ord(f[off-1]) <= 0x31):
101 if check_valid_mcode(f, off-11, maxoff, is_via):
102 maxoff = off-11
103 print "Scanning...\n%08X" % off
104 off -= 1
105 if (off & 0xFFFFF) == 0:
106 print "%08X" % off
107 print "\nDone"
108
109if len(sys.argv) < 2:
110 print "Usage: microcode_extract.py <file.rom> [-i|-v]"
111 print " -i: look for Intel microcode (default)"
112 print " -v: look for VIA Nano microcode"
113 sys.exit(1)
114else:
115 fn = None
116 is_via = False
117 for arg in sys.argv[1:]:
118 if arg == "-v":
119 is_via = True
120 elif arg == "-i":
121 is_via = False
122 else:
123 fn = arg
124
125 f = open(fn, "rb").read()
126 find_ucode(f, is_via)