mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-02 01:19:49 +00:00
binman: Add a ProcessFdt() method
Some entry types modify the device tree, e.g. to remove microcode or add a property. So far this just modifies their local copy and does not affect a 'shared' device tree. Rather than doing this modification in the ObtainContents() method, and a new ProcessFdt() method which is specifically designed to modify this shared device tree. Move the existing device-tree code over to use this method, reducing ObtainContents() to the goal of just obtaining the contents without any processing, even for device tree. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
0a4357c4c2
commit
ecab89737a
9 changed files with 117 additions and 37 deletions
|
@ -462,7 +462,14 @@ Order of image creation
|
||||||
|
|
||||||
Image creation proceeds in the following order, for each entry in the image.
|
Image creation proceeds in the following order, for each entry in the image.
|
||||||
|
|
||||||
1. GetEntryContents() - the contents of each entry are obtained, normally by
|
1. ProcessFdt() - process the device tree information as required by the
|
||||||
|
particular entry. This may involve adding or deleting properties. If the
|
||||||
|
processing is complete, this method should return True. If the processing
|
||||||
|
cannot complete because it needs the ProcessFdt() method of another entry to
|
||||||
|
run first, this method should return False, in which case it will be called
|
||||||
|
again later.
|
||||||
|
|
||||||
|
2. GetEntryContents() - the contents of each entry are obtained, normally by
|
||||||
reading from a file. This calls the Entry.ObtainContents() to read the
|
reading from a file. This calls the Entry.ObtainContents() to read the
|
||||||
contents. The default version of Entry.ObtainContents() calls
|
contents. The default version of Entry.ObtainContents() calls
|
||||||
Entry.GetDefaultFilename() and then reads that file. So a common mechanism
|
Entry.GetDefaultFilename() and then reads that file. So a common mechanism
|
||||||
|
@ -471,35 +478,35 @@ functions must return True when they have read the contents. Binman will
|
||||||
retry calling the functions a few times if False is returned, allowing
|
retry calling the functions a few times if False is returned, allowing
|
||||||
dependencies between the contents of different entries.
|
dependencies between the contents of different entries.
|
||||||
|
|
||||||
2. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can
|
3. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can
|
||||||
return a dict containing entries that need updating. The key should be the
|
return a dict containing entries that need updating. The key should be the
|
||||||
entry name and the value is a tuple (pos, size). This allows an entry to
|
entry name and the value is a tuple (pos, size). This allows an entry to
|
||||||
provide the position and size for other entries. The default implementation
|
provide the position and size for other entries. The default implementation
|
||||||
of GetEntryPositions() returns {}.
|
of GetEntryPositions() returns {}.
|
||||||
|
|
||||||
3. PackEntries() - calls Entry.Pack() which figures out the position and
|
4. PackEntries() - calls Entry.Pack() which figures out the position and
|
||||||
size of an entry. The 'current' image position is passed in, and the function
|
size of an entry. The 'current' image position is passed in, and the function
|
||||||
returns the position immediately after the entry being packed. The default
|
returns the position immediately after the entry being packed. The default
|
||||||
implementation of Pack() is usually sufficient.
|
implementation of Pack() is usually sufficient.
|
||||||
|
|
||||||
4. CheckSize() - checks that the contents of all the entries fits within
|
5. CheckSize() - checks that the contents of all the entries fits within
|
||||||
the image size. If the image does not have a defined size, the size is set
|
the image size. If the image does not have a defined size, the size is set
|
||||||
large enough to hold all the entries.
|
large enough to hold all the entries.
|
||||||
|
|
||||||
5. CheckEntries() - checks that the entries do not overlap, nor extend
|
6. CheckEntries() - checks that the entries do not overlap, nor extend
|
||||||
outside the image.
|
outside the image.
|
||||||
|
|
||||||
6. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
|
7. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
|
||||||
The default implementatoin does nothing. This can be overriden to adjust the
|
The default implementatoin does nothing. This can be overriden to adjust the
|
||||||
contents of an entry in some way. For example, it would be possible to create
|
contents of an entry in some way. For example, it would be possible to create
|
||||||
an entry containing a hash of the contents of some other entries. At this
|
an entry containing a hash of the contents of some other entries. At this
|
||||||
stage the position and size of entries should not be adjusted.
|
stage the position and size of entries should not be adjusted.
|
||||||
|
|
||||||
7. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
|
8. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
|
||||||
See 'Access to binman entry positions at run time' below for a description of
|
See 'Access to binman entry positions at run time' below for a description of
|
||||||
what happens in this stage.
|
what happens in this stage.
|
||||||
|
|
||||||
8. BuildImage() - builds the image and writes it to a file. This is the final
|
9. BuildImage() - builds the image and writes it to a file. This is the final
|
||||||
step.
|
step.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,21 @@ class Section(object):
|
||||||
entry.SetPrefix(self._name_prefix)
|
entry.SetPrefix(self._name_prefix)
|
||||||
self._entries[node.name] = entry
|
self._entries[node.name] = entry
|
||||||
|
|
||||||
|
def ProcessFdt(self, fdt):
|
||||||
|
todo = self._entries.values()
|
||||||
|
for passnum in range(3):
|
||||||
|
next_todo = []
|
||||||
|
for entry in todo:
|
||||||
|
if not entry.ProcessFdt(fdt):
|
||||||
|
next_todo.append(entry)
|
||||||
|
todo = next_todo
|
||||||
|
if not todo:
|
||||||
|
break
|
||||||
|
if todo:
|
||||||
|
self._Raise('Internal error: Could not complete processing of Fdt: '
|
||||||
|
'remaining %s' % todo)
|
||||||
|
return True
|
||||||
|
|
||||||
def CheckSize(self):
|
def CheckSize(self):
|
||||||
"""Check that the section contents does not exceed its size, etc."""
|
"""Check that the section contents does not exceed its size, etc."""
|
||||||
contents_size = 0
|
contents_size = 0
|
||||||
|
|
|
@ -21,6 +21,11 @@ import tout
|
||||||
# Make this global so that it can be referenced from tests
|
# Make this global so that it can be referenced from tests
|
||||||
images = OrderedDict()
|
images = OrderedDict()
|
||||||
|
|
||||||
|
# Records the device-tree files known to binman, keyed by filename (e.g.
|
||||||
|
# 'u-boot-spl.dtb')
|
||||||
|
fdt_files = {}
|
||||||
|
|
||||||
|
|
||||||
def _ReadImageDesc(binman_node):
|
def _ReadImageDesc(binman_node):
|
||||||
"""Read the image descriptions from the /binman node
|
"""Read the image descriptions from the /binman node
|
||||||
|
|
||||||
|
@ -53,6 +58,24 @@ def _FindBinmanNode(dtb):
|
||||||
return node
|
return node
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def GetFdt(fname):
|
||||||
|
"""Get the Fdt object for a particular device-tree filename
|
||||||
|
|
||||||
|
Binman keeps track of at least one device-tree file called u-boot.dtb but
|
||||||
|
can also have others (e.g. for SPL). This function looks up the given
|
||||||
|
filename and returns the associated Fdt object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fname: Filename to look up (e.g. 'u-boot.dtb').
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Fdt object associated with the filename
|
||||||
|
"""
|
||||||
|
return fdt_files[fname]
|
||||||
|
|
||||||
|
def GetFdtPath(fname):
|
||||||
|
return fdt_files[fname]._fname
|
||||||
|
|
||||||
def Binman(options, args):
|
def Binman(options, args):
|
||||||
"""The main control code for binman
|
"""The main control code for binman
|
||||||
|
|
||||||
|
@ -93,12 +116,39 @@ def Binman(options, args):
|
||||||
try:
|
try:
|
||||||
tools.SetInputDirs(options.indir)
|
tools.SetInputDirs(options.indir)
|
||||||
tools.PrepareOutputDir(options.outdir, options.preserve)
|
tools.PrepareOutputDir(options.outdir, options.preserve)
|
||||||
dtb = fdt.FdtScan(dtb_fname)
|
|
||||||
|
# Get the device tree ready by compiling it and copying the compiled
|
||||||
|
# output into a file in our output directly. Then scan it for use
|
||||||
|
# in binman.
|
||||||
|
dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
|
||||||
|
fname = tools.GetOutputFilename('u-boot-out.dtb')
|
||||||
|
with open(dtb_fname) as infd:
|
||||||
|
with open(fname, 'wb') as outfd:
|
||||||
|
outfd.write(infd.read())
|
||||||
|
dtb = fdt.FdtScan(fname)
|
||||||
|
|
||||||
|
# Note the file so that GetFdt() can find it
|
||||||
|
fdt_files['u-boot.dtb'] = dtb
|
||||||
node = _FindBinmanNode(dtb)
|
node = _FindBinmanNode(dtb)
|
||||||
if not node:
|
if not node:
|
||||||
raise ValueError("Device tree '%s' does not have a 'binman' "
|
raise ValueError("Device tree '%s' does not have a 'binman' "
|
||||||
"node" % dtb_fname)
|
"node" % dtb_fname)
|
||||||
|
|
||||||
images = _ReadImageDesc(node)
|
images = _ReadImageDesc(node)
|
||||||
|
|
||||||
|
# Prepare the device tree by making sure that any missing
|
||||||
|
# properties are added (e.g. 'pos' and 'size'). The values of these
|
||||||
|
# may not be correct yet, but we add placeholders so that the
|
||||||
|
# size of the device tree is correct. Later, in
|
||||||
|
# SetCalculatedProperties() we will insert the correct values
|
||||||
|
# without changing the device-tree size, thus ensuring that our
|
||||||
|
# entry positions remain the same.
|
||||||
|
for image in images.values():
|
||||||
|
image.ProcessFdt(dtb)
|
||||||
|
|
||||||
|
dtb.Pack()
|
||||||
|
dtb.Flush()
|
||||||
|
|
||||||
for image in images.values():
|
for image in images.values():
|
||||||
# Perform all steps for this image, including checking and
|
# Perform all steps for this image, including checking and
|
||||||
# writing it. This means that errors found with a later
|
# writing it. This means that errors found with a later
|
||||||
|
|
|
@ -130,6 +130,9 @@ class Entry(object):
|
||||||
self.align_end = fdt_util.GetInt(self._node, 'align-end')
|
self.align_end = fdt_util.GetInt(self._node, 'align-end')
|
||||||
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
|
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
|
||||||
|
|
||||||
|
def ProcessFdt(self, fdt):
|
||||||
|
return True
|
||||||
|
|
||||||
def SetPrefix(self, prefix):
|
def SetPrefix(self, prefix):
|
||||||
"""Set the name prefix for a node
|
"""Set the name prefix for a node
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ class Entry_section(Entry):
|
||||||
Entry.__init__(self, image, etype, node)
|
Entry.__init__(self, image, etype, node)
|
||||||
self._section = bsection.Section(node.name, node)
|
self._section = bsection.Section(node.name, node)
|
||||||
|
|
||||||
|
def ProcessFdt(self, fdt):
|
||||||
|
return self._section.ProcessFdt(fdt)
|
||||||
|
|
||||||
def ObtainContents(self):
|
def ObtainContents(self):
|
||||||
return self._section.GetEntryContents()
|
return self._section.GetEntryContents()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
# Entry-type module for U-Boot device tree with the microcode removed
|
# Entry-type module for U-Boot device tree with the microcode removed
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import control
|
||||||
import fdt
|
import fdt
|
||||||
from entry import Entry
|
from entry import Entry
|
||||||
from blob import Entry_blob
|
from blob import Entry_blob
|
||||||
|
@ -22,13 +23,13 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
|
||||||
self.collate = False
|
self.collate = False
|
||||||
self.ucode_offset = None
|
self.ucode_offset = None
|
||||||
self.ucode_size = None
|
self.ucode_size = None
|
||||||
|
self.ucode = None
|
||||||
|
self.ready = False
|
||||||
|
|
||||||
def GetDefaultFilename(self):
|
def GetDefaultFilename(self):
|
||||||
return 'u-boot.dtb'
|
return 'u-boot.dtb'
|
||||||
|
|
||||||
def ObtainContents(self):
|
def ProcessFdt(self, fdt):
|
||||||
Entry_blob.ObtainContents(self)
|
|
||||||
|
|
||||||
# If the section does not need microcode, there is nothing to do
|
# If the section does not need microcode, there is nothing to do
|
||||||
ucode_dest_entry = self.section.FindEntryType(
|
ucode_dest_entry = self.section.FindEntryType(
|
||||||
'u-boot-spl-with-ucode-ptr')
|
'u-boot-spl-with-ucode-ptr')
|
||||||
|
@ -38,36 +39,35 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
|
||||||
if not ucode_dest_entry or not ucode_dest_entry.target_pos:
|
if not ucode_dest_entry or not ucode_dest_entry.target_pos:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Create a new file to hold the copied device tree
|
|
||||||
dtb_name = 'u-boot-dtb-with-ucode.dtb'
|
|
||||||
fname = tools.GetOutputFilename(dtb_name)
|
|
||||||
with open(fname, 'wb') as fd:
|
|
||||||
fd.write(self.data)
|
|
||||||
|
|
||||||
# Remove the microcode
|
# Remove the microcode
|
||||||
dtb = fdt.FdtScan(fname)
|
fname = self.GetDefaultFilename()
|
||||||
ucode = dtb.GetNode('/microcode')
|
fdt = control.GetFdt(fname)
|
||||||
if not ucode:
|
self.ucode = fdt.GetNode('/microcode')
|
||||||
|
if not self.ucode:
|
||||||
raise self.Raise("No /microcode node found in '%s'" % fname)
|
raise self.Raise("No /microcode node found in '%s'" % fname)
|
||||||
|
|
||||||
# There's no need to collate it (move all microcode into one place)
|
# There's no need to collate it (move all microcode into one place)
|
||||||
# if we only have one chunk of microcode.
|
# if we only have one chunk of microcode.
|
||||||
self.collate = len(ucode.subnodes) > 1
|
self.collate = len(self.ucode.subnodes) > 1
|
||||||
for node in ucode.subnodes:
|
for node in self.ucode.subnodes:
|
||||||
data_prop = node.props.get('data')
|
data_prop = node.props.get('data')
|
||||||
if data_prop:
|
if data_prop:
|
||||||
self.ucode_data += ''.join(data_prop.bytes)
|
self.ucode_data += ''.join(data_prop.bytes)
|
||||||
if self.collate:
|
if self.collate:
|
||||||
prop = node.DeleteProp('data')
|
node.DeleteProp('data')
|
||||||
else:
|
return True
|
||||||
|
|
||||||
|
def ObtainContents(self):
|
||||||
|
# Call the base class just in case it does something important.
|
||||||
|
Entry_blob.ObtainContents(self)
|
||||||
|
self._pathname = control.GetFdtPath(self._filename)
|
||||||
|
self.ReadContents()
|
||||||
|
if self.ucode:
|
||||||
|
for node in self.ucode.subnodes:
|
||||||
|
data_prop = node.props.get('data')
|
||||||
|
if data_prop and not self.collate:
|
||||||
# Find the offset in the device tree of the ucode data
|
# Find the offset in the device tree of the ucode data
|
||||||
self.ucode_offset = data_prop.GetOffset() + 12
|
self.ucode_offset = data_prop.GetOffset() + 12
|
||||||
self.ucode_size = len(data_prop.bytes)
|
self.ucode_size = len(data_prop.bytes)
|
||||||
if self.collate:
|
self.ready = True
|
||||||
dtb.Pack()
|
|
||||||
dtb.Flush()
|
|
||||||
|
|
||||||
# Make this file the contents of this entry
|
|
||||||
self._pathname = fname
|
|
||||||
self.ReadContents()
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -71,7 +71,7 @@ class Entry_u_boot_ucode(Entry_blob):
|
||||||
fdt_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
fdt_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
||||||
if not fdt_entry:
|
if not fdt_entry:
|
||||||
return True
|
return True
|
||||||
if not fdt_entry.ucode_data:
|
if not fdt_entry.ready:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not fdt_entry.collate:
|
if not fdt_entry.collate:
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
||||||
def GetDefaultFilename(self):
|
def GetDefaultFilename(self):
|
||||||
return 'u-boot-nodtb.bin'
|
return 'u-boot-nodtb.bin'
|
||||||
|
|
||||||
def ObtainContents(self):
|
def ProcessFdt(self, fdt):
|
||||||
# Figure out where to put the microcode pointer
|
# Figure out where to put the microcode pointer
|
||||||
fname = tools.GetInputFilename(self.elf_fname)
|
fname = tools.GetInputFilename(self.elf_fname)
|
||||||
sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
|
sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
|
||||||
|
@ -36,8 +36,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
||||||
self.target_pos = sym
|
self.target_pos = sym
|
||||||
elif not fdt_util.GetBool(self._node, 'optional-ucode'):
|
elif not fdt_util.GetBool(self._node, 'optional-ucode'):
|
||||||
self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
|
self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
|
||||||
|
return True
|
||||||
return Entry_blob.ObtainContents(self)
|
|
||||||
|
|
||||||
def ProcessContents(self):
|
def ProcessContents(self):
|
||||||
# If the image does not need microcode, there is nothing to do
|
# If the image does not need microcode, there is nothing to do
|
||||||
|
@ -73,7 +72,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
||||||
pos, size = ucode_entry.pos, ucode_entry.size
|
pos, size = ucode_entry.pos, ucode_entry.size
|
||||||
else:
|
else:
|
||||||
dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
||||||
if not dtb_entry:
|
if not dtb_entry or not dtb_entry.ready:
|
||||||
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
|
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
|
||||||
pos = dtb_entry.pos + dtb_entry.ucode_offset
|
pos = dtb_entry.pos + dtb_entry.ucode_offset
|
||||||
size = dtb_entry.ucode_size
|
size = dtb_entry.ucode_size
|
||||||
|
|
|
@ -54,6 +54,9 @@ class Image:
|
||||||
self._filename = filename
|
self._filename = filename
|
||||||
self._section = bsection.Section('main-section', self._node)
|
self._section = bsection.Section('main-section', self._node)
|
||||||
|
|
||||||
|
def ProcessFdt(self, fdt):
|
||||||
|
return self._section.ProcessFdt(fdt)
|
||||||
|
|
||||||
def GetEntryContents(self):
|
def GetEntryContents(self):
|
||||||
"""Call ObtainContents() for the section
|
"""Call ObtainContents() for the section
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue