binman: Add VPL support

Add support for U-Boot's Verifying Program Loader phase.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2022-02-28 07:16:54 -07:00
parent 6dca1d9ad3
commit 6ad2452bc6
12 changed files with 349 additions and 22 deletions

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for vpl/u-boot-vpl.bin
#
from binman import elf
from binman.entry import Entry
from binman.etype.blob import Entry_blob
class Entry_u_boot_vpl(Entry_blob):
"""U-Boot VPL binary
Properties / Entry arguments:
- filename: Filename of u-boot-vpl.bin (default 'vpl/u-boot-vpl.bin')
This is the U-Boot VPL (Verifying Program Loader) binary. This is a small
binary which loads before SPL, typically into on-chip SRAM. It is
responsible for locating, loading and jumping to SPL, the next-stage
loader. Note that VPL is not relocatable so must be loaded to the correct
address in SRAM, or written to run from the correct address if direct
flash execution is possible (e.g. on x86 devices).
SPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since
binman uses that to look up symbols to write into the VPL binary.
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.elf_fname = 'vpl/u-boot-vpl'
def GetDefaultFilename(self):
return 'vpl/u-boot-vpl.bin'
def WriteSymbols(self, section):
elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage())

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2021 Google LLC
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for BSS padding for vpl/u-boot-vpl.bin. This padding
# can be added after the VPL binary to ensure that anything concatenated
# to it will appear to VPL to be at the end of BSS rather than the start.
#
from binman import elf
from binman.entry import Entry
from binman.etype.blob import Entry_blob
from patman import tools
class Entry_u_boot_vpl_bss_pad(Entry_blob):
"""U-Boot VPL binary padded with a BSS region
Properties / Entry arguments:
None
This holds the padding added after the VPL binary to cover the BSS (Block
Started by Symbol) region. This region holds the various variables used by
VPL. It is set to 0 by VPL when it starts up. If you want to append data to
the VPL image (such as a device tree file), you must pad out the BSS region
to avoid the data overlapping with U-Boot variables. This entry is useful in
that case. It automatically pads out the entry size to cover both the code,
data and BSS.
The contents of this entry will a certain number of zero bytes, determined
by __bss_size
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since
binman uses that to look up the BSS address.
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
def ObtainContents(self):
fname = tools.get_input_filename('vpl/u-boot-vpl')
bss_size = elf.GetSymbolAddress(fname, '__bss_size')
if not bss_size:
self.Raise('Expected __bss_size symbol in vpl/u-boot-vpl')
self.SetContents(tools.get_bytes(0, bss_size))
return True

View File

@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot device tree in VPL (Verifying Program Loader)
#
from binman.entry import Entry
from binman.etype.blob_dtb import Entry_blob_dtb
class Entry_u_boot_vpl_dtb(Entry_blob_dtb):
"""U-Boot VPL device tree
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'vpl/u-boot-vpl.dtb')
This is the VPL device tree, containing configuration information for
VPL. VPL needs this to know what devices are present and which drivers
to activate.
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
def GetDefaultFilename(self):
return 'vpl/u-boot-vpl.dtb'
def GetFdtEtype(self):
return 'u-boot-vpl-dtb'

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2021 Google LLC
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for expanded U-Boot VPL binary
#
from patman import tout
from binman import state
from binman.etype.blob_phase import Entry_blob_phase
class Entry_u_boot_vpl_expanded(Entry_blob_phase):
"""U-Boot VPL flat binary broken out into its component parts
Properties / Entry arguments:
- vpl-dtb: Controls whether this entry is selected (set to 'y' or '1' to
select)
This is a section containing the U-Boot binary, BSS padding if needed and a
devicetree. Using this entry type automatically creates this section, with
the following entries in it:
u-boot-vpl-nodtb
u-boot-vpl-bss-pad
u-boot-dtb
Having the devicetree separate allows binman to update it in the final
image, so that the entries positions are provided to the running U-Boot.
This entry is selected based on the value of the 'vpl-dtb' entryarg. If
this is non-empty (and not 'n' or '0') then this expanded entry is selected.
"""
def __init__(self, section, etype, node):
bss_pad = state.GetEntryArgBool('vpl-bss-pad')
super().__init__(section, etype, node, 'u-boot-vpl', 'u-boot-vpl-dtb',
bss_pad)
@classmethod
def UseExpanded(cls, node, etype, new_etype):
val = state.GetEntryArgBool('vpl-dtb')
tout.do_output(tout.INFO if val else tout.DETAIL,
"Node '%s': etype '%s': %s %sselected" %
(node.path, etype, new_etype, '' if val else 'not '))
return val

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for 'u-boot-vpl-nodtb.bin'
#
from binman import elf
from binman.entry import Entry
from binman.etype.blob import Entry_blob
class Entry_u_boot_vpl_nodtb(Entry_blob):
"""VPL binary without device tree appended
Properties / Entry arguments:
- filename: Filename to include (default 'vpl/u-boot-vpl-nodtb.bin')
This is the U-Boot VPL binary, It does not include a device tree blob at
the end of it so may not be able to work without it, assuming VPL needs
a device tree to operate on your platform. You can add a u_boot_vpl_dtb
entry after this one, or use a u_boot_vpl entry instead, which normally
expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and
u-boot-vpl-dtb
VPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since
binman uses that to look up symbols to write into the VPL binary.
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.elf_fname = 'vpl/u-boot-vpl'
def GetDefaultFilename(self):
return 'vpl/u-boot-vpl-nodtb.bin'
def WriteSymbols(self, section):
elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage())

View File

@ -44,12 +44,14 @@ U_BOOT_DATA = b'1234'
U_BOOT_IMG_DATA = b'img'
U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
BLOB_DATA = b'89'
ME_DATA = b'0abcd'
VGA_DATA = b'vga'
U_BOOT_DTB_DATA = b'udtb'
U_BOOT_SPL_DTB_DATA = b'spldtb'
U_BOOT_TPL_DTB_DATA = b'tpldtb'
U_BOOT_VPL_DTB_DATA = b'vpldtb'
X86_START16_DATA = b'start16'
X86_START16_SPL_DATA = b'start16spl'
X86_START16_TPL_DATA = b'start16tpl'
@ -60,6 +62,7 @@ PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
@ -140,6 +143,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
TestFunctional._MakeInputFile('me.bin', ME_DATA)
TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
@ -165,6 +169,8 @@ class TestFunctional(unittest.TestCase):
U_BOOT_SPL_NODTB_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
U_BOOT_TPL_NODTB_DATA)
TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
U_BOOT_VPL_NODTB_DATA)
TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
@ -296,6 +302,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
def _RunBinman(self, *args, **kwargs):
"""Run binman using the command line
@ -431,8 +438,8 @@ class TestFunctional(unittest.TestCase):
shutil.rmtree(tmpdir)
return data
def _GetDtbContentsForSplTpl(self, dtb_data, name):
"""Create a version of the main DTB for SPL or SPL
def _GetDtbContentsForSpls(self, dtb_data, name):
"""Create a version of the main DTB for SPL / TPL / VPL
For testing we don't actually have different versions of the DTB. With
U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
@ -502,11 +509,11 @@ class TestFunctional(unittest.TestCase):
# For testing purposes, make a copy of the DT for SPL and TPL. Add
# a node indicating which it is, so aid verification.
for name in ['spl', 'tpl']:
for name in ['spl', 'tpl', 'vpl']:
dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
outfile = os.path.join(self._indir, dtb_fname)
TestFunctional._MakeInputFile(dtb_fname,
self._GetDtbContentsForSplTpl(dtb_data, name))
self._GetDtbContentsForSpls(dtb_data, name))
try:
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
@ -612,6 +619,16 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('tpl/u-boot-tpl',
tools.read_file(cls.ElfTestFile(src_fname)))
@classmethod
def _SetupVplElf(cls, src_fname='bss_data'):
"""Set up an ELF file with a '_dt_ucode_base_size' symbol
Args:
Filename of ELF file to use as VPL
"""
TestFunctional._MakeInputFile('vpl/u-boot-vpl',
tools.read_file(cls.ElfTestFile(src_fname)))
@classmethod
def _SetupDescriptor(cls):
with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
@ -1907,21 +1924,24 @@ class TestFunctional(unittest.TestCase):
data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
base_expected = {
'section:image-pos': 0,
'u-boot-tpl-dtb:size': 513,
'u-boot-spl-dtb:size': 513,
'u-boot-spl-dtb:offset': 493,
'image-pos': 0,
'section/u-boot-dtb:image-pos': 0,
'u-boot-spl-dtb:image-pos': 493,
'section/u-boot-dtb:size': 493,
'u-boot-tpl-dtb:image-pos': 1006,
'section/u-boot-dtb:offset': 0,
'section:size': 493,
'offset': 0,
'image-pos': 0,
'size': 2320,
'section:offset': 0,
'u-boot-tpl-dtb:offset': 1006,
'size': 1519
'section:image-pos': 0,
'section:size': 565,
'section/u-boot-dtb:offset': 0,
'section/u-boot-dtb:image-pos': 0,
'section/u-boot-dtb:size': 565,
'u-boot-spl-dtb:offset': 565,
'u-boot-spl-dtb:image-pos': 565,
'u-boot-spl-dtb:size': 585,
'u-boot-tpl-dtb:offset': 1150,
'u-boot-tpl-dtb:image-pos': 1150,
'u-boot-tpl-dtb:size': 585,
'u-boot-vpl-dtb:image-pos': 1735,
'u-boot-vpl-dtb:offset': 1735,
'u-boot-vpl-dtb:size': 585,
}
# We expect three device-tree files in the output, one after the other.
@ -1929,11 +1949,12 @@ class TestFunctional(unittest.TestCase):
# and 'tpl' in the TPL tree, to make sure they are distinct from the
# main U-Boot tree. All three should have the same postions and offset.
start = 0
for item in ['', 'spl', 'tpl']:
self.maxDiff = None
for item in ['', 'spl', 'tpl', 'vpl']:
dtb = fdt.Fdt.FromData(data[start:])
dtb.Scan()
props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
['spl', 'tpl'])
['spl', 'tpl', 'vpl'])
expected = dict(base_expected)
if item:
expected[item] = 0
@ -1953,7 +1974,7 @@ class TestFunctional(unittest.TestCase):
# over to the expected place.
start = 0
for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
'tpl/u-boot-tpl.dtb.out']:
'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
dtb = fdt.Fdt.FromData(data[start:])
size = dtb._fdt_obj.totalsize()
pathname = tools.get_output_filename(os.path.split(fname)[1])
@ -1961,7 +1982,7 @@ class TestFunctional(unittest.TestCase):
name = os.path.split(fname)[0]
if name:
orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
else:
orig_indata = dtb_data
self.assertNotEqual(outdata, orig_indata,
@ -5928,6 +5949,52 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
fname = tools.get_output_filename('mkimage-test.bin')
self.assertTrue(os.path.exists(fname))
def testVpl(self):
"""Test that an image with VPL and its device tree can be created"""
# ELF file with a '__bss_size' symbol
self._SetupVplElf()
data = self._DoReadFile('255_u_boot_vpl.dts')
self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
def testVplNoDtb(self):
"""Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
self._SetupVplElf()
data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
self.assertEqual(U_BOOT_VPL_NODTB_DATA,
data[:len(U_BOOT_VPL_NODTB_DATA)])
def testExpandedVpl(self):
"""Test that an expanded entry type is selected for TPL when needed"""
self._SetupVplElf()
entry_args = {
'vpl-bss-pad': 'y',
'vpl-dtb': 'y',
}
self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
entry_args=entry_args)
image = control.images['image']
entries = image.GetEntries()
self.assertEqual(1, len(entries))
# We only have u-boot-vpl, which be expanded
self.assertIn('u-boot-vpl', entries)
entry = entries['u-boot-vpl']
self.assertEqual('u-boot-vpl-expanded', entry.etype)
subent = entry.GetEntries()
self.assertEqual(3, len(subent))
self.assertIn('u-boot-vpl-nodtb', subent)
self.assertIn('u-boot-vpl-bss-pad', subent)
self.assertIn('u-boot-vpl-dtb', subent)
def testVplBssPadMissing(self):
"""Test that a missing symbol is detected"""
self._SetupVplElf('u_boot_ucode_ptr')
with self.assertRaises(ValueError) as e:
self._DoReadFile('258_vpl_bss_pad.dts')
self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
str(e.exception))
if __name__ == "__main__":
unittest.main()

View File

@ -22,6 +22,7 @@ OUR_PATH = os.path.dirname(os.path.realpath(__file__))
DTB_TYPE_FNAME = {
'u-boot-spl-dtb': 'spl/u-boot-spl.dtb',
'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb',
'u-boot-vpl-dtb': 'vpl/u-boot-vpl.dtb',
}
# Records the device-tree files known to binman, keyed by entry type (e.g.
@ -292,7 +293,7 @@ def GetAllFdts():
"""Yield all device tree files being used by binman
Yields:
Device trees being used (U-Boot proper, SPL, TPL)
Device trees being used (U-Boot proper, SPL, TPL, VPL)
"""
if main_dtb:
yield main_dtb

View File

@ -14,5 +14,7 @@
};
u-boot-tpl-dtb {
};
u-boot-vpl-dtb {
};
};
};

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot-vpl {
};
u-boot-vpl-dtb {
};
};
};

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot-vpl-nodtb {
};
};
};

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot-vpl {
};
};
};

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot-vpl {
};
u-boot-vpl-bss-pad {
};
u-boot {
};
};
};