-
Notifications
You must be signed in to change notification settings - Fork 52
/
nvidia_vbios_vfio_patcher.py
executable file
·203 lines (163 loc) · 6.03 KB
/
nvidia_vbios_vfio_patcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env python
from __future__ import print_function
import sys
import binascii
import argparse
import re
# raw_input doesn't exist in Python 3
try:
raw_input
except NameError:
raw_input = input
PROMPT_TEXT = "I agree to be careful"
WARNING_TEXT = """
USE THIS SOFTWARE AT YOUR OWN DISCRETION. THIS SOFTWARE HAS *NOT* BEEN
EXTENSIVELY TESTED AND MAY NOT WORK WITH YOUR GRAPHICS CARD.
If you want to save the created vBIOS file, type the following phrase
EXACTLY as it is written below:
%s
""" % PROMPT_TEXT
class CheckException(Exception):
pass
class VBIOSROM(object):
def __init__(self, f):
"""
Load a VBIOS and convert it into a hex-ascii format
for easier editing
"""
content = f.read()
self.offsets = {
"header": None,
"footer": None
}
self.content = binascii.hexlify(content)
def detect_offsets(self, disable_footer=False):
"""
Search the ROM for known sections of data and raise an AssertionError
if any of the checks fails
"""
# Search for the header that starts the file
# Examples of this header:
#
# U.y.K7400.L.w.VIDEO
# U.x.K7400.L.w.VIDEO
#
HEADER_REGEX = (
b'55aa(([a-z]|[0-9]){2})(eb)(([a-z]|[0-9]){20})(564944454f)'
)
result = re.compile(HEADER_REGEX).search(self.content)
if not result or len(result.groups()) != 6:
raise CheckException("Couldn't find the ROM header!")
self.offsets["header"] = result.start(0)
if not disable_footer:
# Search for the footer, which are shortly followed by
# 'NPDS' and 'NPDE' strings. 'NPDS' and 'NPDE' markers are separated by
# 28 ASCII characters
FOOTER_REGEX = (
b'564e(([a-z]|[0-9]){348})(4e504453)(([a-z]|[0-9]){56})(4e504445)'
)
result = re.compile(FOOTER_REGEX).search(self.content)
if not result or len(result.groups()) != 6:
raise CheckException("Couldn't find the ROM footer!")
self.offsets["footer"] = result.start(0)
def run_sanity_tests(self, ignore_check=False):
"""
Run a few sanity tests on the ROM to be a little more sure we are
working with a valid ROM
"""
try:
# There should be one 'NPDS' marker and three 'NPDE' markers
# before the footer we've already found
#
# The 'NPDS' marker should be followed by two 'NPDE' markers
npds_count = self.content.count(
b"4e504453", self.offsets["header"], self.offsets["footer"])
if npds_count != 1:
raise CheckException(
"Expected only one 'NPDS' marker between header and "
"footer, found %d" % npds_count)
npde_count = self.content.count(
b"4e504445", self.offsets["header"], self.offsets["footer"])
if npde_count != 3:
raise CheckException(
"Expected only three 'NPDE' markers between header and "
"footer, found %d" % npde_count)
npde_after_npds_count = self.content.count(
b"4e504445", self.content.find(b"4e504453"),
self.offsets["footer"])
if npde_after_npds_count != 2:
raise CheckException(
"Expected two 'NPDE' markers after the 'NPDS' marker")
except CheckException as e:
if ignore_check:
print("Encountered error during sanity check: %s" % str(e))
print("Ignoring...")
return
else:
raise
print("No problems found.")
def get_spliced_rom(self, disable_footer=False):
"""
Convert the internal hex-ascii representation of the ROM
into binary data for saving
"""
start = self.offsets["header"]
if not disable_footer:
end = self.offsets["footer"]
spliced = self.content[start:end]
else:
spliced = self.content[start:]
return binascii.unhexlify(spliced)
def main():
parser = argparse.ArgumentParser(
description=(
"Convert a full NVIDIA vBIOS ROM into a form compatible "
"for PCI passthrough."
)
)
parser.add_argument(
"-i", type=str, required=True,
help="The full ROM to read")
parser.add_argument(
"-o", type=str, required=True,
help="Path for saving the newly generated ROM")
parser.add_argument(
"--ignore-sanity-check", default=False, action="store_true",
help="Don't halt the script if any of the sanity checks fails"
)
parser.add_argument(
"--disable-footer-strip", default=False, action="store_true",
help="Don't strip the footer from the vBIOS (Allows you to convert older gen GPUs)"
)
parser.add_argument(
"--skip-the-very-important-warning",
default=False, action="store_true",
help=(
"Skip the very important warning and save the ROM without asking "
"for any input."
)
)
args = parser.parse_args()
print("Opening the ROM file...")
with open(args.i, "rb") as f:
rom = VBIOSROM(f)
print("Scanning for ROM offsets...")
rom.detect_offsets(args.disable_footer_strip)
print("Offsets found!")
if not args.disable_footer_strip:
print("Running sanity checks...")
rom.run_sanity_tests(args.ignore_sanity_check)
spliced_rom = rom.get_spliced_rom(args.disable_footer_strip)
if not args.skip_the_very_important_warning:
print(WARNING_TEXT)
print("Type here: ", end="")
answer = raw_input()
if answer != PROMPT_TEXT:
print("Wrong answer, halting...")
sys.exit(1)
print("Writing the edited ROM...")
with open(args.o, "wb") as f:
f.write(spliced_rom)
print("Done!")
if __name__ == "__main__":
main()