cgtIVThelper.py 12.2 KB
Newer Older
Alexander Pockes's avatar
Init...  
Alexander Pockes committed
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 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
#!/usr/bin/env python3
# ##############################################################################
# cgtIVTHelper.py - Extraction and Modification of IVT/DCD data
# Copyright (C) 2016 Alexander Pockes <alexander.pockes@congatec.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
# ##############################################################################


# -->
# imports
# --->

import sys
from optparse import OptionParser


# -->
# constants
# --->

# version information
VERSION_MAJ = 0
VERSION_MIN = 2

# locating/identifying DCD/IVT header
HEADER_HAB_VERSION0 = 0x40
HEADER_HAB_VERSION1 = 0x41
HEADER_TAG_IVT = 0xD1
HEADER_TAG_DCD = 0xD2

# size of ivt area in bytes; 8 x 4 byte registers
IVT_AREA_SIZE = 8 * 4
# register size in bytes
REG_SIZE_BYTES = 4

# offset of HEADER_TAG_DCD + 1 = fileoffset of dcd length field (dcd header)
DCD_HEADER_LENGTH_OFFSET = 1
DCD_HEADER_DCDLENGTH_SIZE = 2

# offset between ivt header and dcdptr
DCD_PTR_OFFSET = 3 * 4
# DCD PTR SIZE is 4 byte
DCD_PTR_SIZE = 4

# list of \0 bytes in order to clear/set the dcd ptr to 0x00 0x00 0x00 0x00
EMPTY_DCD_PTR_VAL_LIST = [0x0, 0x0, 0x0, 0x0]


# -->
# function definitions
# --->

def find_ivtheader(file_obj):
    """Find the offset of the ivt header of a given u-boot binary image file

    Keyword arguments:
    file_obj -- file object returned by an open() call

    Return value:
    int : offset to ivt header
    """
    chunk_size = 4
    offset = 0

    if file is None:
        return None

    try:
        while True:
            br = file_obj.read(chunk_size)
            if br[0] == 0xD1 and (br[3] == 0x40 or br[3] == 0x41):
                # print("dcd found, offset = 0x{:X}".format(offset))
                break
            offset += chunk_size

    except:
        print("ERR: reading input file failed")
        return None

    return offset


def find_header_offset(file_obj, header_tag):
    """Find the offset of the IVT or DCD header of a given u-boot binary image file

    Keyword arguments:
    file_obj   -- file object returned by an open() call
    header_tag -- specification of the offset type to find;
                  DCD header -> HEADER_TAG_IVT = 0xD1
                  IVT header -> HEADER_TAG_IVT = 0xD2

    Return value:
    int : offset to specified header
    """
    chunk_size = 4
    offset = 0

    if file is None:
        return None

    try:
        file_obj.seek(0, 0)

        while True:
            br = file_obj.read(chunk_size)
            if (br[0] == header_tag and
                (br[3] == HEADER_HAB_VERSION0 or
                 br[3] == HEADER_HAB_VERSION1)):
                break
            offset += chunk_size

    except:
        print("ERR: reading input file failed")
        return None

    return offset


def get_bin_hex_data(file_obj, file_offset, byte_count):
    """binary read <byte_count> amount of bytes from file <file_obj>
    starting at offset <file_offset>

    Keyword arguments:
    file_obj    -- file object returned by an open() call
    file_offset -- specification of the offset to start reading;
    byte_count  -- number of bytes to read

    Return value:
    None  : on fail
    bytes : read binary data
    """
    if file_obj is None:
        return None

    try:
        file_obj.seek(file_offset, 0)
        br = file_obj.read(byte_count)
    except:
        return None

    return br


def get_dcdlength(file_obj):
    """extracting/returning the length of the dcdarea

    Keyword arguments:
    file_obj    -- file object returned by an open() call

    Return value:
    bytes : length of the dcd area (extracted from the dcd header)
    """
    dcdheader_offset = find_header_offset(file_obj, HEADER_TAG_DCD)
    dcdlength_addr = dcdheader_offset + DCD_HEADER_LENGTH_OFFSET
    dcdlength = get_bin_hex_data(file_obj, dcdlength_addr,
                                 DCD_HEADER_DCDLENGTH_SIZE)
    return dcdlength


def get_dcd_habblocks(file_obj):
    """extracting/returning the dcd-habblocks required in order to sign
    mfg u-boot images

    Keyword arguments:
    file_obj    -- file object returned by an open() call

    Return value:
    (int, bytes) : (offset to dcd header, length of dcd area)
    """
    dcdheader_offset = find_header_offset(file_obj, HEADER_TAG_DCD)
    dcdlength_addr = dcdheader_offset + DCD_HEADER_LENGTH_OFFSET
    dcdlength = get_bin_hex_data(file_obj, dcdlength_addr,
                                 DCD_HEADER_DCDLENGTH_SIZE)
    return (dcdheader_offset, dcdlength)


def get_dcdptr(file_obj, dcdptr_offset):
    """extracing/returning the pointer to the dcd area

    Keyword arguments:
    file_obj      -- file object returned by an open() call
    dcdptr_offset -- TBD

    Return value:
    bytes : ptr to/addr of dcd area
    """
    return get_bin_hex_data(file_obj, dcdptr_offset, DCD_PTR_SIZE)


def set_bin_hex_data(file_obj, file_offset, ba_hex_data):
    """binary writing data <ba_hex_data> (bytearray) to file <file_obj>
    starting at offset <offset>

    Keyword arguments:
    file_obj    -- file object returned by an open() call
    offset      -- fileoffset to start writing
    ba_hex_data -- TBD
    """
    if file_obj is None:
        return None

    if not isinstance(ba_hex_data, bytearray):
        raise TypeError

    file_obj.seek(file_offset, 0)
    file.write(ba_hex_data)
    file.flush()


def print_bin_hex_data(bin_hex_data):
    """prints a bytes object hexadecimal to stdout.
    printing byte per byte without prefix (0x) or seperators (spaces)

    Keyword arguments:
    bin_hex_data -- binary data (bytes) to print to stdout
    """
    # print("bin_hex_data: ", end="")
    for i in bin_hex_data:
        print("{:02x}".format(i), end="")
    print("")


def clear_dcdptr(file_obj):
    """clearing dcd ptr of binary image file <file_obj>

    Keyword arguments:
    file_obj    -- file object returned by an open() call
    """
    if file_obj is None:
        return None

    set_bin_hex_data(file_obj, ivtheader_offset + DCD_PTR_OFFSET,
                     bytearray(EMPTY_DCD_PTR_VAL_LIST))


def slice_str_every_n_chars(str, n):
    """slicing string <str> at every <n> chars

    Keyword arguments:
    str -- string to slice
    n   -- slice length

    Return Value:
    list : list of <n>-chars
    """
    return [str[i:i+n] for i in range(0, len(str), n)]


def conv_hexstr_to_bytearray(str):
    """convertig a hexadecimal string (e.g. 0x1234) to a bytearray
    of single bytes (\b'12', \b'34')

    Keyword arguments:
    str -- hexadecimal string presentation to convert

    Return Value:
    bytearray : single bytes...
    """
    return bytearray([int(i, 16) for i in slice_str_every_n_chars(str, 2)])


# -->
# main
# --->

if __name__ == "__main__":
    # creating cmdl arguments parser object
    cmd_arg_parser = OptionParser("{} [OPTIONS]".format(sys.argv[0]))
    # add cmdl options
    cmd_arg_parser.add_option("-f",
                              "--filename",
                              dest="arg_filename",
                              type="string",
                              action="store",
                              help="specify u-boot binary filename")
    cmd_arg_parser.add_option("--value",
                              dest="arg_value",
                              action="store",
                              type="string",
                              help="specify value; required by --set-dcdptr")
    cmd_arg_parser.add_option("--get-dcdptr",
                              dest="action_print_dcdptr",
                              action="store_true",
                              help="print dcdptr to stdout")
    cmd_arg_parser.add_option("--get-dcdlength",
                              dest="action_print_dcdlength",
                              action="store_true",
                              help="print dcdlength to stdout")
    cmd_arg_parser.add_option("--get-dcd-habblocks",
                              dest="action_print_dcd_habblocks",
                              action="store_true",
                              help="print dcd habblocks to stdout")
    cmd_arg_parser.add_option("--clear-dcdptr",
                              dest="action_clear_dcdptr",
                              action="store_true",
                              help="clear/set the dcdptr to 00000000")
    cmd_arg_parser.add_option("--set-dcdptr",
                              dest="action_set_dcdptr",
                              action="store_true",
                              help="set dcd-ptr to the value specified by \
                              --value")
    cmd_arg_parser.add_option("--get-ivtheader-offset",
                              dest="action_print_ivtheader_offset",
                              action="store_true",
                              help="print ivtheader offset to stdout")
    cmd_arg_parser.add_option("--version",
                              dest="action_print_version_info",
                              action="store_true",
                              help="print version information to stdout")

    # trigger parsing of cmdl options/arguments
    (cmd_options, cmd_args) = cmd_arg_parser.parse_args()

    # check if there is a filename given via -f
    if cmd_options.arg_filename:
        filename = cmd_options.arg_filename
        try:
            file = open(filename, "r+b")
        except:
            print("ERR: could not open file {}, exiting...".format(filename))
            exit(1)
    else:
        # action: action_print_version_info
        if cmd_options.action_print_version_info:
            print("{} v{}.{}".format(sys.argv[0], VERSION_MAJ, VERSION_MIN))
            exit(0)

        cmd_arg_parser.print_help()
        exit(1)

    # (1) finding ivtheader / ivtheader_offset
    ivtheader_offset = find_ivtheader(file)

    # (2) perform action specified by commandline

    # action: action_print-ivtheader_offset
    if cmd_options.action_print_ivtheader_offset:
        if ivtheader_offset is not None:
            # print("IVTHeaderOffset: 0x{:X}".format(ivtheader_offset))
            print("0x{:X}".format(ivtheader_offset))
        else:
            print("FAIL: could not find IVT Header offset, exiting...")
            exit(2)

    # action: action_print_dcdptr
    if cmd_options.action_print_dcdptr:
        dcdptr = get_dcdptr(file, ivtheader_offset + DCD_PTR_OFFSET)
        print_bin_hex_data(dcdptr)

    # action: action_print_dcdlength
    if cmd_options.action_print_dcdlength:
        dcdlength = get_dcdlength(file)
        print_bin_hex_data(dcdlength)

    # action: action_print_dcd_habblocks
    if cmd_options.action_print_dcd_habblocks:
        dcdlength_offset, dcdlength = get_dcd_habblocks(file)
        print("# DCD OFFSET | DCD LENGTH")
        print("0x{:08x}{}".format(dcdlength_offset, 5*" "), end="")
        # DIRTY
        print("0x", end="")
        # DIRTY
        print_bin_hex_data(dcdlength)

    # action: action_clear_dcdptr
    if cmd_options.action_clear_dcdptr:
        set_bin_hex_data(file, ivtheader_offset + DCD_PTR_OFFSET,
                         bytearray(EMPTY_DCD_PTR_VAL_LIST))

    # action: set_dcdptr
    if cmd_options.action_set_dcdptr:
        if (not cmd_options.arg_value or
                len(cmd_options.arg_value) != 8):
            print("ERR: please specify dcdptr \
value to set via --value XXXXXXXX, exiting...")
            exit(1)

        ba_dcdptr = conv_hexstr_to_bytearray(cmd_options.arg_value)
        print("INFO: setting dcdptr to: ", end="")
        for i in ba_dcdptr:
            print("0x{:02x} ".format(i), end="")
        print("")

        set_bin_hex_data(file, ivtheader_offset + DCD_PTR_OFFSET, ba_dcdptr)

    # (3) cleaning up...
    if file:
        # print("INFO: closing file...")
        file.close()

    exit(0)