Skip to content

Instantly share code, notes, and snippets.

@jfoster
Last active May 11, 2023 07:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jfoster/f1a93e017389b0679d573fe988585346 to your computer and use it in GitHub Desktop.
Save jfoster/f1a93e017389b0679d573fe988585346 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
"""
Copyright 2015 tx12 http://www.overclock.net/u/376106
Portions of this software are based in part on the work of atomdis authors
Portions of this software are based in part on the work of amdgpu Linux
kernel driver authors
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
Simple ATOMBIOS table manipulation tool v1.1
optional arguments:
-h, --help show this help message and exit
commands:
{f,x,e,c,d,a}
f Fix BIOS image checksum
x Extract ATOM command table
e Extract ATOM data table
c Replace ATOM command table
d Replace ATOM data table
a Activate disabled cores
Usage:
Fix BIOS image checksum:
atomtool.py f [-h] infile outfile
arguments:
infile input rom file
outfile output rom file
Extract ATOM command table
atomtool.py x [-h] -t tableid infile table.bin
arguments:
-t tableid table id to extract
infile input rom file
table.bin output binary table file
Extract ATOM data table
atomtool.py e [-h] -t tableid infile table.bin
arguments:
-t tableid table id to extract
infile input rom file
table.bin output binary table file
Replace ATOM command table
atomtool.py c [-h] -t tableid -f table.bin infile outfile
arguments:
-t tableid table id to replace
-f table.bin input binary table file
infile input rom file
outfile output rom file
Replace ATOM data table
atomtool.py d [-h] -t tableid -f table.bin infile outfile
arguments:
-t tableid table id to replace
-f table.bin input binary table file
infile input rom file
outfile output rom file
NOTE: replacement is only possible to the table of the same size
Activate disabled cores
atomtool.py a [-h] -p type infile outfile
arguments:
-p type patch type (1 for 4-LOW, 2 for 4-HIGH, 3 for LOW+HIGH, or 65535 for all)
infile input rom file
outfile output rom file
Examples:
- extract PowerPlay table from file.rom to PowerPlay.bin:
atomtool.py e -t 15 file.rom PowerPlay.bin
- update PowerPlay table in file.rom from PowerPlay.bin and save rom as file_new.rom:
atomtool.py d -t 15 -f PowerPlay.bin file.rom file_new.rom
- fix checksum of PCBIOS image in file.rom and save rom as file_csok.rom:
atomtool.py f file.rom file_csok.rom
- try to activate cores in column #1 (4LOW) and save rom as file_4low.rom:
atomtool.py a -p 1 file.rom file_4low.rom
- try to activate all cores and save rom as file_all.rom (NOT RECOMMENDED!):
atomtool.py a -p 65535 file.rom file_all.rom
"""
import array
import struct
import argparse
import sys
import md5
class AtomBios:
def __init__(self, file):
self.ATOM_COMMON_TABLE_HEADER = "<HBB"
self.__loadbios(file)
self.__init_atombios()
def __loadbios(self, f):
# PC PCI BIOS Expansion ROM header, per 6.3.3.1 PCI local bus specification 2.2
PCBIOS_HEADER="<HB21xH"
f.seek(0,2)
l = f.tell()
f.seek(0,0)
if l < 32768 or l > 262144:
raise ValueError("unexpected file length ({0})".format(l))
b = array.array('B', f.read(l))
sig,rom_size,pcir_offset = struct.unpack_from(PCBIOS_HEADER,b)
if sig != 0xAA55:
raise ValueError("unexpected signature 0x{0:04X}".format(sig))
self.bios = b
self.file_len = l
self.pcbios_len = rom_size*512
self.pcir_offset = pcir_offset
def __init_atombios(self):
b = self.bios
# ATOM ROM HEADER, extremely simplified, per atombios.h
atom_rom_hdr_offset = struct.unpack_from("<H",b,0x48)[0]
ATOM_SIMPLE_HEADER = "<4x I22xHHxx"
ATOM_SIGNATURE = 0x4D4F5441
atomsig, self.amct, self.amdt = struct.unpack_from(
ATOM_SIMPLE_HEADER, b, atom_rom_hdr_offset
)
if atomsig != ATOM_SIGNATURE:
raise ValueError("unexpected ATOM signature 0x{0:08X}".format(atomsig))
self.command_tables = (
struct.unpack_from(
self.ATOM_COMMON_TABLE_HEADER, b, self.amct
)[0] - struct.calcsize(self.ATOM_COMMON_TABLE_HEADER) ) / 2
self.data_tables = (
struct.unpack_from(
self.ATOM_COMMON_TABLE_HEADER, b, self.amdt
)[0] - struct.calcsize(self.ATOM_COMMON_TABLE_HEADER) ) / 2
def __command_table(self, table):
b = self.bios
ATOM_MASTER_COMMAND_TABLE = "<4x {0}H".format(self.command_tables)
if table < 0 or table >= self.command_tables:
raise IndexError("command table index out of range: {0}".format(table))
offset = struct.unpack_from(ATOM_MASTER_COMMAND_TABLE, b, self.amct)[table]
size = struct.unpack_from(self.ATOM_COMMON_TABLE_HEADER, b, offset)[0]
return offset, size
def __data_table(self, table):
b = self.bios
ATOM_MASTER_DATA_TABLE = "<4x {0}H".format(self.data_tables)
if table < 0 or table >= self.data_tables:
raise IndexError("data table index out of range: {0}".format(table))
offset = struct.unpack_from(ATOM_MASTER_DATA_TABLE, b, self.amdt)[table]
size = struct.unpack_from(self.ATOM_COMMON_TABLE_HEADER, b, offset)[0]
return offset, size
def __replace_table(self, offset, size, newtable):
if offset == 0:
raise ValueError("can't replace target table because it doesn't exist")
new_len = struct.unpack_from(self.ATOM_COMMON_TABLE_HEADER, newtable)[0]
if new_len != len(newtable):
raise ValueError("incorrect length in new table data ({0})".format(new_len))
if size != len(newtable):
raise ValueError("size mismatch between new and old table data ({0} != {1})".format(new_len, size))
self.bios[offset:offset+size] = newtable
def get_command_table(self, table):
offset, size = self.__command_table(table)
return self.bios[offset:offset+size] if offset else None
def get_data_table(self, table):
offset, size = self.__data_table(table)
return self.bios[offset:offset+size] if offset else None
def replace_command_table(self, table, newtable):
offset, size = self.__command_table(table)
self.__replace_table(offset, size, newtable)
def replace_data_table(self, table, newtable):
offset, size = self.__data_table(table)
self.__replace_table(offset, size, newtable)
def calculate_checksum(self, fix=False):
CHECKSUM_OFFSET=0x21
cs = sum(self.bios[0:self.pcbios_len]) & 0xFF
if(cs and fix):
ss = self.bios[CHECKSUM_OFFSET]
self.bios[CHECKSUM_OFFSET] = (ss - cs) & 0xFF
cs = sum(self.bios[0:self.pcbios_len]) & 0xFF
return cs
def tofile(self, file):
return self.bios.tofile(file)
def read_binary_file(file, sizelimit = 262144):
file.seek(0,2)
len = file.tell()
file.seek(0,0)
if(len > sizelimit):
raise VaueError("file is too long ({0})!".format(len))
buffer = array.array('B', file.read(len))
file.close()
return buffer
def save_binary(buffer, filename):
file = open(filename, "wb")
buffer.tofile(file)
file.close()
return 0
def fix_checksum(atom, args):
atom.calculate_checksum(True)
return save_binary(atom, args.outfile)
def extract_command_table(atom, args):
table = atom.get_command_table(args.t)
if None == table:
raise ValueError("command table {0} is empty".format(args.t))
return save_binary(table, args.outfile)
def extract_data_table(atom, args):
table = atom.get_data_table(args.t)
if None == table:
raise ValueError("data table {0} is empty".format(args.t))
return save_binary(table, args.outfile)
def replace_command_table(atom, args):
atom.replace_command_table(args.t, read_binary_file(args.f))
return fix_checksum(atom, args)
def replace_data_table(atom, args):
atom.replace_data_table(args.t, read_binary_file(args.f))
return fix_checksum(atom, args)
def patch_table(table, mask):
# check table against known one
compat = set(["cb3aa12ad70d1f3d44f86b7bd7f02a00", "9c360f623acd511eaff96676d46c5050"])
hash = md5.new(table).digest().encode('hex')
table_len = struct.unpack_from("<H", table)[0]
if hash not in compat:
raise ValueError("incompatible table in BIOS image")
PATCHADDR = 0x48
PATCH_MASK_ADDR = 4
patchdata = array.array(
'B',
"078d0100feff2d0d4202002d2540013e254004493900010500c2000000e05b7a6700".decode('hex')
)
patch_size_addr = len(patchdata) - 2
db_size = table_len - PATCHADDR - patch_size_addr - 2
mask = int(mask) ^ 0xFFFF
struct.pack_into("<H", patchdata, PATCH_MASK_ADDR, mask)
struct.pack_into("<H", patchdata, patch_size_addr, db_size)
l = len(patchdata)
table[PATCHADDR:PATCHADDR+l] = patchdata
return table
def activate_cores(atom, args):
t = 32;
table = atom.get_command_table(t)
if None == table:
raise ValueError("incompatible BIOS image")
table = patch_table(table, args.p)
atom.replace_command_table(t, table)
return fix_checksum(atom, args)
def main():
parser = argparse.ArgumentParser(description="Simple ATOMBIOS table manipulation tool v1.1")
subparsers = parser.add_subparsers(title="commands")
# Fix PC BIOS checksum
fixchecksum = subparsers.add_parser("f", help="Fix BIOS image checksum")
fixchecksum.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
fixchecksum.add_argument('outfile', type=str, help="output rom file")
fixchecksum.set_defaults(func=fix_checksum)
# Extract ATOM command table
extractcmd = subparsers.add_parser("x", help="Extract ATOM command table")
extractcmd.add_argument('-t', metavar="tableid", required=True, type=int, help="table id to extract")
extractcmd.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
extractcmd.add_argument('outfile', metavar="table.bin", type=str, help="output binary table file")
extractcmd.set_defaults(func=extract_command_table)
# Extract ATOM data table
extractdata = subparsers.add_parser("e", help="Extract ATOM data table")
extractdata.add_argument('-t', metavar="tableid", required=True, type=int, help="table id to extract")
extractdata.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
extractdata.add_argument('outfile', type=str, help="output binary table file")
extractdata.set_defaults(func=extract_data_table)
# Replace ATOM command table
replacecmd = subparsers.add_parser("c", help="Replace ATOM command table")
replacecmd.add_argument('-t', metavar="tableid", required=True, type=int, help="table id to replace")
replacecmd.add_argument('-f', metavar="table.bin", required=True, type=argparse.FileType("rb"), help="input binary table file")
replacecmd.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
replacecmd.add_argument('outfile', type=str, help="output rom file")
replacecmd.set_defaults(func=replace_command_table)
# Replace ATOM data table
replacedata = subparsers.add_parser("d", help="Replace ATOM data table")
replacedata.add_argument('-t', metavar="tableid", required=True, type=int, help="table id to replace")
replacedata.add_argument('-f', metavar="table.bin", required=True, type=argparse.FileType("rb"), help="input binary table file")
replacedata.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
replacedata.add_argument('outfile', type=str, help="output rom file")
replacedata.set_defaults(func=replace_data_table)
# Activate cores
activate = subparsers.add_parser("a", help="Activate disabled cores")
activate.add_argument(
'-p', metavar="type", required=True, choices = [1,2,3,65535], type=int,
help="patch type (1 for 4-LOW, 2 for 4-HIGH or 65535 for all)"
)
activate.add_argument('infile', type=argparse.FileType("rb"), help="input rom file")
activate.add_argument('outfile', type=str, help="output rom file")
activate.set_defaults(func=activate_cores)
args = parser.parse_args()
try:
a = AtomBios(args.infile)
except ValueError as err:
print "BIOS load error:", err.args[0]
return -1
try:
return args.func(a, args)
except IndexError as err:
print "Error:", err.args[0]
return -1
except ValueError as err:
print "Error:", err.args[0]
return -1
if __name__ == '__main__':
sys.exit(main())
Simple ATOMBIOS table manipulation tool
DISCLAIMER
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
IN NO EVENT SHALL AUTHOR, OR ANY PERSON BE LIABLE FOR ANY LOSS,
EXPENSE OR DAMAGE, OF ANY TYPE OR NATURE ARISING OUT OF THE USE
OF, OR INABILITY TO USE THIS SOFTWARE OR PROGRAM, INCLUDING,
BUT NOT LIMITED TO, CLAIMS, SUITS OR CAUSES OF ACTION INVOLVING
ALLEGED INFRINGEMENT OF COPYRIGHTS, PATENTS, OR TRADE SECRETS.
This tool supports simple ATOMBIOS table manipulation like table read and same size table write.
Another included feature is possibility to unlock inactive cores for some chips.
Short help:
Simple ATOMBIOS table manipulation tool
optional arguments:
-h, --help show this help message and exit
commands:
{f,x,e,c,d,a}
f Fix BIOS image checksum
x Extract ATOM command table
e Extract ATOM data table
c Replace ATOM command table
d Replace ATOM data table
a Activate disabled cores
Usage:
Fix BIOS image checksum:
atomtool.py f [-h] infile outfile
arguments:
infile input rom file
outfile output rom file
Extract ATOM command table
atomtool.py x [-h] -t tableid infile table.bin
arguments:
-t tableid table id to extract
infile input rom file
table.bin output binary table file
Extract ATOM data table
atomtool.py e [-h] -t tableid infile table.bin
arguments:
-t tableid table id to extract
infile input rom file
table.bin output binary table file
Replace ATOM command table
atomtool.py c [-h] -t tableid -f table.bin infile outfile
arguments:
-t tableid table id to replace
-f table.bin input binary table file
infile input rom file
outfile output rom file
Replace ATOM data table
atomtool.py d [-h] -t tableid -f table.bin infile outfile
arguments:
-t tableid table id to replace
-f table.bin input binary table file
infile input rom file
outfile output rom file
NOTE: replacement is only possible to the table of the same size
Activate disabled cores
atomtool.py a [-h] -p type infile outfile
arguments:
-p type patch type (1 for 4-LOW, 2 for 4-HIGH, 3 for LOW+HIGH, or 65535 for all)
infile input rom file
outfile output rom file
Examples:
- extract PowerPlay table from file.rom to PowerPlay.bin:
atomtool.py e -t 15 file.rom PowerPlay.bin
- update PowerPlay table in file.rom from PowerPlay.bin and save rom as file_new.rom:
atomtool.py d -t 15 -f PowerPlay.bin file.rom file_new.rom
- fix checksum of PCBIOS image in file.rom and save rom as file_csok.rom:
atomtool.py f file.rom file_csok.rom
- try to activate cores in column #1 (4LOW) and save rom as file_4low.rom:
atomtool.py a -p 1 file.rom file_4low.rom
- try to activate all cores and save rom as file_all.rom (NOT RECOMMENDED!):
atomtool.py a -p 65535 file.rom file_all.rom
@echo off
atomtool.py a -p 1 %1 %~n1_4low.rom
atomtool.py a -p 2 %1 %~n1_4high.rom
atomtool.py a -p 3 %1 %~n1_low+high.rom
atomtool.py a -p 65535 %1 %~n1_all.rom
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment