binman: Convert Image to a subclass of Entry
[platform/kernel/u-boot.git] / tools / binman / image.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # Class for an image, the output of binman
6 #
7
8 from __future__ import print_function
9
10 from collections import OrderedDict
11 from operator import attrgetter
12 import re
13 import sys
14
15 from entry import Entry
16 from etype import fdtmap
17 from etype import image_header
18 from etype import section
19 import fdt
20 import fdt_util
21 import tools
22
23 class Image(section.Entry_section):
24     """A Image, representing an output from binman
25
26     An image is comprised of a collection of entries each containing binary
27     data. The image size must be large enough to hold all of this data.
28
29     This class implements the various operations needed for images.
30
31     Attributes:
32         filename: Output filename for image
33
34     Args:
35         test: True if this is being called from a test of Images. This this case
36             there is no device tree defining the structure of the section, so
37             we create a section manually.
38     """
39     def __init__(self, name, node, test=False):
40         self.image = self
41         section.Entry_section.__init__(self, None, 'section', node, test)
42         self.name = 'main-section'
43         self.image_name = name
44         self._filename = '%s.bin' % self.image_name
45         if not test:
46             filename = fdt_util.GetString(self._node, 'filename')
47             if filename:
48                 self._filename = filename
49
50     @classmethod
51     def FromFile(cls, fname):
52         """Convert an image file into an Image for use in binman
53
54         Args:
55             fname: Filename of image file to read
56
57         Returns:
58             Image object on success
59
60         Raises:
61             ValueError if something goes wrong
62         """
63         data = tools.ReadFile(fname)
64         size = len(data)
65
66         # First look for an image header
67         pos = image_header.LocateHeaderOffset(data)
68         if pos is None:
69             # Look for the FDT map
70             pos = fdtmap.LocateFdtmap(data)
71         if pos is None:
72             raise ValueError('Cannot find FDT map in image')
73
74         # We don't know the FDT size, so check its header first
75         probe_dtb = fdt.Fdt.FromData(
76             data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
77         dtb_size = probe_dtb.GetFdtObj().totalsize()
78         fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
79         dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
80         dtb.Scan()
81
82         # Return an Image with the associated nodes
83         return Image('image', dtb.GetRoot())
84
85     def Raise(self, msg):
86         """Convenience function to raise an error referencing an image"""
87         raise ValueError("Image '%s': %s" % (self._node.path, msg))
88
89     def PackEntries(self):
90         """Pack all entries into the image"""
91         section.Entry_section.Pack(self, 0)
92
93     def SetImagePos(self):
94         # This first section in the image so it starts at 0
95         section.Entry_section.SetImagePos(self, 0)
96
97     def ProcessEntryContents(self):
98         """Call the ProcessContents() method for each entry
99
100         This is intended to adjust the contents as needed by the entry type.
101
102         Returns:
103             True if the new data size is OK, False if expansion is needed
104         """
105         sizes_ok = True
106         for entry in self._entries.values():
107             if not entry.ProcessContents():
108                 sizes_ok = False
109                 print("Entry '%s' size change" % self._node.path)
110         return sizes_ok
111
112     def WriteSymbols(self):
113         """Write symbol values into binary files for access at run time"""
114         section.Entry_section.WriteSymbols(self, self)
115
116     def BuildSection(self, fd, base_offset):
117         """Write the section to a file"""
118         fd.seek(base_offset)
119         fd.write(self.GetData())
120
121     def BuildImage(self):
122         """Write the image to a file"""
123         fname = tools.GetOutputFilename(self._filename)
124         with open(fname, 'wb') as fd:
125             self.BuildSection(fd, 0)
126
127     def WriteMap(self):
128         """Write a map of the image to a .map file
129
130         Returns:
131             Filename of map file written
132         """
133         filename = '%s.map' % self.image_name
134         fname = tools.GetOutputFilename(filename)
135         with open(fname, 'w') as fd:
136             print('%8s  %8s  %8s  %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
137                   file=fd)
138             section.Entry_section.WriteMap(self, fd, 0)
139         return fname
140
141     def BuildEntryList(self):
142         """List the files in an image
143
144         Returns:
145             List of entry.EntryInfo objects describing all entries in the image
146         """
147         entries = []
148         self.ListEntries(entries, 0)
149         return entries