Yilin Yang | 19da693 | 2019-12-10 13:39:28 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Hung-Te Lin | 1990b74 | 2017-08-09 17:34:57 +0800 | [diff] [blame] | 2 | # Copyright 2011 The Chromium OS Authors. All rights reserved. |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | """ |
| 7 | Chrome OS bitmap block parser. |
| 8 | |
| 9 | This module helps parsing firmware bitmap blocks so that we can |
| 10 | retrieve the version and other information from a blob. |
| 11 | |
| 12 | See help(unpack_bmpblock) for more information. |
| 13 | |
| 14 | ref: src/platform/vboot_reference/firmware/include/bmpblk_header.h |
| 15 | """ |
| 16 | |
| 17 | import struct |
| 18 | import sys |
| 19 | |
| 20 | # Constant Definition |
Hung-Te Lin | 56b1840 | 2015-01-16 14:52:30 +0800 | [diff] [blame] | 21 | BMPBLOCK_SIGNATURE = '$BMP' |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 22 | BMPBLOCK_SIGNATURE_SIZE = 4 |
| 23 | MAX_IMAGE_IN_LAYOUT = 8 |
| 24 | |
| 25 | # Blob Structure |
| 26 | |
| 27 | # typedef struct BmpBlockHeader { |
| 28 | # uint8_t signature[BMPBLOCK_SIGNATURE_SIZE]; |
| 29 | # uint16_t major_version; |
| 30 | # uint16_t minor_version; |
| 31 | # uint32_t number_of_localizations; |
| 32 | # uint32_t number_of_screenlayouts; |
| 33 | # uint32_t number_of_imageinfos; |
| 34 | # uint32_t locale_string_offset; |
| 35 | # uint32_t reserved[2]; |
| 36 | # }; |
| 37 | FORMAT_BMPBLOCK_HEADER = '<4shhIIII2I' |
| 38 | NAMES_BMPBLOCK_HEADER = ('signature', |
| 39 | 'major_version', |
| 40 | 'minor_version', |
| 41 | 'number_of_localizations', |
| 42 | 'number_of_screenlayouts', |
| 43 | 'number_of_imageinfos', |
| 44 | 'locale_string_offset', |
| 45 | 'reserved') |
| 46 | |
| 47 | # typedef struct ScreenLayout { |
| 48 | # struct { |
| 49 | # uint32_t x; |
| 50 | # uint32_t y; |
| 51 | # uint32_t image_info_offset; |
| 52 | # } images[MAX_IMAGE_IN_LAYOUT]; |
| 53 | # }; |
| 54 | FORMAT_SCREEN_LAYOUT_IMAGE = '<III' |
| 55 | NAMES_SCREEN_LAYOUT_IMAGE = ('x', 'y', 'image_info_offset') |
| 56 | |
| 57 | # typedef struct ImageInfo { |
| 58 | # uint32_t tag; |
| 59 | # uint32_t width; |
| 60 | # uint32_t height; |
| 61 | # uint32_t format; |
| 62 | # uint32_t compression; |
| 63 | # uint32_t original_size; |
| 64 | # uint32_t compressed_size; |
| 65 | # uint32_t reserved; |
| 66 | # }; |
| 67 | FORMAT_IMAGE_INFO = '<IIIIIIII' |
| 68 | NAMES_IMAGE_INFO = ( |
| 69 | 'tag', |
| 70 | 'width', |
| 71 | 'height', |
| 72 | 'format', |
| 73 | 'compression', |
| 74 | 'original_size', |
| 75 | 'compressed_size', |
| 76 | 'reserved') |
| 77 | |
| 78 | |
| 79 | def unpack_BmpBlockHeader(blob, offset=0): |
| 80 | """ Unpacks a BmpBlockHeader from a blob, starting from offset. """ |
| 81 | fields = struct.unpack_from(FORMAT_BMPBLOCK_HEADER, blob, offset) |
| 82 | header = dict(zip(NAMES_BMPBLOCK_HEADER, fields)) |
| 83 | # check signature |
| 84 | if header['signature'] != BMPBLOCK_SIGNATURE: |
Hung-Te Lin | eec4e33 | 2017-06-01 23:16:40 +0800 | [diff] [blame] | 85 | raise ValueError('unknown bmpblock signature: %s' % header['signature']) |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 86 | return header |
| 87 | |
| 88 | |
| 89 | def unpack_ImageInfo(blob, offset=0): |
| 90 | """ Unpacks a ImageInfo from a blob, starting from offset. """ |
| 91 | fields = struct.unpack_from(FORMAT_IMAGE_INFO, blob, offset) |
| 92 | info = dict(zip(NAMES_IMAGE_INFO, fields)) |
| 93 | return info |
| 94 | |
| 95 | |
| 96 | def unpack_ScreenLayout(blob, base=0, offset=0): |
| 97 | """ Unpacks a ScreenLayout from a blob, starting from offset. """ |
| 98 | layout = [] |
Tammo Spalink | 01e1172 | 2012-07-24 10:17:54 -0700 | [diff] [blame] | 99 | for _ in range(MAX_IMAGE_IN_LAYOUT): |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 100 | fields = struct.unpack_from(FORMAT_SCREEN_LAYOUT_IMAGE, blob, offset) |
| 101 | offset += struct.calcsize(FORMAT_SCREEN_LAYOUT_IMAGE) |
| 102 | image = dict(zip(NAMES_SCREEN_LAYOUT_IMAGE, fields)) |
| 103 | info_offset = image['image_info_offset'] |
| 104 | if info_offset > 0: |
| 105 | image.update(unpack_ImageInfo(blob, base + info_offset)) |
| 106 | layout.append(image) |
| 107 | return layout |
| 108 | |
| 109 | |
| 110 | def unpack_LocaleString(blob, base=0, offset=0): |
| 111 | """ Unpacks a double NUL-terminated locale string, starting from offset. """ |
| 112 | end = blob.find('\x00\x00', base + offset) |
| 113 | if end < 0: |
| 114 | return [] |
| 115 | locale_string = blob[base + offset:end] |
| 116 | return locale_string.split('\x00') |
| 117 | |
| 118 | |
| 119 | def unpack_bmpblock(blob, offset=0): |
Hung-Te Lin | 56b1840 | 2015-01-16 14:52:30 +0800 | [diff] [blame] | 120 | """Unpacks a Chrome OS Bitmap Block. |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 121 | |
| 122 | Returns a dictionary of unpacked data |
| 123 | """ |
| 124 | data = unpack_BmpBlockHeader(blob, offset) |
| 125 | layout_offset = offset + struct.calcsize(FORMAT_BMPBLOCK_HEADER) |
| 126 | localizations = [] |
Tammo Spalink | 01e1172 | 2012-07-24 10:17:54 -0700 | [diff] [blame] | 127 | for _ in range(data['number_of_localizations']): |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 128 | layouts = [] |
Tammo Spalink | 01e1172 | 2012-07-24 10:17:54 -0700 | [diff] [blame] | 129 | for _ in range(data['number_of_screenlayouts']): |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 130 | layouts.append(unpack_ScreenLayout(blob, offset, layout_offset)) |
| 131 | layout_offset += (struct.calcsize(FORMAT_SCREEN_LAYOUT_IMAGE) * |
| 132 | MAX_IMAGE_IN_LAYOUT) |
| 133 | localizations.append(layouts) |
| 134 | data['localizations'] = localizations |
| 135 | # locale string is optional. |
| 136 | locale_string_offset = data['locale_string_offset'] |
| 137 | data['locales'] = (unpack_LocaleString(blob, offset, locale_string_offset) |
| 138 | if locale_string_offset else []) |
| 139 | return data |
| 140 | |
| 141 | |
| 142 | # ----------------------------------------------------------------------------- |
| 143 | |
| 144 | |
Peter Shih | 533566a | 2018-09-05 17:48:03 +0800 | [diff] [blame] | 145 | def main(): |
Hung-Te Lin | d0447d0 | 2011-05-19 19:06:00 +0800 | [diff] [blame] | 146 | # Only load pprint if we are in console (debug / test) mode |
| 147 | import pprint |
| 148 | for filename in sys.argv[1:]: |
Hung-Te Lin | 56b1840 | 2015-01-16 14:52:30 +0800 | [diff] [blame] | 149 | bmpblk = unpack_bmpblock(open(filename, 'rb').read(), 0) |
Yilin Yang | 71e3941 | 2019-09-24 09:26:46 +0800 | [diff] [blame] | 150 | print(pprint.pformat(bmpblk)) |
Peter Shih | 533566a | 2018-09-05 17:48:03 +0800 | [diff] [blame] | 151 | |
| 152 | |
| 153 | # When running in command line, try to report blob in the parameters |
| 154 | if __name__ == '__main__': |
| 155 | main() |