fmap_util.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. # SPDX-License-Identifier: GPL-2.0+
  2. # Copyright (c) 2018 Google, Inc
  3. # Written by Simon Glass <sjg@chromium.org>
  4. #
  5. # Support for flashrom's FMAP format. This supports a header followed by a
  6. # number of 'areas', describing regions of a firmware storage device,
  7. # generally SPI flash.
  8. import collections
  9. import struct
  10. import sys
  11. from u_boot_pylib import tools
  12. # constants imported from lib/fmap.h
  13. FMAP_SIGNATURE = b'__FMAP__'
  14. FMAP_VER_MAJOR = 1
  15. FMAP_VER_MINOR = 0
  16. FMAP_STRLEN = 32
  17. FMAP_AREA_STATIC = 1 << 0
  18. FMAP_AREA_COMPRESSED = 1 << 1
  19. FMAP_AREA_RO = 1 << 2
  20. FMAP_HEADER_LEN = 56
  21. FMAP_AREA_LEN = 42
  22. FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
  23. FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
  24. FMAP_HEADER_NAMES = (
  25. 'signature',
  26. 'ver_major',
  27. 'ver_minor',
  28. 'base',
  29. 'image_size',
  30. 'name',
  31. 'nareas',
  32. )
  33. FMAP_AREA_NAMES = (
  34. 'offset',
  35. 'size',
  36. 'name',
  37. 'flags',
  38. )
  39. # Flags supported by areas (bits 2:0 are unused so not included here)
  40. FMAP_AREA_PRESERVE = 1 << 3 # Preserved by any firmware updates
  41. # These are the two data structures supported by flashrom, a header (which
  42. # appears once at the start) and an area (which is repeated until the end of
  43. # the list of areas)
  44. FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
  45. FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
  46. def NameToFmap(name):
  47. if type(name) == bytes:
  48. name = name.decode('utf-8')
  49. return name.replace('\0', '').replace('-', '_').upper()
  50. def ConvertName(field_names, fields):
  51. """Convert a name to something flashrom likes
  52. Flashrom requires upper case, underscores instead of hyphens. We remove any
  53. null characters as well. This updates the 'name' value in fields.
  54. Args:
  55. field_names: List of field names for this struct
  56. fields: Dict:
  57. key: Field name
  58. value: value of that field (string for the ones we support)
  59. """
  60. name_index = field_names.index('name')
  61. fields[name_index] = tools.to_bytes(NameToFmap(fields[name_index]))
  62. def DecodeFmap(data):
  63. """Decode a flashmap into a header and list of areas
  64. Args:
  65. data: Data block containing the FMAP
  66. Returns:
  67. Tuple:
  68. header: FmapHeader object
  69. List of FmapArea objects
  70. """
  71. fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
  72. ConvertName(FMAP_HEADER_NAMES, fields)
  73. header = FmapHeader(*fields)
  74. areas = []
  75. data = data[FMAP_HEADER_LEN:]
  76. for area in range(header.nareas):
  77. fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
  78. ConvertName(FMAP_AREA_NAMES, fields)
  79. areas.append(FmapArea(*fields))
  80. data = data[FMAP_AREA_LEN:]
  81. return header, areas
  82. def EncodeFmap(image_size, name, areas):
  83. """Create a new FMAP from a list of areas
  84. Args:
  85. image_size: Size of image, to put in the header
  86. name: Name of image, to put in the header
  87. areas: List of FmapArea objects
  88. Returns:
  89. String containing the FMAP created
  90. """
  91. def _FormatBlob(fmt, names, obj):
  92. params = [getattr(obj, name) for name in names]
  93. ConvertName(names, params)
  94. return struct.pack(fmt, *params)
  95. values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))
  96. blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
  97. for area in areas:
  98. blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
  99. return blob