FIT improvements with split-elf, especially for Rockchip

Binman positioning by ELF symbol
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmPTNLgRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreagiwgAr1Q+8qPghgomUK9cVjBuNzG2i88YWy7/
 oitshEE73lm92kP4YIQrSm1ZBy6mm0A0wyy4pRLC0fFrovzAWq1o7xfUEkuhxyTV
 pR7BiLPGUsHfL1cP4EuGSNMgrfX0QOddQgZTns4s7k4fbrFBLmTTa/+1jrU3AJNW
 1FVfVSc4eMqHM5gD7mfqSHRsxrbZDHpzbJEKIMS1xkVy/BSQbDWtF1f6Lri3M9VT
 Q7eNxFH5OptRAttcQreVNSNu28z00x0TuJNKLORXJa5AAxGW4yvNWgDFegU10NZc
 ADTKaNDsDz4D//Ar3PSt+10eGWWKqrY9ClQwls4lJmJJTIqldf+QDw==
 =l2A4
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-26jan23' of https://source.denx.de/u-boot/custodians/u-boot-dm

FIT improvements with split-elf, especially for Rockchip
Binman positioning by ELF symbol
This commit is contained in:
Tom Rini 2023-01-26 21:57:38 -05:00
commit b3b6cc28c2
15 changed files with 441 additions and 23 deletions

View file

@ -37,6 +37,7 @@
fit,fdt-list = "of-list"; fit,fdt-list = "of-list";
filename = "u-boot.itb"; filename = "u-boot.itb";
fit,external-offset = <CONFIG_FIT_EXTERNAL_OFFSET>; fit,external-offset = <CONFIG_FIT_EXTERNAL_OFFSET>;
fit,align = <512>;
offset = <CONFIG_SPL_PAD_TO>; offset = <CONFIG_SPL_PAD_TO>;
images { images {
u-boot { u-boot {
@ -49,6 +50,11 @@
entry = <CONFIG_TEXT_BASE>; entry = <CONFIG_TEXT_BASE>;
u-boot-nodtb { u-boot-nodtb {
}; };
#ifdef CONFIG_SPL_FIT_SIGNATURE
hash {
algo = "sha256";
};
#endif
}; };
@atf-SEQ { @atf-SEQ {
@ -64,6 +70,11 @@
atf-bl31 { atf-bl31 {
}; };
#ifdef CONFIG_SPL_FIT_SIGNATURE
hash {
algo = "sha256";
};
#endif
}; };
@tee-SEQ { @tee-SEQ {
fit,operation = "split-elf"; fit,operation = "split-elf";
@ -79,12 +90,22 @@
tee-os { tee-os {
optional; optional;
}; };
#ifdef CONFIG_SPL_FIT_SIGNATURE
hash {
algo = "sha256";
};
#endif
}; };
@fdt-SEQ { @fdt-SEQ {
description = "fdt-NAME"; description = "fdt-NAME";
compression = "none"; compression = "none";
type = "flat_dt"; type = "flat_dt";
#ifdef CONFIG_SPL_FIT_SIGNATURE
hash {
algo = "sha256";
};
#endif
}; };
}; };
@ -93,7 +114,7 @@
@config-SEQ { @config-SEQ {
description = "NAME.dtb"; description = "NAME.dtb";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
firmware = "u-boot"; fit,firmware = "atf-1", "u-boot";
fit,loadables; fit,loadables;
}; };
}; };

View file

@ -39,7 +39,7 @@ static void show_devices(struct udevice *dev, int depth, int last_flag,
u32 flags = dev_get_flags(dev); u32 flags = dev_get_flags(dev);
/* print the first 20 characters to not break the tree-format. */ /* print the first 20 characters to not break the tree-format. */
printf(IS_ENABLED(CONFIG_SPL_BUILD) ? " %s %d [ %c ] %s " : printf(CONFIG_IS_ENABLED(USE_TINY_PRINTF) ? " %s %d [ %c ] %s " :
" %-10.10s %3d [ %c ] %-20.20s ", dev->uclass->uc_drv->name, " %-10.10s %3d [ %c ] %-20.20s ", dev->uclass->uc_drv->name,
dev_get_uclass_index(dev, NULL), dev_get_uclass_index(dev, NULL),
flags & DM_FLAG_ACTIVATED ? '+' : ' ', dev->driver->name); flags & DM_FLAG_ACTIVATED ? '+' : ' ', dev->driver->name);

View file

@ -615,6 +615,14 @@ size:
this size. If this is not provided, it will be set to the size of the this size. If this is not provided, it will be set to the size of the
contents. contents.
min-size:
Sets the minimum size of the entry. This size includes explicit padding
('pad-before' and 'pad-after'), but not padding added to meet alignment
requirements. While this does not affect the contents of the entry within
binman itself (the padding is performed only when its parent section is
assembled), the end result will be that the entry ends with the padding
bytes, so may grow. Defaults to 0.
pad-before: pad-before:
Padding before the contents of the entry. Normally this is 0, meaning Padding before the contents of the entry. Normally this is 0, meaning
that the contents start at the beginning of the entry. This can be used that the contents start at the beginning of the entry. This can be used

View file

@ -22,7 +22,7 @@ class Bintoolmkimage(bintool.Bintool):
# pylint: disable=R0913 # pylint: disable=R0913
def run(self, reset_timestamp=False, output_fname=None, external=False, def run(self, reset_timestamp=False, output_fname=None, external=False,
pad=None): pad=None, align=None):
"""Run mkimage """Run mkimage
Args: Args:
@ -33,6 +33,7 @@ class Bintoolmkimage(bintool.Bintool):
pad: Bytes to use for padding the FIT devicetree output. This allows pad: Bytes to use for padding the FIT devicetree output. This allows
other things to be easily added later, if required, such as other things to be easily added later, if required, such as
signatures signatures
align: Bytes to use for alignment of the FIT and its external data
version: True to get the mkimage version version: True to get the mkimage version
""" """
args = [] args = []
@ -40,6 +41,8 @@ class Bintoolmkimage(bintool.Bintool):
args.append('-E') args.append('-E')
if pad: if pad:
args += ['-p', f'{pad:x}'] args += ['-p', f'{pad:x}']
if align:
args += ['-B', f'{align:x}']
if reset_timestamp: if reset_timestamp:
args.append('-t') args.append('-t')
if output_fname: if output_fname:

View file

@ -242,7 +242,7 @@ class TestElf(unittest.TestCase):
end = offset['embed_end'].offset end = offset['embed_end'].offset
data = tools.read_file(fname) data = tools.read_file(fname)
embed_data = data[start:end] embed_data = data[start:end]
expect = struct.pack('<III', 0x1234, 0x5678, 0) expect = struct.pack('<IIIII', 2, 3, 0x1234, 0x5678, 0)
self.assertEqual(expect, embed_data) self.assertEqual(expect, embed_data)
def testEmbedFail(self): def testEmbedFail(self):
@ -358,6 +358,17 @@ class TestElf(unittest.TestCase):
self.assertEqual(True, elf.is_valid(data)) self.assertEqual(True, elf.is_valid(data))
self.assertEqual(False, elf.is_valid(data[4:])) self.assertEqual(False, elf.is_valid(data[4:]))
def test_get_symbol_offset(self):
fname = self.ElfTestFile('embed_data')
syms = elf.GetSymbols(fname, ['embed_start', 'embed'])
expected = syms['embed'].address - syms['embed_start'].address
val = elf.GetSymbolOffset(fname, 'embed', 'embed_start')
self.assertEqual(expected, val)
with self.assertRaises(KeyError) as e:
elf.GetSymbolOffset(fname, 'embed')
self.assertIn('__image_copy_start', str(e.exception))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -604,6 +604,11 @@ The top-level 'fit' node supports the following special properties:
Indicates that the contents of the FIT are external and provides the Indicates that the contents of the FIT are external and provides the
external offset. This is passed to mkimage via the -E and -p flags. external offset. This is passed to mkimage via the -E and -p flags.
fit,align
Indicates what alignment to use for the FIT and its external data,
and provides the alignment to use. This is passed to mkimage via
the -B flag.
fit,fdt-list fit,fdt-list
Indicates the entry argument which provides the list of device tree Indicates the entry argument which provides the list of device tree
files for the gen-fdt-nodes operation (as below). This is often files for the gen-fdt-nodes operation (as below). This is often
@ -716,6 +721,12 @@ split-elf
fit,data fit,data
Generates a `data = <...>` property with the contents of the segment Generates a `data = <...>` property with the contents of the segment
fit,firmware
Generates a `firmware = <...>` property. Provides a list of possible
nodes to be used as the `firmware` property value. The first valid
node is picked as the firmware. Any remaining valid nodes is
prepended to the `loadable` property generated by `fit,loadables`
fit,loadables fit,loadables
Generates a `loadable = <...>` property with a list of the generated Generates a `loadable = <...>` property with a list of the generated
nodes (including all nodes if this operation is used multiple times) nodes (including all nodes if this operation is used multiple times)
@ -757,6 +768,9 @@ Here is an example showing ATF, TEE and a device tree all combined::
atf-bl31 { atf-bl31 {
}; };
hash {
algo = "sha256";
};
}; };
@tee-SEQ { @tee-SEQ {
@ -772,6 +786,9 @@ Here is an example showing ATF, TEE and a device tree all combined::
tee-os { tee-os {
}; };
hash {
algo = "sha256";
};
}; };
}; };
@ -780,7 +797,7 @@ Here is an example showing ATF, TEE and a device tree all combined::
@config-SEQ { @config-SEQ {
description = "conf-NAME.dtb"; description = "conf-NAME.dtb";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
firmware = "u-boot"; fit,firmware = "atf-1", "u-boot";
fit,loadables; fit,loadables;
}; };
}; };
@ -800,6 +817,10 @@ ELF file, for example::
arch = "arm64"; arch = "arm64";
type = "firmware"; type = "firmware";
description = "ARM Trusted Firmware"; description = "ARM Trusted Firmware";
hash {
algo = "sha256";
value = <...hash of first segment...>;
};
}; };
atf-2 { atf-2 {
data = <...contents of second segment...>; data = <...contents of second segment...>;
@ -809,6 +830,10 @@ ELF file, for example::
arch = "arm64"; arch = "arm64";
type = "firmware"; type = "firmware";
description = "ARM Trusted Firmware"; description = "ARM Trusted Firmware";
hash {
algo = "sha256";
value = <...hash of second segment...>;
};
}; };
}; };
@ -827,15 +852,15 @@ is::
configurations { configurations {
default = "config-1"; default = "config-1";
config-1 { config-1 {
loadables = "atf-1", "atf-2", "atf-3", "tee-1", "tee-2"; loadables = "u-boot", "atf-2", "atf-3", "tee-1", "tee-2";
description = "rk3399-firefly.dtb"; description = "rk3399-firefly.dtb";
fdt = "fdt-1"; fdt = "fdt-1";
firmware = "u-boot"; firmware = "atf-1";
}; };
}; };
U-Boot SPL can then load the firmware (U-Boot proper) and all the loadables U-Boot SPL can then load the firmware (ATF) and all the loadables (U-Boot
(ATF and TEE), then proceed with the boot. proper, ATF and TEE), then proceed with the boot.

View file

@ -49,6 +49,7 @@ class Entry(object):
offset: Offset of entry within the section, None if not known yet (in offset: Offset of entry within the section, None if not known yet (in
which case it will be calculated by Pack()) which case it will be calculated by Pack())
size: Entry size in bytes, None if not known size: Entry size in bytes, None if not known
min_size: Minimum entry size in bytes
pre_reset_size: size as it was before ResetForPack(). This allows us to pre_reset_size: size as it was before ResetForPack(). This allows us to
keep track of the size we started with and detect size changes keep track of the size we started with and detect size changes
uncomp_size: Size of uncompressed data in bytes, if the entry is uncomp_size: Size of uncompressed data in bytes, if the entry is
@ -114,6 +115,7 @@ class Entry(object):
self.name = node and (name_prefix + node.name) or 'none' self.name = node and (name_prefix + node.name) or 'none'
self.offset = None self.offset = None
self.size = None self.size = None
self.min_size = 0
self.pre_reset_size = None self.pre_reset_size = None
self.uncomp_size = None self.uncomp_size = None
self.data = None self.data = None
@ -270,6 +272,7 @@ class Entry(object):
self.Raise("Please use 'extend-size' instead of 'expand-size'") self.Raise("Please use 'extend-size' instead of 'expand-size'")
self.offset = fdt_util.GetInt(self._node, 'offset') self.offset = fdt_util.GetInt(self._node, 'offset')
self.size = fdt_util.GetInt(self._node, 'size') self.size = fdt_util.GetInt(self._node, 'size')
self.min_size = fdt_util.GetInt(self._node, 'min-size', 0)
self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset') self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
self.orig_size = fdt_util.GetInt(self._node, 'orig-size') self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
if self.GetImage().copy_to_orig: if self.GetImage().copy_to_orig:
@ -507,6 +510,7 @@ class Entry(object):
else: else:
self.offset = tools.align(offset, self.align) self.offset = tools.align(offset, self.align)
needed = self.pad_before + self.contents_size + self.pad_after needed = self.pad_before + self.contents_size + self.pad_after
needed = max(needed, self.min_size)
needed = tools.align(needed, self.align_size) needed = tools.align(needed, self.align_size)
size = self.size size = self.size
if not size: if not size:

View file

@ -114,6 +114,25 @@ class TestEntry(unittest.TestCase):
self.assertEquals(tools.get_bytes(0, 1024), base.CompressData(b'abc')) self.assertEquals(tools.get_bytes(0, 1024), base.CompressData(b'abc'))
self.assertEquals(tools.get_bytes(0, 1024), base.DecompressData(b'abc')) self.assertEquals(tools.get_bytes(0, 1024), base.DecompressData(b'abc'))
def testLookupOffset(self):
"""Test the lookup_offset() method of the base class"""
def MyFindEntryByNode(node):
return self.found
base = entry.Entry.Create(None, self.GetNode(), 'blob-dtb')
base.FindEntryByNode = MyFindEntryByNode
base.section = base
self.found = None
base.offset_from_elf = [self.GetNode(), 'start', 0]
with self.assertRaises(ValueError) as e:
base.lookup_offset()
self.assertIn("Cannot find entry for node 'u-boot'", str(e.exception))
self.found = base
with self.assertRaises(ValueError) as e:
base.lookup_offset()
self.assertIn("Need elf-fname property 'u-boot'", str(e.exception))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -70,6 +70,11 @@ class Entry_fit(Entry_section):
Indicates that the contents of the FIT are external and provides the Indicates that the contents of the FIT are external and provides the
external offset. This is passed to mkimage via the -E and -p flags. external offset. This is passed to mkimage via the -E and -p flags.
fit,align
Indicates what alignment to use for the FIT and its external data,
and provides the alignment to use. This is passed to mkimage via
the -B flag.
fit,fdt-list fit,fdt-list
Indicates the entry argument which provides the list of device tree Indicates the entry argument which provides the list of device tree
files for the gen-fdt-nodes operation (as below). This is often files for the gen-fdt-nodes operation (as below). This is often
@ -182,6 +187,12 @@ class Entry_fit(Entry_section):
fit,data fit,data
Generates a `data = <...>` property with the contents of the segment Generates a `data = <...>` property with the contents of the segment
fit,firmware
Generates a `firmware = <...>` property. Provides a list of possible
nodes to be used as the `firmware` property value. The first valid
node is picked as the firmware. Any remaining valid nodes is
prepended to the `loadable` property generated by `fit,loadables`
fit,loadables fit,loadables
Generates a `loadable = <...>` property with a list of the generated Generates a `loadable = <...>` property with a list of the generated
nodes (including all nodes if this operation is used multiple times) nodes (including all nodes if this operation is used multiple times)
@ -223,6 +234,9 @@ class Entry_fit(Entry_section):
atf-bl31 { atf-bl31 {
}; };
hash {
algo = "sha256";
};
}; };
@tee-SEQ { @tee-SEQ {
@ -238,6 +252,9 @@ class Entry_fit(Entry_section):
tee-os { tee-os {
}; };
hash {
algo = "sha256";
};
}; };
}; };
@ -246,7 +263,7 @@ class Entry_fit(Entry_section):
@config-SEQ { @config-SEQ {
description = "conf-NAME.dtb"; description = "conf-NAME.dtb";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
firmware = "u-boot"; fit,firmware = "atf-1", "u-boot";
fit,loadables; fit,loadables;
}; };
}; };
@ -266,6 +283,10 @@ class Entry_fit(Entry_section):
arch = "arm64"; arch = "arm64";
type = "firmware"; type = "firmware";
description = "ARM Trusted Firmware"; description = "ARM Trusted Firmware";
hash {
algo = "sha256";
value = <...hash of first segment...>;
};
}; };
atf-2 { atf-2 {
data = <...contents of second segment...>; data = <...contents of second segment...>;
@ -275,6 +296,10 @@ class Entry_fit(Entry_section):
arch = "arm64"; arch = "arm64";
type = "firmware"; type = "firmware";
description = "ARM Trusted Firmware"; description = "ARM Trusted Firmware";
hash {
algo = "sha256";
value = <...hash of second segment...>;
};
}; };
}; };
@ -293,15 +318,15 @@ class Entry_fit(Entry_section):
configurations { configurations {
default = "config-1"; default = "config-1";
config-1 { config-1 {
loadables = "atf-1", "atf-2", "atf-3", "tee-1", "tee-2"; loadables = "u-boot", "atf-2", "atf-3", "tee-1", "tee-2";
description = "rk3399-firefly.dtb"; description = "rk3399-firefly.dtb";
fdt = "fdt-1"; fdt = "fdt-1";
firmware = "u-boot"; firmware = "atf-1";
}; };
}; };
U-Boot SPL can then load the firmware (U-Boot proper) and all the loadables U-Boot SPL can then load the firmware (ATF) and all the loadables (U-Boot
(ATF and TEE), then proceed with the boot. proper, ATF and TEE), then proceed with the boot.
""" """
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
""" """
@ -423,6 +448,9 @@ class Entry_fit(Entry_section):
'external': True, 'external': True,
'pad': fdt_util.fdt32_to_cpu(ext_offset.value) 'pad': fdt_util.fdt32_to_cpu(ext_offset.value)
} }
align = self._fit_props.get('fit,align')
if align is not None:
args.update({'align': fdt_util.fdt32_to_cpu(align.value)})
if self.mkimage.run(reset_timestamp=True, output_fname=output_fname, if self.mkimage.run(reset_timestamp=True, output_fname=output_fname,
**args) is None: **args) is None:
# Bintool is missing; just use empty data as the output # Bintool is missing; just use empty data as the output
@ -488,6 +516,42 @@ class Entry_fit(Entry_section):
return return
fsw.property(pname, prop.bytes) fsw.property(pname, prop.bytes)
def _process_firmware_prop(node):
"""Process optional fit,firmware property
Picks the first valid entry for use as the firmware, remaining valid
entries is prepended to loadables
Args:
node (Node): Generator node to process
Returns:
firmware (str): Firmware or None
result (list): List of remaining loadables
"""
val = fdt_util.GetStringList(node, 'fit,firmware')
if val is None:
return None, self._loadables
valid_entries = list(self._loadables)
for name, entry in self.GetEntries().items():
missing = []
entry.CheckMissing(missing)
entry.CheckOptional(missing)
if not missing:
valid_entries.append(name)
firmware = None
result = []
for name in val:
if name in valid_entries:
if not firmware:
firmware = name
elif name not in result:
result.append(name)
for name in self._loadables:
if name != firmware and name not in result:
result.append(name)
return firmware, result
def _gen_fdt_nodes(base_node, node, depth, in_images): def _gen_fdt_nodes(base_node, node, depth, in_images):
"""Generate FDT nodes """Generate FDT nodes
@ -498,20 +562,24 @@ class Entry_fit(Entry_section):
first. first.
Args: Args:
node (None): Generator node to process node (Node): Generator node to process
depth: Current node depth (0 is the base 'fit' node) depth: Current node depth (0 is the base 'fit' node)
in_images: True if this is inside the 'images' node, so that in_images: True if this is inside the 'images' node, so that
'data' properties should be generated 'data' properties should be generated
""" """
if self._fdts: if self._fdts:
firmware, fit_loadables = _process_firmware_prop(node)
# Generate nodes for each FDT # Generate nodes for each FDT
for seq, fdt_fname in enumerate(self._fdts): for seq, fdt_fname in enumerate(self._fdts):
node_name = node.name[1:].replace('SEQ', str(seq + 1)) node_name = node.name[1:].replace('SEQ', str(seq + 1))
fname = tools.get_input_filename(fdt_fname + '.dtb') fname = tools.get_input_filename(fdt_fname + '.dtb')
with fsw.add_node(node_name): with fsw.add_node(node_name):
for pname, prop in node.props.items(): for pname, prop in node.props.items():
if pname == 'fit,loadables': if pname == 'fit,firmware':
val = '\0'.join(self._loadables) + '\0' if firmware:
fsw.property_string('firmware', firmware)
elif pname == 'fit,loadables':
val = '\0'.join(fit_loadables) + '\0'
fsw.property('loadables', val.encode('utf-8')) fsw.property('loadables', val.encode('utf-8'))
elif pname == 'fit,operation': elif pname == 'fit,operation':
pass pass
@ -540,12 +608,13 @@ class Entry_fit(Entry_section):
else: else:
self.Raise("Generator node requires 'fit,fdt-list' property") self.Raise("Generator node requires 'fit,fdt-list' property")
def _gen_split_elf(base_node, node, segments, entry_addr): def _gen_split_elf(base_node, node, depth, segments, entry_addr):
"""Add nodes for the ELF file, one per group of contiguous segments """Add nodes for the ELF file, one per group of contiguous segments
Args: Args:
base_node (Node): Template node from the binman definition base_node (Node): Template node from the binman definition
node (Node): Node to replace (in the FIT being built) node (Node): Node to replace (in the FIT being built)
depth: Current node depth (0 is the base 'fit' node)
segments (list): list of segments, each: segments (list): list of segments, each:
int: Segment number (0 = first) int: Segment number (0 = first)
int: Start address of segment in memory int: Start address of segment in memory
@ -570,6 +639,10 @@ class Entry_fit(Entry_section):
self._raise_subnode( self._raise_subnode(
node, f"Unknown directive '{pname}'") node, f"Unknown directive '{pname}'")
for subnode in node.subnodes:
with fsw.add_node(subnode.name):
_add_node(node, depth + 1, subnode)
def _gen_node(base_node, node, depth, in_images, entry): def _gen_node(base_node, node, depth, in_images, entry):
"""Generate nodes from a template """Generate nodes from a template
@ -623,7 +696,7 @@ class Entry_fit(Entry_section):
self._raise_subnode( self._raise_subnode(
node, f'Failed to read ELF file: {str(exc)}') node, f'Failed to read ELF file: {str(exc)}')
_gen_split_elf(base_node, node, segments, entry_addr) _gen_split_elf(base_node, node, depth, segments, entry_addr)
def _add_node(base_node, depth, node): def _add_node(base_node, depth, node):
"""Add nodes to the output FIT """Add nodes to the output FIT

View file

@ -883,9 +883,9 @@ class TestFunctional(unittest.TestCase):
self.assertIn('image', control.images) self.assertIn('image', control.images)
image = control.images['image'] image = control.images['image']
entries = image.GetEntries() entries = image.GetEntries()
self.assertEqual(5, len(entries)) self.assertEqual(6, len(entries))
# First u-boot with padding before and after # First u-boot with padding before and after (included in minimum size)
self.assertIn('u-boot', entries) self.assertIn('u-boot', entries)
entry = entries['u-boot'] entry = entries['u-boot']
self.assertEqual(0, entry.offset) self.assertEqual(0, entry.offset)
@ -934,8 +934,17 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)), self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
data[pos:pos + entry.size]) data[pos:pos + entry.size])
# Sixth u-boot with both minimum size and aligned size
self.assertIn('u-boot-min-size', entries)
entry = entries['u-boot-min-size']
self.assertEqual(128, entry.offset)
self.assertEqual(32, entry.size)
self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
data[pos:pos + entry.size])
self.CheckNoGaps(entries) self.CheckNoGaps(entries)
self.assertEqual(128, image.size) self.assertEqual(160, image.size)
dtb = fdt.Fdt(out_dtb_fname) dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan() dtb.Scan()
@ -943,7 +952,7 @@ class TestFunctional(unittest.TestCase):
expected = { expected = {
'image-pos': 0, 'image-pos': 0,
'offset': 0, 'offset': 0,
'size': 128, 'size': 160,
'u-boot:image-pos': 0, 'u-boot:image-pos': 0,
'u-boot:offset': 0, 'u-boot:offset': 0,
@ -964,6 +973,10 @@ class TestFunctional(unittest.TestCase):
'u-boot-align-both:image-pos': 64, 'u-boot-align-both:image-pos': 64,
'u-boot-align-both:offset': 64, 'u-boot-align-both:offset': 64,
'u-boot-align-both:size': 64, 'u-boot-align-both:size': 64,
'u-boot-min-size:image-pos': 128,
'u-boot-min-size:offset': 128,
'u-boot-min-size:size': 32,
} }
self.assertEqual(expected, props) self.assertEqual(expected, props)
@ -5439,6 +5452,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
fdt_util.fdt32_to_cpu(atf1.props['load'].value)) fdt_util.fdt32_to_cpu(atf1.props['load'].value))
self.assertEqual(data, atf1.props['data'].bytes) self.assertEqual(data, atf1.props['data'].bytes)
hash_node = atf1.FindNode('hash')
self.assertIsNotNone(hash_node)
self.assertEqual({'algo', 'value'}, hash_node.props.keys())
atf2 = dtb.GetNode('/images/atf-2') atf2 = dtb.GetNode('/images/atf-2')
self.assertEqual(base_keys, atf2.props.keys()) self.assertEqual(base_keys, atf2.props.keys())
_, start, data = segments[1] _, start, data = segments[1]
@ -5446,6 +5463,14 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
fdt_util.fdt32_to_cpu(atf2.props['load'].value)) fdt_util.fdt32_to_cpu(atf2.props['load'].value))
self.assertEqual(data, atf2.props['data'].bytes) self.assertEqual(data, atf2.props['data'].bytes)
hash_node = atf2.FindNode('hash')
self.assertIsNotNone(hash_node)
self.assertEqual({'algo', 'value'}, hash_node.props.keys())
hash_node = dtb.GetNode('/images/tee-1/hash-1')
self.assertIsNotNone(hash_node)
self.assertEqual({'algo', 'value'}, hash_node.props.keys())
conf = dtb.GetNode('/configurations') conf = dtb.GetNode('/configurations')
self.assertEqual({'default'}, conf.props.keys()) self.assertEqual({'default'}, conf.props.keys())
@ -6309,6 +6334,66 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertEqual(base + 8, inset.image_pos); self.assertEqual(base + 8, inset.image_pos);
self.assertEqual(4, inset.size); self.assertEqual(4, inset.size);
def testFitAlign(self):
"""Test an image with an FIT with aligned external data"""
data = self._DoReadFile('275_fit_align.dts')
self.assertEqual(4096, len(data))
dtb = fdt.Fdt.FromData(data)
dtb.Scan()
props = self._GetPropTree(dtb, ['data-position'])
expected = {
'u-boot:data-position': 1024,
'fdt-1:data-position': 2048,
'fdt-2:data-position': 3072,
}
self.assertEqual(expected, props)
def testFitFirmwareLoadables(self):
"""Test an image with an FIT that use fit,firmware"""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
entry_args = {
'of-list': 'test-fdt1',
'default-dt': 'test-fdt1',
'atf-bl31-path': 'bl31.elf',
'tee-os-path': 'missing.bin',
}
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
data = self._DoReadFileDtb(
'276_fit_firmware_loadables.dts',
entry_args=entry_args,
extra_indirs=[test_subdir])[0]
dtb = fdt.Fdt.FromData(data)
dtb.Scan()
node = dtb.GetNode('/configurations/conf-uboot-1')
self.assertEqual('u-boot', node.props['firmware'].value)
self.assertEqual(['atf-1', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
node = dtb.GetNode('/configurations/conf-atf-1')
self.assertEqual('atf-1', node.props['firmware'].value)
self.assertEqual(['u-boot', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
node = dtb.GetNode('/configurations/conf-missing-uboot-1')
self.assertEqual('u-boot', node.props['firmware'].value)
self.assertEqual(['atf-1', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
node = dtb.GetNode('/configurations/conf-missing-atf-1')
self.assertEqual('atf-1', node.props['firmware'].value)
self.assertEqual(['u-boot', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
node = dtb.GetNode('/configurations/conf-missing-tee-1')
self.assertEqual('atf-1', node.props['firmware'].value)
self.assertEqual(['u-boot', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -6,6 +6,7 @@
binman { binman {
u-boot { u-boot {
min-size = <12>;
pad-before = <3>; pad-before = <3>;
pad-after = <5>; pad-after = <5>;
}; };
@ -31,5 +32,11 @@
align = <64>; align = <64>;
align-end = <128>; align-end = <128>;
}; };
u-boot-min-size {
type = "u-boot";
min-size = <24>;
align-size = <16>;
};
}; };
}; };

View file

@ -33,6 +33,9 @@
atf-bl31 { atf-bl31 {
}; };
hash {
algo = "sha256";
};
}; };
@tee-SEQ { @tee-SEQ {
@ -48,6 +51,9 @@
tee-os { tee-os {
}; };
hash-1 {
algo = "sha256";
};
}; };
}; };

View file

@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
fit {
description = "test desc";
#address-cells = <1>;
fit,external-offset = <1024>;
fit,align = <1024>;
images {
u-boot {
description = "test u-boot";
type = "standalone";
arch = "arm64";
os = "u-boot";
compression = "none";
load = <00000000>;
entry = <00000000>;
u-boot-nodtb {
};
};
fdt-1 {
description = "test fdt";
type = "flat_dt";
compression = "none";
u-boot-dtb {
};
};
fdt-2 {
description = "test fdt";
type = "flat_dt";
compression = "none";
u-boot-dtb {
};
};
};
configurations {
default = "config-1";
config-1 {
description = "test config";
fdt = "fdt-1";
firmware = "u-boot";
};
};
};
};
};

View file

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
fit {
description = "test desc";
#address-cells = <1>;
fit,fdt-list = "of-list";
images {
u-boot {
description = "test u-boot";
type = "standalone";
arch = "arm64";
os = "u-boot";
compression = "none";
load = <0x00000000>;
entry = <0x00000000>;
u-boot-nodtb {
};
};
tee {
description = "test tee";
type = "tee";
arch = "arm64";
os = "tee";
compression = "none";
load = <0x00200000>;
tee-os {
optional;
};
};
@atf-SEQ {
fit,operation = "split-elf";
description = "test tf-a";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
fit,load;
fit,entry;
fit,data;
atf-bl31 {
};
};
@fdt-SEQ {
description = "test fdt";
type = "flat_dt";
compression = "none";
};
};
configurations {
default = "@conf-uboot-DEFAULT-SEQ";
@conf-uboot-SEQ {
description = "uboot config";
fdt = "fdt-SEQ";
fit,firmware = "u-boot";
fit,loadables;
};
@conf-atf-SEQ {
description = "atf config";
fdt = "fdt-SEQ";
fit,firmware = "atf-1", "u-boot";
fit,loadables;
};
@conf-missing-uboot-SEQ {
description = "missing uboot config";
fdt = "fdt-SEQ";
fit,firmware = "missing-1", "u-boot";
fit,loadables;
};
@conf-missing-atf-SEQ {
description = "missing atf config";
fdt = "fdt-SEQ";
fit,firmware = "missing-1", "atf-1", "u-boot";
fit,loadables;
};
@conf-missing-tee-SEQ {
description = "missing tee config";
fdt = "fdt-SEQ";
fit,firmware = "atf-1", "u-boot", "tee";
fit,loadables;
};
};
};
};
};

View file

@ -7,6 +7,7 @@
*/ */
int first[10] = {1}; int first[10] = {1};
int before[2] __attribute__((section(".embed"))) = {2, 3};
int embed[3] __attribute__((section(".embed"))) = {0x1234, 0x5678}; int embed[3] __attribute__((section(".embed"))) = {0x1234, 0x5678};
int second[10] = {1}; int second[10] = {1};