From a0ac1d90373354a3ad48344a1722b32aea2fee01 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 22 Mar 2021 08:22:53 +1300 Subject: [PATCH 001/120] azure: Use --board flag with sandbox_spl At present there is only one board which uses sandbox SPL. But with sandbox_noinst being added, this is no longer true. Add a --board flag so that we just build one board on azure, as is done in gitlab. Signed-off-by: Simon Glass Reviewed-by: Tom Rini --- .azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 506e0c0618..e4d0a2dfd1 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -148,7 +148,7 @@ jobs: export UBOOT_TRAVIS_BUILD_DIR=/tmp/sandbox_spl export PYTHONPATH=${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt export PATH=${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH} - ./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w sandbox_spl + ./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w --board sandbox_spl ./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test ./tools/buildman/buildman -t ./tools/dtoc/dtoc -t From c7e42cabed3d761f4aa6007135fd126db82a2ae1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Jan 2021 08:56:14 -0700 Subject: [PATCH 002/120] patman: Update documentation to match new usage With the subcommands some of the documentation examples are no-longer correct. Fix all of them, so it is consistent. Signed-off-by: Simon Glass --- tools/patman/README | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/patman/README b/tools/patman/README index 6b806632f8..53f55ce95d 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -136,17 +136,17 @@ How to run it First do a dry run: -$ ./tools/patman/patman -n +$ ./tools/patman/patman send -n If it can't detect the upstream branch, try telling it how many patches there are in your series: -$ ./tools/patman/patman -n -c5 +$ ./tools/patman/patman -c5 send -n This will create patch files in your current directory and tell you who it is thinking of sending them to. Take a look at the patch files. -$ ./tools/patman/patman -n -c5 -s1 +$ ./tools/patman/patman -c5 -s1 send -n Similar to the above, but skip the first commit and take the next 5. This is useful if your top commit is for setting up testing. @@ -433,12 +433,12 @@ but that you don't want to submit because there is an existing patch for it on the list. So you can tell patman to create and check some patches (skipping the first patch) with: - patman -s1 -n + patman -s1 send -n If you want to do all of them including the work-in-progress one, then (if you are tracking an upstream branch): - patman -n + patman send -n Let's say that patman reports an error in the second patch. Then: @@ -450,7 +450,7 @@ Let's say that patman reports an error in the second patch. Then: Now you have an updated patch series. To check it: - patman -s1 -n + patman -s1 send -n Let's say it is now clean and you want to send it. Now you need to set up the destination. So amend the top commit with: @@ -485,7 +485,7 @@ mmc and sparc, and the last one to sandbox. Now to send the patches, take off the -n flag: - patman -s1 + patman -s1 send The patches will be created, shown in your editor, and then sent along with the cover letter. Note that patman's tags are automatically removed so that From 0fb560d9a71e9bd3c36944dda9b6122df63f49a3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Jan 2021 08:56:15 -0700 Subject: [PATCH 003/120] patman: Quieten down the alias checking When a tag is used in a patch subject (e.g. "tag: rest of message") and it cannot be found as an alias, patman currently reports a fatal error, unless -t is provided, in which case it reports a warning. Experience suggest that the fatal error is not very useful. Instead, default to reporting a warning, with -t tell patman to ignore it altogether. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 2 +- tools/patman/gitutil.py | 45 +++++++++++++++++---------------------- tools/patman/main.py | 6 +++++- tools/patman/series.py | 10 ++++----- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 89072b1ae7..450fe6659c 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -186,7 +186,7 @@ class TestFunctional(unittest.TestCase): - Commit-notes """ process_tags = True - ignore_bad_tags = True + ignore_bad_tags = False stefan = b'Stefan Br\xc3\xbcns '.decode('utf-8') rick = 'Richard III ' mel = b'Lord M\xc3\xablchett '.decode('utf-8') diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index bf1271ded7..5e4c1128dc 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -345,7 +345,7 @@ def CreatePatches(branch, start, count, ignore_binary, series, signoff = True): else: return None, files -def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): +def BuildEmailList(in_list, tag=None, alias=None, warn_on_error=True): """Build a list of email addresses based on an input list. Takes a list of email addresses and aliases, and turns this into a list @@ -359,7 +359,7 @@ def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): in_list: List of aliases/email addresses tag: Text to put before each address alias: Alias dictionary - raise_on_error: True to raise an error when an alias fails to match, + warn_on_error: True to raise an error when an alias fails to match, False to just print a message. Returns: @@ -382,7 +382,7 @@ def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): quote = '"' if tag and tag[0] == '-' else '' raw = [] for item in in_list: - raw += LookupEmail(item, alias, raise_on_error=raise_on_error) + raw += LookupEmail(item, alias, warn_on_error=warn_on_error) result = [] for item in raw: if not item in result: @@ -416,7 +416,7 @@ def CheckSuppressCCConfig(): return True -def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, +def EmailPatches(series, cover_fname, args, dry_run, warn_on_error, cc_fname, self_only=False, alias=None, in_reply_to=None, thread=False, smtp_server=None): """Email a patch series. @@ -426,8 +426,8 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, cover_fname: filename of cover letter args: list of filenames of patch files dry_run: Just return the command that would be run - raise_on_error: True to raise an error when an alias fails to match, - False to just print a message. + warn_on_error: True to print a warning when an alias fails to match, + False to ignore it. cc_fname: Filename of Cc file for per-commit Cc self_only: True to just email to yourself as a test in_reply_to: If set we'll pass this to git as --in-reply-to. @@ -475,7 +475,7 @@ send --cc-cmd cc-fname" cover p1 p2' # Restore argv[0] since we clobbered it. >>> sys.argv[0] = _old_argv0 """ - to = BuildEmailList(series.get('to'), '--to', alias, raise_on_error) + to = BuildEmailList(series.get('to'), '--to', alias, warn_on_error) if not to: git_config_to = command.Output('git', 'config', 'sendemail.to', raise_on_error=False) @@ -487,9 +487,9 @@ send --cc-cmd cc-fname" cover p1 p2' "git config sendemail.to u-boot@lists.denx.de") return cc = BuildEmailList(list(set(series.get('cc')) - set(series.get('to'))), - '--cc', alias, raise_on_error) + '--cc', alias, warn_on_error) if self_only: - to = BuildEmailList([os.getenv('USER')], '--to', alias, raise_on_error) + to = BuildEmailList([os.getenv('USER')], '--to', alias, warn_on_error) cc = [] cmd = ['git', 'send-email', '--annotate'] if smtp_server: @@ -511,7 +511,7 @@ send --cc-cmd cc-fname" cover p1 p2' return cmdstr -def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): +def LookupEmail(lookup_name, alias=None, warn_on_error=True, level=0): """If an email address is an alias, look it up and return the full name TODO: Why not just use git's own alias feature? @@ -519,8 +519,8 @@ def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): Args: lookup_name: Alias or email address to look up alias: Dictionary containing aliases (None to use settings default) - raise_on_error: True to raise an error when an alias fails to match, - False to just print a message. + warn_on_error: True to print a warning when an alias fails to match, + False to ignore it. Returns: tuple: @@ -547,18 +547,16 @@ def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): >>> LookupEmail('all', alias) ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz', 'm.poppins@cloud.net'] >>> LookupEmail('odd', alias) - Traceback (most recent call last): - ... - ValueError: Alias 'odd' not found + Alias 'odd' not found + [] >>> LookupEmail('loop', alias) Traceback (most recent call last): ... OSError: Recursive email alias at 'other' - >>> LookupEmail('odd', alias, raise_on_error=False) - Alias 'odd' not found + >>> LookupEmail('odd', alias, warn_on_error=False) [] >>> # In this case the loop part will effectively be ignored. - >>> LookupEmail('loop', alias, raise_on_error=False) + >>> LookupEmail('loop', alias, warn_on_error=False) Recursive email alias at 'other' Recursive email alias at 'john' Recursive email alias at 'mary' @@ -576,7 +574,7 @@ def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): out_list = [] if level > 10: msg = "Recursive email alias at '%s'" % lookup_name - if raise_on_error: + if warn_on_error: raise OSError(msg) else: print(col.Color(col.RED, msg)) @@ -585,18 +583,15 @@ def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): if lookup_name: if not lookup_name in alias: msg = "Alias '%s' not found" % lookup_name - if raise_on_error: - raise ValueError(msg) - else: + if warn_on_error: print(col.Color(col.RED, msg)) - return out_list + return out_list for item in alias[lookup_name]: - todo = LookupEmail(item, alias, raise_on_error, level + 1) + todo = LookupEmail(item, alias, warn_on_error, level + 1) for new_item in todo: if not new_item in out_list: out_list.append(new_item) - #print("No match for alias '%s'" % lookup_name) return out_list def GetTopLevel(): diff --git a/tools/patman/main.py b/tools/patman/main.py index c4e4d80d42..4e0a3533e9 100755 --- a/tools/patman/main.py +++ b/tools/patman/main.py @@ -68,7 +68,8 @@ send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run', send.add_argument('-r', '--in-reply-to', type=str, action='store', help="Message ID that this series is in reply to") send.add_argument('-t', '--ignore-bad-tags', action='store_true', - default=False, help='Ignore bad tags / aliases') + default=False, + help='Ignore bad tags / aliases (default=warn)') send.add_argument('-T', '--thread', action='store_true', dest='thread', default=False, help='Create patches as a single thread') send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store', @@ -176,6 +177,9 @@ elif args.cmd == 'send': command.Run(pager, fname) else: + # If we are not processing tags, no need to warning about bad ones + if not args.process_tags: + args.ignore_bad_tags = True control.send(args) # Check status of patches in patchwork diff --git a/tools/patman/series.py b/tools/patman/series.py index 41a11732fc..8ae218d3a4 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -234,7 +234,7 @@ class Series(dict): str = 'Change log exists, but no version is set' print(col.Color(col.RED, str)) - def MakeCcFile(self, process_tags, cover_fname, raise_on_error, + def MakeCcFile(self, process_tags, cover_fname, warn_on_error, add_maintainers, limit): """Make a cc file for us to use for per-commit Cc automation @@ -243,8 +243,8 @@ class Series(dict): Args: process_tags: Process tags as if they were aliases cover_fname: If non-None the name of the cover letter. - raise_on_error: True to raise an error when an alias fails to match, - False to just print a message. + warn_on_error: True to print a warning when an alias fails to match, + False to ignore it. add_maintainers: Either: True/False to call the get_maintainers to CC maintainers List of maintainers to include (for testing) @@ -261,9 +261,9 @@ class Series(dict): cc = [] if process_tags: cc += gitutil.BuildEmailList(commit.tags, - raise_on_error=raise_on_error) + warn_on_error=warn_on_error) cc += gitutil.BuildEmailList(commit.cc_list, - raise_on_error=raise_on_error) + warn_on_error=warn_on_error) if type(add_maintainers) == type(cc): cc += add_maintainers elif add_maintainers: From f7691a6d736bec7915c227ac14076f9993a27367 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 10 Feb 2021 18:54:25 +0100 Subject: [PATCH 004/120] sandbox: allow cross-compiling sandbox UEFI test files like helloworld.efi require an architecture specific PE-COFF header. Currently this does not work for cross compiling. If $CROSS_COMPILE is set, use the first part of the architecture triplet from the variable to choose the PE-COFF header. Now we can cross-compile the sandbox, e.g. make sandbox_defconfig NO_SDL=1 CROSS_COMPILE=/opt/bin/aarch64-linux-gnu- NO_SDL=1 MK_ARCH=aarch64 make Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b72d8d20c0..a790be6226 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,13 @@ NAME = # o Look for make include files relative to root of kernel src MAKEFLAGS += -rR --include-dir=$(CURDIR) -# Determine host architecture +# Determine target architecture for the sandbox include include/host_arch.h -MK_ARCH="${shell uname -m}" +ifeq ("", "$(CROSS_COMPILE)") + MK_ARCH="${shell uname -m}" +else + MK_ARCH="${shell echo $(CROSS_COMPILE) | sed -n 's/^\s*\([^\/]*\/\)*\([^-]*\)-\S*/\2/p'}" +endif unexport HOST_ARCH ifeq ("x86_64", $(MK_ARCH)) export HOST_ARCH=$(HOST_ARCH_X86_64) @@ -27,7 +31,7 @@ else ifneq (,$(findstring $(MK_ARCH), "i386" "i486" "i586" "i686")) export HOST_ARCH=$(HOST_ARCH_X86) else ifneq (,$(findstring $(MK_ARCH), "aarch64" "armv8l")) export HOST_ARCH=$(HOST_ARCH_AARCH64) -else ifeq ("armv7l", $(MK_ARCH)) +else ifneq (,$(findstring $(MK_ARCH), "arm" "armv7" "armv7l")) export HOST_ARCH=$(HOST_ARCH_ARM) else ifeq ("riscv32", $(MK_ARCH)) export HOST_ARCH=$(HOST_ARCH_RISCV32) From ac549ac82d1b09fa9b912f64784426dec9052c20 Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Wed, 10 Feb 2021 19:43:09 -0500 Subject: [PATCH 005/120] tools: fdtgrep: Use unsigned chars for arrays Otherwise, values over 127 end up prefixed with ffffff. Signed-off-by: Samuel Dionne-Riel Cc: Simon Glass Reviewed-by: Simon Glass --- tools/fdtgrep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/fdtgrep.c b/tools/fdtgrep.c index e4112b8f69..db512465db 100644 --- a/tools/fdtgrep.c +++ b/tools/fdtgrep.c @@ -213,7 +213,7 @@ static void utilfdt_print_data(const char *data, int len) } else { printf(" = ["); for (i = 0; i < len; i++) - printf("%02x%s", *p++, i < len - 1 ? " " : ""); + printf("%02x%s", (unsigned char)*p++, i < len - 1 ? " " : ""); printf("]"); } } From 511dcc3b902f5c2201bf5cf73276942d32e025ae Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 15 Feb 2021 20:01:44 +0800 Subject: [PATCH 006/120] mmc: pci: Fix Kconfig dependency The PCI MMC driver depends on the generic MMC SDHCI driver, otherwise it does not compile. Signed-off-by: Bin Meng Reviewed-by: Jaehoon Chung --- drivers/mmc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index f8ca52efb6..c34fce370e 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -311,6 +311,7 @@ config MMC_MXS config MMC_PCI bool "Support for MMC controllers on PCI" + depends on MMC_SDHCI help This selects PCI-based MMC controllers. If you have an MMC controller on a PCI bus, say Y here. From f0d04972973e1a35a8b4de997b0ec4aede526b7d Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 17 Feb 2021 17:04:06 +0800 Subject: [PATCH 007/120] test: cmd: setexpr: Fix a typo SETEXPR_TEST is for a new setexpr test, not mem. Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- test/cmd/setexpr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cmd/setexpr.c b/test/cmd/setexpr.c index 27113c083c..c537e89353 100644 --- a/test/cmd/setexpr.c +++ b/test/cmd/setexpr.c @@ -15,7 +15,7 @@ #define BUF_SIZE 0x100 -/* Declare a new mem test */ +/* Declare a new setexpr test */ #define SETEXPR_TEST(_name, _flags) UNIT_TEST(_name, _flags, setexpr_test) /* Test 'setexpr' command with simply setting integers */ From 3a03553aaaaee68c0807867a7ff518146c19d10e Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 17 Feb 2021 17:31:47 +0800 Subject: [PATCH 008/120] test: print_ut: Fix potential build error This files uses the macro U_BOOT_CMD which is defined in command.h, but command.h is conditionally included. Fix it. Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- test/print_ut.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/print_ut.c b/test/print_ut.c index a456a449ef..61ea432e46 100644 --- a/test/print_ut.c +++ b/test/print_ut.c @@ -6,8 +6,8 @@ #define DEBUG #include -#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) #include +#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) #include #endif #include From b1b6efc343a5c7b64bdbc0493a16b5e36fb846fb Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 17 Feb 2021 18:41:43 +0800 Subject: [PATCH 009/120] patman: Use less for help file, if available It's convenient to be able to scroll up in `patman -H`. Signed-off-by: Nicolas Boichat Reviewed-by: Simon Glass --- tools/patman/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/patman/main.py b/tools/patman/main.py index 4e0a3533e9..04e37a5931 100755 --- a/tools/patman/main.py +++ b/tools/patman/main.py @@ -9,6 +9,7 @@ from argparse import ArgumentParser import os import re +import shutil import sys import traceback import unittest @@ -170,6 +171,8 @@ elif args.cmd == 'send': elif args.full_help: pager = os.getenv('PAGER') + if not pager: + pager = shutil.which('less') if not pager: pager = 'more' fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), From 4a2a78ca5c2d70ff1e94a74ff2a96a440379dc01 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 18 Feb 2021 13:01:35 +0100 Subject: [PATCH 010/120] sandbox: enable cros-ec-keyb in test.dtb Currently keyboard input fails in the GUI window opened by ./u-boot -T -l Add the missing include to test.dts. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2600360224..f15d1ebabc 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1402,3 +1402,4 @@ }; #include "sandbox_pmic.dtsi" +#include "cros-ec-keyboard.dtsi" From c803838fa85d681a0ea60409fc81f596a2e9c07c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 20 Feb 2021 10:41:22 +0100 Subject: [PATCH 011/120] dm: error handling dev_get_dma_range() goto after return has not effect. Calling of_node_put() in case of some errors and not for others is inconsistent. Fixes: 51bdb50904b ("dm: Introduce xxx_get_dma_range()") Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/core/of_addr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c index 5bc6ca1de0..b3e384d2ee 100644 --- a/drivers/core/of_addr.c +++ b/drivers/core/of_addr.c @@ -372,7 +372,7 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, bus_node->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { printf("Bad cell count for %s\n", of_node_full_name(dev)); - return -EINVAL; + ret = -EINVAL; goto out_parent; } @@ -380,7 +380,7 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, bus_node->count_cells(parent, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { printf("Bad cell count for %s\n", of_node_full_name(parent)); - return -EINVAL; + ret = -EINVAL; goto out_parent; } From 2a38e712652f678c9e6141f5d83bc4fdfafe161f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 4 Mar 2021 18:28:37 +0000 Subject: [PATCH 012/120] sandbox: add FAT to the list of usable env drivers Add the FAT environment driver to the priority list. When testing the UEFI sub-system the EFI system partition is formatted with FAT so it is reasonable to store the environment there. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- board/sandbox/sandbox.c | 1 + 1 file changed, 1 insertion(+) diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 3c63d45dd3..902b99ed50 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -48,6 +48,7 @@ unsigned long timer_read_counter(void) static enum env_location env_locations[] = { ENVL_NOWHERE, ENVL_EXT4, + ENVL_FAT, }; enum env_location env_get_location(enum env_operation op, int prio) From c58662fc65046770506862de060c92312d8593b3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:50 -0700 Subject: [PATCH 013/120] dtoc: Scan drivers for available information At present we simply record the name of a driver parsed from its implementation file. We also need to get the uclass and a few other things so we can instantiate devices at build time. Add support for collecting this information. This requires parsing each driver file. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 194 ++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 131 +++++++++++++++++++++++- 2 files changed, 311 insertions(+), 14 deletions(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index f63c9fc166..095fb6d476 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -54,15 +54,30 @@ class Driver: Attributes: name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' + fname: Filename where the driver was found + uclass_id: Name of uclass, e.g. 'UCLASS_I2C' + compat: Driver data for each compatible string: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + fname: Filename where the driver was found + priv (str): struct name of the priv_auto member, e.g. 'serial_priv' """ - def __init__(self, name): + def __init__(self, name, fname): self.name = name + self.fname = fname + self.uclass_id = None + self.compat = None + self.priv = '' def __eq__(self, other): - return self.name == other.name + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.compat == other.compat and + self.priv == other.priv) def __repr__(self): - return "Driver(name='%s')" % self.name + return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % + (self.name, self.uclass_id, self.compat, self.priv)) class Scanner: @@ -81,6 +96,12 @@ class Scanner: _warning_disabled: true to disable warnings about driver names not found _drivers_additional (list or str): List of additional drivers to use during scanning + _of_match: Dict holding information about compatible strings + key: Name of struct udevice_id variable + value: Dict of compatible info in that variable: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + _compat_to_driver: Maps compatible strings to Driver """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -94,6 +115,8 @@ class Scanner: self._driver_aliases = {} self._drivers_additional = drivers_additional or [] self._warning_disabled = warning_disabled + self._of_match = {} + self._compat_to_driver = {} def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -131,10 +154,163 @@ class Scanner: return compat_list_c[0], compat_list_c[1:] + @classmethod + def _get_re_for_member(cls, member): + """_get_re_for_member: Get a compiled regular expression + + Args: + member (str): Struct member name, e.g. 'priv_auto' + + Returns: + re.Pattern: Compiled regular expression that parses: + + .member = sizeof(struct fred), + + and returns "fred" as group 1 + """ + return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member) + + def _parse_driver(self, fname, buff): + """Parse a C file to extract driver information contained within + + This parses U_BOOT_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + + Raises: + ValueError: Compatible variable is mentioned in .of_match in + U_BOOT_DRIVER() but not found in the file + """ + # Dict holding information about compatible strings collected in this + # function so far + # key: Name of struct udevice_id variable + # value: Dict of compatible info in that variable: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + of_match = {} + + # Dict holding driver information collected in this function so far + # key: Driver name (C name as in U_BOOT_DRIVER(xxx)) + # value: Driver + drivers = {} + + # Collect the driver info + driver = None + re_driver = re.compile(r'U_BOOT_DRIVER\((.*)\)') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Collect the compatible string, e.g. 'rockchip,rk3288-grf' + compat = None + re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*' + r'(,\s*.data\s*=\s*(\S*))?\s*},') + + # This is a dict of compatible strings that were found: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + compat_dict = {} + + # Holds the var nane of the udevice_id list, e.g. + # 'rk3288_syscon_ids_noc' in + # static const struct udevice_id rk3288_syscon_ids_noc[] = { + ids_name = None + re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=') + + # Matches the references to the udevice_id list + re_of_match = re.compile( + r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') + + # Matches the struct name for priv + re_priv = self._get_re_for_member('priv_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If this line contains U_BOOT_DRIVER()... + if driver: + m_id = re_id.search(line) + m_of_match = re_of_match.search(line) + m_priv = re_priv.match(line) + if m_priv: + driver.priv = m_priv.group(1) + elif m_id: + driver.uclass_id = m_id.group(1) + elif m_of_match: + compat = m_of_match.group(2) + elif '};' in line: + if driver.uclass_id and compat: + if compat not in of_match: + raise ValueError( + "%s: Unknown compatible var '%s' (found: %s)" % + (fname, compat, ','.join(of_match.keys()))) + driver.compat = of_match[compat] + + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for compat_id in of_match[compat]: + old = self._compat_to_driver.get(compat_id) + if not old or driver.name < old.name: + self._compat_to_driver[compat_id] = driver + drivers[driver.name] = driver + else: + # The driver does not have a uclass or compat string. + # The first is required but the second is not, so just + # ignore this. + pass + driver = None + ids_name = None + compat = None + compat_dict = {} + + elif ids_name: + compat_m = re_compat.search(line) + if compat_m: + compat_dict[compat_m.group(1)] = compat_m.group(3) + elif '};' in line: + of_match[ids_name] = compat_dict + ids_name = None + elif driver_match: + driver_name = driver_match.group(1) + driver = Driver(driver_name, fname) + else: + ids_m = re_ids.search(line) + if ids_m: + ids_name = ids_m.group(1) + + # Make the updates based on what we found + self._drivers.update(drivers) + self._of_match.update(of_match) + def scan_driver(self, fname): """Scan a driver file to build a list of driver names and aliases - This procedure will populate self._drivers and self._driver_aliases + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name Args fname: Driver filename to scan @@ -147,12 +323,10 @@ class Scanner: print("Skipping file '%s' due to unicode error" % fname) return - # The following re will search for driver names declared as - # U_BOOT_DRIVER(driver_name) - drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) - - for driver in drivers: - self._drivers[driver] = Driver(driver) + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'U_BOOT_DRIVER' in buff: + self._parse_driver(fname, buff) # The following re will search for driver aliases declared as # DM_DRIVER_ALIAS(alias, driver_name) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 7d686530d6..25e4866f20 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -17,6 +17,20 @@ from dtoc import src_scan from patman import test_util from patman import tools +OUR_PATH = os.path.dirname(os.path.realpath(__file__)) + +class FakeNode: + """Fake Node object for testing""" + def __init__(self): + self.name = None + self.props = {} + +class FakeProp: + """Fake Prop object for testing""" + def __init__(self): + self.name = None + self.value = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -69,10 +83,22 @@ class TestSrcScan(unittest.TestCase): def test_driver(self): """Test the Driver class""" - drv1 = src_scan.Driver('fred') - drv2 = src_scan.Driver('mary') - drv3 = src_scan.Driver('fred') - self.assertEqual("Driver(name='fred')", str(drv1)) + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv1 = src_scan.Driver('fred', 'fred.c') + drv2 = src_scan.Driver('mary', 'mary.c') + drv3 = src_scan.Driver('fred', 'fred.c') + drv1.uclass_id = i2c + drv1.compat = compat + drv2.uclass_id = i2c + drv2.compat = compat + drv3.uclass_id = i2c + drv3.compat = compat + self.assertEqual( + "Driver(name='fred', uclass_id='I2C_UCLASS', " + "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', " + "'rockchip,rk3288-srf': None}, priv=)", str(drv1)) self.assertEqual(drv1, drv3) self.assertNotEqual(drv1, drv2) self.assertNotEqual(drv2, drv3) @@ -105,3 +131,100 @@ class TestSrcScan(unittest.TestCase): mocked.mock_calls[1]) finally: shutil.rmtree(indir) + + def test_scan(self): + """Test scanning of a driver""" + fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c') + buff = tools.ReadFile(fname, False) + scan = src_scan.Scanner(None, False, None) + scan._parse_driver(fname, buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114', + 'nvidia,tegra20-i2c': 'TYPE_STD', + 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat) + self.assertEqual('i2c_bus', drv.priv) + self.assertEqual(1, len(scan._drivers)) + + def test_normalized_name(self): + """Test operation of get_normalized_compat_name()""" + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'rockchip,rk3288-grf' + node = FakeNode() + node.props = {'compatible': prop} + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + self.assertEqual( + 'WARNING: the driver rockchip_rk3288_grf was not found in the driver list', + stdout.getvalue().strip()) + + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv = src_scan.Driver('fred', 'fred.c') + drv.uclass_id = i2c + drv.compat = compat + scan._drivers['rockchip_rk3288_grf'] = drv + + scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf' + + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + + prop.value = 'rockchip,rk3288-srf' + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual(['rockchip_rk3288_srf'], aliases) + + def test_scan_errors(self): + """Test detection of scanning errors""" + buff = ''' +static const struct udevice_id tegra_i2c_ids2[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = tegra_i2c_ids, +}; +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_driver('file.c', buff) + self.assertIn( + "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)", + str(exc.exception)) + + def test_of_match(self): + """Test detection of of_match_ptr() member""" + buff = ''' +static const struct udevice_id tegra_i2c_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = of_match_ptr(tegra_i2c_ids), +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name) From a32eb7dca7c3ea8d23e1bc0ce927ad2a00d8bf45 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:51 -0700 Subject: [PATCH 014/120] dtoc: Save scan information across test runs At present most of the tests scan the U-Boot source tree as part of their run. This information does not change across tests, so we can save time by remembering it. Add a way to set up this information and use it for each test, taking a copy first, so as not to mess up the original. This reduces the run time from about 1.6 seconds to 1.5 seconds on my machine. For code coverage (which cannot run in parallel), it reduces from 33 seconds to 5. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 11 ++++++++--- tools/dtoc/main.py | 2 ++ tools/dtoc/test_dtoc.py | 40 +++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index b7abaed67a..e9be5985c7 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -670,7 +670,8 @@ OUTPUT_FILES = { def run_steps(args, dtb_file, include_disabled, output, output_dirs, - warning_disabled=False, drivers_additional=None, basedir=None): + warning_disabled=False, drivers_additional=None, basedir=None, + scan=None): """Run all the steps of the dtoc tool Args: @@ -687,6 +688,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, scanning basedir (str): Base directory of U-Boot source code. Defaults to the grandparent of this file's directory + scan (src_src.Scanner): Scanner from a previous run. This can help speed + up tests. Use None for normal operation + Raises: ValueError: if args has no command, or an unknown command """ @@ -695,9 +699,10 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') - scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + if not scan: + scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + scan.scan_drivers() plat = DtbPlatdata(scan, dtb_file, include_disabled) - scan.scan_drivers() plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index b0ad0f3952..355b1e6277 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -53,6 +53,8 @@ def run_tests(processes, args): sys.argv = [sys.argv[0]] test_name = args and args[0] or None + test_dtoc.setup() + test_util.RunTestSuites( result, debug=True, verbosity=1, test_preserve_dirs=False, processes=processes, test_name=test_name, toolpath=[], diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d961d67b8f..6865d949a0 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -10,6 +10,7 @@ tool. """ import collections +import copy import glob import os import struct @@ -20,6 +21,7 @@ from dtb_platdata import tab_to from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util +from dtoc import src_scan from dtoc.src_scan import conv_name_to_c from dtoc.src_scan import get_compat_name from patman import test_util @@ -53,6 +55,9 @@ C_HEADER = '''/* #include ''' +# Scanner saved from a previous run of the tests (to speed things up) +saved_scan = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -71,6 +76,19 @@ def get_dtb_file(dts_fname, capture_stderr=False): capture_stderr=capture_stderr) +def setup(): + global saved_scan + + # Disable warnings so that calls to get_normalized_compat_name() will not + # output things. + saved_scan = src_scan.Scanner(None, True, False) + saved_scan.scan_drivers() + +def copy_scan(): + """Get a copy of saved_scan so that each test can start clean""" + return copy.deepcopy(saved_scan) + + class TestDtoc(unittest.TestCase): """Tests for dtoc""" @classmethod @@ -120,7 +138,8 @@ class TestDtoc(unittest.TestCase): dtb_file (str): Filename of .dtb file output (str): Filename of output file """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], True) + dtb_platdata.run_steps(args, dtb_file, False, output, [], True, + None, None, scan=copy_scan()) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -175,7 +194,9 @@ class TestDtoc(unittest.TestCase): """Test output from a device tree file with no nodes""" dtb_file = get_dtb_file('dtoc_test_empty.dts') output = tools.GetOutputFilename('output') - self.run_test(['struct'], dtb_file, output) + + # Run this one without saved_scan to complete test coverage + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], True) with open(output) as infile: lines = infile.read().splitlines() self.assertEqual(HEADER.splitlines(), lines) @@ -343,7 +364,8 @@ U_BOOT_DRVINFO(gpios_at_0) = { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, []) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -352,7 +374,8 @@ struct dtd_invalid { ''', data) with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, []) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -502,7 +525,8 @@ U_BOOT_DRVINFO(phandle_target) = { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -903,7 +927,8 @@ U_BOOT_DRVINFO(spl_test2) = { def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True) + dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True, + scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception)) @@ -919,7 +944,8 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames)) - dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True) + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True, + scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames)) From 36b2220cbd62c7829b8e845777318f323a963e47 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:52 -0700 Subject: [PATCH 015/120] dtoc: Ignore unwanted files when scanning for drivers We should ignore anything in the .git directory or any of the build-sandbox, etc. directories created by 'make check'. These can confuse dtoc. Update the code to ignore these. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 5 +++++ tools/dtoc/test_src_scan.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 095fb6d476..761164a9c9 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -345,6 +345,11 @@ class Scanner: This procedure will populate self._drivers and self._driver_aliases """ for (dirpath, _, filenames) in os.walk(self._basedir): + rel_path = dirpath[len(self._basedir):] + if rel_path.startswith('/'): + rel_path = rel_path[1:] + if rel_path.startswith('build') or rel_path.startswith('.git'): + continue for fname in filenames: if not fname.endswith('.c'): continue diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 25e4866f20..ada49fb704 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -117,7 +117,9 @@ class TestSrcScan(unittest.TestCase): fname_list = [] add_file('fname.c') + add_file('.git/ignoreme.c') add_file('dir/fname2.c') + add_file('build-sandbox/ignoreme2.c') # Mock out scan_driver and check that it is called with the # expected files @@ -127,7 +129,8 @@ class TestSrcScan(unittest.TestCase): self.assertEqual(2, len(mocked.mock_calls)) self.assertEqual(mock.call(fname_list[0]), mocked.mock_calls[0]) - self.assertEqual(mock.call(fname_list[1]), + # .git file should be ignored + self.assertEqual(mock.call(fname_list[2]), mocked.mock_calls[1]) finally: shutil.rmtree(indir) From c8b19b0694014ac4423558f4e65937791fd6d574 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:53 -0700 Subject: [PATCH 016/120] dtoc: Collect priv/plat struct info from drivers In order to output variables to hold the priv/plat information used by each device, dtoc needs to know the struct for each. With this, it can declare this at build time: u8 xxx_priv [sizeof(struct )]; Collect the various struct names from the drivers. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 25 +++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 761164a9c9..ff3ab409e4 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -61,6 +61,11 @@ class Driver: value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None fname: Filename where the driver was found priv (str): struct name of the priv_auto member, e.g. 'serial_priv' + plat (str): struct name of the plat_auto member, e.g. 'serial_plat' + child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' """ def __init__(self, name, fname): self.name = name @@ -68,12 +73,16 @@ class Driver: self.uclass_id = None self.compat = None self.priv = '' + self.plat = '' + self.child_priv = '' + self.child_plat = '' def __eq__(self, other): return (self.name == other.name and self.uclass_id == other.uclass_id and self.compat == other.compat and - self.priv == other.priv) + self.priv == other.priv and + self.plat == other.plat) def __repr__(self): return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % @@ -230,8 +239,11 @@ class Scanner: re_of_match = re.compile( r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') - # Matches the struct name for priv + # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') + re_plat = self._get_re_for_member('plat_auto') + re_child_priv = self._get_re_for_member('per_child_auto') + re_child_plat = self._get_re_for_member('per_child_plat_auto') prefix = '' for line in buff.splitlines(): @@ -250,8 +262,17 @@ class Scanner: m_id = re_id.search(line) m_of_match = re_of_match.search(line) m_priv = re_priv.match(line) + m_plat = re_plat.match(line) + m_cplat = re_child_plat.match(line) + m_cpriv = re_child_priv.match(line) if m_priv: driver.priv = m_priv.group(1) + elif m_plat: + driver.plat = m_plat.group(1) + elif m_cplat: + driver.child_plat = m_cplat.group(1) + elif m_cpriv: + driver.child_priv = m_cpriv.group(1) elif m_id: driver.uclass_id = m_id.group(1) elif m_of_match: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index ada49fb704..62dea2a961 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -231,3 +231,35 @@ U_BOOT_DRIVER(i2c_tegra) = { self.assertIn('i2c_tegra', scan._drivers) drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) + + def test_priv(self): + """Test collection of struct info from drivers""" + buff = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(testing) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + .priv_auto = sizeof(struct some_priv), + .plat_auto = sizeof(struct some_plat), + .per_child_auto = sizeof(struct some_cpriv), + .per_child_plat_auto = sizeof(struct some_cplat), +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('testing', scan._drivers) + drv = scan._drivers['testing'] + self.assertEqual('testing', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('some_plat', drv.plat) + self.assertEqual('some_cpriv', drv.child_priv) + self.assertEqual('some_cplat', drv.child_plat) + self.assertEqual(1, len(scan._drivers)) From 1a8b4b9d94b295f3dae06c72931a99d74fd19179 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:54 -0700 Subject: [PATCH 017/120] dtoc: Support scanning of uclasses Uclasses can have per-device private / platform data so dtoc needs to scan these drivers. This allows it to find out the size of this data so it can be allocated a build time. Add a parser for uclass information, similar to drivers. Keep a dict of the uclasses that were found. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 122 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_src_scan.py | 55 ++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index ff3ab409e4..3245d02e09 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -89,6 +89,43 @@ class Driver: (self.name, self.uclass_id, self.compat, self.priv)) +class UclassDriver: + """Holds information about a uclass driver + + Attributes: + name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C + uclass_id: Uclass ID, e.g. 'UCLASS_I2C' + priv: struct name of the private data, e.g. 'i2c_priv' + per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info' + per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip' + per_child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + per_child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' + """ + def __init__(self, name): + self.name = name + self.uclass_id = None + self.priv = '' + self.per_dev_priv = '' + self.per_dev_plat = '' + self.per_child_priv = '' + self.per_child_plat = '' + + def __eq__(self, other): + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.priv == other.priv) + + def __repr__(self): + return ("UclassDriver(name='%s', uclass_id='%s')" % + (self.name, self.uclass_id)) + + def __hash__(self): + # We can use the uclass ID since it is unique among uclasses + return hash(self.uclass_id) + + class Scanner: """Scanning of the U-Boot source tree @@ -111,6 +148,9 @@ class Scanner: key: Compatible string, e.g. 'rockchip,rk3288-grf' value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None _compat_to_driver: Maps compatible strings to Driver + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -126,6 +166,7 @@ class Scanner: self._warning_disabled = warning_disabled self._of_match = {} self._compat_to_driver = {} + self._uclass = {} def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -179,6 +220,85 @@ class Scanner: """ return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member) + def _parse_uclass_driver(self, fname, buff): + """Parse a C file to extract uclass driver information contained within + + This parses UCLASS_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following member: + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + """ + uc_drivers = {} + + # Collect the driver name and associated Driver + driver = None + re_driver = re.compile(r'UCLASS_DRIVER\((.*)\)') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Matches the header/size information for uclass-private data + re_priv = self._get_re_for_member('priv_auto') + + # Set up parsing for the auto members + re_per_device_priv = self._get_re_for_member('per_device_auto') + re_per_device_plat = self._get_re_for_member('per_device_plat_auto') + re_per_child_priv = self._get_re_for_member('per_child_auto') + re_per_child_plat = self._get_re_for_member('per_child_plat_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If we have seen UCLASS_DRIVER()... + if driver: + m_id = re_id.search(line) + m_priv = re_priv.match(line) + m_per_dev_priv = re_per_device_priv.match(line) + m_per_dev_plat = re_per_device_plat.match(line) + m_per_child_priv = re_per_child_priv.match(line) + m_per_child_plat = re_per_child_plat.match(line) + if m_id: + driver.uclass_id = m_id.group(1) + elif m_priv: + driver.priv = m_priv.group(1) + elif m_per_dev_priv: + driver.per_dev_priv = m_per_dev_priv.group(1) + elif m_per_dev_plat: + driver.per_dev_plat = m_per_dev_plat.group(1) + elif m_per_child_priv: + driver.per_child_priv = m_per_child_priv.group(1) + elif m_per_child_plat: + driver.per_child_plat = m_per_child_plat.group(1) + elif '};' in line: + if not driver.uclass_id: + raise ValueError( + "%s: Cannot parse uclass ID in driver '%s'" % + (fname, driver.name)) + uc_drivers[driver.uclass_id] = driver + driver = None + + elif driver_match: + driver_name = driver_match.group(1) + driver = UclassDriver(driver_name) + + self._uclass.update(uc_drivers) + def _parse_driver(self, fname, buff): """Parse a C file to extract driver information contained within @@ -348,6 +468,8 @@ class Scanner: # obtain driver information if 'U_BOOT_DRIVER' in buff: self._parse_driver(fname, buff) + if 'UCLASS_DRIVER' in buff: + self._parse_uclass_driver(fname, buff) # The following re will search for driver aliases declared as # DM_DRIVER_ALIAS(alias, driver_name) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 62dea2a961..641d6495de 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -7,6 +7,7 @@ This includes unit tests for scanning of the source code """ +import copy import os import shutil import tempfile @@ -263,3 +264,57 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) self.assertEqual(1, len(scan._drivers)) + + def test_uclass_scan(self): + """Test collection of uclass-driver info""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct some_priv), + .per_device_auto = sizeof(struct per_dev_priv), + .per_device_plat_auto = sizeof(struct per_dev_plat), + .per_child_auto = sizeof(struct per_child_priv), + .per_child_plat_auto = sizeof(struct per_child_plat), + .child_post_bind = i2c_child_post_bind, +}; + +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_uclass_driver('file.c', buff) + self.assertIn('UCLASS_I2C', scan._uclass) + drv = scan._uclass['UCLASS_I2C'] + self.assertEqual('i2c', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('per_dev_priv', drv.per_dev_priv) + self.assertEqual('per_dev_plat', drv.per_dev_plat) + self.assertEqual('per_child_priv', drv.per_child_priv) + self.assertEqual('per_child_plat', drv.per_child_plat) + self.assertEqual(1, len(scan._uclass)) + + drv2 = copy.deepcopy(drv) + self.assertEqual(drv, drv2) + drv2.priv = 'other_priv' + self.assertNotEqual(drv, drv2) + + # The hashes only depend on the uclass ID, so should be equal + self.assertEqual(drv.__hash__(), drv2.__hash__()) + + self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')", + str(drv)) + + def test_uclass_scan_errors(self): + """Test detection of uclass scanning errors""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .name = "i2c", +}; + +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_uclass_driver('file.c', buff) + self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'", + str(exc.exception)) From acf5cb88b403540408e87d078d916269df371584 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:55 -0700 Subject: [PATCH 018/120] dtoc: Support scanning of structs in header files Drivers can have private / platform data contained in structs and these struct definitions are generally kept in header files. In order to generate build-time devices, dtoc needs to generate code that declares the data contained in those structs. This generated code must include the relevant header file, to avoid a build error. We need a way for dtoc to scan header files for struct definitions. Then, when it wants to generate code that uses a struct, it can make sure it includes the correct header file, first. Add a parser for struct information, similar to drivers. Keep a dict of the structs that were found. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 86 +++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 45 +++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 3245d02e09..bf3e5de9b1 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -126,6 +126,22 @@ class UclassDriver: return hash(self.uclass_id) +class Struct: + """Holds information about a struct definition + + Attributes: + name: Struct name, e.g. 'fred' if the struct is 'struct fred' + fname: Filename containing the struct, in a format that C files can + include, e.g. 'asm/clk.h' + """ + def __init__(self, name, fname): + self.name = name + self.fname =fname + + def __repr__(self): + return ("Struct(name='%s', fname='%s')" % (self.name, self.fname)) + + class Scanner: """Scanning of the U-Boot source tree @@ -151,6 +167,9 @@ class Scanner: _uclass: Dict of uclass information key: uclass name, e.g. 'UCLASS_I2C' value: UClassDriver + _structs: Dict of all structs found in U-Boot: + key: Name of struct + value: Struct object """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -167,6 +186,7 @@ class Scanner: self._of_match = {} self._compat_to_driver = {} self._uclass = {} + self._structs = {} def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -204,6 +224,41 @@ class Scanner: return compat_list_c[0], compat_list_c[1:] + def _parse_structs(self, fname, buff): + """Parse a H file to extract struct definitions contained within + + This parses 'struct xx {' definitions to figure out what structs this + header defines. + + Args: + buff (str): Contents of file + fname (str): Filename (to use when printing errors) + """ + structs = {} + + re_struct = re.compile('^struct ([a-z0-9_]+) {$') + re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)') + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + m_struct = re_struct.match(line) + if m_struct: + name = m_struct.group(1) + include_dir = os.path.join(self._basedir, 'include') + rel_fname = os.path.relpath(fname, include_dir) + m_asm = re_asm.match(rel_fname) + if m_asm: + rel_fname = 'asm/' + m_asm.group(1) + structs[name] = Struct(name, rel_fname) + self._structs.update(structs) + @classmethod def _get_re_for_member(cls, member): """_get_re_for_member: Get a compiled regular expression @@ -482,6 +537,29 @@ class Scanner: continue self._driver_aliases[alias[1]] = alias[0] + def scan_header(self, fname): + """Scan a header file to build a list of struct definitions + + It updates the following members: + _structs - updated with new Struct records for each struct found + in the file + + Args + fname: header filename to scan + """ + with open(fname, encoding='utf-8') as inf: + try: + buff = inf.read() + except UnicodeDecodeError: + # This seems to happen on older Python versions + print("Skipping file '%s' due to unicode error" % fname) + return + + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'struct' in buff: + self._parse_structs(fname, buff) + def scan_drivers(self): """Scan the driver folders to build a list of driver names and aliases @@ -494,9 +572,11 @@ class Scanner: if rel_path.startswith('build') or rel_path.startswith('.git'): continue for fname in filenames: - if not fname.endswith('.c'): - continue - self.scan_driver(dirpath + '/' + fname) + pathname = dirpath + '/' + fname + if fname.endswith('.c'): + self.scan_driver(pathname) + elif fname.endswith('.h'): + self.scan_header(pathname) for fname in self._drivers_additional: if not isinstance(fname, str) or len(fname) == 0: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 641d6495de..a0b0e097eb 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -318,3 +318,48 @@ UCLASS_DRIVER(i2c) = { scan._parse_uclass_driver('file.c', buff) self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'", str(exc.exception)) + + def test_struct_scan(self): + """Test collection of struct info""" + buff = ''' +/* some comment */ +struct some_struct1 { + struct i2c_msg *msgs; + uint nmsgs; +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._basedir = os.path.join(OUR_PATH, '..', '..') + scan._parse_structs('arch/arm/include/asm/file.h', buff) + self.assertIn('some_struct1', scan._structs) + struc = scan._structs['some_struct1'] + self.assertEqual('some_struct1', struc.name) + self.assertEqual('asm/file.h', struc.fname) + + buff = ''' +/* another comment */ +struct another_struct { + int speed_hz; + int max_transaction_bytes; +}; +''' + scan._parse_structs('include/file2.h', buff) + self.assertIn('another_struct', scan._structs) + struc = scan._structs['another_struct'] + self.assertEqual('another_struct', struc.name) + self.assertEqual('file2.h', struc.fname) + + self.assertEqual(2, len(scan._structs)) + + self.assertEqual("Struct(name='another_struct', fname='file2.h')", + str(struc)) + + def test_struct_scan_errors(self): + """Test scanning a header file with an invalid unicode file""" + output = tools.GetOutputFilename('output.h') + tools.WriteFile(output, b'struct this is a test \x81 of bad unicode') + + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + scan.scan_header(output) + self.assertIn('due to unicode error', stdout.getvalue()) From dff51a524c873673fc19f31927ba25445784fb35 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:56 -0700 Subject: [PATCH 019/120] dtoc: Move test files into a test/ directory It is confusing to have the test files in the same places as the implementation. Move them into a separate directory. Add a helper function for test_dtoc, to avoid repeating the same path. Signed-off-by: Simon Glass --- tools/dtoc/{ => test}/dtoc_test.dts | 0 tools/dtoc/{ => test}/dtoc_test_add_prop.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32_64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64_32.dts | 0 tools/dtoc/{ => test}/dtoc_test_aliases.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts | 0 .../{ => test}/dtoc_test_driver_alias.dts | 0 tools/dtoc/{ => test}/dtoc_test_empty.dts | 0 .../{ => test}/dtoc_test_invalid_driver.dts | 0 tools/dtoc/{ => test}/dtoc_test_phandle.dts | 0 .../dtoc/{ => test}/dtoc_test_phandle_bad.dts | 0 .../{ => test}/dtoc_test_phandle_bad2.dts | 0 .../{ => test}/dtoc_test_phandle_cd_gpios.dts | 0 .../{ => test}/dtoc_test_phandle_reorder.dts | 0 .../{ => test}/dtoc_test_phandle_single.dts | 0 .../{ => test}/dtoc_test_scan_drivers.cxx | 0 tools/dtoc/{ => test}/dtoc_test_simple.dts | 0 tools/dtoc/test_dtoc.py | 2 +- tools/dtoc/test_fdt.py | 31 +++++++++++++------ tools/dtoc/test_src_scan.py | 3 +- 23 files changed, 24 insertions(+), 12 deletions(-) rename tools/dtoc/{ => test}/dtoc_test.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_add_prop.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32_64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64_32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_aliases.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_driver_alias.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_empty.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_invalid_driver.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_cd_gpios.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_reorder.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_single.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_scan_drivers.cxx (100%) rename tools/dtoc/{ => test}/dtoc_test_simple.dts (100%) diff --git a/tools/dtoc/dtoc_test.dts b/tools/dtoc/test/dtoc_test.dts similarity index 100% rename from tools/dtoc/dtoc_test.dts rename to tools/dtoc/test/dtoc_test.dts diff --git a/tools/dtoc/dtoc_test_add_prop.dts b/tools/dtoc/test/dtoc_test_add_prop.dts similarity index 100% rename from tools/dtoc/dtoc_test_add_prop.dts rename to tools/dtoc/test/dtoc_test_add_prop.dts diff --git a/tools/dtoc/dtoc_test_addr32.dts b/tools/dtoc/test/dtoc_test_addr32.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr32.dts rename to tools/dtoc/test/dtoc_test_addr32.dts diff --git a/tools/dtoc/dtoc_test_addr32_64.dts b/tools/dtoc/test/dtoc_test_addr32_64.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr32_64.dts rename to tools/dtoc/test/dtoc_test_addr32_64.dts diff --git a/tools/dtoc/dtoc_test_addr64.dts b/tools/dtoc/test/dtoc_test_addr64.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr64.dts rename to tools/dtoc/test/dtoc_test_addr64.dts diff --git a/tools/dtoc/dtoc_test_addr64_32.dts b/tools/dtoc/test/dtoc_test_addr64_32.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr64_32.dts rename to tools/dtoc/test/dtoc_test_addr64_32.dts diff --git a/tools/dtoc/dtoc_test_aliases.dts b/tools/dtoc/test/dtoc_test_aliases.dts similarity index 100% rename from tools/dtoc/dtoc_test_aliases.dts rename to tools/dtoc/test/dtoc_test_aliases.dts diff --git a/tools/dtoc/dtoc_test_bad_reg.dts b/tools/dtoc/test/dtoc_test_bad_reg.dts similarity index 100% rename from tools/dtoc/dtoc_test_bad_reg.dts rename to tools/dtoc/test/dtoc_test_bad_reg.dts diff --git a/tools/dtoc/dtoc_test_bad_reg2.dts b/tools/dtoc/test/dtoc_test_bad_reg2.dts similarity index 100% rename from tools/dtoc/dtoc_test_bad_reg2.dts rename to tools/dtoc/test/dtoc_test_bad_reg2.dts diff --git a/tools/dtoc/dtoc_test_driver_alias.dts b/tools/dtoc/test/dtoc_test_driver_alias.dts similarity index 100% rename from tools/dtoc/dtoc_test_driver_alias.dts rename to tools/dtoc/test/dtoc_test_driver_alias.dts diff --git a/tools/dtoc/dtoc_test_empty.dts b/tools/dtoc/test/dtoc_test_empty.dts similarity index 100% rename from tools/dtoc/dtoc_test_empty.dts rename to tools/dtoc/test/dtoc_test_empty.dts diff --git a/tools/dtoc/dtoc_test_invalid_driver.dts b/tools/dtoc/test/dtoc_test_invalid_driver.dts similarity index 100% rename from tools/dtoc/dtoc_test_invalid_driver.dts rename to tools/dtoc/test/dtoc_test_invalid_driver.dts diff --git a/tools/dtoc/dtoc_test_phandle.dts b/tools/dtoc/test/dtoc_test_phandle.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle.dts rename to tools/dtoc/test/dtoc_test_phandle.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad.dts b/tools/dtoc/test/dtoc_test_phandle_bad.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_bad.dts rename to tools/dtoc/test/dtoc_test_phandle_bad.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad2.dts b/tools/dtoc/test/dtoc_test_phandle_bad2.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_bad2.dts rename to tools/dtoc/test/dtoc_test_phandle_bad2.dts diff --git a/tools/dtoc/dtoc_test_phandle_cd_gpios.dts b/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_cd_gpios.dts rename to tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts diff --git a/tools/dtoc/dtoc_test_phandle_reorder.dts b/tools/dtoc/test/dtoc_test_phandle_reorder.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_reorder.dts rename to tools/dtoc/test/dtoc_test_phandle_reorder.dts diff --git a/tools/dtoc/dtoc_test_phandle_single.dts b/tools/dtoc/test/dtoc_test_phandle_single.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_single.dts rename to tools/dtoc/test/dtoc_test_phandle_single.dts diff --git a/tools/dtoc/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx similarity index 100% rename from tools/dtoc/dtoc_test_scan_drivers.cxx rename to tools/dtoc/test/dtoc_test_scan_drivers.cxx diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts similarity index 100% rename from tools/dtoc/dtoc_test_simple.dts rename to tools/dtoc/test/dtoc_test_simple.dts diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 6865d949a0..523f0a923e 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -72,7 +72,7 @@ def get_dtb_file(dts_fname, capture_stderr=False): Returns: str: Filename of compiled file in output directory """ - return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, dts_fname), + return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, 'test', dts_fname), capture_stderr=capture_stderr) diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index e8fbbd5d10..1c3a8a2ab1 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -48,6 +48,17 @@ def _GetPropertyValue(dtb, node, prop_name): data = dtb.GetContents()[offset:offset + len(prop.value)] return prop, [chr(x) for x in data] +def find_dtb_file(dts_fname): + """Locate a test file in the test/ directory + + Args: + dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts] + + Returns: + str: Path to the test filename + """ + return os.path.join('tools/dtoc/test', dts_fname) + class TestFdt(unittest.TestCase): """Tests for the Fdt module @@ -64,7 +75,7 @@ class TestFdt(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) def testFdt(self): """Test that we can open an Fdt""" @@ -141,7 +152,7 @@ class TestNode(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') def testOffset(self): @@ -203,7 +214,7 @@ class TestNode(unittest.TestCase): def testLookupPhandle(self): """Test looking up a single phandle""" - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] target = dtb.GetNode('/phandle-target') @@ -222,7 +233,7 @@ class TestProp(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') self.fdt = self.dtb.GetFdtObj() @@ -230,7 +241,7 @@ class TestProp(unittest.TestCase): self.assertEqual(None, self.dtb.GetNode('missing')) def testPhandle(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] self.assertTrue(fdt32_to_cpu(prop.value) > 0) @@ -488,7 +499,7 @@ class TestFdtUtil(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') def testGetInt(self): @@ -531,7 +542,7 @@ class TestFdtUtil(unittest.TestCase): str(e.exception)) def testGetPhandleList(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks')) node = dtb.GetNode('/phandle-source') @@ -551,7 +562,7 @@ class TestFdtUtil(unittest.TestCase): self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0)) self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1)) - dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts') + dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts')) node1 = dtb2.GetNode('/test1') val = node1.props['reg'].value self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2)) @@ -565,7 +576,7 @@ class TestFdtUtil(unittest.TestCase): def testEnsureCompiled(self): """Test a degenerate case of this function (file already compiled)""" - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts') + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts')) self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb)) def testEnsureCompiledTmpdir(self): @@ -574,7 +585,7 @@ class TestFdtUtil(unittest.TestCase): old_outdir = tools.outdir tools.outdir= None tmpdir = tempfile.mkdtemp(prefix='test_fdt.') - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts', + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'), tmpdir) self.assertEqual(tmpdir, os.path.dirname(dtb)) shutil.rmtree(tmpdir) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index a0b0e097eb..a7eba3005e 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -59,7 +59,8 @@ class TestSrcScan(unittest.TestCase): def test_additional(self): """Test with additional drivers to scan""" scan = src_scan.Scanner( - None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) + None, True, + [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx']) scan.scan_drivers() self.assertIn('sandbox_gpio_alias2', scan._driver_aliases) self.assertEqual('sandbox_gpio', From f38161c5769ffd175f688dac6c3044f0e3e095ba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:57 -0700 Subject: [PATCH 020/120] dtoc: Rename sandbox_i2c_test and sandbox_pmic_test These have '_test' suffixes which are not present on the drivers in the source code. Drop the suffixes to avoid a mismatch when scanning. Signed-off-by: Simon Glass --- tools/dtoc/test/dtoc_test_simple.dts | 4 ++-- tools/dtoc/test_dtoc.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/dtoc/test/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts index 1c87b89192..d8ab8613ee 100644 --- a/tools/dtoc/test/dtoc_test_simple.dts +++ b/tools/dtoc/test/dtoc_test_simple.dts @@ -45,12 +45,12 @@ }; i2c@0 { - compatible = "sandbox,i2c-test"; + compatible = "sandbox,i2c"; u-boot,dm-pre-reloc; #address-cells = <1>; #size-cells = <0>; pmic@9 { - compatible = "sandbox,pmic-test"; + compatible = "sandbox,pmic"; u-boot,dm-pre-reloc; reg = <9>; low-power; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 523f0a923e..9049c2895f 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -207,9 +207,9 @@ class TestDtoc(unittest.TestCase): self.assertEqual(C_HEADER.splitlines() + [''], lines) struct_text = HEADER + ''' -struct dtd_sandbox_i2c_test { +struct dtd_sandbox_i2c { }; -struct dtd_sandbox_pmic_test { +struct dtd_sandbox_pmic { \tbool\t\tlow_power; \tfdt64_t\t\treg[2]; }; @@ -229,22 +229,22 @@ struct dtd_sandbox_spl_test { platdata_text = C_HEADER + ''' /* Node /i2c@0 index 0 */ -static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { +static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { -\t.name\t\t= "sandbox_i2c_test", +\t.name\t\t= "sandbox_i2c", \t.plat\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), \t.parent_idx\t= -1, }; /* Node /i2c@0/pmic@9 index 1 */ -static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = { +static struct dtd_sandbox_pmic dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, }; U_BOOT_DRVINFO(pmic_at_9) = { -\t.name\t\t= "sandbox_pmic_test", +\t.name\t\t= "sandbox_pmic", \t.plat\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), \t.parent_idx\t= 0, From 51d5d051fa419dc4bb66ce232708346859389b1c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:58 -0700 Subject: [PATCH 021/120] dtoc: Add some extra properties to nodes It is convenient to attach drivers, etc. to nodes so that we can use the Node object as the main data structure in this module. Add a function which adds the new properties, along with documentation. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e9be5985c7..8c36fbc68d 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -354,8 +354,44 @@ class DtbPlatdata(): self.scan_node(self._fdt.GetRoot(), valid_nodes) self._valid_nodes = sorted(valid_nodes, key=lambda x: conv_name_to_c(x.name)) + + def prepare_nodes(self): + """Add extra properties to the nodes we are using + + The following properties are added for use by dtoc: + idx: Index number of this node (0=first, etc.) + struct_name: Name of the struct dtd used by this node + var_name: C name for this node + child_devs: List of child devices for this node, each a None + child_refs: Dict of references for each child: + key: Position in child list (-1=head, 0=first, 1=second, ... + n-1=last, n=head) + seq: Sequence number of the device (unique within its uclass), or + -1 not not known yet + dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)' + driver: Driver record for this node, or None if not known + uclass: Uclass record for this node, or None if not known + uclass_seq: Position of this device within the uclass list (0=first, + n-1=last) + parent_seq: Position of this device within it siblings (0=first, + n-1=last) + parent_driver: Driver record of the node's parent, or None if none. + We don't use node.parent.driver since node.parent may not be in + the list of valid nodes + """ for idx, node in enumerate(self._valid_nodes): node.idx = idx + node.struct_name, _ = self._scan.get_normalized_compat_name(node) + node.var_name = conv_name_to_c(node.name) + node.child_devs = [] + node.child_refs = {} + node.seq = -1 + node.dev_ref = None + node.driver = None + node.uclass = None + node.uclass_seq = None + node.parent_seq = None + node.parent_driver = None @staticmethod def get_num_cells(node): @@ -705,6 +741,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() plat.scan_tree() + plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs() From e525fea211d18a84bc33c6f5842f14153924cf50 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:00:59 -0700 Subject: [PATCH 022/120] dtoc: Make use of node properties Now that we have these available, use them instead of recalculating things each time. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 45 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 8c36fbc68d..2ec22edfbf 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -470,7 +470,6 @@ class DtbPlatdata(): """ structs = self._struct_data for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) fields = {} # Get a list of all the valid properties in this node. @@ -478,9 +477,9 @@ class DtbPlatdata(): if name not in PROP_IGNORE_LIST and name[0] != '#': fields[name] = copy.deepcopy(prop) - # If we've seen this node_name before, update the existing struct. - if node_name in structs: - struct = structs[node_name] + # If we've seen this struct_name before, update the existing struct + if node.struct_name in structs: + struct = structs[node.struct_name] for name, prop in fields.items(): oldprop = struct.get(name) if oldprop: @@ -490,11 +489,10 @@ class DtbPlatdata(): # Otherwise store this as a new struct. else: - structs[node_name] = fields + structs[node.struct_name] = fields for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) - struct = structs[node_name] + struct = structs[node.struct_name] for name, prop in node.props.items(): if name not in PROP_IGNORE_LIST and name[0] != '#': prop.Widen(struct[name]) @@ -598,23 +596,22 @@ class DtbPlatdata(): self.buf(', '.join(vals[i:i + 8])) self.buf('}') - def _declare_device(self, var_name, struct_name, node_parent): + def _declare_device(self, node): """Add a device declaration to the output This declares a U_BOOT_DRVINFO() for the device being processed Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node_parent (Node): Parent of the node (or None if none) + node: Node to process """ - self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name) - self.buf('\t.name\t\t= "%s",\n' % struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) - self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) + self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name) + self.buf('\t.name\t\t= "%s",\n' % node.struct_name) + self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + self.buf('\t.plat_size\t= sizeof(%s%s),\n' % + (VAL_PREFIX, node.var_name)) idx = -1 - if node_parent and node_parent in self._valid_nodes: - idx = node_parent.idx + if node.parent and node.parent in self._valid_nodes: + idx = node.parent.idx self.buf('\t.parent_idx\t= %d,\n' % idx) self.buf('};\n') self.buf('\n') @@ -638,16 +635,14 @@ class DtbPlatdata(): self.buf(get_value(prop.type, prop.value)) self.buf(',\n') - def _output_values(self, var_name, struct_name, node): + def _output_values(self, node): """Output the definition of a device's struct values Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node (Node): Node being output + node (Node): Node to output """ self.buf('static struct %s%s %s%s = {\n' % - (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name)) for pname in sorted(node.props): self._output_prop(node, node.props[pname]) self.buf('};\n') @@ -658,12 +653,10 @@ class DtbPlatdata(): Args: node (fdt.Node): node to output """ - struct_name, _ = self._scan.get_normalized_compat_name(node) - var_name = conv_name_to_c(node.name) self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) - self._output_values(var_name, struct_name, node) - self._declare_device(var_name, struct_name, node.parent) + self._output_values(node) + self._declare_device(node) self.out(''.join(self.get_buf())) From fd471e2ce14342e7186cf9f95a82ce55a9bea6e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:00 -0700 Subject: [PATCH 023/120] dtoc: Process nodes to set up required properties Add logic to assign property values to nodes as required by dtoc. The references allow nodes to refer to each other in C code. The macros used by dtoc are not yet defined in driver model. They will be added along with the actual driver model implementation. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++ tools/dtoc/src_scan.py | 11 ++++++ tools/dtoc/test_dtoc.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 2ec22edfbf..ad71f703e5 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -647,6 +647,38 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n') + def process_nodes(self, need_drivers): + nodes_to_output = list(self._valid_nodes) + + for node in nodes_to_output: + node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name + driver = self._scan.get_driver(node.struct_name) + if not driver: + if not need_drivers: + continue + raise ValueError("Cannot parse/find driver for '%s'" % + node.struct_name) + node.driver = driver + parent_driver = None + if node.parent in self._valid_nodes: + parent_driver = self._scan.get_driver(node.parent.struct_name) + if not parent_driver: + if not need_drivers: + continue + raise ValueError( + "Cannot parse/find parent driver '%s' for '%s'" % + (node.parent.struct_name, node.struct_name)) + node.parent_seq = len(node.parent.child_devs) + node.parent.child_devs.append(node) + node.parent.child_refs[node.parent_seq] = \ + '&%s->sibling_node' % node.dev_ref + node.parent_driver = parent_driver + + for node in nodes_to_output: + ref = '&%s->child_head' % node.dev_ref + node.child_refs[-1] = ref + node.child_refs[len(node.child_devs)] = ref + def output_node(self, node): """Output the C code for a node @@ -731,6 +763,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if not scan: scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) scan.scan_drivers() + do_process = True + else: + do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() plat.scan_tree() @@ -739,6 +774,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() + if do_process: + plat.process_nodes(False) cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index bf3e5de9b1..504dac008d 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -188,6 +188,17 @@ class Scanner: self._uclass = {} self._structs = {} + def get_driver(self, name): + """Get a driver given its name + + Args: + name (str): Driver name + + Returns: + Driver: Driver or None if not found + """ + return self._drivers.get(name) + def get_normalized_compat_name(self, node): """Get a node's normalized compat name diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 9049c2895f..3e98e36312 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -953,3 +953,79 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) + + def setup_process_test(self): + """Set up a test of process_nodes() + + This uses saved_scan but returns a deep copy of it, so it is safe to + modify it in these tests + + Returns: + tuple: + DtbPlatdata: object to test + Scanner: scanner to use + """ + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy.deepcopy(saved_scan) + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + plat.scan_tree() + plat.prepare_nodes() + return plat, scan + + def test_process_nodes(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + pmic = scan._drivers['sandbox_pmic'] + i2c = scan._drivers['sandbox_i2c'] + self.assertEqual('DM_DEVICE_REF(pmic_at_9)', pmic_node.dev_ref) + self.assertEqual(pmic, pmic_node.driver) + self.assertEqual(i2c_node, pmic_node.parent) + self.assertEqual(i2c, pmic_node.parent_driver) + + # The pmic is the only child + self.assertEqual(pmic_node.parent_seq, 0) + self.assertEqual([pmic_node], i2c_node.child_devs) + + # Start and end of the list should be the child_head + ref = '&DM_DEVICE_REF(i2c_at_0)->child_head' + self.assertEqual( + {-1: ref, 0: '&DM_DEVICE_REF(pmic_at_9)->sibling_node', 1: ref}, + i2c_node.child_refs) + + def test_process_nodes_bad_parent(self): + # Pretend that i2c has a parent (the pmic) and delete that driver + plat, scan = self.setup_process_test() + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + del scan._drivers['sandbox_pmic'] + i2c_node.parent = pmic_node + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn( + "Cannot parse/find parent driver 'sandbox_pmic' for 'sandbox_i2c", + str(exc.exception)) + + def test_process_nodes_bad_node(self): + plat, scan = self.setup_process_test() + + # Now remove the pmic driver + del scan._drivers['sandbox_pmic'] + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find driver for 'sandbox_pmic", + str(exc.exception)) From b9319c4f9b103b6c5b10e8592471a29bd9645caf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:01 -0700 Subject: [PATCH 024/120] dtoc: Track nodes which are actually used Mark all nodes that are actually used, so we can perform extra checks on them. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 3 +++ tools/dtoc/src_scan.py | 25 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 11 +++++++++++ tools/dtoc/test_src_scan.py | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ad71f703e5..28669f3121 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -650,6 +650,9 @@ class DtbPlatdata(): def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes) + # Figure out which drivers we actually use + self._scan.mark_used(nodes_to_output) + for node in nodes_to_output: node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name driver = self._scan.get_driver(node.struct_name) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 504dac008d..1a02d41063 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -66,6 +66,7 @@ class Driver: e.g. 'pci_child_priv' child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' + used (bool): True if the driver is used by the structs being output """ def __init__(self, name, fname): self.name = name @@ -76,17 +77,19 @@ class Driver: self.plat = '' self.child_priv = '' self.child_plat = '' + self.used = False def __eq__(self, other): return (self.name == other.name and self.uclass_id == other.uclass_id and self.compat == other.compat and self.priv == other.priv and - self.plat == other.plat) + self.plat == other.plat and + self.used == other.used) def __repr__(self): - return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % - (self.name, self.uclass_id, self.compat, self.priv)) + return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" % + (self.name, self.used, self.uclass_id, self.compat, self.priv)) class UclassDriver: @@ -596,3 +599,19 @@ class Scanner: self.scan_driver(fname) else: self.scan_driver(self._basedir + '/' + fname) + + def mark_used(self, nodes): + """Mark the drivers associated with a list of nodes as 'used' + + This takes a list of nodes, finds the driver for each one and marks it + as used. + + Args: + nodes (list of None): Nodes that are in use + """ + # Figure out which drivers we actually use + for node in nodes: + struct_name, _ = self.get_normalized_compat_name(node) + driver = self._drivers.get(struct_name) + if driver: + driver.used = True diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 3e98e36312..d90ece205d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -1029,3 +1029,14 @@ U_BOOT_DRVINFO(spl_test2) = { plat.process_nodes(True) self.assertIn("Cannot parse/find driver for 'sandbox_pmic", str(exc.exception)) + + def test_process_nodes_used(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + pmic = scan._drivers['sandbox_pmic'] + self.assertTrue(pmic.used) + + gpio = scan._drivers['sandbox_gpio'] + self.assertFalse(gpio.used) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index a7eba3005e..ebdc12abc8 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -98,7 +98,7 @@ class TestSrcScan(unittest.TestCase): drv3.uclass_id = i2c drv3.compat = compat self.assertEqual( - "Driver(name='fred', uclass_id='I2C_UCLASS', " + "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', " "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', " "'rockchip,rk3288-srf': None}, priv=)", str(drv1)) self.assertEqual(drv1, drv3) From b00f0066e52413e0f8fd4a84681e6e27c4270c26 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:02 -0700 Subject: [PATCH 025/120] dtoc: Support tracking the phase of U-Boot U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. The latter does not use dtoc. In some rare cases different drivers are used for two phases. For example, in TPL it may not be necessary to use the full PCI subsystem, so a simple driver can be used instead. This works in the build system simply by compiling in one driver or the other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has no way of knowing which code is compiled in for which phase, since it does not inspect Makefiles or dependency graphs. So to make this work for dtoc, we need to be able to explicitly mark drivers with their phase. This is done by adding an empty macro to the driver. Add support for this in dtoc. Signed-off-by: Simon Glass --- include/dm/device.h | 16 ++++++++++++++++ tools/dtoc/dtb_platdata.py | 7 +++++-- tools/dtoc/main.py | 5 ++++- tools/dtoc/src_scan.py | 12 +++++++++++- tools/dtoc/test_dtoc.py | 16 ++++++++-------- tools/dtoc/test_src_scan.py | 3 +++ 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/include/dm/device.h b/include/dm/device.h index bb9faa0ed9..84df5b9e95 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -369,6 +369,22 @@ struct driver { */ #define DM_DRIVER_ALIAS(__name, __alias) +/** + * Declare a macro to indicate which phase of U-Boot this driver is fore. + * + * + * This macro produces no code but its information will be parsed by dtoc. The + * macro can be only be used once in a driver. Put it within the U_BOOT_DRIVER() + * declaration, e.g.: + * + * U_BOOT_DRIVER(cpu) = { + * .name = ... + * ... + * DM_PHASE(tpl) + * }; + */ +#define DM_PHASE(_phase) + /** * dev_get_plat() - Get the platform data for a device * diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 28669f3121..ef0454c890 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -733,7 +733,7 @@ OUTPUT_FILES = { } -def run_steps(args, dtb_file, include_disabled, output, output_dirs, +def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, warning_disabled=False, drivers_additional=None, basedir=None, scan=None): """Run all the steps of the dtoc tool @@ -746,6 +746,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, output_dirs (tuple of str): Directory to put C output files Directory to put H output files + phase: The phase of U-Boot that we are generating data for, e.g. 'spl' + or 'tpl'. None if not known warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during @@ -764,7 +766,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, raise ValueError('Must specify either output or output_dirs, not both') if not scan: - scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional, + phase) scan.scan_drivers() do_process = True else: diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 355b1e6277..15a8ff167a 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -85,6 +85,8 @@ parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', help='Select output filename') +parser.add_option('-p', '--phase', type=str, + help='set phase of U-Boot this invocation is for (spl/tpl)') parser.add_option('-P', '--processes', type=int, help='set number of processes to use for running tests') parser.add_option('-t', '--test', action='store_true', dest='test', @@ -104,4 +106,5 @@ elif options.test_coverage: else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, options.output, - [options.c_output_dir, options.h_output_dir]) + [options.c_output_dir, options.h_output_dir], + phase=options.phase) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 1a02d41063..2699153016 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -67,6 +67,7 @@ class Driver: child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' used (bool): True if the driver is used by the structs being output + phase (str): Which phase of U-Boot to use this driver """ def __init__(self, name, fname): self.name = name @@ -78,6 +79,7 @@ class Driver: self.child_priv = '' self.child_plat = '' self.used = False + self.phase = '' def __eq__(self, other): return (self.name == other.name and @@ -173,8 +175,10 @@ class Scanner: _structs: Dict of all structs found in U-Boot: key: Name of struct value: Struct object + _phase: The phase of U-Boot that we are generating data for, e.g. 'spl' + or 'tpl'. None if not known """ - def __init__(self, basedir, warning_disabled, drivers_additional): + def __init__(self, basedir, warning_disabled, drivers_additional, phase=''): """Set up a new Scanner """ if not basedir: @@ -190,6 +194,7 @@ class Scanner: self._compat_to_driver = {} self._uclass = {} self._structs = {} + self._phase = phase def get_driver(self, name): """Get a driver given its name @@ -428,6 +433,8 @@ class Scanner: re_of_match = re.compile( r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') + re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') + # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') re_plat = self._get_re_for_member('plat_auto') @@ -454,6 +461,7 @@ class Scanner: m_plat = re_plat.match(line) m_cplat = re_child_plat.match(line) m_cpriv = re_child_priv.match(line) + m_phase = re_phase.match(line) if m_priv: driver.priv = m_priv.group(1) elif m_plat: @@ -466,6 +474,8 @@ class Scanner: driver.uclass_id = m_id.group(1) elif m_of_match: compat = m_of_match.group(2) + elif m_phase: + driver.phase = m_phase.group(1) elif '};' in line: if driver.uclass_id and compat: if compat not in of_match: diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d90ece205d..c1fafb656f 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -138,8 +138,8 @@ class TestDtoc(unittest.TestCase): dtb_file (str): Filename of .dtb file output (str): Filename of output file """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], True, - None, None, scan=copy_scan()) + dtb_platdata.run_steps(args, dtb_file, False, output, [], None, + warning_disabled=True, scan=copy_scan()) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -365,7 +365,7 @@ U_BOOT_DRVINFO(gpios_at_0) = { output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], - scan=copy_scan()) + None, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -375,7 +375,7 @@ struct dtd_invalid { with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], - scan=copy_scan()) + None, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -927,8 +927,8 @@ U_BOOT_DRVINFO(spl_test2) = { def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True, - scan=copy_scan()) + dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], None, + warning_disabled=True, scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception)) @@ -944,8 +944,8 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames)) - dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True, - scan=copy_scan()) + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], None, + warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames)) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index ebdc12abc8..8d35b33c28 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -233,6 +233,7 @@ U_BOOT_DRIVER(i2c_tegra) = { self.assertIn('i2c_tegra', scan._drivers) drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('', drv.phase) def test_priv(self): """Test collection of struct info from drivers""" @@ -250,6 +251,7 @@ U_BOOT_DRIVER(testing) = { .plat_auto = sizeof(struct some_plat), .per_child_auto = sizeof(struct some_cpriv), .per_child_plat_auto = sizeof(struct some_cplat), + DM_PHASE(tpl) }; ''' scan = src_scan.Scanner(None, False, None) @@ -264,6 +266,7 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_plat', drv.plat) self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) + self.assertEqual('tpl', drv.phase) self.assertEqual(1, len(scan._drivers)) def test_uclass_scan(self): From 67c053341ff65e1fa6386f633492c7fc9d03fe18 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:03 -0700 Subject: [PATCH 026/120] Makefile: Pass the U-Boot phase to dtoc Pass the U-Boot phase as a parameter so dtoc can use it. At present it is ether "spl" or "tpl". Signed-off-by: Simon Glass --- scripts/Makefile.spl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 1fd63efdfd..bfae9a4fca 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -311,7 +311,7 @@ $(obj)/$(SPL_BIN).dtb: $(obj)/dts/dt-$(SPL_NAME).dtb FORCE pythonpath = PYTHONPATH=scripts/dtc/pylibfdt DTOC_ARGS := $(pythonpath) $(srctree)/tools/dtoc/dtoc \ - -d $(obj)/$(SPL_BIN).dtb + -d $(obj)/$(SPL_BIN).dtb -p $(SPL_NAME) quiet_cmd_dtoc = DTOC $@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all From 735ddfc638bf3ffdf6888f5502bddbbfa6b3636e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:04 -0700 Subject: [PATCH 027/120] dtoc: Support headers needed for drivers Typically dtoc can detect the header file needed for a driver by looking for the structs that it uses. For example, if a driver as a .priv_auto that uses 'struct serial_priv', then dtoc can search header files for the definition of that struct and use the file. In some cases, enums are used in drivers, typically with the .data field of struct udevice_id. Since dtoc does not support searching for these, add a way to tell dtoc which header to use. This works as a macro included in the driver definition. Signed-off-by: Simon Glass --- include/dm/device.h | 18 ++++++++++++++++++ tools/dtoc/src_scan.py | 7 +++++++ tools/dtoc/test_src_scan.py | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/include/dm/device.h b/include/dm/device.h index 84df5b9e95..45010b4df9 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -385,6 +385,24 @@ struct driver { */ #define DM_PHASE(_phase) +/** + * Declare a macro to declare a header needed for a driver. Often the correct + * header can be found automatically, but only for struct declarations. For + * enums and #defines used in the driver declaration and declared in a different + * header from the structs, this macro must be used. + * + * This macro produces no code but its information will be parsed by dtoc. The + * macro can be used multiple times with different headers, for the same driver. + * Put it within the U_BOOT_DRIVER() declaration, e.g.: + * + * U_BOOT_DRIVER(cpu) = { + * .name = ... + * ... + * DM_HEADER() + * }; + */ +#define DM_HEADER(_hdr) + /** * dev_get_plat() - Get the platform data for a device * diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 2699153016..206b2b3758 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -68,6 +68,8 @@ class Driver: e.g. 'pci_child_plat' used (bool): True if the driver is used by the structs being output phase (str): Which phase of U-Boot to use this driver + headers (list): List of header files needed for this driver (each a str) + e.g. [''] """ def __init__(self, name, fname): self.name = name @@ -80,6 +82,7 @@ class Driver: self.child_plat = '' self.used = False self.phase = '' + self.headers = [] def __eq__(self, other): return (self.name == other.name and @@ -434,6 +437,7 @@ class Scanner: r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') + re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$') # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') @@ -462,6 +466,7 @@ class Scanner: m_cplat = re_child_plat.match(line) m_cpriv = re_child_priv.match(line) m_phase = re_phase.match(line) + m_hdr = re_hdr.match(line) if m_priv: driver.priv = m_priv.group(1) elif m_plat: @@ -476,6 +481,8 @@ class Scanner: compat = m_of_match.group(2) elif m_phase: driver.phase = m_phase.group(1) + elif m_hdr: + driver.headers.append(m_hdr.group(1)) elif '};' in line: if driver.uclass_id and compat: if compat not in of_match: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 8d35b33c28..245b7302fd 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -234,6 +234,7 @@ U_BOOT_DRIVER(i2c_tegra) = { drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) self.assertEqual('', drv.phase) + self.assertEqual([], drv.headers) def test_priv(self): """Test collection of struct info from drivers""" @@ -252,6 +253,8 @@ U_BOOT_DRIVER(testing) = { .per_child_auto = sizeof(struct some_cpriv), .per_child_plat_auto = sizeof(struct some_cplat), DM_PHASE(tpl) + DM_HEADER() + DM_HEADER() }; ''' scan = src_scan.Scanner(None, False, None) @@ -267,6 +270,7 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) self.assertEqual('tpl', drv.phase) + self.assertEqual(['', ''], drv.headers) self.assertEqual(1, len(scan._drivers)) def test_uclass_scan(self): From 8d6f2d359e7cf5a6960d55281ee378fac7db0bbb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:05 -0700 Subject: [PATCH 028/120] dtoc: Process driver aliases along with drivers Instead of using a separate step for this processing, handle it while scanning its associated driver. This allows us to drop the code coverage exception in this case. Note that only files containing drivers are scanned by dtoc, so aliases declared in a file that doesn't hold a driver will not be noticed. It would be confusing to put them anywhere other than in the driver that they relate to, but update the documentation to say this explicitly, just in case. Signed-off-by: Simon Glass --- doc/driver-model/of-plat.rst | 3 ++- tools/dtoc/src_scan.py | 16 +++++----------- tools/dtoc/test/dtoc_test_scan_drivers.cxx | 4 ++++ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst index 4ef2fe699a..a5a6e46e3e 100644 --- a/doc/driver-model/of-plat.rst +++ b/doc/driver-model/of-plat.rst @@ -183,7 +183,8 @@ each 'compatible' string. In order to make this a bit more flexible DM_DRIVER_ALIAS macro can be used to declare an alias for a driver name, typically a 'compatible' string. -This macro produces no code, but it is by dtoc tool. +This macro produces no code, but it is by dtoc tool. It must be located in the +same file as its associated driver, ideally just after it. The parent_idx is the index of the parent driver_info structure within its linker list (instantiated by the U_BOOT_DRVINFO() macro). This is used to support diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 206b2b3758..9d161a2cbc 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -387,6 +387,7 @@ class Scanner: in the file _of_match - updated with each compatible string found in the file _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name Args: fname (str): Filename being parsed (used for warnings) @@ -438,6 +439,7 @@ class Scanner: re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$') + re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)') # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') @@ -522,8 +524,11 @@ class Scanner: driver = Driver(driver_name, fname) else: ids_m = re_ids.search(line) + m_alias = re_alias.match(line) if ids_m: ids_name = ids_m.group(1) + elif m_alias: + self._driver_aliases[m_alias[2]] = m_alias[1] # Make the updates based on what we found self._drivers.update(drivers) @@ -557,17 +562,6 @@ class Scanner: if 'UCLASS_DRIVER' in buff: self._parse_uclass_driver(fname, buff) - # The following re will search for driver aliases declared as - # DM_DRIVER_ALIAS(alias, driver_name) - driver_aliases = re.findall( - r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', - buff) - - for alias in driver_aliases: # pragma: no cover - if len(alias) != 2: - continue - self._driver_aliases[alias[1]] = alias[0] - def scan_header(self, fname): """Scan a header file to build a list of struct definitions diff --git a/tools/dtoc/test/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx index f448767670..f370b8951d 100644 --- a/tools/dtoc/test/dtoc_test_scan_drivers.cxx +++ b/tools/dtoc/test/dtoc_test_scan_drivers.cxx @@ -1 +1,5 @@ +/* Aliases must be in driver files */ +U_BOOT_DRIVER(sandbox_gpio) { +}; + DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) From 1d97269756a60945e3b9690074d1814198ce5a4e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:06 -0700 Subject: [PATCH 029/120] dtoc: Warn of duplicate drivers If drivers have the same name then we cannot distinguish them. This only matters if the driver is actually used by dtoc, but in that case, issue a warning. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 28 ++++++++++- tools/dtoc/test_src_scan.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 9d161a2cbc..fb78536e00 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -70,6 +70,10 @@ class Driver: phase (str): Which phase of U-Boot to use this driver headers (list): List of header files needed for this driver (each a str) e.g. [''] + dups (list): Driver objects with the same name as this one, that were + found after this one + warn_dups (bool): True if the duplicates are not distinguisble using + the phase """ def __init__(self, name, fname): self.name = name @@ -83,6 +87,8 @@ class Driver: self.used = False self.phase = '' self.headers = [] + self.dups = [] + self.warn_dups = False def __eq__(self, other): return (self.name == other.name and @@ -531,7 +537,21 @@ class Scanner: self._driver_aliases[m_alias[2]] = m_alias[1] # Make the updates based on what we found - self._drivers.update(drivers) + for driver in drivers.values(): + if driver.name in self._drivers: + orig = self._drivers[driver.name] + if self._phase: + # If the original driver matches our phase, use it + if orig.phase == self._phase: + orig.dups.append(driver) + continue + + # Otherwise use the new driver, which is assumed to match + else: + # We have no way of distinguishing them + driver.warn_dups = True + driver.dups.append(orig) + self._drivers[driver.name] = driver self._of_match.update(of_match) def scan_driver(self, fname): @@ -617,6 +637,8 @@ class Scanner: This takes a list of nodes, finds the driver for each one and marks it as used. + If two used drivers have the same name, issue a warning. + Args: nodes (list of None): Nodes that are in use """ @@ -626,3 +648,7 @@ class Scanner: driver = self._drivers.get(struct_name) if driver: driver.used = True + if driver.dups and driver.warn_dups: + print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % + (driver.name, driver.fname, + ', '.join([drv.fname for drv in driver.dups]))) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 245b7302fd..598ff256a6 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -371,3 +371,98 @@ struct another_struct { with test_util.capture_sys_output() as (stdout, _): scan.scan_header(output) self.assertIn('due to unicode error', stdout.getvalue()) + + def setup_dup_drivers(self, name, phase=''): + """Set up for a duplcate test + + Returns: + tuple: + Scanner to use + Driver record for first driver + Text of second driver declaration + Node for driver 1 + """ + driver1 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + %s +}; +''' % (name, 'DM_PHASE(%s)' % phase if phase else '') + driver2 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-dvc" }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_RAM, + .of_match = test_ids, +}; +''' % name + scan = src_scan.Scanner(None, False, None, phase) + scan._parse_driver('file1.c', driver1) + self.assertIn(name, scan._drivers) + drv1 = scan._drivers[name] + + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'nvidia,tegra114-i2c' + node = FakeNode() + node.name = 'testing' + node.props = {'compatible': prop} + + return scan, drv1, driver2, node + + def test_dup_drivers(self): + """Test handling of duplicate drivers""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name) + self.assertEqual('', drv1.phase) + + # The driver should not have a duplicate yet + self.assertEqual([], drv1.dups) + + scan._parse_driver('file2.c', driver2) + + # The first driver should now be a duplicate of the second + drv2 = scan._drivers[name] + self.assertEqual('', drv2.phase) + self.assertEqual(1, len(drv2.dups)) + self.assertEqual([drv1], drv2.dups) + + # There is no way to distinguish them, so we should expect a warning + self.assertTrue(drv2.warn_dups) + + # We should see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual( + "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)", + stdout.getvalue().strip()) + + def test_dup_drivers_phase(self): + """Test handling of duplicate drivers but with different phases""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl') + scan._parse_driver('file2.c', driver2) + self.assertEqual('spl', drv1.phase) + + # The second driver should now be a duplicate of the second + self.assertEqual(1, len(drv1.dups)) + drv2 = drv1.dups[0] + + # The phase is different, so we should not warn of dups + self.assertFalse(drv1.warn_dups) + + # We should not see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual('', stdout.getvalue().strip()) From 059535291c6785bb64594dd0e47c767e8c9e02bf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:07 -0700 Subject: [PATCH 030/120] dtoc: Read aliases for uclasses Scan the aliases in the device tree to establish the number of devices within each uclass, and the sequence number of each. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 28 ++++++++++ tools/dtoc/src_scan.py | 32 ++++++++++- tools/dtoc/test/dtoc_test_alias_bad.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_path.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_uc.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_inst.dts | 58 ++++++++++++++++++++ tools/dtoc/test_dtoc.py | 56 ++++++++++++++++++- 7 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 tools/dtoc/test/dtoc_test_alias_bad.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_path.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_uc.dts create mode 100644 tools/dtoc/test/dtoc_test_inst.dts diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ef0454c890..f6dcf47d49 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -647,6 +647,29 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n') + def read_aliases(self): + """Read the aliases and attach the information to self._alias + + Raises: + ValueError: The alias path is not found + """ + alias_node = self._fdt.GetNode('/aliases') + if not alias_node: + return + re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$') + for prop in alias_node.props.values(): + m_alias = re_num.match(prop.name) + if not m_alias: + raise ValueError("Cannot decode alias '%s'" % prop.name) + name, num = m_alias.groups() + node = self._fdt.GetNode(prop.value) + result = self._scan.add_uclass_alias(name, num, node) + if result is None: + raise ValueError("Alias '%s' path '%s' not found" % + (prop.name, prop.value)) + elif result is False: + print("Could not find uclass for alias '%s'" % prop.name) + def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes) @@ -757,6 +780,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, scan (src_src.Scanner): Scanner from a previous run. This can help speed up tests. Use None for normal operation + Returns: + DtbPlatdata object + Raises: ValueError: if args has no command, or an unknown command """ @@ -782,6 +808,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.scan_phandles() if do_process: plat.process_nodes(False) + plat.read_aliases() cmds = args[0].split(',') if 'all' in cmds: @@ -796,3 +823,4 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.out_header(outfile) outfile.method(plat) plat.finish_output() + return plat diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index fb78536e00..a275032179 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -116,6 +116,13 @@ class UclassDriver: e.g. 'pci_child_priv' per_child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' + alias_num_to_node (dict): Aliases for this uclasses (for sequence + numbers) + key (int): Alias number, e.g. 2 for "pci2" + value (str): Node the alias points to + alias_path_to_num (dict): Convert a path to an alias number + key (str): Full path to node (e.g. '/soc/pci') + seq (int): Alias number, e.g. 2 for "pci2" """ def __init__(self, name): self.name = name @@ -125,6 +132,8 @@ class UclassDriver: self.per_dev_plat = '' self.per_child_priv = '' self.per_child_plat = '' + self.alias_num_to_node = {} + self.alias_path_to_num = {} def __eq__(self, other): return (self.name == other.name and @@ -622,7 +631,6 @@ class Scanner: self.scan_driver(pathname) elif fname.endswith('.h'): self.scan_header(pathname) - for fname in self._drivers_additional: if not isinstance(fname, str) or len(fname) == 0: continue @@ -652,3 +660,25 @@ class Scanner: print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % (driver.name, driver.fname, ', '.join([drv.fname for drv in driver.dups]))) + + def add_uclass_alias(self, name, num, node): + """Add an alias to a uclass + + Args: + name: Name of uclass, e.g. 'i2c' + num: Alias number, e.g. 2 for alias 'i2c2' + node: Node the alias points to, or None if None + + Returns: + True if the node was added + False if the node was not added (uclass of that name not found) + None if the node could not be added because it was None + """ + for uclass in self._uclass.values(): + if uclass.name == name: + if node is None: + return None + uclass.alias_num_to_node[int(num)] = node + uclass.alias_path_to_num[node.path] = int(num) + return True + return False diff --git a/tools/dtoc/test/dtoc_test_alias_bad.dts b/tools/dtoc/test/dtoc_test_alias_bad.dts new file mode 100644 index 0000000000..d4f502ad0a --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4- = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_path.dts b/tools/dtoc/test/dtoc_test_alias_bad_path.dts new file mode 100644 index 0000000000..0beca4f0d0 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_path.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = "/does/not/exist"; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_uc.dts b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts new file mode 100644 index 0000000000..ae64f5b3b2 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + other1 = &testfdt_1; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_inst.dts b/tools/dtoc/test/dtoc_test_inst.dts new file mode 100644 index 0000000000..b8177fcef5 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_inst.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c1fafb656f..706cc39b3d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -137,9 +137,12 @@ class TestDtoc(unittest.TestCase): args (list of str): List of arguments for dtoc dtb_file (str): Filename of .dtb file output (str): Filename of output file + + Returns: + DtbPlatdata object """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], None, - warning_disabled=True, scan=copy_scan()) + return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, + warning_disabled=True, scan=copy_scan()) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -1040,3 +1043,52 @@ U_BOOT_DRVINFO(spl_test2) = { gpio = scan._drivers['sandbox_gpio'] self.assertFalse(gpio.used) + + def test_alias_read(self): + """Test obtaining aliases""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt_node = plat._fdt.GetNode('/some-bus/test') + self.assertIn('UCLASS_TEST_FDT', scan._uclass) + uc = scan._uclass['UCLASS_TEST_FDT'] + self.assertEqual({1: testfdt_node}, uc.alias_num_to_node) + self.assertEqual({'/some-bus/test': 1}, uc.alias_path_to_num) + + # Try adding an alias that doesn't exist + self.assertFalse(scan.add_uclass_alias('fred', 3, None)) + + # Try adding an alias for a missing node + self.assertIsNone(scan.add_uclass_alias('testfdt', 3, None)) + + def test_alias_read_bad(self): + """Test invalid alias property name""" + dtb_file = get_dtb_file('dtoc_test_alias_bad.dts') + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Cannot decode alias 'i2c4-'", str(exc.exception)) + + def test_alias_read_bad_path(self): + """Test alias pointing to a non-existent node""" + # This line may produce a warning, so capture it: + # Warning (alias_paths): /aliases:i2c4: aliases property is not a valid + # node (/does/not/exist) + dtb_file = get_dtb_file('dtoc_test_alias_bad_path.dts', True) + + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Alias 'i2c4' path '/does/not/exist' not found", + str(exc.exception)) + + def test_alias_read_bad_uclass(self): + """Test alias for a uclass that doesn't exist""" + dtb_file = get_dtb_file('dtoc_test_alias_bad_uc.dts') + output = tools.GetOutputFilename('output') + with test_util.capture_sys_output() as (stdout, _): + plat = self.run_test(['struct'], dtb_file, output) + self.assertEqual("Could not find uclass for alias 'other1'", + stdout.getvalue().strip()) From 1712f8b2b748ec96442e36d733d421a7ebaa1168 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:08 -0700 Subject: [PATCH 031/120] dtoc: Detect drivers only at the start of start of line If a driver declaration is included in a comment, dtoc currently gets confused. Update the parser to only consider declarations that begin at the start of a line. Since multi-line comments begin with an asterisk, this avoids the problem. Signed-off-by: Simon Glass --- tools/dtoc/src_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index a275032179..bb22b0b64f 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -331,7 +331,7 @@ class Scanner: # Collect the driver name and associated Driver driver = None - re_driver = re.compile(r'UCLASS_DRIVER\((.*)\)') + re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)') # Collect the uclass ID, e.g. 'UCLASS_SPI' re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') @@ -427,7 +427,7 @@ class Scanner: # Collect the driver info driver = None - re_driver = re.compile(r'U_BOOT_DRIVER\((.*)\)') + re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)') # Collect the uclass ID, e.g. 'UCLASS_SPI' re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') From 074197aadfef2cfe437ecc1fe057ef21f4eef0da Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:09 -0700 Subject: [PATCH 032/120] dtoc: Assign a sequence number to each node Now that we have the alias information we can assign a sequence number to each device in the uclass. Store this in the node associated with each device. This requires renaming the sandbox test drivers to have the right name. Note that test coverage is broken with this patch, but fixed in the next one. Signed-off-by: Simon Glass --- drivers/misc/test_drv.c | 6 +++-- test/dm/test-fdt.c | 6 ++--- tools/dtoc/dtb_platdata.py | 55 +++++++++++++++++++++++++++----------- tools/dtoc/test_dtoc.py | 6 +++++ 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index 827a50e954..a2a77d36bb 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -86,7 +86,7 @@ static const struct udevice_id testbus_ids[] = { { } }; -U_BOOT_DRIVER(testbus_drv) = { +U_BOOT_DRIVER(denx_u_boot_test_bus) = { .name = "testbus_drv", .of_match = testbus_ids, .id = UCLASS_TEST_BUS, @@ -160,7 +160,9 @@ static const struct udevice_id testfdt_ids[] = { { } }; -U_BOOT_DRIVER(testfdt_drv) = { +DM_DRIVER_ALIAS(denx_u_boot_fdt_test, google_another_fdt_test) + +U_BOOT_DRIVER(denx_u_boot_fdt_test) = { .name = "testfdt_drv", .of_match = testfdt_ids, .id = UCLASS_TEST_FDT, diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 6e83aeecd9..6552d09ba3 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -330,7 +330,7 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts) /* Check creating a device with an alias */ node = ofnode_path("/some-bus/c-test@1"); - ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "c-test@1", NULL, node, &dev)); ut_asserteq(12, dev_seq(dev)); ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 12, &dev)); @@ -350,11 +350,11 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts) * * So next available is 19 */ - ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "fred", NULL, ofnode_null(), &dev)); ut_asserteq(19, dev_seq(dev)); - ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "fred2", NULL, ofnode_null(), &dev)); ut_asserteq(20, dev_seq(dev)); diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index f6dcf47d49..9e99c63ae7 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -136,8 +136,10 @@ class DtbPlatdata(): from the U-Boot source code _fdt: Fdt object, referencing the device tree _dtb_fname: Filename of the input device tree binary file - _valid_nodes: A list of Node object with compatible strings. The list - is ordered by conv_name_to_c(node.name) + _valid_nodes_unsorted: A list of Node object with compatible strings, + ordered by devicetree node order + _valid_nodes: A list of Node object with compatible strings, ordered by + conv_name_to_c(node.name) _include_disabled: true to include nodes marked status = "disabled" _outfile: The current output file (sys.stdout or a real file) _lines: Stashed list of output lines for outputting in the future @@ -155,6 +157,7 @@ class DtbPlatdata(): self._fdt = None self._dtb_fname = dtb_fname self._valid_nodes = None + self._valid_nodes_unsorted = None self._include_disabled = include_disabled self._outfile = None self._lines = [] @@ -324,34 +327,38 @@ class DtbPlatdata(): """ self._fdt = fdt.FdtScan(self._dtb_fname) - def scan_node(self, root, valid_nodes): + def scan_node(self, node, valid_nodes): """Scan a node and subnodes to build a tree of node and phandle info - This adds each node to self._valid_nodes. + This adds each subnode to self._valid_nodes if it is enabled and has a + compatible string. Args: - root (Node): Root node for scan + node (Node): Node for scan for subnodes valid_nodes (list of Node): List of Node objects to add to """ - for node in root.subnodes: - if 'compatible' in node.props: - status = node.props.get('status') + for subnode in node.subnodes: + if 'compatible' in subnode.props: + status = subnode.props.get('status') if (not self._include_disabled and not status or status.value != 'disabled'): - valid_nodes.append(node) + valid_nodes.append(subnode) # recurse to handle any subnodes - self.scan_node(node, valid_nodes) + self.scan_node(subnode, valid_nodes) def scan_tree(self): """Scan the device tree for useful information This fills in the following properties: - _valid_nodes: A list of nodes we wish to consider include in the - platform data + _valid_nodes_unsorted: A list of nodes we wish to consider include + in the platform data (in devicetree node order) + _valid_nodes: Sorted version of _valid_nodes_unsorted """ + root = self._fdt.GetRoot() valid_nodes = [] - self.scan_node(self._fdt.GetRoot(), valid_nodes) + self.scan_node(root, valid_nodes) + self._valid_nodes_unsorted = valid_nodes self._valid_nodes = sorted(valid_nodes, key=lambda x: conv_name_to_c(x.name)) @@ -670,6 +677,24 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name) + def assign_seq(self): + """Assign a sequence number to each node""" + for node in self._valid_nodes_unsorted: + if node.driver and node.seq == -1 and node.uclass: + uclass = node.uclass + num = uclass.alias_path_to_num.get(node.path) + if num is not None: + node.seq = num + else: + # Dynamically allocate the next available value after all + # existing ones + for seq in range(1000): + if seq not in uclass.alias_num_to_node: + break + node.seq = seq + uclass.alias_path_to_num[node.path] = seq + uclass.alias_num_to_node[seq] = node + def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes) @@ -806,9 +831,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() - if do_process: - plat.process_nodes(False) + plat.process_nodes(False) plat.read_aliases() + plat.assign_seq() cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 706cc39b3d..b4c0a042a9 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -1092,3 +1092,9 @@ U_BOOT_DRVINFO(spl_test2) = { plat = self.run_test(['struct'], dtb_file, output) self.assertEqual("Could not find uclass for alias 'other1'", stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output) From 337d6972f5f5294971db52809f83c0454cb4e7ba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:10 -0700 Subject: [PATCH 033/120] dtoc: Set up the uclasses that are used We only care about uclasses that are actually used. This is determined by the drivers that use them. Check all the used drivers and build a list of 'valid' uclasses. Also add references to the uclasses so we can generate C code that uses them. Attach a uclass to each valid driver. For the tests, now that we have uclasses we must create an explicit test for the case where a node does not have one. This should only happen if the source code does not build, or the source-code scanning fails to find it. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 46 ++++++++++++++++++++++++------------- tools/dtoc/src_scan.py | 45 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 29 +++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 17 ++++++++++++++ 4 files changed, 119 insertions(+), 18 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 9e99c63ae7..af21156659 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -151,6 +151,8 @@ class DtbPlatdata(): key (str): Field name value: Prop object with field information _basedir (str): Base directory of source tree + _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for + the selected devices (see _valid_node), in alphabetical order """ def __init__(self, scan, dtb_fname, include_disabled): self._scan = scan @@ -164,6 +166,7 @@ class DtbPlatdata(): self._dirnames = [None] * len(Ftype) self._struct_data = collections.OrderedDict() self._basedir = None + self._valid_uclasses = None def setup_output_dirs(self, output_dirs): """Set up the output directories @@ -677,23 +680,12 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name) - def assign_seq(self): + def assign_seqs(self): """Assign a sequence number to each node""" for node in self._valid_nodes_unsorted: - if node.driver and node.seq == -1 and node.uclass: - uclass = node.uclass - num = uclass.alias_path_to_num.get(node.path) - if num is not None: - node.seq = num - else: - # Dynamically allocate the next available value after all - # existing ones - for seq in range(1000): - if seq not in uclass.alias_num_to_node: - break - node.seq = seq - uclass.alias_path_to_num[node.path] = seq - uclass.alias_num_to_node[seq] = node + seq = self._scan.assign_seq(node) + if seq is not None: + node.seq = seq def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes) @@ -710,6 +702,16 @@ class DtbPlatdata(): raise ValueError("Cannot parse/find driver for '%s'" % node.struct_name) node.driver = driver + uclass = self._scan._uclass.get(driver.uclass_id) + if not uclass: + raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" % + (driver.uclass_id, node.struct_name)) + node.uclass = uclass + node.uclass_seq = len(node.uclass.devs) + node.uclass.devs.append(node) + uclass.node_refs[node.uclass_seq] = \ + '&%s->uclass_node' % node.dev_ref + parent_driver = None if node.parent in self._valid_nodes: parent_driver = self._scan.get_driver(node.parent.struct_name) @@ -730,6 +732,18 @@ class DtbPlatdata(): node.child_refs[-1] = ref node.child_refs[len(node.child_devs)] = ref + uclass_set = set() + for driver in self._scan._drivers.values(): + if driver.used and driver.uclass: + uclass_set.add(driver.uclass) + self._valid_uclasses = sorted(list(uclass_set), + key=lambda uc: uc.uclass_id) + + for seq, uclass in enumerate(uclass_set): + ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name + uclass.node_refs[-1] = ref + uclass.node_refs[len(uclass.devs)] = ref + def output_node(self, node): """Output the C code for a node @@ -833,7 +847,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.scan_phandles() plat.process_nodes(False) plat.read_aliases() - plat.assign_seq() + plat.assign_seqs() cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index bb22b0b64f..8619206a8d 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -74,6 +74,7 @@ class Driver: found after this one warn_dups (bool): True if the duplicates are not distinguisble using the phase + uclass (Uclass): uclass for this driver """ def __init__(self, name, fname): self.name = name @@ -89,6 +90,7 @@ class Driver: self.headers = [] self.dups = [] self.warn_dups = False + self.uclass = None def __eq__(self, other): return (self.name == other.name and @@ -123,6 +125,10 @@ class UclassDriver: alias_path_to_num (dict): Convert a path to an alias number key (str): Full path to node (e.g. '/soc/pci') seq (int): Alias number, e.g. 2 for "pci2" + devs (list): List of devices in this uclass, each a Node + node_refs (dict): References in the linked list of devices: + key (int): Sequence number (0=first, n-1=last, -1=head, n=tail) + value (str): Reference to the device at that position """ def __init__(self, name): self.name = name @@ -134,6 +140,8 @@ class UclassDriver: self.per_child_plat = '' self.alias_num_to_node = {} self.alias_path_to_num = {} + self.devs = [] + self.node_refs = {} def __eq__(self, other): return (self.name == other.name and @@ -639,6 +647,12 @@ class Scanner: else: self.scan_driver(self._basedir + '/' + fname) + # Get the uclass for each driver + # TODO: Can we just get the uclass for the ones we use, e.g. in + # mark_used()? + for driver in self._drivers.values(): + driver.uclass = self._uclass.get(driver.uclass_id) + def mark_used(self, nodes): """Mark the drivers associated with a list of nodes as 'used' @@ -682,3 +696,34 @@ class Scanner: uclass.alias_path_to_num[node.path] = int(num) return True return False + + def assign_seq(self, node): + """Figure out the sequence number for a node + + This looks in the node's uclass and assigns a sequence number if needed, + based on the aliases and other nodes in that uclass. + + It updates the uclass alias_path_to_num and alias_num_to_node + + Args: + node (Node): Node object to look up + """ + if node.driver and node.seq == -1 and node.uclass: + uclass = node.uclass + num = uclass.alias_path_to_num.get(node.path) + if num is not None: + return num + else: + # Dynamically allocate the next available value after all + # existing ones + if uclass.alias_num_to_node: + start = max(uclass.alias_num_to_node.keys()) + else: + start = -1 + for seq in range(start + 1, 1000): + if seq not in uclass.alias_num_to_node: + break + uclass.alias_path_to_num[node.path] = seq + uclass.alias_num_to_node[seq] = node + return seq + return None diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b4c0a042a9..b077cf0e76 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -141,6 +141,8 @@ class TestDtoc(unittest.TestCase): Returns: DtbPlatdata object """ + # Make a copy of the 'scan' object, since it includes uclasses and + # drivers, which get updated during execution. return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, warning_disabled=True, scan=copy_scan()) @@ -1033,6 +1035,16 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertIn("Cannot parse/find driver for 'sandbox_pmic", str(exc.exception)) + def test_process_nodes_bad_uclass(self): + plat, scan = self.setup_process_test() + + self.assertIn('UCLASS_I2C', scan._uclass) + del scan._uclass['UCLASS_I2C'] + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find uclass 'UCLASS_I2C' for driver 'sandbox_i2c'", + str(exc.exception)) + def test_process_nodes_used(self): """Test processing nodes to add various info""" plat, scan = self.setup_process_test() @@ -1052,10 +1064,13 @@ U_BOOT_DRVINFO(spl_test2) = { scan = plat._scan testfdt_node = plat._fdt.GetNode('/some-bus/test') + test0_node = plat._fdt.GetNode('/some-bus/test0') self.assertIn('UCLASS_TEST_FDT', scan._uclass) uc = scan._uclass['UCLASS_TEST_FDT'] - self.assertEqual({1: testfdt_node}, uc.alias_num_to_node) - self.assertEqual({'/some-bus/test': 1}, uc.alias_path_to_num) + self.assertEqual({1: testfdt_node, 2: test0_node}, + uc.alias_num_to_node) + self.assertEqual({'/some-bus/test': 1, '/some-bus/test0': 2}, + uc.alias_path_to_num) # Try adding an alias that doesn't exist self.assertFalse(scan.add_uclass_alias('fred', 3, None)) @@ -1098,3 +1113,13 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt = plat._fdt.GetNode('/some-bus/test') + self.assertEqual(1, testfdt.seq) + i2c = plat._fdt.GetNode('/i2c') + + # For now this uclass is not compiled in, so no sequence is assigned + self.assertEqual(4, i2c.seq) + spl = plat._fdt.GetNode('/spl-test') + self.assertEqual(0, spl.seq) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 598ff256a6..d32aa58400 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -466,3 +466,20 @@ U_BOOT_DRIVER(%s) = { with test_util.capture_sys_output() as (stdout, _): scan.mark_used([node]) self.assertEqual('', stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + scan = src_scan.Scanner(None, False, None, '') + node = FakeNode() + uc = src_scan.UclassDriver('UCLASS_I2C') + node.uclass = uc + node.driver = True + node.seq = -1 + node.path = 'mypath' + uc.alias_num_to_node[2] = node + + # This should assign 3 (after the 2 that exists) + seq = scan.assign_seq(node) + self.assertEqual(3, seq) + self.assertEqual({'mypath': 3}, uc.alias_path_to_num) + self.assertEqual({2: node, 3: node}, uc.alias_num_to_node) From 50aae3e62d57931afcafec7eb973f222cb3131c7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:11 -0700 Subject: [PATCH 034/120] dtoc: Support processing the root node The device for the root node is normally bound by driver model on init. With devices being instantiated at build time, we must handle the root device also. Add support for processing the root node, which may not have a compatible string. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 10 ++++++++-- tools/dtoc/src_scan.py | 37 ++++++++++++++++++++++--------------- tools/dtoc/test_dtoc.py | 23 +++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 7 +++++++ 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index af21156659..e08b92cf8a 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -350,16 +350,22 @@ class DtbPlatdata(): # recurse to handle any subnodes self.scan_node(subnode, valid_nodes) - def scan_tree(self): + def scan_tree(self, add_root): """Scan the device tree for useful information This fills in the following properties: _valid_nodes_unsorted: A list of nodes we wish to consider include in the platform data (in devicetree node order) _valid_nodes: Sorted version of _valid_nodes_unsorted + + Args: + add_root: True to add the root node also (which wouldn't normally + be added as it may not have a compatible string) """ root = self._fdt.GetRoot() valid_nodes = [] + if add_root: + valid_nodes.append(root) self.scan_node(root, valid_nodes) self._valid_nodes_unsorted = valid_nodes self._valid_nodes = sorted(valid_nodes, @@ -839,7 +845,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() - plat.scan_tree() + plat.scan_tree(add_root=False) plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 8619206a8d..114212cfe2 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -33,6 +33,8 @@ def conv_name_to_c(name): new = new.replace('-', '_') new = new.replace(',', '_') new = new.replace('.', '_') + if new == '/': + return 'root' return new def get_compat_name(node): @@ -250,7 +252,10 @@ class Scanner: In case of no match found, the return will be the same as get_compat_name() """ - compat_list_c = get_compat_name(node) + if not node.parent: + compat_list_c = ['root_driver'] + else: + compat_list_c = get_compat_name(node) for compat_c in compat_list_c: if not compat_c in self._drivers.keys(): @@ -509,21 +514,23 @@ class Scanner: elif m_hdr: driver.headers.append(m_hdr.group(1)) elif '};' in line: - if driver.uclass_id and compat: - if compat not in of_match: - raise ValueError( - "%s: Unknown compatible var '%s' (found: %s)" % - (fname, compat, ','.join(of_match.keys()))) - driver.compat = of_match[compat] + is_root = driver.name == 'root_driver' + if driver.uclass_id and (compat or is_root): + if not is_root: + if compat not in of_match: + raise ValueError( + "%s: Unknown compatible var '%s' (found: %s)" % + (fname, compat, ','.join(of_match.keys()))) + driver.compat = of_match[compat] - # This needs to be deterministic, since a driver may - # have multiple compatible strings pointing to it. - # We record the one earliest in the alphabet so it - # will produce the same result on all machines. - for compat_id in of_match[compat]: - old = self._compat_to_driver.get(compat_id) - if not old or driver.name < old.name: - self._compat_to_driver[compat_id] = driver + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for compat_id in of_match[compat]: + old = self._compat_to_driver.get(compat_id) + if not old or driver.name < old.name: + self._compat_to_driver[compat_id] = driver drivers[driver.name] = driver else: # The driver does not have a uclass or compat string. diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b077cf0e76..ed8c7e4788 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -974,10 +974,10 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') # Take a copy before messing with it - scan = copy.deepcopy(saved_scan) + scan = copy_scan() plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) plat.scan_dtb() - plat.scan_tree() + plat.scan_tree(False) plat.prepare_nodes() return plat, scan @@ -1123,3 +1123,22 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual(4, i2c.seq) spl = plat._fdt.GetNode('/spl-test') self.assertEqual(0, spl.seq) + + def test_process_root(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy_scan() + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + root = plat._fdt.GetRoot() + + plat.scan_tree(False) + self.assertNotIn(root, plat._valid_nodes) + + plat.scan_tree(True) + self.assertIn(root, plat._valid_nodes) + self.assertEqual('root_driver', + scan.get_normalized_compat_name(root)[0]) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index d32aa58400..0af86dcf0c 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -161,6 +161,10 @@ class TestSrcScan(unittest.TestCase): prop.value = 'rockchip,rk3288-grf' node = FakeNode() node.props = {'compatible': prop} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + scan = src_scan.Scanner(None, False, None) with test_util.capture_sys_output() as (stdout, _): name, aliases = scan.get_normalized_compat_name(node) @@ -419,6 +423,9 @@ U_BOOT_DRIVER(%s) = { node.name = 'testing' node.props = {'compatible': prop} + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + return scan, drv1, driver2, node def test_dup_drivers(self): From 4a092350d18d1aea637de6be8802a9de0cca194f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:12 -0700 Subject: [PATCH 035/120] dtoc: Add an option for device instantiation Add an option to instantiate devices at build time. For now this just parses the option and sets up a few parameters. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 17 +++++++++++------ tools/dtoc/main.py | 4 +++- tools/dtoc/test_dtoc.py | 37 ++++++++++++++++++++++--------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e08b92cf8a..46e2725a86 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -153,8 +153,10 @@ class DtbPlatdata(): _basedir (str): Base directory of source tree _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for the selected devices (see _valid_node), in alphabetical order + _instantiate: Instantiate devices so they don't need to be bound at + run-time """ - def __init__(self, scan, dtb_fname, include_disabled): + def __init__(self, scan, dtb_fname, include_disabled, instantiate=False): self._scan = scan self._fdt = None self._dtb_fname = dtb_fname @@ -167,6 +169,7 @@ class DtbPlatdata(): self._struct_data = collections.OrderedDict() self._basedir = None self._valid_uclasses = None + self._instantiate = instantiate def setup_output_dirs(self, output_dirs): """Set up the output directories @@ -802,8 +805,8 @@ OUTPUT_FILES = { def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, - warning_disabled=False, drivers_additional=None, basedir=None, - scan=None): + instantiate, warning_disabled=False, drivers_additional=None, + basedir=None, scan=None): """Run all the steps of the dtoc tool Args: @@ -816,6 +819,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, Directory to put H output files phase: The phase of U-Boot that we are generating data for, e.g. 'spl' or 'tpl'. None if not known + instantiate: Instantiate devices so they don't need to be bound at + run-time warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during @@ -843,15 +848,15 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, do_process = True else: do_process = False - plat = DtbPlatdata(scan, dtb_file, include_disabled) + plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate) plat.scan_dtb() - plat.scan_tree(add_root=False) + plat.scan_tree(add_root=instantiate) plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() - plat.process_nodes(False) + plat.process_nodes(instantiate) plat.read_aliases() plat.assign_seqs() diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 15a8ff167a..93706de89b 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -81,6 +81,8 @@ parser.add_option('-C', '--h-output-dir', action='store', help='Select output directory for H files (defaults to --c-output-di)') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') +parser.add_option('-i', '--instantiate', action='store_true', default=False, + help='Instantiate devices to avoid needing device_bind()') parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', @@ -107,4 +109,4 @@ else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, options.output, [options.c_output_dir, options.h_output_dir], - phase=options.phase) + options.phase, instantiate=options.instantiate) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index ed8c7e4788..cbddd62424 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -130,7 +130,7 @@ class TestDtoc(unittest.TestCase): self.assertEqual(expected, actual) @staticmethod - def run_test(args, dtb_file, output): + def run_test(args, dtb_file, output, instantiate=False): """Run a test using dtoc Args: @@ -143,8 +143,9 @@ class TestDtoc(unittest.TestCase): """ # Make a copy of the 'scan' object, since it includes uclasses and # drivers, which get updated during execution. - return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, - warning_disabled=True, scan=copy_scan()) + return dtb_platdata.run_steps( + args, dtb_file, False, output, [], None, instantiate, + warning_disabled=True, scan=copy_scan()) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -201,7 +202,8 @@ class TestDtoc(unittest.TestCase): output = tools.GetOutputFilename('output') # Run this one without saved_scan to complete test coverage - dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], True) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], None, + False) with open(output) as infile: lines = infile.read().splitlines() self.assertEqual(HEADER.splitlines(), lines) @@ -369,8 +371,9 @@ U_BOOT_DRVINFO(gpios_at_0) = { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], - None, scan=copy_scan()) + dtb_platdata.run_steps( + ['struct'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -379,8 +382,9 @@ struct dtd_invalid { ''', data) with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], - None, scan=copy_scan()) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -530,8 +534,9 @@ U_BOOT_DRVINFO(phandle_target) = { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True, - scan=copy_scan()) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + warning_disabled=True, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -925,15 +930,16 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: - self.run_test(['invalid-cmd'], dtb_file, output) + self.run_test(['invalid-cmd'], dtb_file, output, False) self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", str(exc.exception)) def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], None, - warning_disabled=True, scan=copy_scan()) + dtb_platdata.run_steps( + ['all'], None, False, 'out', ['cdir'], None, False, + warning_disabled=True, scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception)) @@ -949,8 +955,9 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames)) - dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], None, - warning_disabled=True, scan=copy_scan()) + dtb_platdata.run_steps( + ['all'], dtb_file, False, None, [outdir], None, False, + warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames)) From 5a1b25c2011e88e44626202f3cde05e65209c1e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:13 -0700 Subject: [PATCH 036/120] dm: of-platadata: Add option for device instantiation Add Kconfig options to support build-time device instantiation. When fully implemented, this will allow dtoc to create U-Boot devices (i.e. struct udevice records) at build time, thus reducing code space in SPL. For now this defaults to off, but will be enabled when the rest of the implementation is in place. Signed-off-by: Simon Glass --- dts/Kconfig | 23 +++++++++++++++++++++-- scripts/Makefile.spl | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/dts/Kconfig b/dts/Kconfig index 00ac29a457..c39cc36888 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -355,15 +355,24 @@ config SPL_OF_PLATDATA compatible string, then adding platform data and U_BOOT_DRVINFO declarations for each node. See of-plat.txt for more information. +if SPL_OF_PLATDATA + config SPL_OF_PLATDATA_PARENT bool "Support parent information in devices" - depends on SPL_OF_PLATDATA default y help Generally it is useful to be able to access the parent of a device with of-platdata. To save space this can be disabled, but in that case dev_get_parent() will always return NULL; +config SPL_OF_PLATDATA_INST + bool "Declare devices at build time" + help + Declare devices as udevice instances so that they do not need to be + bound when U-Boot starts. This can save time and code space. + +endif + config TPL_OF_PLATDATA bool "Generate platform data for use in TPL" depends on TPL_OF_CONTROL @@ -385,13 +394,23 @@ config TPL_OF_PLATDATA compatible string, then adding platform data and U_BOOT_DRVINFO declarations for each node. See of-plat.txt for more information. +if TPL_OF_PLATDATA + config TPL_OF_PLATDATA_PARENT bool "Support parent information in devices" - depends on TPL_OF_PLATDATA default y help Generally it is useful to be able to access the parent of a device with of-platdata. To save space this can be disabled, but in that case dev_get_parent() will always return NULL; +config TPL_OF_PLATDATA_INST + bool "Declare devices at build time" + + help + Declare devices as udevice instances so that they do not need to be + bound when U-Boot starts. This can save time and code space. + +endif + endmenu diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index bfae9a4fca..f3bb793681 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -313,6 +313,10 @@ pythonpath = PYTHONPATH=scripts/dtc/pylibfdt DTOC_ARGS := $(pythonpath) $(srctree)/tools/dtoc/dtoc \ -d $(obj)/$(SPL_BIN).dtb -p $(SPL_NAME) +ifneq ($(CONFIG_$(SPL_TPL_)OF_PLATDATA_INST),) +DTOC_ARGS += -i +endif + quiet_cmd_dtoc = DTOC $@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all From 426d12f42f47b0b004c838f753dcf415c3268c37 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:14 -0700 Subject: [PATCH 037/120] dtoc: Add support for decl file Add an option to generate the declaration file, which declares all drivers and uclasses, so references can be used in the code. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 36 +++++++++++++++ tools/dtoc/test_dtoc.py | 91 +++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 46e2725a86..040b724678 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -689,6 +689,39 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name) + def generate_decl(self): + nodes_to_output = list(self._valid_nodes) + + self.buf('#include \n') + self.buf('#include \n') + self.buf('\n') + self.buf( + '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n') + for node in nodes_to_output: + self.buf('DM_DRIVER_DECL(%s);\n' % node.struct_name); + self.buf('\n') + + if self._instantiate: + self.buf( + '/* device declarations - these allow DM_DEVICE_REF() to be used */\n') + for node in nodes_to_output: + self.buf('DM_DEVICE_DECL(%s);\n' % node.var_name) + self.buf('\n') + + uclass_list = self._valid_uclasses + + self.buf( + '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n') + for uclass in uclass_list: + self.buf('DM_UCLASS_DRIVER_DECL(%s);\n' % uclass.name) + + if self._instantiate: + self.buf('\n') + self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n') + for uclass in uclass_list: + self.buf('DM_UCLASS_DECL(%s);\n' % uclass.name) + self.out(''.join(self.get_buf())) + def assign_seqs(self): """Assign a sequence number to each node""" for node in self._valid_nodes_unsorted: @@ -794,6 +827,9 @@ class DtbPlatdata(): # key: Command used to generate this file # value: OutputFile for this command OUTPUT_FILES = { + 'decl': + OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl, + 'Declares externs for all device/uclass instances'), 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h', DtbPlatdata.generate_structs, diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index cbddd62424..a51a7f301c 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -40,6 +40,14 @@ HEADER = '''/* #include #include ''' +DECL_HEADER = '''/* + * DO NOT MODIFY + * + * Declares externs for all device/uclass instances. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + C_HEADER = '''/* * DO NOT MODIFY * @@ -213,6 +221,54 @@ class TestDtoc(unittest.TestCase): lines = infile.read().splitlines() self.assertEqual(C_HEADER.splitlines() + [''], lines) + decl_text = DECL_HEADER + ''' +#include +#include + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +DM_DRIVER_DECL(sandbox_i2c); +DM_DRIVER_DECL(sandbox_pmic); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +DM_UCLASS_DRIVER_DECL(i2c); +DM_UCLASS_DRIVER_DECL(misc); +DM_UCLASS_DRIVER_DECL(pmic); +''' + decl_text_inst = DECL_HEADER + ''' +#include +#include + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +DM_DRIVER_DECL(sandbox_i2c); +DM_DRIVER_DECL(sandbox_pmic); +DM_DRIVER_DECL(root_driver); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); + +/* device declarations - these allow DM_DEVICE_REF() to be used */ +DM_DEVICE_DECL(i2c_at_0); +DM_DEVICE_DECL(pmic_at_9); +DM_DEVICE_DECL(root); +DM_DEVICE_DECL(spl_test); +DM_DEVICE_DECL(spl_test2); +DM_DEVICE_DECL(spl_test3); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +DM_UCLASS_DRIVER_DECL(i2c); +DM_UCLASS_DRIVER_DECL(misc); +DM_UCLASS_DRIVER_DECL(pmic); +DM_UCLASS_DRIVER_DECL(root); + +/* uclass declarations - needed for DM_UCLASS_REF() */ +DM_UCLASS_DECL(i2c); +DM_UCLASS_DECL(misc); +DM_UCLASS_DECL(pmic); +DM_UCLASS_DECL(root); +''' struct_text = HEADER + ''' struct dtd_sandbox_i2c { }; @@ -327,10 +383,17 @@ U_BOOT_DRVINFO(spl_test3) = { self._check_strings(self.platdata_text, data) + self.run_test(['decl'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text, data) + # Try the 'all' command self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.platdata_text + self.struct_text, data) + self._check_strings(self.decl_text + self.platdata_text + + self.struct_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -916,7 +979,8 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.platdata_text + self.struct_text, data) + self._check_strings(self.decl_text + self.platdata_text + + self.struct_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -930,9 +994,10 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: - self.run_test(['invalid-cmd'], dtb_file, output, False) - self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", - str(exc.exception)) + self.run_test(['invalid-cmd'], dtb_file, output) + self.assertIn( + "Unknown command 'invalid-cmd': (use: decl, platdata, struct)", + str(exc.exception)) def test_output_conflict(self): """Test a conflict between and output dirs and output file""" @@ -959,11 +1024,12 @@ U_BOOT_DRVINFO(spl_test2) = { ['all'], dtb_file, False, None, [outdir], None, False, warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') - self.assertEqual(4, len(fnames)) + self.assertEqual(5, len(fnames)) leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( - {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, + {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', + 'dt-decl.h'}, leafs) def setup_process_test(self): @@ -1149,3 +1215,14 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertIn(root, plat._valid_nodes) self.assertEqual('root_driver', scan.get_normalized_compat_name(root)[0]) + + def test_simple_inst(self): + """Test output from some simple nodes with instantiate enabled""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + self.run_test(['decl'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text_inst, data) From 4b91be2fd8e7ab95da7b18714687e08849a4b835 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:15 -0700 Subject: [PATCH 038/120] dtoc: Don't generate platform data with instantiation This file is not used when instantiating devices. Update dtoc to skip generating its contents and just add a comment instead. Also it is useful to see the driver name and parent for each device. Update the file to show that information, to avoid updating the same tests twice. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 35 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 53 +++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 040b724678..befe7c1490 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -786,19 +786,46 @@ class DtbPlatdata(): uclass.node_refs[-1] = ref uclass.node_refs[len(uclass.devs)] = ref - def output_node(self, node): + def output_node_plat(self, node): """Output the C code for a node Args: node (fdt.Node): node to output """ - self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) + driver = node.driver + parent_driver = node.parent_driver + + line1 = 'Node %s index %d' % (node.path, node.idx) + if driver: + self.buf('/*\n') + self.buf(' * %s\n' % line1) + self.buf(' * driver %s parent %s\n' % (driver.name, + parent_driver.name if parent_driver else 'None')) + self.buf(' */\n') + else: + self.buf('/* %s */\n' % line1) self._output_values(node) self._declare_device(node) self.out(''.join(self.get_buf())) + def check_instantiate(self, require): + """Check if self._instantiate is set to the required value + + If not, this outputs a message into the current file + + Args: + require: True to require --instantiate, False to require that it not + be enabled + """ + if require != self._instantiate: + self.out( + '/* This file is not used: --instantiate was %senabled */\n' % + ('not ' if require else '')) + return False + return True + def generate_plat(self): """Generate device defintions for the platform data @@ -809,6 +836,8 @@ class DtbPlatdata(): See the documentation in doc/driver-model/of-plat.rst for more information. """ + if not self.check_instantiate(False): + return self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') self.out('#define DT_PLAT_C\n') self.out('\n') @@ -818,7 +847,7 @@ class DtbPlatdata(): self.out('\n') for node in self._valid_nodes: - self.output_node(node) + self.output_node_plat(node) self.out(''.join(self.get_buf())) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index a51a7f301c..c6e33d3422 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -48,13 +48,15 @@ DECL_HEADER = '''/* */ ''' -C_HEADER = '''/* +C_HEADER_PRE = '''/* * DO NOT MODIFY * * Declares the U_BOOT_DRIVER() records and platform data. * This was generated by dtoc from a .dtb (device tree binary) file. */ +''' +C_HEADER = C_HEADER_PRE + ''' /* Allow use of U_BOOT_DRVINFO() in this file */ #define DT_PLAT_C @@ -289,9 +291,11 @@ struct dtd_sandbox_spl_test { \tconst char *\tstringval; }; ''' - platdata_text = C_HEADER + ''' -/* Node /i2c@0 index 0 */ +/* + * Node /i2c@0 index 0 + * driver sandbox_i2c parent None + */ static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { @@ -301,7 +305,10 @@ U_BOOT_DRVINFO(i2c_at_0) = { \t.parent_idx\t= -1, }; -/* Node /i2c@0/pmic@9 index 1 */ +/* + * Node /i2c@0/pmic@9 index 1 + * driver sandbox_pmic parent sandbox_i2c + */ static struct dtd_sandbox_pmic dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, @@ -313,7 +320,10 @@ U_BOOT_DRVINFO(pmic_at_9) = { \t.parent_idx\t= 0, }; -/* Node /spl-test index 2 */ +/* + * Node /spl-test index 2 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.boolval\t\t= true, \t.bytearray\t\t= {0x6, 0x0, 0x0}, @@ -333,7 +343,10 @@ U_BOOT_DRVINFO(spl_test) = { \t.parent_idx\t= -1, }; -/* Node /spl-test2 index 3 */ +/* + * Node /spl-test2 index 3 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.acpi_name\t\t= "\\\\_SB.GPO0", \t.bytearray\t\t= {0x1, 0x23, 0x34}, @@ -352,7 +365,10 @@ U_BOOT_DRVINFO(spl_test2) = { \t.parent_idx\t= -1, }; -/* Node /spl-test3 index 4 */ +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test3 = { \t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, \t\t0x0}, @@ -414,7 +430,10 @@ struct dtd_sandbox_gpio { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /gpios@0 index 0 */ +/* + * Node /gpios@0 index 0 + * driver sandbox_gpio parent None + */ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { \t.gpio_bank_name\t\t= "a", \t.gpio_controller\t= true, @@ -942,7 +961,10 @@ struct dtd_sandbox_spl_test { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /spl-test index 0 */ +/* + * Node /spl-test index 0 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.intval\t\t\t= 0x1, }; @@ -953,7 +975,10 @@ U_BOOT_DRVINFO(spl_test) = { \t.parent_idx\t= -1, }; -/* Node /spl-test2 index 1 */ +/* + * Node /spl-test2 index 1 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.intarray\t\t= 0x5, }; @@ -1226,3 +1251,11 @@ U_BOOT_DRVINFO(spl_test2) = { data = infile.read() self._check_strings(self.decl_text_inst, data) + + self.run_test(['platdata'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(C_HEADER_PRE + ''' +/* This file is not used: --instantiate was enabled */ +''', data) From 23f40a3abfab9540f91bff966a33a232b56247e2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:16 -0700 Subject: [PATCH 039/120] sandbox: Make sandbox,emul more conventional At present this property is a phandle but does not have a #xxx-cells property to match it. Add one so that is works the same as gpio and clock phandles. Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dtsi | 6 +++++- doc/driver-model/pci-info.rst | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index dc933f3bfc..7455c99a73 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -101,15 +101,19 @@ }; i2c_emul: emul { + u-boot,dm-pre-reloc; reg = <0xff>; compatible = "sandbox,i2c-emul-parent"; emul_eeprom: emul-eeprom { compatible = "sandbox,i2c-eeprom"; sandbox,filename = "i2c.bin"; sandbox,size = <256>; + #emul-cells = <0>; }; emul0: emul0 { - compatible = "sandbox,i2c-rtc"; + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c-rtc-emul"; + #emul-cells = <0>; }; }; }; diff --git a/doc/driver-model/pci-info.rst b/doc/driver-model/pci-info.rst index 8b9faa1066..251601a51e 100644 --- a/doc/driver-model/pci-info.rst +++ b/doc/driver-model/pci-info.rst @@ -125,6 +125,7 @@ emulator driver. For example:: compatible = "sandbox,pci-emul-parent"; emul_1f: emul@1f,0 { compatible = "sandbox,swap-case"; + #emul-cells = <0>; }; }; From c4085d733bd22cc77815283f72fda56240e76a45 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:17 -0700 Subject: [PATCH 040/120] sandbox: i2c: Rename driver names to work with of-platdata Some of these do not follow the rules. Make sure the driver name matches the compatible string in all cases. Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 4 ++-- drivers/i2c/i2c-emul-uclass.c | 4 ++-- drivers/rtc/i2c_rtc_emul.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index f15d1ebabc..899e75f260 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -604,10 +604,10 @@ sandbox,size = <256>; }; emul0: emul0 { - compatible = "sandbox,i2c-rtc"; + compatible = "sandbox,i2c-rtc-emul"; }; emul1: emull { - compatible = "sandbox,i2c-rtc"; + compatible = "sandbox,i2c-rtc-emul"; }; }; diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c index 085b824e89..75d7988338 100644 --- a/drivers/i2c/i2c-emul-uclass.c +++ b/drivers/i2c/i2c-emul-uclass.c @@ -85,8 +85,8 @@ static const struct udevice_id i2c_emul_parent_ids[] = { { } }; -U_BOOT_DRIVER(i2c_emul_parent_drv) = { - .name = "i2c_emul_parent_drv", +U_BOOT_DRIVER(sandbox_i2c_emul_parent) = { + .name = "sandbox_i2c_emul_parent", .id = UCLASS_I2C_EMUL_PARENT, .of_match = i2c_emul_parent_ids, }; diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c index f25b976e54..fdc885c518 100644 --- a/drivers/rtc/i2c_rtc_emul.c +++ b/drivers/rtc/i2c_rtc_emul.c @@ -223,7 +223,7 @@ static int sandbox_i2c_rtc_bind(struct udevice *dev) } static const struct udevice_id sandbox_i2c_rtc_ids[] = { - { .compatible = "sandbox,i2c-rtc" }, + { .compatible = "sandbox,i2c-rtc-emul" }, { } }; From 8840bc56fb8510a6284f0334f2236a302fe934dc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:18 -0700 Subject: [PATCH 041/120] dtoc: Tidy up the list of supported phandle properties For now dtoc only supports a hard-coded list of phandle properties, to avoid any situation where it makes a mistake in its determination. Make this into a constant dict, recording both the phandle property name and the associated #cells property in the target node. This makes it easier to find and modify. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index befe7c1490..ca2e55fa52 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -52,6 +52,20 @@ TYPE_NAMES = { STRUCT_PREFIX = 'dtd_' VAL_PREFIX = 'dtv_' +# Properties which are considered to be phandles +# key: property name +# value: name of associated #cells property in the target node +# +# New phandle properties must be added here; otherwise they will come through as +# simple integers and finding devices by phandle will not work. +# Any property that ends with one of these (e.g. 'cd-gpios') will be considered +# a phandle property. +PHANDLE_PROPS = { + 'clocks': '#clock-cells', + 'gpios': '#gpio-cells', + 'sandbox,emul': '#emul-cells', + } + class Ftype(IntEnum): SOURCE, HEADER = range(2) @@ -290,7 +304,11 @@ class DtbPlatdata(): ValueError: if the phandle cannot be parsed or the required property is not present """ - if prop.name in ['clocks', 'cd-gpios']: + cells_prop = None + for name, cprop in PHANDLE_PROPS.items(): + if prop.name.endswith(name): + cells_prop = cprop + if cells_prop: if not isinstance(prop.value, list): prop.value = [prop.value] val = prop.value @@ -310,14 +328,10 @@ class DtbPlatdata(): if not target: raise ValueError("Cannot parse '%s' in node '%s'" % (prop.name, node_name)) - cells = None - for prop_name in ['#clock-cells', '#gpio-cells']: - cells = target.props.get(prop_name) - if cells: - break + cells = target.props.get(cells_prop) if not cells: raise ValueError("Node '%s' has no cells property" % - (target.name)) + target.name) num_args = fdt_util.fdt32_to_cpu(cells.value) max_args = max(max_args, num_args) args.append(num_args) From 9763e4eb93bfcb5cc50edf13b152c231b218591f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:19 -0700 Subject: [PATCH 042/120] dtoc: Generate a summary in the dt-plat.c file Add a summary to the top of the generated code, to make it easier to see what the file contains. Also add a tab to .plat so that its value lines up with the others. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 20 +++- tools/dtoc/test_dtoc.py | 184 ++++++++++++++++++++++++++++++------- 2 files changed, 169 insertions(+), 35 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ca2e55fa52..ab26c4adca 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -639,7 +639,7 @@ class DtbPlatdata(): """ self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name) self.buf('\t.name\t\t= "%s",\n' % node.struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, node.var_name)) idx = -1 @@ -860,8 +860,22 @@ class DtbPlatdata(): self.out('#include \n') self.out('\n') - for node in self._valid_nodes: - self.output_node_plat(node) + if self._valid_nodes: + self.out('/*\n') + self.out( + " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n") + self.out(' *\n') + self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver')) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + for node in self._valid_nodes: + self.out(' * %3d: %-20s %-s\n' % + (node.idx, node.var_name, node.struct_name)) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + self.out(' */\n') + self.out('\n') + + for node in self._valid_nodes: + self.output_node_plat(node) self.out(''.join(self.get_buf())) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c6e33d3422..56d5c8d6b3 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -292,6 +292,19 @@ struct dtd_sandbox_spl_test { }; ''' platdata_text = C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: i2c_at_0 sandbox_i2c + * 1: pmic_at_9 sandbox_pmic + * 2: spl_test sandbox_spl_test + * 3: spl_test2 sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * --- -------------------- -------------------- + */ + /* * Node /i2c@0 index 0 * driver sandbox_i2c parent None @@ -300,7 +313,7 @@ static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { \t.name\t\t= "sandbox_i2c", -\t.plat\t= &dtv_i2c_at_0, +\t.plat\t\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), \t.parent_idx\t= -1, }; @@ -315,7 +328,7 @@ static struct dtd_sandbox_pmic dtv_pmic_at_9 = { }; U_BOOT_DRVINFO(pmic_at_9) = { \t.name\t\t= "sandbox_pmic", -\t.plat\t= &dtv_pmic_at_9, +\t.plat\t\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), \t.parent_idx\t= 0, }; @@ -338,7 +351,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -360,7 +373,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, }; @@ -376,7 +389,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test3 = { }; U_BOOT_DRVINFO(spl_test3) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test3, +\t.plat\t\t= &dtv_spl_test3, \t.plat_size\t= sizeof(dtv_spl_test3), \t.parent_idx\t= -1, }; @@ -430,6 +443,15 @@ struct dtd_sandbox_gpio { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: gpios_at_0 sandbox_gpio + * --- -------------------- -------------------- + */ + /* * Node /gpios@0 index 0 * driver sandbox_gpio parent None @@ -441,7 +463,7 @@ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { }; U_BOOT_DRVINFO(gpios_at_0) = { \t.name\t\t= "sandbox_gpio", -\t.plat\t= &dtv_gpios_at_0, +\t.plat\t\t= &dtv_gpios_at_0, \t.plat_size\t= sizeof(dtv_gpios_at_0), \t.parent_idx\t= -1, }; @@ -470,12 +492,21 @@ struct dtd_invalid { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test invalid + * --- -------------------- -------------------- + */ + /* Node /spl-test index 0 */ static struct dtd_invalid dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "invalid", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -502,13 +533,26 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -519,7 +563,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -534,7 +578,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -546,7 +590,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -557,7 +601,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -588,6 +632,16 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle_source2 source + * 1: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle-source2 index 0 */ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { @@ -595,7 +649,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -605,7 +659,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -622,13 +676,26 @@ U_BOOT_DRVINFO(phandle_target) = { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -639,7 +706,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -654,7 +721,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -666,7 +733,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -677,7 +744,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -727,13 +794,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -744,7 +822,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -755,7 +833,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -782,13 +860,23 @@ struct dtd_test2 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -799,7 +887,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -829,13 +917,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -846,7 +945,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -857,7 +956,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -887,13 +986,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -904,7 +1014,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -915,7 +1025,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -961,6 +1071,16 @@ struct dtd_sandbox_spl_test { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test sandbox_spl_test + * 1: spl_test2 sandbox_spl_test + * --- -------------------- -------------------- + */ + /* * Node /spl-test index 0 * driver sandbox_spl_test parent None @@ -970,7 +1090,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -984,7 +1104,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, }; From ea74c95103c66282b8c43e7893bdcd533cab220f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:20 -0700 Subject: [PATCH 043/120] dtoc: Generate uclass devices Add support for generating a file containing uclass instances. This avoids the need to create these at run time. Update a test uclass to include a 'priv_auto' member, to increase test coverage. Signed-off-by: Simon Glass --- drivers/misc/test_drv.c | 1 + include/dm/test.h | 5 ++ tools/dtoc/dtb_platdata.py | 93 +++++++++++++++++++++ tools/dtoc/test_dtoc.py | 164 ++++++++++++++++++++++++++++++++++--- 4 files changed, 250 insertions(+), 13 deletions(-) diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index a2a77d36bb..f431a576f1 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -205,6 +205,7 @@ UCLASS_DRIVER(testfdt) = { .name = "testfdt", .id = UCLASS_TEST_FDT, .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct dm_test_uc_priv), }; static const struct udevice_id testfdtm_ids[] = { diff --git a/include/dm/test.h b/include/dm/test.h index fe1cc2e278..30f71edbd9 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -71,6 +71,11 @@ struct dm_test_priv { int uclass_postp; }; +/* struct dm_test_uc_priv - private data for the testdrv uclass */ +struct dm_test_uc_priv { + int dummy; +}; + /** * struct dm_test_perdev_class_priv - private per-device data for test uclass */ diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ab26c4adca..6dadf37582 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -246,6 +246,7 @@ class DtbPlatdata(): """ if self._outfile != sys.stdout: self._outfile.close() + self._outfile = None def out(self, line): """Output a string to the output file @@ -649,6 +650,27 @@ class DtbPlatdata(): self.buf('};\n') self.buf('\n') + def prep_priv(self, struc, name, suffix, section='.priv_data'): + if not struc: + return None + var_name = '_%s%s' % (name, suffix) + hdr = self._scan._structs.get(struc) + if hdr: + self.buf('#include <%s>\n' % hdr.fname) + else: + print('Warning: Cannot find header file for struct %s' % struc) + attr = '__attribute__ ((section ("%s")))' % section + return var_name, struc, attr + + def alloc_priv(self, info, name, extra, suffix='_priv'): + result = self.prep_priv(info, name, suffix) + if not result: + return None + var_name, struc, section = result + self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' % + (var_name, extra, struc.strip(), section)) + return '%s_%s' % (var_name, extra) + def _output_prop(self, node, prop): """Output a line containing the value of a struct member @@ -680,6 +702,74 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n') + def list_head(self, head_member, node_member, node_refs, var_name): + self.buf('\t.%s\t= {\n' % head_member) + if node_refs: + last = node_refs[-1].dev_ref + first = node_refs[0].dev_ref + member = node_member + else: + last = 'DM_DEVICE_REF(%s)' % var_name + first = last + member = head_member + self.buf('\t\t.prev = &%s->%s,\n' % (last, member)) + self.buf('\t\t.next = &%s->%s,\n' % (first, member)) + self.buf('\t},\n') + + def list_node(self, member, node_refs, seq): + self.buf('\t.%s\t= {\n' % member) + self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1]) + self.buf('\t\t.next = %s,\n' % node_refs[seq + 1]) + self.buf('\t},\n') + + def generate_uclasses(self): + if not self.check_instantiate(True): + return + self.out('\n') + self.out('#include \n') + self.out('#include \n') + self.out('#include \n') + self.out('\n') + self.buf('/*\n') + self.buf(' * uclass declarations\n') + self.buf(' *\n') + self.buf(' * Sequence numbers:\n') + uclass_list = self._valid_uclasses + for uclass in uclass_list: + if uclass.alias_num_to_node: + self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id)) + for seq, node in uclass.alias_num_to_node.items(): + self.buf(' * %d: %s\n' % (seq, node.path)) + self.buf(' */\n') + + uclass_node = {} + for seq, uclass in enumerate(uclass_list): + uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' % + uclass.name) + uclass_node[-1] = '&uclass_head' + uclass_node[len(uclass_list)] = '&uclass_head' + self.buf('\n') + self.buf('struct list_head %s = {\n' % 'uclass_head') + self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1]) + self.buf('\t.next = %s,\n' % uclass_node[0]) + self.buf('};\n') + self.buf('\n') + + for seq, uclass in enumerate(uclass_list): + uc_drv = self._scan._uclass.get(uclass.uclass_id) + + priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '') + + self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name) + if priv_name: + self.buf('\t.priv_\t\t= %s,\n' % priv_name) + self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name) + self.list_node('sibling_node', uclass_node, seq) + self.list_head('dev_head', 'uclass_node', uc_drv.devs, None) + self.buf('};\n') + self.buf('\n') + self.out(''.join(self.get_buf())) + def read_aliases(self): """Read the aliases and attach the information to self._alias @@ -894,6 +984,9 @@ OUTPUT_FILES = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + 'uclass': + OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses, + 'Declares the uclass instances (struct uclass)'), } diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 56d5c8d6b3..053d140664 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -16,6 +16,7 @@ import os import struct import unittest +from dtb_platdata import Ftype from dtb_platdata import get_value from dtb_platdata import tab_to from dtoc import dtb_platdata @@ -65,6 +66,18 @@ C_HEADER = C_HEADER_PRE + ''' #include ''' +UCLASS_HEADER_COMMON = '''/* + * DO NOT MODIFY + * + * Declares the uclass instances (struct uclass). + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + +UCLASS_HEADER = UCLASS_HEADER_COMMON + ''' +/* This file is not used: --instantiate was not enabled */ +''' + # Scanner saved from a previous run of the tests (to speed things up) saved_scan = None @@ -245,31 +258,35 @@ DM_UCLASS_DRIVER_DECL(pmic); /* driver declarations - these allow DM_DRIVER_GET() to be used */ DM_DRIVER_DECL(sandbox_i2c); -DM_DRIVER_DECL(sandbox_pmic); DM_DRIVER_DECL(root_driver); +DM_DRIVER_DECL(denx_u_boot_test_bus); DM_DRIVER_DECL(sandbox_spl_test); DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(denx_u_boot_fdt_test); +DM_DRIVER_DECL(denx_u_boot_fdt_test); /* device declarations - these allow DM_DEVICE_REF() to be used */ -DM_DEVICE_DECL(i2c_at_0); -DM_DEVICE_DECL(pmic_at_9); +DM_DEVICE_DECL(i2c); DM_DEVICE_DECL(root); +DM_DEVICE_DECL(some_bus); DM_DEVICE_DECL(spl_test); -DM_DEVICE_DECL(spl_test2); DM_DEVICE_DECL(spl_test3); +DM_DEVICE_DECL(test); +DM_DEVICE_DECL(test0); /* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ DM_UCLASS_DRIVER_DECL(i2c); DM_UCLASS_DRIVER_DECL(misc); -DM_UCLASS_DRIVER_DECL(pmic); DM_UCLASS_DRIVER_DECL(root); +DM_UCLASS_DRIVER_DECL(testbus); +DM_UCLASS_DRIVER_DECL(testfdt); /* uclass declarations - needed for DM_UCLASS_REF() */ DM_UCLASS_DECL(i2c); DM_UCLASS_DECL(misc); -DM_UCLASS_DECL(pmic); DM_UCLASS_DECL(root); +DM_UCLASS_DECL(testbus); +DM_UCLASS_DECL(testfdt); ''' struct_text = HEADER + ''' struct dtd_sandbox_i2c { @@ -394,6 +411,101 @@ U_BOOT_DRVINFO(spl_test3) = { \t.parent_idx\t= -1, }; +''' + uclass_text = UCLASS_HEADER + uclass_text_inst = ''' + +#include +#include +#include + +/* + * uclass declarations + * + * Sequence numbers: + * i2c: UCLASS_I2C + * 4: /i2c + * misc: UCLASS_MISC + * 0: /spl-test + * 1: /spl-test3 + * root: UCLASS_ROOT + * 0: / + * testbus: UCLASS_TEST_BUS + * 2: /some-bus + * testfdt: UCLASS_TEST_FDT + * 1: /some-bus/test + * 2: /some-bus/test0 + */ + +struct list_head uclass_head = { + .prev = &DM_UCLASS_REF(testfdt)->sibling_node, + .next = &DM_UCLASS_REF(i2c)->sibling_node, +}; + +DM_UCLASS_INST(i2c) = { + .uc_drv = DM_UCLASS_DRIVER_REF(i2c), + .sibling_node = { + .prev = &uclass_head, + .next = &DM_UCLASS_REF(misc)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(i2c)->uclass_node, + .next = &DM_DEVICE_REF(i2c)->uclass_node, + }, +}; + +DM_UCLASS_INST(misc) = { + .uc_drv = DM_UCLASS_DRIVER_REF(misc), + .sibling_node = { + .prev = &DM_UCLASS_REF(i2c)->sibling_node, + .next = &DM_UCLASS_REF(root)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(spl_test3)->uclass_node, + .next = &DM_DEVICE_REF(spl_test)->uclass_node, + }, +}; + +DM_UCLASS_INST(root) = { + .uc_drv = DM_UCLASS_DRIVER_REF(root), + .sibling_node = { + .prev = &DM_UCLASS_REF(misc)->sibling_node, + .next = &DM_UCLASS_REF(testbus)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(root)->uclass_node, + .next = &DM_DEVICE_REF(root)->uclass_node, + }, +}; + +DM_UCLASS_INST(testbus) = { + .uc_drv = DM_UCLASS_DRIVER_REF(testbus), + .sibling_node = { + .prev = &DM_UCLASS_REF(root)->sibling_node, + .next = &DM_UCLASS_REF(testfdt)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(some_bus)->uclass_node, + .next = &DM_DEVICE_REF(some_bus)->uclass_node, + }, +}; + +#include +u8 _testfdt_priv_[sizeof(struct dm_test_uc_priv)] + __attribute__ ((section (".priv_data"))); +DM_UCLASS_INST(testfdt) = { + .priv_ = _testfdt_priv_, + .uc_drv = DM_UCLASS_DRIVER_REF(testfdt), + .sibling_node = { + .prev = &DM_UCLASS_REF(testbus)->sibling_node, + .next = &uclass_head, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(test0)->uclass_node, + .next = &DM_DEVICE_REF(test)->uclass_node, + }, +}; + ''' def test_simple(self): @@ -422,7 +534,7 @@ U_BOOT_DRVINFO(spl_test3) = { self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) self._check_strings(self.decl_text + self.platdata_text + - self.struct_text, data) + self.struct_text + self.uclass_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -1125,7 +1237,7 @@ U_BOOT_DRVINFO(spl_test2) = { self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) self._check_strings(self.decl_text + self.platdata_text + - self.struct_text, data) + self.struct_text + self.uclass_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -1141,7 +1253,7 @@ U_BOOT_DRVINFO(spl_test2) = { with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn( - "Unknown command 'invalid-cmd': (use: decl, platdata, struct)", + "Unknown command 'invalid-cmd': (use: decl, platdata, struct, uclass)", str(exc.exception)) def test_output_conflict(self): @@ -1169,12 +1281,12 @@ U_BOOT_DRVINFO(spl_test2) = { ['all'], dtb_file, False, None, [outdir], None, False, warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') - self.assertEqual(5, len(fnames)) + self.assertEqual(6, len(fnames)) leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', - 'dt-decl.h'}, + 'dt-uclass.c', 'dt-decl.h'}, leafs) def setup_process_test(self): @@ -1363,7 +1475,7 @@ U_BOOT_DRVINFO(spl_test2) = { def test_simple_inst(self): """Test output from some simple nodes with instantiate enabled""" - dtb_file = get_dtb_file('dtoc_test_simple.dts') + dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') self.run_test(['decl'], dtb_file, output, True) @@ -1379,3 +1491,29 @@ U_BOOT_DRVINFO(spl_test2) = { self._check_strings(C_HEADER_PRE + ''' /* This file is not used: --instantiate was enabled */ ''', data) + + self.run_test(['uclass'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(UCLASS_HEADER_COMMON + self.uclass_text_inst, data) + + def test_inst_no_hdr(self): + """Test dealing with a struct that has no header""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + + # Run it once to set everything up + plat = self.run_test(['decl'], dtb_file, output, True) + scan = plat._scan + + # Restart the output file and delete any record of the uclass' struct + plat.setup_output(Ftype.SOURCE, output) + del scan._structs['dm_test_uc_priv'] + + # Now generate the uclasses, which should provide a warning + with test_util.capture_sys_output() as (stdout, _): + plat.generate_uclasses() + self.assertEqual( + 'Warning: Cannot find header file for struct dm_test_uc_priv', + stdout.getvalue().strip()) From d392d32fd8d5fb28cd5c5888cd303e57a507dda9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:21 -0700 Subject: [PATCH 044/120] dtoc: Generate device instances Add support for generating a file containing udevice instances. This avoids the need to create these at run time. Update a test uclass to include a 'per_device_plat_auto' member, to increase test coverage. Add another tab to the driver_info output so it lines up nicely like the device-instance output. Signed-off-by: Simon Glass --- drivers/misc/test_drv.c | 4 + tools/dtoc/dtb_platdata.py | 168 ++++++++++++++++++- tools/dtoc/test_dtoc.py | 327 +++++++++++++++++++++++++++++++++++-- 3 files changed, 484 insertions(+), 15 deletions(-) diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index f431a576f1..5d72982f25 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -98,6 +98,7 @@ U_BOOT_DRIVER(denx_u_boot_test_bus) = { .per_child_plat_auto = sizeof(struct dm_test_parent_plat), .child_pre_probe = testbus_child_pre_probe, .child_post_remove = testbus_child_post_remove, + DM_HEADER() }; UCLASS_DRIVER(testbus) = { @@ -106,6 +107,9 @@ UCLASS_DRIVER(testbus) = { .flags = DM_UC_FLAG_SEQ_ALIAS, .child_pre_probe = testbus_child_pre_probe_uclass, .child_post_probe = testbus_child_post_probe_uclass, + + /* This is for dtoc testing only */ + .per_device_plat_auto = sizeof(struct dm_test_uclass_priv), }; static int testfdt_drv_ping(struct udevice *dev, int pingval, int *pingret) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 6dadf37582..f5b5ad5f71 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -671,7 +671,104 @@ class DtbPlatdata(): (var_name, extra, struc.strip(), section)) return '%s_%s' % (var_name, extra) - def _output_prop(self, node, prop): + def alloc_plat(self, info, name, extra, node): + result = self.prep_priv(info, name, '_plat') + if not result: + return None + var_name, struc, section = result + self.buf('struct %s %s\n\t%s_%s = {\n' % + (struc.strip(), section, var_name, extra)) + self.buf('\t.dtplat = {\n') + for pname in sorted(node.props): + self._output_prop(node, node.props[pname], 2) + self.buf('\t},\n') + self.buf('};\n') + return '&%s_%s' % (var_name, extra) + + def _declare_device_inst(self, node, parent_driver): + """Add a device instance declaration to the output + + This declares a DM_DEVICE_INST() for the device being processed + + Args: + node: Node to output + """ + driver = node.driver + uclass = node.uclass + self.buf('\n') + num_lines = len(self._lines) + plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name, + node) + priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name) + parent_plat_name = None + parent_priv_name = None + if parent_driver: + # TODO: deal with uclass providing these values + parent_plat_name = self.alloc_priv( + parent_driver.child_plat, driver.name, node.var_name, + '_parent_plat') + parent_priv_name = self.alloc_priv( + parent_driver.child_priv, driver.name, node.var_name, + '_parent_priv') + uclass_plat_name = self.alloc_priv( + uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat') + uclass_priv_name = self.alloc_priv(uclass.per_dev_priv, + driver.name + '_uc', node.var_name) + for hdr in driver.headers: + self.buf('#include %s\n' % hdr) + + # Add a blank line if we emitted any stuff above, for readability + if num_lines != len(self._lines): + self.buf('\n') + + self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name) + self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name) + self.buf('\t.name\t\t= "%s",\n' % node.struct_name) + if plat_name: + self.buf('\t.plat_\t\t= %s,\n' % plat_name) + else: + self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + if parent_plat_name: + self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name) + if uclass_plat_name: + self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name) + driver_date = None + + if node != self._fdt.GetRoot(): + compat_list = node.props['compatible'].value + if not isinstance(compat_list, list): + compat_list = [compat_list] + for compat in compat_list: + driver_data = driver.compat.get(compat) + if driver_data: + self.buf('\t.driver_data\t= %s,\n' % driver_data) + break + + if node.parent and node.parent.parent: + self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' % + node.parent.var_name) + if priv_name: + self.buf('\t.priv_\t\t= %s,\n' % priv_name) + self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name) + + if uclass_priv_name: + self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name) + if parent_priv_name: + self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name) + self.list_node('uclass_node', uclass.node_refs, node.uclass_seq) + self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name) + if node.parent in self._valid_nodes: + self.list_node('sibling_node', node.parent.child_refs, + node.parent_seq) + # flags is left as 0 + + self.buf('\t.seq_ = %d,\n' % node.seq) + + self.buf('};\n') + self.buf('\n') + return parent_plat_name + + def _output_prop(self, node, prop, tabs=1): """Output a line containing the value of a struct member Args: @@ -681,7 +778,7 @@ class DtbPlatdata(): if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': return member_name = conv_name_to_c(prop.name) - self.buf('\t%s= ' % tab_to(3, '.' + member_name)) + self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name))) # Special handling for lists if isinstance(prop.value, list): @@ -731,10 +828,13 @@ class DtbPlatdata(): self.out('#include \n') self.out('\n') self.buf('/*\n') - self.buf(' * uclass declarations\n') - self.buf(' *\n') - self.buf(' * Sequence numbers:\n') + self.buf( + " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n") uclass_list = self._valid_uclasses + for seq, uclass in enumerate(uclass_list): + self.buf(' * %3d: %s\n' % (seq, uclass.name)) + self.buf(' *\n') + self.buf(' * Sequence numbers allocated in each uclass:\n') for uclass in uclass_list: if uclass.alias_num_to_node: self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id)) @@ -914,6 +1014,26 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) + def output_node_instance(self, node): + """Output the C code for a node + + Args: + node (fdt.Node): node to output + """ + parent_driver = node.parent_driver + + self.buf('/*\n') + self.buf(' * Node %s index %d\n' % (node.path, node.idx)) + self.buf(' * driver %s parent %s\n' % (node.driver.name, + parent_driver.name if parent_driver else 'None')) + self.buf('*/\n') + + if not node.driver.plat: + self._output_values(node) + self._declare_device_inst(node, parent_driver) + + self.out(''.join(self.get_buf())) + def check_instantiate(self, require): """Check if self._instantiate is set to the required value @@ -969,6 +1089,41 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) + def generate_device(self): + """Generate device instances + + This writes out DM_DEVICE_INST() records for each device in the + build. + + See the documentation in doc/driver-model/of-plat.rst for more + information. + """ + if not self.check_instantiate(True): + return + self.out('#include \n') + self.out('#include \n') + self.out('#include \n') + self.out('\n') + + if self._valid_nodes: + self.out('/*\n') + self.out( + " * udevice declarations, ordered by 'struct udevice' linker_list position:\n") + self.out(' *\n') + self.out(' * idx %-20s %-s\n' % ('udevice', 'driver')) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + for node in self._valid_nodes: + self.out(' * %3d: %-20s %-s\n' % + (node.idx, node.var_name, node.struct_name)) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + self.out(' */\n') + self.out('\n') + + for node in self._valid_nodes: + self.output_node_instance(node) + + self.out(''.join(self.get_buf())) + # Types of output file we understand # key: Command used to generate this file @@ -984,6 +1139,9 @@ OUTPUT_FILES = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + 'device': + OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device, + 'Declares the DM_DEVICE_INST() records'), 'uclass': OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses, 'Declares the uclass instances (struct uclass)'), diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 053d140664..bb689f3b6e 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -420,9 +420,14 @@ U_BOOT_DRVINFO(spl_test3) = { #include /* - * uclass declarations + * uclass declarations, ordered by 'struct uclass' linker_list idx: + * 0: i2c + * 1: misc + * 2: root + * 3: testbus + * 4: testfdt * - * Sequence numbers: + * Sequence numbers allocated in each uclass: * i2c: UCLASS_I2C * 4: /i2c * misc: UCLASS_MISC @@ -506,6 +511,300 @@ DM_UCLASS_INST(testfdt) = { }, }; +''' + device_text = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +/* This file is not used: --instantiate was not enabled */ +''' + device_text_inst = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include +#include +#include + +/* + * udevice declarations, ordered by 'struct udevice' linker_list position: + * + * idx udevice driver + * --- -------------------- -------------------- + * 0: i2c sandbox_i2c + * 1: root root_driver + * 2: some_bus denx_u_boot_test_bus + * 3: spl_test sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * 5: test denx_u_boot_fdt_test + * 6: test0 denx_u_boot_fdt_test + * --- -------------------- -------------------- + */ + +/* + * Node /i2c index 0 + * driver sandbox_i2c parent root_driver +*/ +static struct dtd_sandbox_i2c dtv_i2c = { +\t.intval\t\t\t= 0x3, +}; + +#include +u8 _sandbox_i2c_priv_i2c[sizeof(struct sandbox_i2c_priv)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _sandbox_i2c_uc_priv_i2c[sizeof(struct dm_i2c_bus)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(i2c) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_i2c), +\t.name\t\t= "sandbox_i2c", +\t.plat_\t\t= &dtv_i2c, +\t.priv_\t\t= _sandbox_i2c_priv_i2c, +\t.uclass\t\t= DM_UCLASS_REF(i2c), +\t.uclass_priv_ = _sandbox_i2c_uc_priv_i2c, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(i2c)->dev_head, +\t\t.next = &DM_UCLASS_REF(i2c)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->child_head, +\t\t.next = &DM_DEVICE_REF(i2c)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(root)->child_head, +\t\t.next = &DM_DEVICE_REF(some_bus)->sibling_node, +\t}, +\t.seq_ = 4, +}; + +/* + * Node / index 1 + * driver root_driver parent None +*/ +static struct dtd_root_driver dtv_root = { +}; + +DM_DEVICE_INST(root) = { +\t.driver\t\t= DM_DRIVER_REF(root_driver), +\t.name\t\t= "root_driver", +\t.plat_\t\t= &dtv_root, +\t.uclass\t\t= DM_UCLASS_REF(root), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(root)->dev_head, +\t\t.next = &DM_UCLASS_REF(root)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t\t.next = &DM_DEVICE_REF(i2c)->sibling_node, +\t}, +\t.seq_ = 0, +}; + +/* + * Node /some-bus index 2 + * driver denx_u_boot_test_bus parent root_driver +*/ + +#include +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_test_bus_plat_some_bus = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x4, +\t\t.ping_expect\t\t= 0x4, +\t\t.reg\t\t\t= {0x3, 0x1}, +\t}, +}; +#include +u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_priv)] +\t__attribute__ ((section (".priv_data"))); +#include + +DM_DEVICE_INST(some_bus) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_test_bus), +\t.name\t\t= "denx_u_boot_test_bus", +\t.plat_\t\t= &_denx_u_boot_test_bus_plat_some_bus, +\t.uclass_plat_\t= _denx_u_boot_test_bus_ucplat_some_bus, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus, +\t.uclass\t\t= DM_UCLASS_REF(testbus), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testbus)->dev_head, +\t\t.next = &DM_UCLASS_REF(testbus)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->sibling_node, +\t\t.next = &DM_DEVICE_REF(test)->sibling_node, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test)->sibling_node, +\t}, +\t.seq_ = 2, +}; + +/* + * Node /spl-test index 3 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.boolval\t\t= true, +\t.intval\t\t\t= 0x1, +}; + +DM_DEVICE_INST(spl_test) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(misc)->dev_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t}, +\t.seq_ = 0, +}; + +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test3 = { +\t.longbytearray\t\t= {0x90a0b0c, 0xd0e0f10}, +\t.stringarray\t\t= "one", +}; + +DM_DEVICE_INST(spl_test3) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test3, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(misc)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(root)->child_head, +\t}, +\t.seq_ = 1, +}; + +/* + * Node /some-bus/test index 5 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_fdt_test_plat_test = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x5, +\t\t.ping_expect\t\t= 0x5, +\t\t.reg\t\t\t= {0x5, 0x0}, +\t}, +}; +#include +u8 _denx_u_boot_fdt_test_priv_test[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_fdt_test_parent_plat_test[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_fdt_test_parent_priv_test[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testfdt)->dev_head, +\t\t.next = &DM_DEVICE_REF(test0)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test)->child_head, +\t\t.next = &DM_DEVICE_REF(test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->sibling_node, +\t}, +\t.seq_ = 1, +}; + +/* + * Node /some-bus/test0 index 6 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_fdt_test_plat_test0 = { +\t.dtplat = { +\t}, +}; +#include +u8 _denx_u_boot_fdt_test_priv_test0[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_fdt_test_parent_plat_test0[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_fdt_test_parent_priv_test0[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test0) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test0, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test0, +\t.driver_data\t= DM_TEST_TYPE_SECOND, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test0, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test0, +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(testfdt)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(some_bus)->child_head, +\t}, +\t.seq_ = 2, +}; + ''' def test_simple(self): @@ -533,8 +832,9 @@ DM_UCLASS_INST(testfdt) = { # Try the 'all' command self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.decl_text + self.platdata_text + - self.struct_text + self.uclass_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -1236,8 +1536,9 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.decl_text + self.platdata_text + - self.struct_text + self.uclass_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -1253,7 +1554,7 @@ U_BOOT_DRVINFO(spl_test2) = { with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn( - "Unknown command 'invalid-cmd': (use: decl, platdata, struct, uclass)", + "Unknown command 'invalid-cmd': (use: decl, device, platdata, struct, uclass)", str(exc.exception)) def test_output_conflict(self): @@ -1281,12 +1582,12 @@ U_BOOT_DRVINFO(spl_test2) = { ['all'], dtb_file, False, None, [outdir], None, False, warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') - self.assertEqual(6, len(fnames)) + self.assertEqual(7, len(fnames)) leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', - 'dt-uclass.c', 'dt-decl.h'}, + 'dt-uclass.c', 'dt-decl.h', 'dt-device.c'}, leafs) def setup_process_test(self): @@ -1498,8 +1799,14 @@ U_BOOT_DRVINFO(spl_test2) = { self._check_strings(UCLASS_HEADER_COMMON + self.uclass_text_inst, data) + self.run_test(['device'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.device_text_inst, data) + def test_inst_no_hdr(self): - """Test dealing with a struct that has no header""" + """Test dealing with a struct tsssshat has no header""" dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') From 929e9940b0e4b9d7dfb0668ae1d7ce76a247b1e8 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Tue, 16 Mar 2021 21:51:44 +0100 Subject: [PATCH 045/120] bus: ti-sysc: change in a normal driver The module defines a duplicate uclass driver for UCLASS_SIMPLE_BUS, but it is not allowed. This breaks of-platdata and makes the result non-deterministic. The driver does not need to be an uclass driver, so lets remove it. I had turned it into an uclass driver because I thought wrongly it had to call the dm_scan_fdt_dev routine to work properly, but some tests on the board have shown otherwise. Signed-off-by: Dario Binacchi Reviewed-by: Simon Glass --- drivers/bus/ti-sysc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 4e3d610300..778c0654f6 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -148,12 +148,6 @@ clocks_err: return err; } -UCLASS_DRIVER(ti_sysc) = { - .id = UCLASS_SIMPLE_BUS, - .name = "ti_sysc", - .post_bind = dm_scan_fdt_dev -}; - U_BOOT_DRIVER(ti_sysc) = { .name = "ti_sysc", .id = UCLASS_SIMPLE_BUS, From cff7dcf3fd0de0120cd40c0c28864cb045143f97 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:11 +1300 Subject: [PATCH 046/120] dtoc: Drop use of DECL() macros We can use extern instead, so let's drop these macros. It adds one more thing to learn about and doesn't make the code any clearer. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 8 ++--- tools/dtoc/test_dtoc.py | 64 +++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index f5b5ad5f71..b5c449ebb4 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -902,14 +902,14 @@ class DtbPlatdata(): self.buf( '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n') for node in nodes_to_output: - self.buf('DM_DRIVER_DECL(%s);\n' % node.struct_name); + self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name); self.buf('\n') if self._instantiate: self.buf( '/* device declarations - these allow DM_DEVICE_REF() to be used */\n') for node in nodes_to_output: - self.buf('DM_DEVICE_DECL(%s);\n' % node.var_name) + self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name) self.buf('\n') uclass_list = self._valid_uclasses @@ -917,13 +917,13 @@ class DtbPlatdata(): self.buf( '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n') for uclass in uclass_list: - self.buf('DM_UCLASS_DRIVER_DECL(%s);\n' % uclass.name) + self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name) if self._instantiate: self.buf('\n') self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n') for uclass in uclass_list: - self.buf('DM_UCLASS_DECL(%s);\n' % uclass.name) + self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name) self.out(''.join(self.get_buf())) def assign_seqs(self): diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index bb689f3b6e..1912a8723f 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -241,52 +241,52 @@ class TestDtoc(unittest.TestCase): #include /* driver declarations - these allow DM_DRIVER_GET() to be used */ -DM_DRIVER_DECL(sandbox_i2c); -DM_DRIVER_DECL(sandbox_pmic); -DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(sandbox_pmic); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); /* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ -DM_UCLASS_DRIVER_DECL(i2c); -DM_UCLASS_DRIVER_DECL(misc); -DM_UCLASS_DRIVER_DECL(pmic); +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(pmic); ''' decl_text_inst = DECL_HEADER + ''' #include #include /* driver declarations - these allow DM_DRIVER_GET() to be used */ -DM_DRIVER_DECL(sandbox_i2c); -DM_DRIVER_DECL(root_driver); -DM_DRIVER_DECL(denx_u_boot_test_bus); -DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(denx_u_boot_fdt_test); -DM_DRIVER_DECL(denx_u_boot_fdt_test); +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(root_driver); +extern U_BOOT_DRIVER(denx_u_boot_test_bus); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); /* device declarations - these allow DM_DEVICE_REF() to be used */ -DM_DEVICE_DECL(i2c); -DM_DEVICE_DECL(root); -DM_DEVICE_DECL(some_bus); -DM_DEVICE_DECL(spl_test); -DM_DEVICE_DECL(spl_test3); -DM_DEVICE_DECL(test); -DM_DEVICE_DECL(test0); +extern DM_DEVICE_INST(i2c); +extern DM_DEVICE_INST(root); +extern DM_DEVICE_INST(some_bus); +extern DM_DEVICE_INST(spl_test); +extern DM_DEVICE_INST(spl_test3); +extern DM_DEVICE_INST(test); +extern DM_DEVICE_INST(test0); /* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ -DM_UCLASS_DRIVER_DECL(i2c); -DM_UCLASS_DRIVER_DECL(misc); -DM_UCLASS_DRIVER_DECL(root); -DM_UCLASS_DRIVER_DECL(testbus); -DM_UCLASS_DRIVER_DECL(testfdt); +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(root); +extern UCLASS_DRIVER(testbus); +extern UCLASS_DRIVER(testfdt); /* uclass declarations - needed for DM_UCLASS_REF() */ -DM_UCLASS_DECL(i2c); -DM_UCLASS_DECL(misc); -DM_UCLASS_DECL(root); -DM_UCLASS_DECL(testbus); -DM_UCLASS_DECL(testfdt); +extern DM_UCLASS_INST(i2c); +extern DM_UCLASS_INST(misc); +extern DM_UCLASS_INST(root); +extern DM_UCLASS_INST(testbus); +extern DM_UCLASS_INST(testfdt); ''' struct_text = HEADER + ''' struct dtd_sandbox_i2c { From b5e514a6abc5cc31d350f9ca25cb6a6d4895c338 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:12 +1300 Subject: [PATCH 047/120] sandbox: Drop debug message in os_spl_to_uboot() This is not needed in normal operation. Drop it. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- arch/sandbox/cpu/os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 2d9583c17c..68825d28d6 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -845,7 +845,6 @@ int os_spl_to_uboot(const char *fname) { struct sandbox_state *state = state_get_current(); - printf("%s\n", __func__); /* U-Boot will delete ram buffer after read: "--rm_memory"*/ state->ram_buf_rm = true; return os_jump_to_file(fname); From 851144350b6ff1d4982b9af2f9b0fe48aed24b3c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:13 +1300 Subject: [PATCH 048/120] linker_lists: Allow use in data structures At present linker lists are designed for use in code. They make use of statements within expressions ({...}), for example. It is possible to generate a reference to a linker_list entry that can be used in data structures, where such features are not permitted. It requires that the reference first be declared as extern. In other words the existing macro needs to be split into two parts. Add new macros to support this. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- include/linker_lists.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linker_lists.h b/include/linker_lists.h index fd98ecd297..0ca30da417 100644 --- a/include/linker_lists.h +++ b/include/linker_lists.h @@ -211,6 +211,18 @@ _ll_result; \ }) +/** + * ll_entry_ref() - Get a reference to a linker-generated array entry + * + * Once ll_entry_decl() has been used to declare the reference, this macro + * allows the entry to be accessed. + * + * This is like ll_entry_get(), but without the extra code, so it is suitable + * for putting into data structures. + */ +#define ll_entry_ref(_type, _name, _list) \ + ((_type *)&_u_boot_list_2_##_list##_2_##_name) + /** * ll_start() - Point to first entry of first linker-generated array * @_type: Data type of the entry From 607f9bcb0d0af57c52bacf4d3aeb3a7d389ea206 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:14 +1300 Subject: [PATCH 049/120] dm: core: Add macros to access the new linker lists Add macros which work with instantiated devices and uclasses, as created at build time by dtoc. Include variants that can be used in data structures. These are mostly used by dtoc but it is worth documenting them fully for the occasional case where they might come up in user code. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- include/dm/device-internal.h | 75 ++++++++++++++++++++++++++++++++++++ include/dm/device.h | 22 +++++++++++ include/dm/uclass-internal.h | 49 +++++++++++++++++++++++ include/dm/uclass.h | 31 +++++++++++++++ include/linker_lists.h | 4 +- 5 files changed, 179 insertions(+), 2 deletions(-) diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 39406c3f35..71e5c75028 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -10,11 +10,86 @@ #ifndef _DM_DEVICE_INTERNAL_H #define _DM_DEVICE_INTERNAL_H +#include #include struct device_node; struct udevice; +/* + * These two macros DM_DEVICE_INST and DM_DEVICE_REF are only allowed in code + * generated by dtoc, because the ordering is important and if other instances + * creep in then they may mess up the ordering expected by dtoc. + * + * It is OK to use them with 'extern' though, since that does not actually + * add a new record to the linker_list. + */ + +/** + * DM_DEVICE_INST() - Declare a bound device ready for run-time use + * + * This adds an actual struct udevice to a list which is found by driver model + * on start-up. + * + * For example: + * + * extern U_BOOT_DRIVER(sandbox_fixed_clock); + * extern DM_UCLASS_INST(clk); + * + * DM_DEVICE_INST(clk_fixed) = { + * .driver = DM_DRIVER_REF(sandbox_fixed_clock), + * .name = "sandbox_fixed_clock", + * .plat_ = &_sandbox_fixed_clock_plat_clk_fixed, + * .uclass = DM_UCLASS_REF(clk), + * ... + * .seq_ = 0, + * }; + * + * @_name: Name of the udevice. This must be a valid C identifier, used by the + * linker_list. + */ +#define DM_DEVICE_INST(_name) \ + ll_entry_declare(struct udevice, _name, udevice) + +/** + * DM_DEVICE_REF() - Get a reference to a device + * + * This is useful in data structures and code for referencing a udevice at + * build time. Before this is used, an extern DM_DEVICE_INST() must have been + * declared. + * + * For example: + * + * extern DM_DEVICE_INST(clk_fixed); + * + * struct udevice *devs[] = { + * DM_DEVICE_REF(clk_fixed), + * }; + * + * @_name: Name of the udevice. This must be a valid C identifier, used by the + * linker_list + * @returns struct udevice * for the device + */ +#define DM_DEVICE_REF(_name) \ + ll_entry_ref(struct udevice, _name, udevice) + +/** + * DM_DEVICE_GET() - Get a pointer to a given device + * + * This is similar to DM_DEVICE_REF() except that it does not need the extern + * declaration before it. However it cannot be used in a data structures, only + * in code within a function. + * + * For example: + * + * void some_function() { + * struct udevice *dev = DM_DEVICE_GET(clk_fixed); + * ... + * } + */ +#define DM_DEVICE_GET(__name) \ + ll_entry_get(struct udevice, __name, udevice) + /** * device_bind() - Create a device and bind it to a driver * diff --git a/include/dm/device.h b/include/dm/device.h index 45010b4df9..5b8f27d455 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -362,6 +362,28 @@ struct driver { #define DM_DRIVER_GET(__name) \ ll_entry_get(struct driver, __name, driver) +/** + * DM_DRIVER_REF() - Get a reference to a driver + * + * This is useful in data structures and code for referencing a driver at + * build time. Before this is used, an extern U_BOOT_DRIVER() must have been + * declared. + * + * For example: + * + * extern U_BOOT_DRIVER(sandbox_fixed_clock); + * + * struct driver *drvs[] = { + * DM_DRIVER_REF(sandbox_fixed_clock), + * }; + * + * @_name: Name of the driver. This must be a valid C identifier, used by the + * linker_list + * @returns struct driver * for the driver + */ +#define DM_DRIVER_REF(_name) \ + ll_entry_ref(struct driver, _name, driver) + /** * Declare a macro to state a alias for a driver name. This macro will * produce no code but its information will be parsed by tools like diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index c5a464be7c..f2a780682b 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -11,6 +11,55 @@ #include +/* + * These next two macros DM_UCLASS_INST() and DM_UCLASS_REF() are only allowed + * in code generated by dtoc, because the ordering is important and if other + * instances creep in then they may mess up the ordering expected by dtoc. + * + * It is OK to use them with 'extern' though, since that does not actually + * add a new record to the linker_list. + */ + +/** + * DM_UCLASS_INST() - Declare a uclass ready for run-time use + * + * This adds an actual struct uclass to a list which is found by driver model + * on start-up. + * + * For example: + * + * DM_UCLASS_INST(clk) = { + * .uc_drv = DM_UCLASS_DRIVER_REF(clk), + * ... + * }; + * + * @_name: Name of the uclass. This must be a valid C identifier, used by the + * linker_list. + */ +#define DM_UCLASS_INST(_name) \ + ll_entry_declare(struct uclass, _name, uclass) + +/** + * DM_UCLASS_REF() - Get a reference to a uclass + * + * This is useful for referencing a uclass at build time. Before this is used, + * an extern DM_UCLASS_INST() must have been declared. + * + * For example: + * + * extern DM_UCLASS_INST(clk); + * + * struct uclass *ucs[] = { + * DM_UCLASS_REF(clk), + * } + * + * @_name: Name of the uclass. This must be a valid C identifier, used by the + * linker_list + * @returns struct uclass * for the device + */ +#define DM_UCLASS_REF(_name) \ + ll_entry_ref(struct uclass, _name, uclass) + /** * uclass_set_priv() - Set the private data for a uclass * diff --git a/include/dm/uclass.h b/include/dm/uclass.h index d95683740c..6752d8ee0b 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -114,6 +114,37 @@ struct uclass_driver { #define UCLASS_DRIVER(__name) \ ll_entry_declare(struct uclass_driver, __name, uclass_driver) +/* + * These two macros DM_UCLASS_DRIVER_REF and DM_UCLASS_DRIVER_REF are only + * allowed in code generated by dtoc, because the ordering is important and if + * other instances creep in then they may mess up the ordering expected by dtoc. + * + * It is OK to use them with 'extern' though, since that does not actually + * add a new record to the linker_list. + */ + +/** + * DM_UCLASS_DRIVER_REF() - Get a reference to a uclass driver + * + * This is useful in data structures and code for referencing a uclass_driver at + * build time. Before this is used, an extern UCLASS_DRIVER() must have been + * declared. + * + * For example: + * + * extern UCLASS_DRIVER(clk); + * + * struct uclass_driver *drvs[] = { + * DM_UCLASS_DRIVER_REF(clk), + * }; + * + * @_name: Name of the uclass_driver. This must be a valid C identifier, used by + * the linker_list. + * @returns struct uclass_driver * for the uclass driver + */ +#define DM_UCLASS_DRIVER_REF(_name) \ + ll_entry_ref(struct uclass_driver, _name, uclass_driver) + /** * uclass_get_priv() - Get the private data for a uclass * diff --git a/include/linker_lists.h b/include/linker_lists.h index 0ca30da417..81a280a884 100644 --- a/include/linker_lists.h +++ b/include/linker_lists.h @@ -214,8 +214,8 @@ /** * ll_entry_ref() - Get a reference to a linker-generated array entry * - * Once ll_entry_decl() has been used to declare the reference, this macro - * allows the entry to be accessed. + * Once an extern ll_entry_declare() has been used to declare the reference, + * this macro allows the entry to be accessed. * * This is like ll_entry_get(), but without the extra code, so it is suitable * for putting into data structures. From 1ef3af3b2717aba9db79879274ab5e1f1d7b2d25 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:15 +1300 Subject: [PATCH 050/120] dm: core: Allow dropping run-time binding of devices With OF_PLATDATA_INST devices are bound at build time. We should not need binding of devices at runtime in most cases. However it is inflexible to absolutely prohibit it, so add an option to control this. Update the driver model core so that it does not bind devices. Update device_bind() to return an error if called. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/device.c | 42 +++++++++++++++++++++++++----------------- dts/Kconfig | 18 ++++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/drivers/core/device.c b/drivers/core/device.c index 81f6880eac..e915b3089d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -45,6 +45,9 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, bool auto_seq = true; void *ptr; + if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND)) + return -ENOSYS; + if (devp) *devp = NULL; if (!name) @@ -395,26 +398,31 @@ int device_of_to_plat(struct udevice *dev) if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID) return 0; - /* Ensure all parents have ofdata */ - if (dev->parent) { - ret = device_of_to_plat(dev->parent); + /* + * This is not needed if binding is disabled, since data is allocated + * at build time. + */ + if (!CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND)) { + /* Ensure all parents have ofdata */ + if (dev->parent) { + ret = device_of_to_plat(dev->parent); + if (ret) + goto fail; + + /* + * The device might have already been probed during + * the call to device_probe() on its parent device + * (e.g. PCI bridge devices). Test the flags again + * so that we don't mess up the device. + */ + if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID) + return 0; + } + + ret = device_alloc_priv(dev); if (ret) goto fail; - - /* - * The device might have already been probed during - * the call to device_probe() on its parent device - * (e.g. PCI bridge devices). Test the flags again - * so that we don't mess up the device. - */ - if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID) - return 0; } - - ret = device_alloc_priv(dev); - if (ret) - goto fail; - drv = dev->driver; assert(drv); diff --git a/dts/Kconfig b/dts/Kconfig index c39cc36888..d289752a13 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -371,6 +371,15 @@ config SPL_OF_PLATDATA_INST Declare devices as udevice instances so that they do not need to be bound when U-Boot starts. This can save time and code space. +config SPL_OF_PLATDATA_NO_BIND + bool "Don't allow run-time binding of devices" + depends on SPL_OF_PLATDATA_INST + default y + help + This removes the ability to bind devices at run time, thus saving + some code space in U-Boot. This can be disabled if binding is needed, + at the code of some code size increase. + endif config TPL_OF_PLATDATA @@ -411,6 +420,15 @@ config TPL_OF_PLATDATA_INST Declare devices as udevice instances so that they do not need to be bound when U-Boot starts. This can save time and code space. +config TPL_OF_PLATDATA_NO_BIND + bool "Don't allow run-time binding of devices" + depends on TPL_OF_PLATDATA_INST + default y + help + This removes the ability to bind devices at run time, thus saving + some code space in U-Boot. This can be disabled if binding is needed, + at the code of some code size increase. + endif endmenu From 3fa9f553c0c0418b9abf93c2f33a44c98380beaf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:16 +1300 Subject: [PATCH 051/120] dm: core: Adjust uclass setup with of-platdata When OF_PLATDATA_INST is enabled we don't need to create the uclass list. Instead we just need to point to the existing list. Update the code accordingly. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/root.c | 8 ++++++-- include/dm/root.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/core/root.c b/drivers/core/root.c index 9bc682cffe..3feadb77b5 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -140,8 +140,12 @@ int dm_init(bool of_live) dm_warn("Virtual root driver already exists!\n"); return -EINVAL; } - gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST; - INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST); + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + gd->uclass_root = &uclass_head; + } else { + gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST; + INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST); + } if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) { fix_drivers(); diff --git a/include/dm/root.h b/include/dm/root.h index 89afbee619..42510b106a 100644 --- a/include/dm/root.h +++ b/include/dm/root.h @@ -11,6 +11,9 @@ struct udevice; +/* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */ +extern struct list_head uclass_head; + /** * dm_root() - Return pointer to the top of the driver tree * From 967a7d483ac5b9075d83c59507c8020636000842 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:17 +1300 Subject: [PATCH 052/120] dm: core: Set up driver model for OF_PLATDATA_INST With this we don't need to scan and bind drivers, not even the root device. We just need to locate the root device that was set up at build time, then set our root in global_data to point to it. Update the code to handle this case. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/root.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/core/root.c b/drivers/core/root.c index 3feadb77b5..3e52452cd8 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -129,6 +129,13 @@ void fix_devices(void) } } +static int dm_setup_inst(void) +{ + DM_ROOT_NON_CONST = DM_DEVICE_GET(root); + + return 0; +} + int dm_init(bool of_live) { int ret; @@ -153,14 +160,23 @@ int dm_init(bool of_live) fix_devices(); } - ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); - if (ret) - return ret; - if (CONFIG_IS_ENABLED(OF_CONTROL)) - dev_set_ofnode(DM_ROOT_NON_CONST, ofnode_root()); - ret = device_probe(DM_ROOT_NON_CONST); - if (ret) - return ret; + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + ret = dm_setup_inst(); + if (ret) { + log_debug("dm_setup_inst() failed: %d\n", ret); + return ret; + } + } else { + ret = device_bind_by_name(NULL, false, &root_info, + &DM_ROOT_NON_CONST); + if (ret) + return ret; + if (CONFIG_IS_ENABLED(OF_CONTROL)) + dev_set_ofnode(DM_ROOT_NON_CONST, ofnode_root()); + ret = device_probe(DM_ROOT_NON_CONST); + if (ret) + return ret; + } return 0; } @@ -351,10 +367,12 @@ int dm_init_and_scan(bool pre_reloc_only) debug("dm_init() failed: %d\n", ret); return ret; } - ret = dm_scan(pre_reloc_only); - if (ret) { - log_debug("dm_scan() failed: %d\n", ret); - return ret; + if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + ret = dm_scan(pre_reloc_only); + if (ret) { + log_debug("dm_scan() failed: %d\n", ret); + return ret; + } } return 0; From 91bcfdf0b6683681c557db55e1d74424fb1cc4fe Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:18 +1300 Subject: [PATCH 053/120] dm: core: Skip adding uclasses with OF_PLATDATA_INST There is no need to ever add new uclasses since these are set up at build time. Update the code to return an error if this is attempted. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/uclass.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 1a4ea7a57a..f753a1f91e 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -148,8 +148,11 @@ int uclass_get(enum uclass_id id, struct uclass **ucp) *ucp = NULL; uc = uclass_find(id); - if (!uc) + if (!uc) { + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) + return -ENOENT; return uclass_add(id, ucp); + } *ucp = uc; return 0; From 4c6f65074e75fc9ddf5aa553f2f0738617fd00d5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:19 +1300 Subject: [PATCH 054/120] dm: Add the new dtoc-generated files to the build Now that dtoc generates some new C files, add these to the build so that the instantiated devices and uclasses can be used. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- scripts/Makefile.spl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index f3bb793681..849e9c7060 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -120,7 +120,8 @@ endif u-boot-spl-init := $(head-y) u-boot-spl-main := $(libs-y) ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA -u-boot-spl-platdata := $(obj)/dts/dt-plat.o +u-boot-spl-platdata := $(obj)/dts/dt-plat.o $(obj)/dts/dt-uclass.o \ + $(obj)/dts/dt-device.o u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata)) endif From 91f414b3a82e5e8419a0ad703b7055884c4be254 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:48 +1300 Subject: [PATCH 055/120] libfdt: Tidy up pylibfdt build rule At present the build rule for pylibfdt depends on _libfdt.so but modern Python versions add a different suffix to the output file, resulting in something like _libfdt.cpython-38-x86_64-linux-gnu.so The result is that pylibfdt is rebuilt every time. Rename the file the standard name so that the rule works correctly. Also add libfdt.py to the dependencies, so that file is always created if missing. Signed-off-by: Simon Glass --- scripts/dtc/pylibfdt/Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/dtc/pylibfdt/Makefile b/scripts/dtc/pylibfdt/Makefile index 80b6ad2ae7..4782dd45c6 100644 --- a/scripts/dtc/pylibfdt/Makefile +++ b/scripts/dtc/pylibfdt/Makefile @@ -23,12 +23,18 @@ quiet_cmd_pymod = PYMOD $@ SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \ $(PYTHON3) $< --quiet build_ext --inplace -$(obj)/_libfdt.so: $(src)/setup.py $(PYLIBFDT_srcs) FORCE +rebuild: $(src)/setup.py $(PYLIBFDT_srcs) @# Remove the library since otherwise Python doesn't seem to regenerate @# the libfdt.py file if it is missing. - rm -f $(obj)/_libfdt*.so + @rm -f $(obj)/_libfdt*.so $(call if_changed,pymod) + @# Rename the file to _libfdt.so so this Makefile doesn't run every time + @if [ ! -e $(obj)/_libfdt.so ]; then \ + mv $(obj)/_libfdt*.so $(obj)/_libfdt.so; \ + fi -always += _libfdt.so +$(obj)/_libfdt.so $(obj)/libfdt.py &: rebuild + +always += _libfdt.so libfdt.py clean-files += libfdt.i _libfdt.so libfdt.py libfdt_wrap.c From 6519da47411e90322c59e3fa4b41ff398ff0a688 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:49 +1300 Subject: [PATCH 056/120] Makefile: Avoid running dtoc every time Since the dst_dir rule always runs, it causes a rebuild of the of-platdata files even if not needed. Create the directory inside the rule instead. Signed-off-by: Simon Glass --- scripts/Makefile.spl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 849e9c7060..4f5876dad9 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -330,12 +330,10 @@ $(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c \ include/generated/dt-structs-gen.h prepare FORCE $(call if_changed,plat) -PHONY += dts_dir -dts_dir: - $(shell [ -d $(obj)/dts ] || mkdir -p $(obj)/dts) - +# Don't use dts_dir here, since it forces running this expensive rule every time include/generated/dt-structs-gen.h $(u-boot-spl-platdata_c) &: \ - $(obj)/$(SPL_BIN).dtb dts_dir FORCE + $(obj)/$(SPL_BIN).dtb + @[ -d $(obj)/dts ] || mkdir -p $(obj)/dts $(call if_changed,dtoc) ifdef CONFIG_SAMSUNG @@ -476,6 +474,10 @@ FORCE: $(obj)/dts/dt-$(SPL_NAME).dtb: dts/dt.dtb $(Q)$(MAKE) $(build)=$(obj)/dts spl_dtbs +PHONY += dts_dir +dts_dir: + $(shell [ -d $(obj)/dts ] || mkdir -p $(obj)/dts) + # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) From 13d71152f1e1323e0a5caf3a78b7a457fff58257 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:50 +1300 Subject: [PATCH 057/120] Makefile: Depend only on required of-platdata files When OF_PLATDATA_INST is enabled, we need dt-uclass.c and dt-device.c for the build to work. When OF_PLATDATA_INST is not enabled, we only need dt-plat.c Update the Makefile rules to indicate this. At present all files are generated and compiled regardless of which are actually needed. Signed-off-by: Simon Glass --- scripts/Makefile.spl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 4f5876dad9..5f37a82931 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -120,8 +120,11 @@ endif u-boot-spl-init := $(head-y) u-boot-spl-main := $(libs-y) ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA -u-boot-spl-platdata := $(obj)/dts/dt-plat.o $(obj)/dts/dt-uclass.o \ - $(obj)/dts/dt-device.o +ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA_INST +u-boot-spl-platdata := $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o +else +u-boot-spl-platdata := $(obj)/dts/dt-plat.o +endif u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata)) endif From 8490c578f4fa04e93cff220722b5ce3f7e756f62 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:51 +1300 Subject: [PATCH 058/120] dtoc: Only generate the required files At present all possible files are generated, even if some of them just have a header and an empty body. It is better to generate only the files that are needed, so that the two types of build (based on the setting of OF_PLATDATA_INST) can be mutually exclusive. This is intended to fix a strange problem sometimes found with CI: Building current source for 1 boards (1 thread, 40 jobs per thread) sandbox: + sandbox_spl +drivers/built-in.o: In function `dm_setup_inst': +drivers/core/root.c:135: undefined reference to `_u_boot_list_2_udevice_2_root' +dts/dt-uclass.o:(.u_boot_list_2_uclass_2_serial+0x10): undefined reference to `_u_boot_list_2_udevice_2_serial' ... This likely happens when switching from !OF_PLATDATA_INST to OF_PLATDATA_INST since running 'make xxx_defconfig" does not currently cause any change in which files are generated. With !OF_PLATDATA_INST the dt-device.c file has no declarations and this is assumed to be the starting state. The error above seems to indicate that, after changing to OF_PLATDATA_INST, the dt-uclass.c file is regenerated but the dt-device.c files is not. This does not seem possible from the relevant Makefile.spl rule: u-boot-spl-platdata := $(obj)/dts/dt-plat.o $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all include/generated/dt-structs-gen.h $(u-boot-spl-platdata_c) &: \ $(obj)/$(SPL_BIN).dtb @[ -d $(obj)/dts ] || mkdir -p $(obj)/dts $(call if_changed,dtoc) It seems that this cannot regenerate dt-uclass.c without dt-device.c since 'dtoc all' is used. So here the trail ends for now. In any case it seems better to generate files that are uses and not bother with those that serve no purpose. So update dtoc to do this automatically. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 23 +++++++++++++++++++---- tools/dtoc/test_dtoc.py | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index b5c449ebb4..c9c657cb9a 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -1128,7 +1128,7 @@ class DtbPlatdata(): # Types of output file we understand # key: Command used to generate this file # value: OutputFile for this command -OUTPUT_FILES = { +OUTPUT_FILES_COMMON = { 'decl': OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl, 'Declares externs for all device/uclass instances'), @@ -1136,9 +1136,17 @@ OUTPUT_FILES = { OutputFile(Ftype.HEADER, 'dt-structs-gen.h', DtbPlatdata.generate_structs, 'Defines the structs used to hold devicetree data'), + } + +# File generated without instantiate +OUTPUT_FILES_NOINST = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + } + +# File generated with instantiate +OUTPUT_FILES_INST = { 'device': OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device, 'Declares the DM_DEVICE_INST() records'), @@ -1204,14 +1212,21 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.read_aliases() plat.assign_seqs() + # Figure out what output files we plan to generate + output_files = OUTPUT_FILES_COMMON + if instantiate: + output_files.update(OUTPUT_FILES_INST) + else: + output_files.update(OUTPUT_FILES_NOINST) + cmds = args[0].split(',') if 'all' in cmds: - cmds = sorted(OUTPUT_FILES.keys()) + cmds = sorted(output_files.keys()) for cmd in cmds: - outfile = OUTPUT_FILES.get(cmd) + outfile = output_files.get(cmd) if not outfile: raise ValueError("Unknown command '%s': (use: %s)" % - (cmd, ', '.join(sorted(OUTPUT_FILES.keys())))) + (cmd, ', '.join(sorted(output_files.keys())))) plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) plat.out_header(outfile) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 1912a8723f..e951283457 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -1554,7 +1554,7 @@ U_BOOT_DRVINFO(spl_test2) = { with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn( - "Unknown command 'invalid-cmd': (use: decl, device, platdata, struct, uclass)", + "Unknown command 'invalid-cmd': (use: decl, platdata, struct)", str(exc.exception)) def test_output_conflict(self): From 56b313d1b637815dd01463698cc722a27ae361e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:52 +1300 Subject: [PATCH 059/120] Makefile: Use a variable for generated of-platdata headers There are actually two generated files but only one is currently mentioned in the Makefile as a dependency. Put them both in a Makefile variable and use that instead, to avoid inconsistency. Signed-off-by: Simon Glass --- scripts/Makefile.spl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 5f37a82931..11e9f6a17e 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -120,13 +120,14 @@ endif u-boot-spl-init := $(head-y) u-boot-spl-main := $(libs-y) ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA +platdata-hdr := include/generated/dt-structs-gen.h include/generated/dt-decl.h ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA_INST u-boot-spl-platdata := $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o else u-boot-spl-platdata := $(obj)/dts/dt-plat.o endif u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata)) -endif +endif # OF_PLATDATA # Linker Script # First test whether there's a linker-script for the specific stage defined... @@ -327,15 +328,11 @@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all quiet_cmd_plat = PLAT $@ cmd_plat = $(CC) $(c_flags) -c $< -o $(filter-out $(PHONY),$@) -targets += $(u-boot-spl-platdata) - -$(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c \ - include/generated/dt-structs-gen.h prepare FORCE +$(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c $(platdata-hdr) $(call if_changed,plat) # Don't use dts_dir here, since it forces running this expensive rule every time -include/generated/dt-structs-gen.h $(u-boot-spl-platdata_c) &: \ - $(obj)/$(SPL_BIN).dtb +$(platdata-hdr) $(u-boot-spl-platdata_c) &: $(obj)/$(SPL_BIN).dtb @[ -d $(obj)/dts ] || mkdir -p $(obj)/dts $(call if_changed,dtoc) From 354d2324635cb77b7bf9a9ad80c4d48b7761c4f7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 25 Mar 2021 06:40:53 +1300 Subject: [PATCH 060/120] Makefile: Remove old of-platdata files before regenerating Which files we generate depends on the setting of OF_PLATDATA_INST in the build. This might change between builds, but the build directory may be reused. Leaving old files around is confusing and switching the OF_PLATDATA_INST setting does not necessarily regenerate the files, e.g. if the devicetree has not changed. Remove all the files before regenerating new ones. Signed-off-by: Simon Glass --- scripts/Makefile.spl | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 11e9f6a17e..ca988224da 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -121,12 +121,22 @@ u-boot-spl-init := $(head-y) u-boot-spl-main := $(libs-y) ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA platdata-hdr := include/generated/dt-structs-gen.h include/generated/dt-decl.h +platdata-inst := $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o +platdata-noinst := $(obj)/dts/dt-plat.o + ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA_INST -u-boot-spl-platdata := $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o +u-boot-spl-platdata := $(platdata-inst) +u-boot-spl-old-platdata := $(platdata-noinst) else -u-boot-spl-platdata := $(obj)/dts/dt-plat.o +u-boot-spl-platdata := $(platdata-noinst) +u-boot-spl-old-platdata := $(platdata-inst) endif + +# Files we need to generate u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata)) + +# Files we won't generate and should remove +u-boot-spl-old-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-old-platdata)) endif # OF_PLATDATA # Linker Script @@ -334,6 +344,11 @@ $(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c $(platdata-hdr) # Don't use dts_dir here, since it forces running this expensive rule every time $(platdata-hdr) $(u-boot-spl-platdata_c) &: $(obj)/$(SPL_BIN).dtb @[ -d $(obj)/dts ] || mkdir -p $(obj)/dts + @# Remove old files since which ones we generate depends on the setting + @# of OF_PLATDATA_INST and this might change between builds. Leaving old + @# ones around is confusing and it is possible that switching the + @# setting again will use the old one instead of regenerating it. + @rm -f $(u-boot-spl-all-platdata_c) $(u-boot-spl-all-platdata) $(call if_changed,dtoc) ifdef CONFIG_SAMSUNG From 910de1092f6e2febf5b9066f7ce437dc2303c1e0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:20 +1300 Subject: [PATCH 061/120] dm: core: Include dt-decl.h automatically When dt-structs.h is used, include the dt-decl.h header as well, so that these declarations are available. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- include/dt-structs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-structs.h b/include/dt-structs.h index f0e1c9cb90..f9ccaf56a4 100644 --- a/include/dt-structs.h +++ b/include/dt-structs.h @@ -24,7 +24,9 @@ struct phandle_2_arg { uint idx; int arg[2]; }; + #include +#include #endif #endif From e8c023c3f95622dace696be2f671a5eb01572c07 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:21 +1300 Subject: [PATCH 062/120] dm: test: Avoid destroying uclasses with of-platdata-inst It is not possible to destroy the uclasses when they are created at build time. Skip this step so that SPL test can complete successfully. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- test/test-main.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/test/test-main.c b/test/test-main.c index e1b49e091a..46a0c2ee2f 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -62,17 +62,25 @@ static int dm_test_post_run(struct unit_test_state *uts) { int id; - for (id = 0; id < UCLASS_COUNT; id++) { - struct uclass *uc; + /* + * With of-platdata-inst the uclasses are created at build time. If we + * destroy them we cannot get them back since uclass_add() is not + * supported. So skip this. + */ + if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + for (id = 0; id < UCLASS_COUNT; id++) { + struct uclass *uc; - /* - * If the uclass doesn't exist we don't want to create it. So - * check that here before we call uclass_find_device(). - */ - uc = uclass_find(id); - if (!uc) - continue; - ut_assertok(uclass_destroy(uc)); + /* + * If the uclass doesn't exist we don't want to create + * it. So check that here before we call + * uclass_find_device(). + */ + uc = uclass_find(id); + if (!uc) + continue; + ut_assertok(uclass_destroy(uc)); + } } return 0; From cc7ffd3adc2e30cdd4b7154ecc7bd505ec258366 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:22 +1300 Subject: [PATCH 063/120] clk: sandbox: Move priv/plat data to a header file At present the structs used by this driver are not accessible outside it, so cannot be used with OF_PLATDATA_INST. Move them to a header file to fix this. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/sandbox/include/asm/clk.h | 16 ++++++++++++++++ drivers/clk/clk_sandbox.c | 7 ------- drivers/clk/clk_sandbox_test.c | 6 ------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h index 68a8687f57..6972b9e743 100644 --- a/arch/sandbox/include/asm/clk.h +++ b/arch/sandbox/include/asm/clk.h @@ -7,6 +7,9 @@ #define __SANDBOX_CLK_H #include +#include +#include +#include struct udevice; @@ -45,6 +48,19 @@ enum sandbox_clk_test_id { #define SANDBOX_CLK_TEST_NON_DEVM_COUNT SANDBOX_CLK_TEST_ID_DEVM1 +struct sandbox_clk_priv { + bool probed; + ulong rate[SANDBOX_CLK_ID_COUNT]; + bool enabled[SANDBOX_CLK_ID_COUNT]; + bool requested[SANDBOX_CLK_ID_COUNT]; +}; + +struct sandbox_clk_test { + struct clk clks[SANDBOX_CLK_TEST_NON_DEVM_COUNT]; + struct clk *clkps[SANDBOX_CLK_TEST_ID_COUNT]; + struct clk_bulk bulk; +}; + /** * sandbox_clk_query_rate - Query the current rate of a sandbox clock. * diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index b28b67b448..e1313f6d88 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -10,13 +10,6 @@ #include #include -struct sandbox_clk_priv { - bool probed; - ulong rate[SANDBOX_CLK_ID_COUNT]; - bool enabled[SANDBOX_CLK_ID_COUNT]; - bool requested[SANDBOX_CLK_ID_COUNT]; -}; - static ulong sandbox_clk_get_rate(struct clk *clk) { struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c index c4e4481508..f665fd3cc4 100644 --- a/drivers/clk/clk_sandbox_test.c +++ b/drivers/clk/clk_sandbox_test.c @@ -11,12 +11,6 @@ #include #include -struct sandbox_clk_test { - struct clk clks[SANDBOX_CLK_TEST_NON_DEVM_COUNT]; - struct clk *clkps[SANDBOX_CLK_TEST_ID_COUNT]; - struct clk_bulk bulk; -}; - static const char * const sandbox_clk_test_names[] = { [SANDBOX_CLK_TEST_ID_FIXED] = "fixed", [SANDBOX_CLK_TEST_ID_SPI] = "spi", From 4ddc91b32f9fc2292686dffe0159e2679362f78b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:23 +1300 Subject: [PATCH 064/120] clk: fixed-rate: Export driver parts for OF_PLATDATA_INST We need to allow SoCs to create their own drivers for this so that they can use their own of-platdata structs. To minimise code duplication, export the driver operations and the ofdata_to_plat() setup function. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/clk/clk_fixed_rate.c | 14 ++++++++++---- include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 3c5a83c523..09f9ef26a4 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -25,18 +25,24 @@ const struct clk_ops clk_fixed_rate_ops = { .enable = dummy_enable, }; -static int clk_fixed_rate_of_to_plat(struct udevice *dev) +void clk_fixed_rate_ofdata_to_plat_(struct udevice *dev, + struct clk_fixed_rate *plat) { - struct clk *clk = &to_clk_fixed_rate(dev)->clk; + struct clk *clk = &plat->clk; #if !CONFIG_IS_ENABLED(OF_PLATDATA) - to_clk_fixed_rate(dev)->fixed_rate = - dev_read_u32_default(dev, "clock-frequency", 0); + plat->fixed_rate = dev_read_u32_default(dev, "clock-frequency", 0); #endif /* Make fixed rate clock accessible from higher level struct clk */ /* FIXME: This is not allowed */ dev_set_uclass_priv(dev, clk); + clk->dev = dev; clk->enable_count = 0; +} + +static int clk_fixed_rate_of_to_plat(struct udevice *dev) +{ + clk_fixed_rate_ofdata_to_plat_(dev, to_clk_fixed_rate(dev)); return 0; } diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index cc9c430512..6fda14f5fe 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -192,6 +192,8 @@ struct clk_fixed_factor { unsigned int div; }; +extern const struct clk_ops clk_fixed_rate_ops; + #define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\ clk) @@ -202,6 +204,9 @@ struct clk_fixed_rate { #define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_plat(dev)) +void clk_fixed_rate_ofdata_to_plat_(struct udevice *dev, + struct clk_fixed_rate *plat); + struct clk_composite { struct clk clk; struct clk_ops ops; From 8813986dfde89ae2eab2a2315e1d23e4784c3d88 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:24 +1300 Subject: [PATCH 065/120] clk: sandbox: Create a special fixed-rate driver Create a version of this driver for sandbox so that it can use the of-platdata struct. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dtsi | 2 +- arch/sandbox/include/asm/clk.h | 8 ++++++++ drivers/clk/clk_sandbox.c | 33 +++++++++++++++++++++++++++++++++ test/dm/of_platdata.c | 2 +- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 7455c99a73..5bf84451c9 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -31,7 +31,7 @@ clk_fixed: clk-fixed { u-boot,dm-pre-reloc; - compatible = "fixed-clock"; + compatible = "sandbox,fixed-clock"; #clock-cells = <0>; clock-frequency = <1234>; }; diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h index 6972b9e743..df7156fe31 100644 --- a/arch/sandbox/include/asm/clk.h +++ b/arch/sandbox/include/asm/clk.h @@ -61,6 +61,14 @@ struct sandbox_clk_test { struct clk_bulk bulk; }; +/* Platform data for the sandbox fixed-rate clock driver */ +struct sandbox_clk_fixed_rate_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_sandbox_fixed_clock dtplat; +#endif + struct clk_fixed_rate fixed; +}; + /** * sandbox_clk_query_rate - Query the current rate of a sandbox clock. * diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index e1313f6d88..57acf7d855 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -9,6 +9,7 @@ #include #include #include +#include static ulong sandbox_clk_get_rate(struct clk *clk) { @@ -171,3 +172,35 @@ int sandbox_clk_query_requested(struct udevice *dev, int id) return -EINVAL; return priv->requested[id]; } + +int clk_fixed_rate_of_to_plat(struct udevice *dev) +{ + struct clk_fixed_rate *cplat; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct sandbox_clk_fixed_rate_plat *plat = dev_get_plat(dev); + + cplat = &plat->fixed; + cplat->fixed_rate = plat->dtplat.clock_frequency; +#else + cplat = to_clk_fixed_rate(dev); +#endif + clk_fixed_rate_ofdata_to_plat_(dev, cplat); + + return 0; +} + +static const struct udevice_id sandbox_clk_fixed_rate_match[] = { + { .compatible = "sandbox,fixed-clock" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sandbox_fixed_clock) = { + .name = "sandbox_fixed_clock", + .id = UCLASS_CLK, + .of_match = sandbox_clk_fixed_rate_match, + .of_to_plat = clk_fixed_rate_of_to_plat, + .plat_auto = sizeof(struct sandbox_clk_fixed_rate_plat), + .ops = &clk_fixed_rate_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index e9804cc27f..93ad572725 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -185,7 +185,7 @@ static int dm_test_of_plat_phandle(struct unit_test_state *uts) plat = dev_get_plat(dev); ut_assertok(device_get_by_driver_info_idx(plat->clocks[0].idx, &clk)); - ut_asserteq_str("fixed_clock", clk->name); + ut_asserteq_str("sandbox_fixed_clock", clk->name); ut_assertok(device_get_by_driver_info_idx(plat->clocks[1].idx, &clk)); ut_asserteq_str("sandbox_clk", clk->name); From 6ba46a0f21df1804e1c98334215ac57a495d9655 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:25 +1300 Subject: [PATCH 066/120] dm: core: Drop device_get_by_driver_info() This function is now only used in a test. Drop it. Also drop DM_DRVINFO_GET() which was the only purpose for having the function. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/device.c | 15 --------------- include/dm/device.h | 15 --------------- include/dm/platdata.h | 15 --------------- test/dm/of_platdata.c | 7 ++----- 4 files changed, 2 insertions(+), 50 deletions(-) diff --git a/drivers/core/device.c b/drivers/core/device.c index e915b3089d..2399f6f70c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -811,21 +811,6 @@ int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp) } #if CONFIG_IS_ENABLED(OF_PLATDATA) -int device_get_by_driver_info(const struct driver_info *info, - struct udevice **devp) -{ - struct driver_info *info_base = - ll_entry_start(struct driver_info, driver_info); - int idx = info - info_base; - struct driver_rt *drt = gd_dm_driver_rt() + idx; - struct udevice *dev; - - dev = drt->dev; - *devp = NULL; - - return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); -} - int device_get_by_driver_info_idx(uint idx, struct udevice **devp) { struct driver_rt *drt = gd_dm_driver_rt() + idx; diff --git a/include/dm/device.h b/include/dm/device.h index 5b8f27d455..ca796059e0 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -666,21 +666,6 @@ int device_find_global_by_ofnode(ofnode node, struct udevice **devp); */ int device_get_global_by_ofnode(ofnode node, struct udevice **devp); -/** - * device_get_by_driver_info() - Get a device based on driver_info - * - * Locates a device by its struct driver_info, by using its reference which - * is updated during the bind process. - * - * The device is probed to activate it ready for use. - * - * @info: Struct driver_info - * @devp: Returns pointer to device if found, otherwise this is set to NULL - * @return 0 if OK, -ve on error - */ -int device_get_by_driver_info(const struct driver_info *info, - struct udevice **devp); - /** * device_get_by_driver_info_idx() - Get a device based on driver_info index * diff --git a/include/dm/platdata.h b/include/dm/platdata.h index 3821a56f2c..4efb1dfe12 100644 --- a/include/dm/platdata.h +++ b/include/dm/platdata.h @@ -71,19 +71,4 @@ struct driver_rt { #define U_BOOT_DRVINFOS(__name) \ ll_entry_declare_list(struct driver_info, __name, driver_info) -/** - * Get a pointer to a given device info given its name - * - * With the declaration U_BOOT_DRVINFO(name), DM_DRVINFO_GET(name) will return a - * pointer to the struct driver_info created by that declaration. - * - * if OF_PLATDATA is enabled, from this it is possible to use the @dev member of - * struct driver_info to find the device pointer itself. - * - * @__name: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000) - * @return struct driver_info * to the driver that created the device - */ -#define DM_DRVINFO_GET(__name) \ - ll_entry_get(struct driver_info, __name, driver_info) - #endif diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index 93ad572725..d68c591282 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -142,8 +142,6 @@ static int find_driver_info(struct unit_test_state *uts, struct udevice *parent, /* Check that every device is recorded in its driver_info struct */ static int dm_test_of_plat_dev(struct unit_test_state *uts) { - const struct driver_info *info = - ll_entry_start(struct driver_info, driver_info); const int n_ents = ll_entry_count(struct driver_info, driver_info); bool found[n_ents]; uint i; @@ -155,18 +153,17 @@ static int dm_test_of_plat_dev(struct unit_test_state *uts) /* Make sure that the driver entries without devices have no ->dev */ for (i = 0; i < n_ents; i++) { const struct driver_rt *drt = gd_dm_driver_rt() + i; - const struct driver_info *entry = info + i; struct udevice *dev; if (found[i]) { /* Make sure we can find it */ ut_assertnonnull(drt->dev); - ut_assertok(device_get_by_driver_info(entry, &dev)); + ut_assertok(device_get_by_driver_info_idx(i, &dev)); ut_asserteq_ptr(dev, drt->dev); } else { ut_assertnull(drt->dev); ut_asserteq(-ENOENT, - device_get_by_driver_info(entry, &dev)); + device_get_by_driver_info_idx(i, &dev)); } } From 168227a19959cf3fec48e82c6c07fe1f84ced4b2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:26 +1300 Subject: [PATCH 067/120] dm: core: Drop uclass_find_device_by_phandle() with of-platdata At present this function is included in the build but with of-platdata it only services to produce a confusing link error complaining about a call to dev_read_u32_default(). Drop it so that any call to uclass_find_device_by_phandle() is flagged as an error, making it easier to see what is going on. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index f753a1f91e..117d35ac49 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -394,7 +394,7 @@ done: return ret; } -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) { From e62ad9c8672dc1dcd3afb791d58da34cc02efbe9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:27 +1300 Subject: [PATCH 068/120] sandbox: i2c: Move platdata structs to header files At present the structs used by these drivers are declared in the C files and so are not accessible to dtoc. Move them to header files, as required. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/sandbox/include/asm/i2c.h | 15 +++++++++++++++ arch/sandbox/include/asm/rtc.h | 24 ++++++++++++++++++++++++ drivers/i2c/i2c-emul-uclass.c | 13 +------------ drivers/rtc/i2c_rtc_emul.c | 19 ------------------- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/arch/sandbox/include/asm/i2c.h b/arch/sandbox/include/asm/i2c.h index b482be485c..4fc190be4b 100644 --- a/arch/sandbox/include/asm/i2c.h +++ b/arch/sandbox/include/asm/i2c.h @@ -11,4 +11,19 @@ struct sandbox_i2c_priv { bool test_mode; }; +/** + * struct i2c_emul_uc_plat - information about the emulator for this device + * + * This is used by devices in UCLASS_I2C_EMUL to record information about the + * device being emulated. It is accessible with dev_get_uclass_plat() + * + * @dev: Device being emulated + * @idx: of-platdata index, set up by the device's bind() method if of-platdata + * is in use + */ +struct i2c_emul_uc_plat { + struct udevice *dev; + int idx; +}; + #endif /* __asn_i2c_h */ diff --git a/arch/sandbox/include/asm/rtc.h b/arch/sandbox/include/asm/rtc.h index 5bb032f59f..025cd6c67c 100644 --- a/arch/sandbox/include/asm/rtc.h +++ b/arch/sandbox/include/asm/rtc.h @@ -9,6 +9,8 @@ #ifndef __asm_rtc_h #define __asm_rtc_h +#include + /* Register numbers in the sandbox RTC */ enum { REG_SEC = 5, @@ -29,4 +31,26 @@ enum { REG_COUNT = 0x80, }; +/** + * struct sandbox_i2c_rtc_plat_data - platform data for the RTC + * + * @base_time: Base system time when RTC device was bound + * @offset: RTC offset from current system time + * @use_system_time: true to use system time, false to use @base_time + * @reg: Register values + */ +struct sandbox_i2c_rtc_plat_data { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_sandbox_i2c_rtc_emul dtplat; +#endif + long base_time; + long offset; + bool use_system_time; + u8 reg[REG_COUNT]; +}; + +struct sandbox_i2c_rtc { + unsigned int offset_secs; +}; + #endif diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c index 75d7988338..9ef4651fa4 100644 --- a/drivers/i2c/i2c-emul-uclass.c +++ b/drivers/i2c/i2c-emul-uclass.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -23,18 +24,6 @@ * uclass so avoid having strange devices on the I2C bus. */ -/** - * struct i2c_emul_uc_plat - information about the emulator for this device - * - * This is used by devices in UCLASS_I2C_EMUL to record information about the - * device being emulated. It is accessible with dev_get_uclass_plat() - * - * @dev: Device being emulated - */ -struct i2c_emul_uc_plat { - struct udevice *dev; -}; - struct udevice *i2c_emul_get_device(struct udevice *emul) { struct i2c_emul_uc_plat *uc_plat = dev_get_uclass_plat(emul); diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c index fdc885c518..ba418c25da 100644 --- a/drivers/rtc/i2c_rtc_emul.c +++ b/drivers/rtc/i2c_rtc_emul.c @@ -28,25 +28,6 @@ #define debug_buffer(x, ...) #endif -/** - * struct sandbox_i2c_rtc_plat_data - platform data for the RTC - * - * @base_time: Base system time when RTC device was bound - * @offset: RTC offset from current system time - * @use_system_time: true to use system time, false to use @base_time - * @reg: Register values - */ -struct sandbox_i2c_rtc_plat_data { - long base_time; - long offset; - bool use_system_time; - u8 reg[REG_COUNT]; -}; - -struct sandbox_i2c_rtc { - unsigned int offset_secs; -}; - long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, int offset) { From cc469b708667d24fc2a2c285e52d0d8ef5b14909 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:28 +1300 Subject: [PATCH 069/120] dm: Rename device_get_by_driver_info_idx() This function finds a device by its driver_info index. With of-platdata-inst we do not use driver_info, but instead instantiate udevice records at build-time. However the semantics of using the function are the same in each case: the caller provides an index and gets back a device. So rename the function to device_get_by_ofplat_idx(), so that it can be used for both situations. The caller does not really need to worry about the details. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/clk/clk-uclass.c | 2 +- drivers/core/device.c | 13 ++++++++++--- drivers/misc/irq-uclass.c | 2 +- drivers/mmc/fsl_esdhc_imx.c | 3 +-- include/dm/device.h | 16 +++++++++++----- test/dm/of_platdata.c | 15 +++++++-------- 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index b87288da7a..4ab3c402ed 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -39,7 +39,7 @@ int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells, { int ret; - ret = device_get_by_driver_info_idx(cells->idx, &clk->dev); + ret = device_get_by_ofplat_idx(cells->idx, &clk->dev); if (ret) return ret; clk->id = cells->arg[0]; diff --git a/drivers/core/device.c b/drivers/core/device.c index 2399f6f70c..1990b6f69f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -811,12 +811,19 @@ int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp) } #if CONFIG_IS_ENABLED(OF_PLATDATA) -int device_get_by_driver_info_idx(uint idx, struct udevice **devp) +int device_get_by_ofplat_idx(uint idx, struct udevice **devp) { - struct driver_rt *drt = gd_dm_driver_rt() + idx; struct udevice *dev; - dev = drt->dev; + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + struct udevice *base = ll_entry_start(struct udevice, udevice); + + dev = base + idx; + } else { + struct driver_rt *drt = gd_dm_driver_rt() + idx; + + dev = drt->dev; + } *devp = NULL; return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index 24b27962a7..3aa26f61d9 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -69,7 +69,7 @@ int irq_get_by_driver_info(struct udevice *dev, { int ret; - ret = device_get_by_driver_info_idx(cells->idx, &irq->dev); + ret = device_get_by_ofplat_idx(cells->idx, &irq->dev); if (ret) return ret; irq->id = cells->arg[0]; diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 6a9403dc00..09a5cd61e3 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1530,8 +1530,7 @@ static int fsl_esdhc_probe(struct udevice *dev) if (CONFIG_IS_ENABLED(DM_GPIO) && !priv->non_removable) { struct udevice *gpiodev; - ret = device_get_by_driver_info_idx(dtplat->cd_gpios->idx, - &gpiodev); + ret = device_get_by_ofplat_idx(dtplat->cd_gpios->idx, &gpiodev); if (ret) return ret; diff --git a/include/dm/device.h b/include/dm/device.h index ca796059e0..9183356468 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -667,18 +667,24 @@ int device_find_global_by_ofnode(ofnode node, struct udevice **devp); int device_get_global_by_ofnode(ofnode node, struct udevice **devp); /** - * device_get_by_driver_info_idx() - Get a device based on driver_info index + * device_get_by_ofplat_idx() - Get a device based on of-platdata index * - * Locates a device by its struct driver_info, by using its index number which - * is written into the idx field of struct phandle_1_arg, etc. + * Locates a device by either its struct driver_info index, or its + * struct udevice index. The latter is used with OF_PLATDATA_INST, since we have + * a list of build-time instantiated struct udevice records, The former is used + * with !OF_PLATDATA_INST since in that case we have a list of + * struct driver_info records. + * + * The index number is written into the idx field of struct phandle_1_arg, etc. + * It is the position of this driver_info/udevice in its linker list. * * The device is probed to activate it ready for use. * - * @idx: Index number of the driver_info structure (0=first) + * @idx: Index number of the driver_info/udevice structure (0=first) * @devp: Returns pointer to device if found, otherwise this is set to NULL * @return 0 if OK, -ve on error */ -int device_get_by_driver_info_idx(uint idx, struct udevice **devp); +int device_get_by_ofplat_idx(uint idx, struct udevice **devp); /** * device_find_first_child() - Find the first child of a device diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index d68c591282..e5f32a0f1c 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -158,12 +158,11 @@ static int dm_test_of_plat_dev(struct unit_test_state *uts) if (found[i]) { /* Make sure we can find it */ ut_assertnonnull(drt->dev); - ut_assertok(device_get_by_driver_info_idx(i, &dev)); + ut_assertok(device_get_by_ofplat_idx(i, &dev)); ut_asserteq_ptr(dev, drt->dev); } else { ut_assertnull(drt->dev); - ut_asserteq(-ENOENT, - device_get_by_driver_info_idx(i, &dev)); + ut_asserteq(-ENOENT, device_get_by_ofplat_idx(i, &dev)); } } @@ -181,22 +180,22 @@ static int dm_test_of_plat_phandle(struct unit_test_state *uts) ut_asserteq_str("sandbox_clk_test", dev->name); plat = dev_get_plat(dev); - ut_assertok(device_get_by_driver_info_idx(plat->clocks[0].idx, &clk)); + ut_assertok(device_get_by_ofplat_idx(plat->clocks[0].idx, &clk)); ut_asserteq_str("sandbox_fixed_clock", clk->name); - ut_assertok(device_get_by_driver_info_idx(plat->clocks[1].idx, &clk)); + ut_assertok(device_get_by_ofplat_idx(plat->clocks[1].idx, &clk)); ut_asserteq_str("sandbox_clk", clk->name); ut_asserteq(1, plat->clocks[1].arg[0]); - ut_assertok(device_get_by_driver_info_idx(plat->clocks[2].idx, &clk)); + ut_assertok(device_get_by_ofplat_idx(plat->clocks[2].idx, &clk)); ut_asserteq_str("sandbox_clk", clk->name); ut_asserteq(0, plat->clocks[2].arg[0]); - ut_assertok(device_get_by_driver_info_idx(plat->clocks[3].idx, &clk)); + ut_assertok(device_get_by_ofplat_idx(plat->clocks[3].idx, &clk)); ut_asserteq_str("sandbox_clk", clk->name); ut_asserteq(3, plat->clocks[3].arg[0]); - ut_assertok(device_get_by_driver_info_idx(plat->clocks[4].idx, &clk)); + ut_assertok(device_get_by_ofplat_idx(plat->clocks[4].idx, &clk)); ut_asserteq_str("sandbox_clk", clk->name); ut_asserteq(2, plat->clocks[4].arg[0]); From abaed70990bd8eb2c9992a16465f8a9daa4c5f61 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:29 +1300 Subject: [PATCH 070/120] sandbox_spl: Increase SPL malloc() size The test framework reinits driver model tests before running each test. Since malloc_simple does not support free(), this eventually runs out of memory. Fix it for now by increasing the space to 32KB. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- configs/sandbox_spl_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 2696d0b6cd..4c020528fd 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -7,6 +7,7 @@ CONFIG_SYS_MEMTEST_END=0x00101000 CONFIG_ENV_SIZE=0x2000 CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_DRIVERS_MISC_SUPPORT=y +CONFIG_SPL_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL=y CONFIG_BOOTSTAGE_STASH_ADDR=0x0 CONFIG_DEFAULT_DEVICE_TREE="sandbox" From 728d04cc72855e23f6a9116e68911dcac5be4693 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:30 +1300 Subject: [PATCH 071/120] sandbox: i2c: Support i2c emulation with of-platdata At present the i2c emulators require access to the devicetree, which is not possible (by design) with of-platdata. Add a way for drivers to record the of-platdata index of their emulator, so that we can still find the emulator. This allows i2c emulation to work with of-platdata. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/i2c/i2c-emul-uclass.c | 17 +++++++++++++++-- drivers/rtc/sandbox_rtc.c | 13 +++++++++++++ include/i2c.h | 15 +++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c index 9ef4651fa4..7917b63c55 100644 --- a/drivers/i2c/i2c-emul-uclass.c +++ b/drivers/i2c/i2c-emul-uclass.c @@ -31,14 +31,27 @@ struct udevice *i2c_emul_get_device(struct udevice *emul) return uc_plat->dev; } +void i2c_emul_set_idx(struct udevice *dev, int emul_idx) +{ + struct dm_i2c_chip *plat = dev_get_parent_plat(dev); + + plat->emul_idx = emul_idx; +} + int i2c_emul_find(struct udevice *dev, struct udevice **emulp) { struct i2c_emul_uc_plat *uc_plat; struct udevice *emul; int ret; - ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev, - "sandbox,emul", &emul); + if (!CONFIG_IS_ENABLED(OF_PLATDATA)) { + ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev, + "sandbox,emul", &emul); + } else { + struct dm_i2c_chip *plat = dev_get_parent_plat(dev); + + ret = device_get_by_ofplat_idx(plat->emul_idx, &emul); + } if (ret) { log_err("No emulators for device '%s'\n", dev->name); return ret; diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c index d0864b1df9..657e5c7be2 100644 --- a/drivers/rtc/sandbox_rtc.c +++ b/drivers/rtc/sandbox_rtc.c @@ -79,6 +79,18 @@ struct acpi_ops sandbox_rtc_acpi_ops = { }; #endif +static int sandbox_rtc_bind(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(PLATDATA) + struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev); + + /* Set up the emul_idx for i2c_emul_find() */ + i2c_emul_set_idx(dev, plat->dtplat.sandbox_emul->idx); +#endif + + return 0; +} + static const struct rtc_ops sandbox_rtc_ops = { .get = sandbox_rtc_get, .set = sandbox_rtc_set, @@ -97,5 +109,6 @@ U_BOOT_DRIVER(sandbox_rtc) = { .id = UCLASS_RTC, .of_match = sandbox_rtc_ids, .ops = &sandbox_rtc_ops, + .bind = sandbox_rtc_bind, ACPI_OPS_PTR(&sandbox_rtc_acpi_ops) }; diff --git a/include/i2c.h b/include/i2c.h index 7ae0c42706..c0fe94c1f3 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -93,6 +93,8 @@ struct udevice; * datasheet explains it's usage of this addressing * mode. * @emul: Emulator for this chip address (only used for emulation) + * @emul_idx: Emulator index, used for of-platdata and set by each i2c chip's + * bind() method. This allows i2c_emul_find() to work with of-platdata. */ struct dm_i2c_chip { uint chip_addr; @@ -102,6 +104,7 @@ struct dm_i2c_chip { #ifdef CONFIG_SANDBOX struct udevice *emul; bool test_mode; + int emul_idx; #endif }; @@ -554,6 +557,18 @@ void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs); */ int i2c_emul_find(struct udevice *dev, struct udevice **emulp); +/** + * i2c_emul_set_idx() - Set the emulator index for an i2c sandbox device + * + * With of-platdata we cannot find the emulator using the device tree, so rely + * on the bind() method of each i2c driver calling this function to tell us + * the of-platdata idx of the emulator + * + * @dev: i2c device to set the emulator for + * @emul_idx: of-platdata index for that emulator + */ +void i2c_emul_set_idx(struct udevice *dev, int emul_idx); + /** * i2c_emul_get_device() - Find the device being emulated * From 3a825d3fa7122a92020b9a85a8bad0b27d55e6a7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:31 +1300 Subject: [PATCH 072/120] Revert "sandbox: Disable I2C emulators in SPL" With recent changes this can be supported again. Add it back. This reverts commit d85f2c4f2970d0ec2f5f075de734afd11200d153. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dtsi | 10 +++------- configs/sandbox_spl_defconfig | 1 + drivers/i2c/Makefile | 2 -- test/dm/of_platdata.c | 8 ++++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 5bf84451c9..69d7d3019e 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -264,14 +264,10 @@ stringarray = "pre-proper"; }; - test-bus { - compatible = "simple-bus"; + spl-test7 { u-boot,dm-spl; - spl-test7 { - u-boot,dm-spl; - compatible = "sandbox,spl-test"; - stringarray = "spl"; - }; + compatible = "sandbox,spl-test"; + stringarray = "spl"; }; square { diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 4c020528fd..1ac7e025b6 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -103,6 +103,7 @@ CONFIG_SYSCON=y CONFIG_SPL_SYSCON=y CONFIG_DEVRES=y CONFIG_DEBUG_DEVRES=y +# CONFIG_SPL_SIMPLE_BUS is not set CONFIG_ADC=y CONFIG_ADC_SANDBOX=y CONFIG_AXI=y diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index acd27ac29d..8c9f1fcd8b 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -39,9 +39,7 @@ obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o -ifndef CONFIG_SPL_BUILD obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o -endif obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index e5f32a0f1c..7cbb1161ed 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -207,11 +207,11 @@ DM_TEST(dm_test_of_plat_phandle, UT_TESTF_SCAN_PDATA); /* Test that device parents are correctly set up */ static int dm_test_of_plat_parent(struct unit_test_state *uts) { - struct udevice *dev, *bus; + struct udevice *rtc, *i2c; - ut_assertok(uclass_first_device_err(UCLASS_SIMPLE_BUS, &bus)); - ut_assertok(device_first_child_err(bus, &dev)); - ut_asserteq_ptr(bus, dev_get_parent(dev)); + ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc)); + ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c)); + ut_asserteq_ptr(i2c, dev_get_parent(rtc)); return 0; } From 424994c48f2bea5c9abd84e0a7b30e698484b8b9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:32 +1300 Subject: [PATCH 073/120] sandbox: Create a new sandbox_noinst build Move sandbox_spl over to use OF_PLATDATA_INST. Create a new board to test the case when this is not enabled, since we will be keeping that code around for several months and want to avoid regressions. Skip the dm_test_of_plat_dev() test since driver info is not available for OF_PLATDATA_INST. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- board/sandbox/MAINTAINERS | 7 + configs/sandbox_noinst_defconfig | 231 +++++++++++++++++++++++++++++++ configs/sandbox_spl_defconfig | 1 + test/dm/of_platdata.c | 4 + 4 files changed, 243 insertions(+) create mode 100644 configs/sandbox_noinst_defconfig diff --git a/board/sandbox/MAINTAINERS b/board/sandbox/MAINTAINERS index 433be48a6f..d32561cd1d 100644 --- a/board/sandbox/MAINTAINERS +++ b/board/sandbox/MAINTAINERS @@ -20,6 +20,13 @@ F: board/sandbox/ F: include/configs/sandbox_spl.h F: configs/sandbox_spl_defconfig +SANDBOX NOINST BOARD +M: Simon Glass +S: Maintained +F: board/sandbox/ +F: include/configs/sandbox_spl.h +F: configs/sandbox_noinst_defconfig + SANDBOX FLAT TREE BOARD M: Simon Glass S: Maintained diff --git a/configs/sandbox_noinst_defconfig b/configs/sandbox_noinst_defconfig new file mode 100644 index 0000000000..1ac7e025b6 --- /dev/null +++ b/configs/sandbox_noinst_defconfig @@ -0,0 +1,231 @@ +CONFIG_SYS_TEXT_BASE=0x200000 +CONFIG_SPL_LIBCOMMON_SUPPORT=y +CONFIG_SPL_LIBGENERIC_SUPPORT=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_SYS_MEMTEST_START=0x00100000 +CONFIG_SYS_MEMTEST_END=0x00101000 +CONFIG_ENV_SIZE=0x2000 +CONFIG_SPL_SERIAL_SUPPORT=y +CONFIG_SPL_DRIVERS_MISC_SUPPORT=y +CONFIG_SPL_SYS_MALLOC_F_LEN=0x8000 +CONFIG_SPL=y +CONFIG_BOOTSTAGE_STASH_ADDR=0x0 +CONFIG_DEFAULT_DEVICE_TREE="sandbox" +CONFIG_SANDBOX_SPL=y +CONFIG_DEBUG_UART=y +CONFIG_DISTRO_DEFAULTS=y +CONFIG_FIT=y +CONFIG_FIT_SIGNATURE=y +CONFIG_FIT_VERBOSE=y +CONFIG_SPL_LOAD_FIT=y +# CONFIG_USE_SPL_FIT_GENERATOR is not set +CONFIG_BOOTSTAGE=y +CONFIG_BOOTSTAGE_REPORT=y +CONFIG_BOOTSTAGE_FDT=y +CONFIG_BOOTSTAGE_STASH=y +CONFIG_BOOTSTAGE_STASH_SIZE=0x4096 +CONFIG_CONSOLE_RECORD=y +CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_HANDOFF=y +CONFIG_SPL_BOARD_INIT=y +CONFIG_SPL_ENV_SUPPORT=y +CONFIG_SPL_I2C_SUPPORT=y +CONFIG_SPL_RTC_SUPPORT=y +CONFIG_CMD_CPU=y +CONFIG_CMD_LICENSE=y +CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTEFI_HELLO=y +# CONFIG_CMD_ELF is not set +CONFIG_CMD_ASKENV=y +CONFIG_CMD_GREPENV=y +CONFIG_CMD_ERASEENV=y +CONFIG_CMD_ENV_CALLBACK=y +CONFIG_CMD_ENV_FLAGS=y +CONFIG_CMD_NVEDIT_INFO=y +CONFIG_CMD_NVEDIT_LOAD=y +CONFIG_CMD_NVEDIT_SELECT=y +CONFIG_LOOPW=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_MX_CYCLIC=y +CONFIG_CMD_MEMTEST=y +CONFIG_CMD_DEMO=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_GPT=y +CONFIG_CMD_IDE=y +CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y +CONFIG_CMD_PCI=y +CONFIG_CMD_REMOTEPROC=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +CONFIG_BOOTP_DNS2=y +CONFIG_CMD_TFTPPUT=y +CONFIG_CMD_TFTPSRV=y +CONFIG_CMD_RARP=y +CONFIG_CMD_CDP=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_BMP=y +CONFIG_CMD_EFIDEBUG=y +CONFIG_CMD_TIME=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_SOUND=y +CONFIG_CMD_QFW=y +CONFIG_CMD_BOOTSTAGE=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y +CONFIG_CMD_CBFS=y +CONFIG_CMD_CRAMFS=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_MAC_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_OF_CONTROL=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_OF_HOSTFILE=y +CONFIG_SPL_OF_PLATDATA=y +CONFIG_ENV_IS_NOWHERE=y +CONFIG_ENV_IS_IN_EXT4=y +CONFIG_ENV_EXT4_INTERFACE="host" +CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0" +CONFIG_BOOTP_SEND_HOSTNAME=y +CONFIG_NETCONSOLE=y +CONFIG_IP_DEFRAG=y +CONFIG_SPL_DM=y +CONFIG_DM_DMA=y +CONFIG_REGMAP=y +CONFIG_SPL_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SPL_SYSCON=y +CONFIG_DEVRES=y +CONFIG_DEBUG_DEVRES=y +# CONFIG_SPL_SIMPLE_BUS is not set +CONFIG_ADC=y +CONFIG_ADC_SANDBOX=y +CONFIG_AXI=y +CONFIG_AXI_SANDBOX=y +CONFIG_CLK=y +CONFIG_SPL_CLK=y +CONFIG_CPU=y +CONFIG_DM_DEMO=y +CONFIG_DM_DEMO_SIMPLE=y +CONFIG_DM_DEMO_SHAPE=y +CONFIG_SPL_FIRMWARE=y +CONFIG_GPIO_HOG=y +CONFIG_PM8916_GPIO=y +CONFIG_SANDBOX_GPIO=y +CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_I2C_CROS_EC_LDO=y +CONFIG_DM_I2C_GPIO=y +CONFIG_SYS_I2C_SANDBOX=y +CONFIG_I2C_MUX=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_CROS_EC_KEYB=y +CONFIG_I8042_KEYB=y +CONFIG_LED=y +CONFIG_LED_BLINK=y +CONFIG_LED_GPIO=y +CONFIG_DM_MAILBOX=y +CONFIG_SANDBOX_MBOX=y +CONFIG_MISC=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_LPC=y +CONFIG_CROS_EC_SANDBOX=y +CONFIG_CROS_EC_SPI=y +CONFIG_IRQ=y +CONFIG_P2SB=y +CONFIG_PWRSEQ=y +CONFIG_SPL_PWRSEQ=y +CONFIG_MMC_SANDBOX=y +CONFIG_SPI_FLASH_SANDBOX=y +CONFIG_SPI_FLASH_ATMEL=y +CONFIG_SPI_FLASH_EON=y +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_SST=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_DM_ETH=y +CONFIG_NVME=y +CONFIG_PCI=y +CONFIG_DM_PCI=y +CONFIG_DM_PCI_COMPAT=y +CONFIG_PCI_SANDBOX=y +CONFIG_PHY=y +CONFIG_PHY_SANDBOX=y +CONFIG_PINCTRL=y +CONFIG_PINCONF=y +CONFIG_PINCTRL_SANDBOX=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_ACT8846=y +CONFIG_DM_PMIC_PFUZE100=y +CONFIG_DM_PMIC_MAX77686=y +CONFIG_DM_PMIC_MC34708=y +CONFIG_PMIC_PM8916=y +CONFIG_PMIC_RK8XX=y +CONFIG_PMIC_S2MPS11=y +CONFIG_DM_PMIC_SANDBOX=y +CONFIG_PMIC_S5M8767=y +CONFIG_PMIC_TPS65090=y +CONFIG_DM_REGULATOR=y +CONFIG_REGULATOR_ACT8846=y +CONFIG_DM_REGULATOR_PFUZE100=y +CONFIG_DM_REGULATOR_MAX77686=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_REGULATOR_RK8XX=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_DM_REGULATOR_SANDBOX=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_PWM=y +CONFIG_PWM_SANDBOX=y +CONFIG_RAM=y +CONFIG_REMOTEPROC_SANDBOX=y +CONFIG_DM_RESET=y +CONFIG_SANDBOX_RESET=y +CONFIG_DM_RTC=y +CONFIG_SPL_DM_RTC=y +CONFIG_SANDBOX_SERIAL=y +CONFIG_SOUND=y +CONFIG_SOUND_SANDBOX=y +CONFIG_SOC_DEVICE=y +CONFIG_SANDBOX_SPI=y +CONFIG_SPMI=y +CONFIG_SPMI_SANDBOX=y +CONFIG_SYSINFO=y +CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSRESET=y +CONFIG_SPL_SYSRESET=y +CONFIG_TIMER=y +CONFIG_TIMER_EARLY=y +CONFIG_SANDBOX_TIMER=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EMUL=y +CONFIG_USB_KEYBOARD=y +CONFIG_DM_VIDEO=y +CONFIG_CONSOLE_ROTATION=y +CONFIG_CONSOLE_TRUETYPE=y +CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y +CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y +CONFIG_SPLASH_SCREEN_ALIGN=y +CONFIG_VIDEO_BMP_RLE8=y +CONFIG_FS_CBFS=y +CONFIG_FS_CRAMFS=y +# CONFIG_SPL_USE_TINY_PRINTF is not set +CONFIG_CMD_DHRYSTONE=y +CONFIG_RSA_VERIFY_WITH_PKEY=y +CONFIG_TPM=y +CONFIG_LZ4=y +CONFIG_ERRNO_STR=y +CONFIG_UNIT_TEST=y +CONFIG_SPL_UNIT_TEST=y +CONFIG_UT_TIME=y +CONFIG_UT_DM=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 1ac7e025b6..ac71cab5f1 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -88,6 +88,7 @@ CONFIG_OF_CONTROL=y CONFIG_SPL_OF_CONTROL=y CONFIG_OF_HOSTFILE=y CONFIG_SPL_OF_PLATDATA=y +CONFIG_SPL_OF_PLATDATA_INST=y CONFIG_ENV_IS_NOWHERE=y CONFIG_ENV_IS_IN_EXT4=y CONFIG_ENV_EXT4_INTERFACE="host" diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index 7cbb1161ed..f2b45b9a86 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -146,6 +146,10 @@ static int dm_test_of_plat_dev(struct unit_test_state *uts) bool found[n_ents]; uint i; + /* Skip this test if there is no platform data */ + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) + return 0; + /* Record the indexes that are found */ memset(found, '\0', sizeof(found)); ut_assertok(find_driver_info(uts, gd->dm_root, found)); From c25b8dcd3c12d6004db0838b43b6c0d2e53b0383 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:33 +1300 Subject: [PATCH 074/120] test: Run sandbox_spl tests on sandbox_noinst Run the tests on this build too, to prevent regressions. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- test/run | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/run b/test/run index 735628e7e3..869406cd8d 100755 --- a/test/run +++ b/test/run @@ -30,6 +30,10 @@ fi run_test "sandbox_spl" ./test/py/test.py --bd sandbox_spl --build \ -k 'test_ofplatdata or test_handoff or test_spl' +# Run the sane tests with sandbox_noinst (i.e. without OF_PLATDATA_INST) +run_test "sandbox_spl" ./test/py/test.py --bd sandbox_noinst --build \ + -k 'test_ofplatdata or test_handoff or test_spl' + if [ -z "$tools_only" ]; then # Run tests for the flat-device-tree version of sandbox. This is a special # build which does not enable CONFIG_OF_LIVE for the live device tree, so we can From 6c914e4232cd262001645e932e9156555f1a5823 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:34 +1300 Subject: [PATCH 075/120] azure/gitlab: Add tests for sandbox_noinst Add this new board to the test plans. Travis-CI is left out, since it is being removed soon due to lack of capacity. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- .azure-pipelines.yml | 3 +++ .gitlab-ci.yml | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index e4d0a2dfd1..ec2f260997 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -187,6 +187,9 @@ jobs: sandbox_spl: TEST_PY_BD: "sandbox_spl" TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl" + sandbox_noinst: + TEST_PY_BD: "sandbox_noinst" + TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl" sandbox_flattree: TEST_PY_BD: "sandbox_flattree" evb_ast2500: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4fb9aa13d4..64e341c262 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -168,7 +168,8 @@ Run binman, buildman, dtoc, Kconfig and patman testsuites: export UBOOT_TRAVIS_BUILD_DIR=/tmp/sandbox_spl; export PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt"; export PATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}"; - ./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w sandbox_spl; + ./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w + --board sandbox_spl; ./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test; ./tools/buildman/buildman -t; ./tools/dtoc/dtoc -t; @@ -204,6 +205,13 @@ sandbox_spl test.py: TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl" <<: *buildman_and_testpy_dfn +sandbox_noinst_test.py: + tags: [ 'all' ] + variables: + TEST_PY_BD: "sandbox_noinst" + TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl" + <<: *buildman_and_testpy_dfn + evb-ast2500 test.py: tags: [ 'all' ] variables: From 95a5825f3134ff47f1e0cf37b4014b5c2e2027b5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:35 +1300 Subject: [PATCH 076/120] dm: core: Add an option to support SPL in read-only memory Some systems (e.g. x86 APL) run SPL from read-only memory. The device instances created by dtoc are therefore not writeable. To make things work we would need to copy the devices to read/write memory. To avoid this, add an option to use a separate runtime struct for devices, just as is done for drivers. This can be used to hold information that changes at runtime, avoiding the need for a copy. Also add a Kconfig option for read-only SPL, which selects this feature. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- common/spl/Kconfig | 24 ++++++++++++++++++++++++ dts/Kconfig | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 774541c02b..584f8483e2 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -276,6 +276,19 @@ config SPL_SEPARATE_BSS location is used. Normally we put the device tree at the end of BSS but with this option enabled, it goes at _image_binary_end. +config SPL_READ_ONLY + bool + depends on SPL_OF_PLATDATA + # Bind cannot be supported because the udevice structs are in read-only + # memory so we cannot update the linked lists. + select SPL_OF_PLATDATA_NO_BIND + select SPL_OF_PLATDATA_RT + help + Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only + section of memory. This means that of-platdata must make a copy (in + writeable memory) of anything it wants to modify, such as + device-private data. + config SPL_BANNER_PRINT bool "Enable output of the SPL banner 'U-Boot SPL ...'" default y @@ -1440,6 +1453,17 @@ config TPL_STACK The address of the initial stack-pointer for the TPL stage. Usually this will be the (aligned) top-of-stack. +config TPL_READ_ONLY + bool + depends on TPL_OF_PLATDATA + select TPL_OF_PLATDATA_NO_BIND + select TPL_OF_PLATDATA_RT + help + Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only + section of memory. This means that of-platdata must make a copy (in + writeable memory) of anything it wants to modify, such as + device-private data. + config TPL_BOOTROM_SUPPORT bool "Support returning to the BOOTROM (from TPL)" help diff --git a/dts/Kconfig b/dts/Kconfig index d289752a13..e6bbdc596b 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -380,6 +380,17 @@ config SPL_OF_PLATDATA_NO_BIND some code space in U-Boot. This can be disabled if binding is needed, at the code of some code size increase. +config SPL_OF_PLATDATA_RT + bool "Use a separate struct for device runtime data" + depends on SPL_OF_PLATDATA_INST + default y + help + For systems running SPL from read-only memory it is convenient to + separate out the runtime information, so that the devices don't need + to be copied before being used. This moves the read-write parts of + struct udevice (at present just the flags) into a separate struct, + which is allocated at runtime. + endif config TPL_OF_PLATDATA @@ -429,6 +440,17 @@ config TPL_OF_PLATDATA_NO_BIND some code space in U-Boot. This can be disabled if binding is needed, at the code of some code size increase. +config TPL_OF_PLATDATA_RT + bool "Use a separate struct for device runtime data" + depends on TPL_OF_PLATDATA_INST + default y + help + For systems running TPL from read-only memory it is convenient to + separate out the runtime information, so that the devices don't need + to be copied before being used. This moves the read-write parts of + struct udevice (at present just the flags) into a separate struct, + which is allocated at runtime. + endif endmenu From ab933d802643ca51d276f0f449921a047126f8a1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:36 +1300 Subject: [PATCH 077/120] dm: core: Create a struct for device runtime info At present when driver model needs to change a device it simply updates the struct udevice structure. But with of-platdata-inst most of the fields are not modified at runtime. In fact, typically only the flags need to change. For systems running SPL from read-only memory it is convenient to separate out the runtime information, so that the devices don't need to be copied before being used. Create a new udevice_rt table, similar to the existing driver_rt. For now it just holds the flags, although they are not used in this patch. Add a new Kconfig for the driver_rt data, since this is not needed when of-platdata-inst is used. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/root.c | 14 +++++++++++++- dts/Kconfig | 20 ++++++++++++++++++++ include/asm-generic/global_data.h | 16 ++++++++++++++-- include/dm/device.h | 15 +++++++++++++++ test/dm/of_platdata.c | 2 +- 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/core/root.c b/drivers/core/root.c index 3e52452cd8..446ac2ff77 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -133,6 +133,18 @@ static int dm_setup_inst(void) { DM_ROOT_NON_CONST = DM_DEVICE_GET(root); + if (CONFIG_IS_ENABLED(OF_PLATDATA_RT)) { + struct udevice_rt *urt; + int n_ents; + + /* Allocate the udevice_rt table */ + n_ents = ll_entry_count(struct udevice, udevice); + urt = calloc(n_ents, sizeof(struct udevice_rt)); + if (!urt) + return log_msg_ret("urt", -ENOMEM); + gd_set_dm_udevice_rt(urt); + } + return 0; } @@ -205,7 +217,7 @@ int dm_scan_plat(bool pre_reloc_only) { int ret; - if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)) { struct driver_rt *dyn; int n_ents; diff --git a/dts/Kconfig b/dts/Kconfig index e6bbdc596b..99ce75e1a2 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -338,6 +338,7 @@ config SPL_OF_PLATDATA bool "Generate platform data for use in SPL" depends on SPL_OF_CONTROL select DTOC + select SPL_OF_PLATDATA_DRIVER_RT if !SPL_OF_PLATDATA_INST help For very constrained SPL environments the overhead of decoding device tree nodes and converting their contents into platform data @@ -391,12 +392,22 @@ config SPL_OF_PLATDATA_RT struct udevice (at present just the flags) into a separate struct, which is allocated at runtime. +config SPL_OF_PLATDATA_DRIVER_RT + bool + help + Use a separate struct for driver runtime data. + + This enables the driver_rt information, used with of-platdata when + of-platdata-inst is not used. It allows finding devices by their + driver data. + endif config TPL_OF_PLATDATA bool "Generate platform data for use in TPL" depends on TPL_OF_CONTROL select DTOC + select TPL_OF_PLATDATA_DRIVER_RT if !TPL_OF_PLATDATA_INST help For very constrained SPL environments the overhead of decoding device tree nodes and converting their contents into platform data @@ -451,6 +462,15 @@ config TPL_OF_PLATDATA_RT struct udevice (at present just the flags) into a separate struct, which is allocated at runtime. +config TPL_OF_PLATDATA_DRIVER_RT + bool + help + Use a separate struct for driver runtime data. + + This enables the driver_rt information, used with of-platdata when + of-platdata-inst is not used. It allows finding devices by their + driver data. + endif endmenu diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index c24f5e0e97..f6189eff4e 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -215,10 +215,14 @@ struct global_data { * @uclass_root_s. */ struct list_head *uclass_root; -# if CONFIG_IS_ENABLED(OF_PLATDATA) +# if CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT) /** @dm_driver_rt: Dynamic info about the driver */ struct driver_rt *dm_driver_rt; # endif +#if CONFIG_IS_ENABLED(OF_PLATDATA_RT) + /** @dm_udevice_rt: Dynamic info about the udevice */ + struct udevice_rt *dm_udevice_rt; +# endif #endif #ifdef CONFIG_TIMER /** @@ -483,7 +487,7 @@ struct global_data { #define gd_set_of_root(_root) #endif -#if CONFIG_IS_ENABLED(OF_PLATDATA) +#if CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT) #define gd_set_dm_driver_rt(dyn) gd->dm_driver_rt = dyn #define gd_dm_driver_rt() gd->dm_driver_rt #else @@ -491,6 +495,14 @@ struct global_data { #define gd_dm_driver_rt() NULL #endif +#if CONFIG_IS_ENABLED(OF_PLATDATA_RT) +#define gd_set_dm_udevice_rt(dyn) gd->dm_udevice_rt = dyn +#define gd_dm_udevice_rt() gd->dm_udevice_rt +#else +#define gd_set_dm_udevice_rt(dyn) +#define gd_dm_udevice_rt() NULL +#endif + #ifdef CONFIG_GENERATE_ACPI_TABLE #define gd_acpi_ctx() gd->acpi_ctx #else diff --git a/include/dm/device.h b/include/dm/device.h index 9183356468..8e16f2ea49 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -190,6 +190,21 @@ struct udevice { #endif }; +/** + * udevice_rt - runtime information set up by U-Boot + * + * This is only used with OF_PLATDATA_RT + * + * There is one of these for every udevice in the linker list, indexed by + * the udevice_info idx value. + * + * @flags_: Flags for this device DM_FLAG_... (do not access outside driver + * model) + */ +struct udevice_rt { + u32 flags_; +}; + /* Maximum sequence number supported */ #define DM_MAX_SEQ 999 diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c index f2b45b9a86..0f89c7a7da 100644 --- a/test/dm/of_platdata.c +++ b/test/dm/of_platdata.c @@ -147,7 +147,7 @@ static int dm_test_of_plat_dev(struct unit_test_state *uts) uint i; /* Skip this test if there is no platform data */ - if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) + if (!CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)) return 0; /* Record the indexes that are found */ From 6f644efdd803e0718d39266f75c0535a534cc601 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:37 +1300 Subject: [PATCH 078/120] dm: core: Move flags to device-runtime info When of-platdata-inst is active, use the flags in the new udevice_rt table, dropping them from the main struct udevice. This ensures that the latter is not updated at runtime. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/device.c | 33 +++++++++++++++++++++++++++++++++ include/dm/device.h | 8 ++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/core/device.c b/drivers/core/device.c index 1990b6f69f..06d2467825 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -1136,3 +1136,36 @@ int dev_enable_by_path(const char *path) return lists_bind_fdt(parent, node, NULL, false); } #endif + +#if CONFIG_IS_ENABLED(OF_PLATDATA_RT) +static struct udevice_rt *dev_get_rt(const struct udevice *dev) +{ + struct udevice *base = ll_entry_start(struct udevice, udevice); + int idx = dev - base; + + struct udevice_rt *urt = gd_dm_udevice_rt() + idx; + + return urt; +} + +u32 dev_get_flags(const struct udevice *dev) +{ + const struct udevice_rt *urt = dev_get_rt(dev); + + return urt->flags_; +} + +void dev_or_flags(const struct udevice *dev, u32 or) +{ + struct udevice_rt *urt = dev_get_rt(dev); + + urt->flags_ |= or; +} + +void dev_bic_flags(const struct udevice *dev, u32 bic) +{ + struct udevice_rt *urt = dev_get_rt(dev); + + urt->flags_ &= ~bic; +} +#endif /* OF_PLATDATA_RT */ diff --git a/include/dm/device.h b/include/dm/device.h index 8e16f2ea49..0a9718a5b8 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -177,7 +177,9 @@ struct udevice { struct list_head uclass_node; struct list_head child_head; struct list_head sibling_node; +#if !CONFIG_IS_ENABLED(OF_PLATDATA_RT) u32 flags_; +#endif int seq_; #if !CONFIG_IS_ENABLED(OF_PLATDATA) ofnode node_; @@ -211,6 +213,11 @@ struct udevice_rt { /* Returns the operations for a device */ #define device_get_ops(dev) (dev->driver->ops) +#if CONFIG_IS_ENABLED(OF_PLATDATA_RT) +u32 dev_get_flags(const struct udevice *dev); +void dev_or_flags(const struct udevice *dev, u32 or); +void dev_bic_flags(const struct udevice *dev, u32 bic); +#else static inline u32 dev_get_flags(const struct udevice *dev) { return dev->flags_; @@ -225,6 +232,7 @@ static inline void dev_bic_flags(struct udevice *dev, u32 bic) { dev->flags_ &= ~bic; } +#endif /* OF_PLATDATA_RT */ /** * dev_ofnode() - get the DT node reference associated with a udevice From baf0371883b243fa793ba3f984704b027705d6a9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:38 +1300 Subject: [PATCH 079/120] dm: core: Allow storing priv/plat data separately At present the device priv/data data allocated by dtoc is stored in the data section along with other variables. On some platforms it is better to allocate space for it separately, e.g. if SPL is running from read-only memory. Create a new space with the same size as that allocated by dtoc, ready for use. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/root.c | 12 ++++++++++++ include/asm-generic/global_data.h | 10 ++++++++++ include/asm-generic/sections.h | 3 +++ 3 files changed, 25 insertions(+) diff --git a/drivers/core/root.c b/drivers/core/root.c index 446ac2ff77..82b3c7de71 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +136,9 @@ static int dm_setup_inst(void) if (CONFIG_IS_ENABLED(OF_PLATDATA_RT)) { struct udevice_rt *urt; + void *base; int n_ents; + uint size; /* Allocate the udevice_rt table */ n_ents = ll_entry_count(struct udevice, udevice); @@ -143,6 +146,15 @@ static int dm_setup_inst(void) if (!urt) return log_msg_ret("urt", -ENOMEM); gd_set_dm_udevice_rt(urt); + + /* Now allocate space for the priv/plat data, and copy it in */ + size = __priv_data_end - __priv_data_start; + + base = calloc(1, size); + if (!base) + return log_msg_ret("priv", -ENOMEM); + memcpy(base, __priv_data_start, size); + gd_set_dm_priv_base(base); } return 0; diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index f6189eff4e..e1a5f4b1d1 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -222,6 +222,12 @@ struct global_data { #if CONFIG_IS_ENABLED(OF_PLATDATA_RT) /** @dm_udevice_rt: Dynamic info about the udevice */ struct udevice_rt *dm_udevice_rt; + /** + * @dm_priv_base: Base address of the priv/plat region used when + * udevices and uclasses are in read-only memory. This is NULL if not + * used + */ + void *dm_priv_base; # endif #endif #ifdef CONFIG_TIMER @@ -498,9 +504,13 @@ struct global_data { #if CONFIG_IS_ENABLED(OF_PLATDATA_RT) #define gd_set_dm_udevice_rt(dyn) gd->dm_udevice_rt = dyn #define gd_dm_udevice_rt() gd->dm_udevice_rt +#define gd_set_dm_priv_base(dyn) gd->dm_priv_base = dyn +#define gd_dm_priv_base() gd->dm_priv_base #else #define gd_set_dm_udevice_rt(dyn) #define gd_dm_udevice_rt() NULL +#define gd_set_dm_priv_base(dyn) +#define gd_dm_priv_base() NULL #endif #ifdef CONFIG_GENERATE_ACPI_TABLE diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index 0577238d60..267f1db73f 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -28,6 +28,9 @@ extern char __efi_helloworld_end[]; extern char __efi_var_file_begin[]; extern char __efi_var_file_end[]; +/* Private data used by of-platdata devices/uclasses */ +extern char __priv_data_start[], __priv_data_end[]; + /* Start and end of .ctors section - used for constructor calls. */ extern char __ctors_start[], __ctors_end[]; From d5cc19288e00cd287a13717f495d5f1a517feffd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:39 +1300 Subject: [PATCH 080/120] sandbox: Define a region for device priv/plat data Collect this together in one place, so driver model can access set it up in a new place if needed. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/sandbox/cpu/u-boot-spl.lds | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/sandbox/cpu/u-boot-spl.lds b/arch/sandbox/cpu/u-boot-spl.lds index 649abeb5ee..18160436a3 100644 --- a/arch/sandbox/cpu/u-boot-spl.lds +++ b/arch/sandbox/cpu/u-boot-spl.lds @@ -13,6 +13,14 @@ SECTIONS KEEP(*(SORT(.u_boot_list*))); } + /* Private data for devices with OF_PLATDATA_RT */ + . = ALIGN(4); + .priv_data : { + __priv_data_start = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.priv_data*))) + __priv_data_end = .; + } + __u_boot_sandbox_option_start = .; _u_boot_sandbox_getopt : { KEEP(*(.u_boot_sandbox_getopt)) } __u_boot_sandbox_option_end = .; From cfb9c9b77c29f48ae2c71ff30ca294bdaf369d87 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:40 +1300 Subject: [PATCH 081/120] dm: core: Use separate priv/plat data region Make use of the new priv/plat data region if enabled. This is implemented as a simple offset from the position set up by dtoc to the new position. So long as all access goes through dm_priv_to_rw() this is safe. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- drivers/core/device.c | 12 ++++++------ drivers/core/root.c | 9 +++++++++ include/dm/util.h | 9 +++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/core/device.c b/drivers/core/device.c index 06d2467825..cb960f8ec4 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -600,7 +600,7 @@ void *dev_get_plat(const struct udevice *dev) return NULL; } - return dev->plat_; + return dm_priv_to_rw(dev->plat_); } void *dev_get_parent_plat(const struct udevice *dev) @@ -610,7 +610,7 @@ void *dev_get_parent_plat(const struct udevice *dev) return NULL; } - return dev->parent_plat_; + return dm_priv_to_rw(dev->parent_plat_); } void *dev_get_uclass_plat(const struct udevice *dev) @@ -620,7 +620,7 @@ void *dev_get_uclass_plat(const struct udevice *dev) return NULL; } - return dev->uclass_plat_; + return dm_priv_to_rw(dev->uclass_plat_); } void *dev_get_priv(const struct udevice *dev) @@ -630,7 +630,7 @@ void *dev_get_priv(const struct udevice *dev) return NULL; } - return dev->priv_; + return dm_priv_to_rw(dev->priv_); } void *dev_get_uclass_priv(const struct udevice *dev) @@ -640,7 +640,7 @@ void *dev_get_uclass_priv(const struct udevice *dev) return NULL; } - return dev->uclass_priv_; + return dm_priv_to_rw(dev->uclass_priv_); } void *dev_get_parent_priv(const struct udevice *dev) @@ -650,7 +650,7 @@ void *dev_get_parent_priv(const struct udevice *dev) return NULL; } - return dev->parent_priv_; + return dm_priv_to_rw(dev->parent_priv_); } static int device_get_device_tail(struct udevice *dev, int ret, diff --git a/drivers/core/root.c b/drivers/core/root.c index 82b3c7de71..d9a19c5e6b 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -347,6 +347,15 @@ __weak int dm_scan_other(bool pre_reloc_only) return 0; } +#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY) +void *dm_priv_to_rw(void *priv) +{ + long offset = priv - (void *)__priv_data_start; + + return gd_dm_priv_base() + offset; +} +#endif + /** * dm_scan() - Scan tables to bind devices * diff --git a/include/dm/util.h b/include/dm/util.h index 01a044992f..138893c935 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -49,3 +49,12 @@ void dm_dump_driver_compat(void); void dm_dump_static_driver_info(void); #endif + +#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY) +void *dm_priv_to_rw(void *priv); +#else +static inline void *dm_priv_to_rw(void *priv) +{ + return priv; +} +#endif From e81bf6d72fbd016027d856f6da6047d39903390b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:41 +1300 Subject: [PATCH 082/120] dm: core: Add warnings to private / platform setters Add a warning to each of these functions so that people do not attempt to use them outside driver model. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- include/dm/device-internal.h | 18 ++++++++++++++++++ include/dm/uclass-internal.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 71e5c75028..e6b71cbfd2 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -284,6 +284,9 @@ static inline int device_chld_remove(struct udevice *dev, struct driver *drv, * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev Device to check * @priv New private-data pointer */ @@ -298,6 +301,9 @@ void dev_set_priv(struct udevice *dev, void *priv); * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev: Device to update * @parent_priv: New parent-private data */ @@ -312,6 +318,9 @@ void dev_set_parent_priv(struct udevice *dev, void *parent_priv); * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev: Device to update * @uclass_priv: New uclass private data */ @@ -326,6 +335,9 @@ void dev_set_uclass_priv(struct udevice *dev, void *uclass_priv); * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev Device to check * @plat New platform-data pointer */ @@ -340,6 +352,9 @@ void dev_set_plat(struct udevice *dev, void *priv); * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev: Device to update * @parent_plat: New parent platform data */ @@ -354,6 +369,9 @@ void dev_set_parent_plat(struct udevice *dev, void *parent_plat); * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @dev: Device to update * @uclass_plat: New uclass platform data */ diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index f2a780682b..57c664c6da 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -69,6 +69,9 @@ * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. * + * If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver + * model code, since the pointer must be within the gd->dm_priv_base region. + * * @uc Uclass to update * @priv New private-data pointer */ From 035e4054ca89461d21b27658cc3b7b568d1663cf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:42 +1300 Subject: [PATCH 083/120] dm: doc: Tidy up of-platdata docs This doc has a few pieces that are out-of-date. Fix these. Also we have started to use 'devicetree' instead of 'device tree' or 'device-tree' since it is easier to see as a single term, so replace all ocurrences accordingly. Also move the caveats to the end, since this is a fairly solid part of U-Boot now. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- doc/driver-model/of-plat.rst | 138 +++++++++++++---------------------- 1 file changed, 52 insertions(+), 86 deletions(-) diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst index a5a6e46e3e..3c9ba01b30 100644 --- a/doc/driver-model/of-plat.rst +++ b/doc/driver-model/of-plat.rst @@ -11,61 +11,25 @@ Device tree is the standard configuration method in U-Boot. It is used to define what devices are in the system and provide configuration information to these devices. -The overhead of adding device tree access to U-Boot is fairly modest, +The overhead of adding devicetree access to U-Boot is fairly modest, approximately 3KB on Thumb 2 (plus the size of the DT itself). This means -that in most cases it is best to use device tree for configuration. +that in most cases it is best to use devicetree for configuration. However there are some very constrained environments where U-Boot needs to work. These include SPL with severe memory limitations. For example, some SoCs require a 16KB SPL image which must include a full MMC stack. In this -case the overhead of device tree access may be too great. +case the overhead of devicetree access may be too great. It is possible to create platform data manually by defining C structures -for it, and reference that data in a U_BOOT_DRVINFO() declaration. This -bypasses the use of device tree completely, effectively creating a parallel +for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This +bypasses the use of devicetree completely, effectively creating a parallel configuration mechanism. But it is an available option for SPL. -As an alternative, a new 'of-platdata' feature is provided. This converts the -device tree contents into C code which can be compiled into the SPL binary. +As an alternative, the 'of-platdata' feature is provided. This converts the +devicetree contents into C code which can be compiled into the SPL binary. This saves the 3KB of code overhead and perhaps a few hundred more bytes due to more efficient storage of the data. -Note: Quite a bit of thought has gone into the design of this feature. -However it still has many rough edges and comments and suggestions are -strongly encouraged! Quite possibly there is a much better approach. - - -Caveats -------- - -There are many problems with this features. It should only be used when -strictly necessary. Notable problems include: - - - Device tree does not describe data types. But the C code must define a - type for each property. These are guessed using heuristics which - are wrong in several fairly common cases. For example an 8-byte value - is considered to be a 2-item integer array, and is byte-swapped. A - boolean value that is not present means 'false', but cannot be - included in the structures since there is generally no mention of it - in the device tree file. - - - Naming of nodes and properties is automatic. This means that they follow - the naming in the device tree, which may result in C identifiers that - look a bit strange. - - - It is not possible to find a value given a property name. Code must use - the associated C member variable directly in the code. This makes - the code less robust in the face of device-tree changes. It also - makes it very unlikely that your driver code will be useful for more - than one SoC. Even if the code is common, each SoC will end up with - a different C struct name, and a likely a different format for the - platform data. - - - The platform data is provided to drivers as a C structure. The driver - must use the same structure to access the data. Since a driver - normally also supports device tree it must use #ifdef to separate - out this code, since the structures are only available in SPL. - How it works ------------ @@ -77,9 +41,9 @@ SPL/TPL and should be tested with: #if CONFIG_IS_ENABLED(OF_PLATDATA) -A new tool called 'dtoc' converts a device tree file either into a set of +A tool called 'dtoc' converts a devicetree file either into a set of struct declarations, one for each compatible node, and a set of -U_BOOT_DRVINFO() declarations along with the actual platform data for each +`U_BOOT_DRVINFO()` declarations along with the actual platform data for each device. As an example, consider this MMC node: .. code-block:: none @@ -170,35 +134,35 @@ accessed using: struct udevice *dev; struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev); -This avoids the code overhead of converting the device tree data to -platform data in the driver. The of_to_plat() method should +This avoids the code overhead of converting the devicetree data to +platform data in the driver. The `of_to_plat()` method should therefore do nothing in such a driver. Note that for the platform data to be matched with a driver, the 'name' -property of the U_BOOT_DRVINFO() declaration has to match a driver declared -via U_BOOT_DRIVER(). This effectively means that a U_BOOT_DRIVER() with a +property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared +via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a 'name' corresponding to the devicetree 'compatible' string (after converting it to a valid name for C) is needed, so a dedicated driver is required for each 'compatible' string. -In order to make this a bit more flexible DM_DRIVER_ALIAS macro can be +In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be used to declare an alias for a driver name, typically a 'compatible' string. -This macro produces no code, but it is by dtoc tool. It must be located in the +This macro produces no code, but is used by dtoc tool. It must be located in the same file as its associated driver, ideally just after it. -The parent_idx is the index of the parent driver_info structure within its -linker list (instantiated by the U_BOOT_DRVINFO() macro). This is used to support -dev_get_parent(). +The parent_idx is the index of the parent `driver_info` structure within its +linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to +support `dev_get_parent()`. -During the build process dtoc parses both U_BOOT_DRIVER and DM_DRIVER_ALIAS -to build a list of valid driver names and driver aliases. If the 'compatible' -string used for a device does not not match a valid driver name, it will be -checked against the list of driver aliases in order to get the right driver -name to use. If in this step there is no match found a warning is issued to -avoid run-time failures. +During the build process dtoc parses both `U_BOOT_DRIVER()` and +`DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases. +If the 'compatible' string used for a device does not not match a valid driver +name, it will be checked against the list of driver aliases in order to get the +right driver name to use. If in this step there is no match found a warning is +issued to avoid run-time failures. -Where a node has multiple compatible strings, a #define is used to make them -equivalent, e.g.: +Where a node has multiple compatible strings, dtoc generates a `#define` to +make them equivalent, e.g.: .. code-block:: c @@ -210,26 +174,26 @@ Converting of-platdata to a useful form Of course it would be possible to use the of-platdata directly in your driver whenever configuration information is required. However this means that the -driver will not be able to support device tree, since the of-platdata -structure is not available when device tree is used. It would make no sense -to use this structure if device tree were available, since the structure has -all the limitations metioned in caveats above. +driver will not be able to support devicetree, since the of-platdata +structure is not available when devicetree is used. It would make no sense +to use this structure if devicetree were available, since the structure has +all the limitations metioned in caveats below. Therefore it is recommended that the of-platdata structure should be used -only in the probe() method of your driver. It cannot be used in the -of_to_plat() method since this is not called when platform data is +only in the `probe()` method of your driver. It cannot be used in the +`of_to_plat()` method since this is not called when platform data is already present. How to structure your driver ---------------------------- -Drivers should always support device tree as an option. The of-platdata +Drivers should always support devicetree as an option. The of-platdata feature is intended as a add-on to existing drivers. -Your driver should convert the plat struct in its probe() method. The -existing device tree decoding logic should be kept in the -of_to_plat() method and wrapped with #if. +Your driver should convert the plat struct in its `probe()` method. The +existing devicetree decoding logic should be kept in the +`of_to_plat()` method and wrapped with `#if`. For example: @@ -244,7 +208,7 @@ For example: #endif /* * Other fields can go here, to be filled in by decoding from - * the device tree (or the C structures when of-platdata is used). + * the devicetree (or the C structures when of-platdata is used). */ int fifo_depth; }; @@ -252,7 +216,7 @@ For example: static int mmc_of_to_plat(struct udevice *dev) { #if !CONFIG_IS_ENABLED(OF_PLATDATA) - /* Decode the device tree data */ + /* Decode the devicetree data */ struct mmc_plat *plat = dev_get_plat(dev); const void *blob = gd->fdt_blob; int node = dev_of_offset(dev); @@ -294,29 +258,29 @@ For example: DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */ -Note that struct mmc_plat is defined in the C file, not in a header. This +Note that `struct mmc_plat` is defined in the C file, not in a header. This is to avoid needing to include dt-structs.h in a header file. The idea is to keep the use of each of-platdata struct to the smallest possible code area. There is just one driver C file for each struct, that can convert from the of-platdata struct to the standard one used by the driver. -In the case where SPL_OF_PLATDATA is enabled, plat_auto is +In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is still used to allocate space for the platform data. This is different from the normal behaviour and is triggered by the use of of-platdata (strictly -speaking it is a non-zero plat_size which triggers this). +speaking it is a non-zero `plat_size` which triggers this). The of-platdata struct contents is copied from the C structure data to the -start of the newly allocated area. In the case where device tree is used, +start of the newly allocated area. In the case where devicetree is used, the platform data is allocated, and starts zeroed. In this case the -of_to_plat() method should still set up the platform data (and the +`of_to_plat()` method should still set up the platform data (and the of-platdata struct will not be present). -SPL must use either of-platdata or device tree. Drivers cannot use both at -the same time, but they must support device tree. Supporting of-platdata is +SPL must use either of-platdata or devicetree. Drivers cannot use both at +the same time, but they must support devicetree. Supporting of-platdata is optional. -The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled, -since the device-tree access code is not compiled in. A corollary is that +The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled, +since the devicetree access code is not compiled in. A corollary is that a board can only move to using of-platdata if all the drivers it uses support it. There would be little point in having some drivers require the device tree data, since then libfdt would still be needed for those drivers and @@ -326,10 +290,10 @@ Internals --------- The dt-structs.h file includes the generated file -(include/generated//dt-structs.h) if CONFIG_SPL_OF_PLATDATA is enabled. +`(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled. Otherwise (such as in U-Boot proper) these structs are not available. This prevents them being used inadvertently. All usage must be bracketed with -#if CONFIG_IS_ENABLED(OF_PLATDATA). +`#if CONFIG_IS_ENABLED(OF_PLATDATA)`. The dt-plat.c file contains the device declarations and is is built in spl/dt-plat.c. @@ -349,8 +313,9 @@ This is an implementation of an idea by Tom Rini . Future work ----------- -- Consider programmatically reading binding files instead of device tree +- Consider programmatically reading binding files instead of devicetree contents +- Allow IS_ENABLED() to be used in the C code instead of #if .. Simon Glass @@ -358,3 +323,4 @@ Future work .. 6/6/16 .. Updated Independence Day 2016 .. Updated 1st October 2020 +.. Updated 5th February 2021 From 34f73c9136b626893c7d338eb4f62f913dc311c9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:43 +1300 Subject: [PATCH 084/120] dm: doc: Add documentation for of-platdata-inst Add a description of the new features, along with internal technical documentation. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- doc/driver-model/of-plat.rst | 587 +++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+) diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst index 3c9ba01b30..74f1932473 100644 --- a/doc/driver-model/of-plat.rst +++ b/doc/driver-model/of-plat.rst @@ -286,9 +286,394 @@ it. There would be little point in having some drivers require the device tree data, since then libfdt would still be needed for those drivers and there would be no code-size benefit. + +Build-time instantiation +------------------------ + +Even with of-platdata there is a fair amount of code required in driver model. +It is possible to have U-Boot handle the instantiation of devices at build-time, +so avoiding the need for the `device_bind()` code and some parts of +`device_probe()`. + +The feature is enabled by CONFIG_OF_PLATDATA_INST. + +Here is an example device, as generated by dtoc:: + + /* + * Node /serial index 6 + * driver sandbox_serial parent root_driver + */ + + #include + struct sandbox_serial_plat __attribute__ ((section (".priv_data"))) + _sandbox_serial_plat_serial = { + .dtplat = { + .sandbox_text_colour = "cyan", + }, + }; + #include + u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)] + __attribute__ ((section (".priv_data"))); + #include + u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(serial) = { + .driver = DM_DRIVER_REF(sandbox_serial), + .name = "sandbox_serial", + .plat_ = &_sandbox_serial_plat_serial, + .priv_ = _sandbox_serial_priv_serial, + .uclass = DM_UCLASS_REF(serial), + .uclass_priv_ = _sandbox_serial_uc_priv_serial, + .uclass_node = { + .prev = &DM_UCLASS_REF(serial)->dev_head, + .next = &DM_UCLASS_REF(serial)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(serial)->child_head, + .next = &DM_DEVICE_REF(serial)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node, + .next = &DM_DEVICE_REF(spl_test)->sibling_node, + }, + .seq_ = 0, + }; + +Here is part of the driver, for reference:: + + static const struct udevice_id sandbox_serial_ids[] = { + { .compatible = "sandbox,serial" }, + { } + }; + + U_BOOT_DRIVER(sandbox_serial) = { + .name = "sandbox_serial", + .id = UCLASS_SERIAL, + .of_match = sandbox_serial_ids, + .of_to_plat = sandbox_serial_of_to_plat, + .plat_auto = sizeof(struct sandbox_serial_plat), + .priv_auto = sizeof(struct sandbox_serial_priv), + .probe = sandbox_serial_probe, + .remove = sandbox_serial_remove, + .ops = &sandbox_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + }; + + +The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the +members are from that struct. The private data is declared immediately above, +as `_sandbox_serial_priv_serial`, so there is no need for run-time memory +allocation. The #include lines are generated as well, since dtoc searches the +U-Boot source code for the definition of `struct sandbox_serial_priv` and adds +the relevant header so that the code will compile without errors. + +The `plat_` member is set to the dtv data which is declared immediately above +the device. This is similar to how it would look without of-platdata-inst, but +node that the `dtplat` member inside is part of the wider +`_sandbox_serial_plat_serial` struct. This is because the driver declares its +own platform data, and the part generated by dtoc can only be a portion of it. +The `dtplat` part is always first in the struct. If the device has no +`.plat_auto` field, then a simple dtv struct can be used as with this example:: + + static struct dtd_sandbox_clk dtv_clk_sbox = { + .assigned_clock_rates = 0x141, + .assigned_clocks = {0x7, 0x3}, + }; + + #include + u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(clk_sbox) = { + .driver = DM_DRIVER_REF(sandbox_clk), + .name = "sandbox_clk", + .plat_ = &dtv_clk_sbox, + +Here is part of the driver, for reference:: + + static const struct udevice_id sandbox_clk_ids[] = { + { .compatible = "sandbox,clk" }, + { } + }; + + U_BOOT_DRIVER(sandbox_clk) = { + .name = "sandbox_clk", + .id = UCLASS_CLK, + .of_match = sandbox_clk_ids, + .ops = &sandbox_clk_ops, + .probe = sandbox_clk_probe, + .priv_auto = sizeof(struct sandbox_clk_priv), + }; + + +You can see that `dtv_clk_sbox` just has the devicetree contents and there is +no need for the `dtplat` separation, since the driver has no platform data of +its own, besides that provided by the devicetree (i.e. no `.plat_auto` field). + +The doubly linked lists are handled by explicitly declaring the value of each +node, as you can see with the `.prev` and `.next` values in the example above. +Since dtoc knows the order of devices it can link them into the appropriate +lists correctly. + +One of the features of driver model is the ability for a uclass to have a +small amount of private data for each device in that uclass. This is used to +provide a generic data structure that the uclass can use for all devices, thus +allowing generic features to be implemented in common code. An example is I2C, +which stores the bus speed there. + +Similarly, parent devices can have data associated with each of their children. +This is used to provide information common to all children of a particular bus. +For an I2C bus, this is used to store the I2C address of each child on the bus. + +This is all handled automatically by dtoc:: + + #include + u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)] + __attribute__ ((section (".priv_data"))); + #include + u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(i2c_at_0) = { + .driver = DM_DRIVER_REF(sandbox_i2c), + .name = "sandbox_i2c", + .plat_ = &dtv_i2c_at_0, + .priv_ = _sandbox_i2c_priv_i2c_at_0, + .uclass = DM_UCLASS_REF(i2c), + .uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0, + ... + +Part of driver, for reference:: + + static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c" }, + { } + }; + + U_BOOT_DRIVER(sandbox_i2c) = { + .name = "sandbox_i2c", + .id = UCLASS_I2C, + .of_match = sandbox_i2c_ids, + .ops = &sandbox_i2c_ops, + .priv_auto = sizeof(struct sandbox_i2c_priv), + }; + +Part of I2C uclass, for reference:: + + UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = i2c_post_bind, + .pre_probe = i2c_pre_probe, + .post_probe = i2c_post_probe, + .per_device_auto = sizeof(struct dm_i2c_bus), + .per_child_plat_auto = sizeof(struct dm_i2c_chip), + .child_post_bind = i2c_child_post_bind, + }; + +Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared +in the device, as required by driver model. The required header file is included +so that the code will compile without errors. A similar mechanism is used for +child devices, but is not shown by this example. + +It would not be that useful to avoid binding devices but still need to allocate +uclasses at runtime. So dtoc generates uclass instances as well:: + + struct list_head uclass_head = { + .prev = &DM_UCLASS_REF(serial)->sibling_node, + .next = &DM_UCLASS_REF(clk)->sibling_node, + }; + + DM_UCLASS_INST(clk) = { + .uc_drv = DM_UCLASS_DRIVER_REF(clk), + .sibling_node = { + .prev = &uclass_head, + .next = &DM_UCLASS_REF(i2c)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(clk_sbox)->uclass_node, + .next = &DM_DEVICE_REF(clk_fixed)->uclass_node, + }, + }; + +At the top is the list head. Driver model uses this on start-up, instead of +creating its own. + +Below that are a set of `DM_UCLASS_INST()` macros, each declaring a +`struct uclass`. The doubly linked lists work as for devices. + +All private data is placed into a `.priv_data` section so that it is contiguous +in the resulting output binary. + + +Indexes +------- + +U-Boot stores drivers, devices and many other things in linker_list structures. +These are sorted by name, so dtoc knows the order that they will appear when +the linker runs. Each driver_info / udevice is referenced by its index in the +linker_list array, called 'idx' in the code. + +When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it +is the driver_info index. In either case, indexes are used to reference devices +using device_get_by_ofplat_idx(). This allows phandles to work as expected. + + +Phases +------ + +U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. +The latter does not use dtoc. + +In some rare cases different drivers are used for two phases. For example, +in TPL it may not be necessary to use the full PCI subsystem, so a simple +driver can be used instead. + +This works in the build system simply by compiling in one driver or the +other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has +no way of knowing which code is compiled in for which phase, since it does +not inspect Makefiles or dependency graphs. + +So to make this work for dtoc, we need to be able to explicitly mark +drivers with their phase. This is done by adding a macro to the driver:: + + /* code in tpl.c only compiled into TPL */ + U_BOOT_DRIVER(pci_x86) = { + .name = "pci_x86", + .id = UCLASS_SIMPLE_BUS, + .of_match = of_match_ptr(tpl_fake_pci_ids), + DM_PHASE(tpl) + }; + + + /* code in pci_x86.c compiled into SPL and U-Boot proper */ + U_BOOT_DRIVER(pci_x86) = { + .name = "pci_x86", + .id = UCLASS_PCI, + .of_match = pci_x86_ids, + .ops = &pci_x86_ops, + }; + + +Notice that the second driver has the same name but no DM_PHASE(), so it will be +used for SPL and U-Boot. + +Note also that this only affects the code generated by dtoc. You still need to +make sure that only the required driver is build into each phase. + + +Header files +------------ + +With OF_PLATDATA_INST, dtoc must include the correct header file in the +generated code for any structs that are used, so that the code will compile. +For example, if `struct ns16550_plat` is used, the code must include the +`ns16550.h` header file. + +Typically dtoc can detect the header file needed for a driver by looking +for the structs that it uses. For example, if a driver as a `.priv_auto` +that uses `struct ns16550_plat`, then dtoc can search header files for the +definition of that struct and use the file. + +In some cases, enums are used in drivers, typically with the `.data` field +of `struct udevice_id`. Since dtoc does not support searching for these, +you must use the `DM_HDR()` macro to tell dtoc which header to use. This works +as a macro included in the driver definition:: + + static const struct udevice_id apl_syscon_ids[] = { + { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT }, + { } + }; + + U_BOOT_DRIVER(intel_apl_punit) = { + .name = "intel_apl_punit", + .id = UCLASS_SYSCON, + .of_match = apl_syscon_ids, + .probe = apl_punit_probe, + DM_HEADER() /* for X86_SYSCON_PUNIT */ + }; + + + +Caveats +------- + +There are various complications with this feature which mean it should only +be used when strictly necessary, i.e. in SPL with limited memory. Notable +caveats include: + + - Device tree does not describe data types. But the C code must define a + type for each property. These are guessed using heuristics which + are wrong in several fairly common cases. For example an 8-byte value + is considered to be a 2-item integer array, and is byte-swapped. A + boolean value that is not present means 'false', but cannot be + included in the structures since there is generally no mention of it + in the devicetree file. + + - Naming of nodes and properties is automatic. This means that they follow + the naming in the devicetree, which may result in C identifiers that + look a bit strange. + + - It is not possible to find a value given a property name. Code must use + the associated C member variable directly in the code. This makes + the code less robust in the face of devicetree changes. To avoid having + a second struct with similar members and names you need to explicitly + declare it as an alias with `DM_DRIVER_ALIAS()`. + + - The platform data is provided to drivers as a C structure. The driver + must use the same structure to access the data. Since a driver + normally also supports devicetree it must use `#ifdef` to separate + out this code, since the structures are only available in SPL. This could + be fixed fairly easily by making the structs available outside SPL, so + that `IS_ENABLED()` could be used. + + - With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning + that (by default) it is not possible to call `device_bind()` from C code. + This means that all devices must have an associated devicetree node and + compatible string. For example if a GPIO device currently creates child + devices in its `bind()` method, it will not work with + CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the + devicetree binding should be updated to declare compatible strings for + the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this + is not recommended since it increases code size. + + Internals --------- +Generated files +``````````````` + +When enabled, dtoc generates the following five files: + +include/generated/dt-decl.h (OF_PLATDATA_INST only) + Contains declarations for all drivers, devices and uclasses. This allows + any `struct udevice`, `struct driver` or `struct uclass` to be located by its + name + +include/generated/dt-structs-gen.h + Contains the struct definitions for the devicetree nodes that are used. This + is the same as without OF_PLATDATA_INST + +spl/dts/dt-plat.c (only with !OF_PLATDATA_INST) + Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices + at start-up. See above for an example + +spl/dts/dt-device.c (only with OF_PLATDATA_INST) + Contains `DM_DEVICE_INST()` declarations for each device that can be used at + run-time. These are declared in the file along with any private/platform data + that they use. Every device has an idx, as above. Since each device must be + part of a double-linked list, the nodes are declared in the code as well. + +spl/dts/dt-uclass.c (only with OF_PLATDATA_INST) + Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at + run-time. These are declared in the file along with any private data + associated with the uclass itself (the `.priv_auto` member). Since each + uclass must be part of a double-linked list, the nodes are declared in the + code as well. + The dt-structs.h file includes the generated file `(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled. Otherwise (such as in U-Boot proper) these structs are not available. This @@ -298,6 +683,208 @@ prevents them being used inadvertently. All usage must be bracketed with The dt-plat.c file contains the device declarations and is is built in spl/dt-plat.c. + +CONFIG options +`````````````` + +Several CONFIG options are used to control the behaviour of of-platdata, all +available for both SPL and TPL: + +OF_PLATDATA + This is the main option which enables the of-platdata feature + +OF_PLATDATA_PARENT + This allows `device_get_parent()` to work. Without this, all devices exist as + direct children of the root node. This option is highly desirable (if not + always absolutely essential) for buses such as I2C. + +OF_PLATDATA_INST + This controls the instantiation of devices at build time. With it disabled, + only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding + in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and + `DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at + runtime. + +OF_PLATDATA_NO_BIND + This controls whether `device_bind()` is supported. It is enabled by default + with OF_PLATDATA_INST since code-size reduction is really the main point of + the feature. It can be disabled if needed but is not likely to be supported + in the long term. + +OF_PLATDATA_DRIVER_RT + This controls whether the `struct driver_rt` records are used by U-Boot. + Normally when a device is bound, U-Boot stores the device pointer in one of + these records. There is one for every `struct driver_info` in the system, + i.e. one for every device that is bound from those records. It provides a + way to locate a device in the code and is used by + `device_get_by_ofplat_idx()`. This option is always enabled with of-platdata, + provided OF_PLATDATA_INST is not. In that case the records are useless since + we don't have any `struct driver_info` records. + +OF_PLATDATA_RT + This controls whether the `struct udevice_rt` records are used by U-Boot. + It moves the updatable fields from `struct udevice` (currently only `flags`) + into a separate structure, allowing the records to be kept in read-only + memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option + also controls whether the private data is used in situ, or first copied into + an allocated region. Again this is to allow the private data declared by + dtoc-generated code to be in read-only memory. Note that access to private + data must be done via accessor functions, such as `dev_get_priv()`, so that + the relocation is handled. + +READ_ONLY + This indicates that the data generated by dtoc should not be modified. Only + a few fields actually do get changed in U-Boot, such as device flags. This + option causes those to move into an allocated space (see OF_PLATDATA_RT). + Also, since updating doubly linked lists is generally impossible when some of + the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled. + +Data structures +``````````````` + +A few extra data structures are used with of-platdata: + +`struct udevice_rt` + Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds + the flags for each device, so that `struct udevice` can remain unchanged by + U-Boot, and potentially reside in read-only memory. Access to flags is then + via functions like `dev_get_flags()` and `dev_or_flags()`. This data + structure is allocated on start-up, where the private data is also copied. + All flags values start at 0 and any changes are handled by `dev_or_flags()` + and `dev_bic_flags()`. It would be more correct for the flags to be set to + `DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since + there is no code to bind/unbind devices and no code to allocate/free + private data / platform data, it doesn't matter. + +`struct driver_rt` + Run-time information for `struct driver_info` records. When + OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device + created by each record. This is needed so that is it possible to locate a + device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to + get a reference to a particular `struct driver_info`, with `name` being the + name of the devicetree node. This is very convenient. It is also fast, since + no searching or string comparison is needed. This data structure is + allocated on start-up, filled out by `device_bind()` and used by + `device_get_by_ofplat_idx()`. + +Other changes +````````````` + +Some other changes are made with of-platdata: + +Accessor functions + Accessing private / platform data via functions such as `dev_get_priv()` has + always been encouraged. With OF_PLATDATA_RT this is essential, since the + `priv_` and `plat_` (etc.) values point to the data generated by dtoc, not + the read-write copy that is sometimes made on start-up. Changing the + private / platform data pointers has always been discouraged (the API is + marked internal) but with OF_PLATDATA_RT this is not currently supported in + general, since it assumes that all such pointers point to the relocated data. + Note also that the renaming of struct members to have a trailing underscore + was partly done to make people aware that they should not be accessed + directly. + +`gd->uclass_root_s` + Normally U-Boot sets up the head of the uclass list here and makes + `gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a + declaration of `uclass_head` in `dt-uclass.c` since it needs to link the + head node into the list. In that case, `gd->uclass_root_s` is not used and + U-Boot just makes `gd->uclass_root` point to `uclass_head`. + +`gd->dm_driver_rt` + This holds a pointer to a list of `struct driver_rt` records, one for each + `struct driver_info`. The list is in alphabetical order by the name used + in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having + an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is + accessed via macros so that it can be used inside IS_ENABLED(), rather than + requiring #ifdefs in the C code when it is not present. + +`gd->dm_udevice_rt` + This holds a pointer to a list of `struct udevice_rt` records, one for each + `struct udevice`. The list is in alphabetical order by the name used + in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by + idx, with the first record having an index of 0. It is only used if + OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be + used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when + it is not present. + +`gd->dm_priv_base` + When OF_PLATDATA_RT is enabled, the private/platform data for each device is + copied into an allocated region by U-Boot on start-up. This points to that + region. All calls to accessor functions (e.g. `dev_get_priv()`) then + translate from the pointer provided by the caller (assumed to lie between + `__priv_data_start` and `__priv_data_end`) to the new allocated region. This + member is accessed via macros so that it can be used inside IS_ENABLED(), + rather than required #ifdefs in the C code when it is not present. + +`struct udevice->flags_` + When OF_PLATDATA_RT is enabled, device flags are no-longer part of + `struct udevice`, but are instead kept in `struct udevice_rt`, as described + above. Flags are accessed via functions, such as `dev_get_flags()` and + `dev_or_flags()`. + +`struct udevice->node_` + When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need + for this field. It is removed, just to save space. + +`DM_PHASE` + This macro is used to indicate which phase of U-Boot a driver is intended + for. See above for details. + +`DM_HDR` + This macro is used to indicate which header file dtoc should use to allow + a driver declaration to compile correctly. See above for details. + +`device_get_by_ofplat_idx()` + There used to be a function called `device_get_by_driver_info()` which + looked up a `struct driver_info` pointer and returned the `struct udevice` + that was created from it. It was only available for use with of-platdata. + This has been removed in favour of `device_get_by_ofplat_idx()` which uses + `idx`, the index of the `struct driver_info` or `struct udevice` in the + linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been + updated to use this index instead of a pointer to `struct driver_info`. + +`DM_DRVINFO_GET` + This has been removed since we now use indexes to obtain a driver from + `struct phandle_0_arg` and the like. + +Two-pass binding + The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated + files so as to have parents declared ahead of children. This was convenient + as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does + not work as the idx value relies on using alphabetical order for everything, + so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are + then bound in order of idx, having no regard to parent/child relationships. + For this reason, device binding now hapens in multiple passes, with parents + being bound before their children. This is important so that children can + find their parents in the bind() method if needed. + +Root device + The root device is generally bound by U-Boot but with OF_PLATDATA_INST it + cannot be, since binding needs to be done at build time. So in this case + dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and + U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot + generally ignores the root node and does not create a `U_BOOT_DRVINFO()` + record for it. This means that the idx numbers used by `struct driver_info` + (when OF_PLATDATA_INST is disabled) and the idx numbers used by + `struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a + root node and the other does not. This does not actually matter, since only + one of them is actually used for any particular build, but it is worth + keeping in mind if comparing index values and switching OF_PLATDATA_INST on + and off. + +`__priv_data_start` and `__priv_data_end` + The private/platform data declared by dtoc is all collected together in + a linker section and these symbols mark the start and end of it. This allows + U-Boot to relocate the area to a new location if needed (with + OF_PLATDATA_RT) + +`dm_priv_to_rw()` + This function converts a private- or platform-data pointer value generated by + dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT + is enabled, in which case it translates the address to the relocated + region. See above for more information. + The dm_populate_phandle_data() function that was previous needed has now been removed, since dtoc can address the drivers directly from dt-plat.c and does not need to fix up things at runtime. From 72db28ee6833ee2bd1ce6dd9148f49538409c58c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:44 +1300 Subject: [PATCH 085/120] x86: Define a region for device priv/plat data Collect this together in one place, so driver model can access set it up in a new place if needed. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/cpu/u-boot-spl.lds | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/cpu/u-boot-spl.lds b/arch/x86/cpu/u-boot-spl.lds index b82e53ab12..4a655bf9b5 100644 --- a/arch/x86/cpu/u-boot-spl.lds +++ b/arch/x86/cpu/u-boot-spl.lds @@ -32,6 +32,14 @@ SECTIONS . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + . = ALIGN(4); + + .priv_data : { + __priv_data_start = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.priv_data*))) + __priv_data_end = .; + } + . = ALIGN(4); .data : { *(.data*) } From 8d0defa44b7faf2eee13a37b525a5c0802d40517 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:45 +1300 Subject: [PATCH 086/120] x86: apl: Fix the header order in pmc The dm.h header should come first. In fact it needs to, since otherwise the driver model definitions are not available to dt-structs.h Fix this, since it causes problems with OF_PLATDATA_INST. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/cpu/apollolake/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/cpu/apollolake/pmc.c b/arch/x86/cpu/apollolake/pmc.c index e23d38ea07..1d21187c96 100644 --- a/arch/x86/cpu/apollolake/pmc.c +++ b/arch/x86/cpu/apollolake/pmc.c @@ -9,8 +9,8 @@ #define LOG_CATEGORY UCLASS_ACPI_PMC #include -#include #include +#include #include #include #include From a65c35ed7784d782e6aecb6c85c84e77a08474b7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:46 +1300 Subject: [PATCH 087/120] x86: apl: Tell of-platdata about a required header file This enum is needed to generate build-time devices. Tell dtoc where to find the header, to avoid compile errors in the generated code. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/cpu/apollolake/punit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/cpu/apollolake/punit.c b/arch/x86/cpu/apollolake/punit.c index e67c011e22..5ed7963579 100644 --- a/arch/x86/cpu/apollolake/punit.c +++ b/arch/x86/cpu/apollolake/punit.c @@ -93,4 +93,5 @@ U_BOOT_DRIVER(intel_apl_punit) = { .id = UCLASS_SYSCON, .of_match = apl_syscon_ids, .probe = apl_punit_probe, + DM_HEADER() /* for X86_SYSCON_PUNIT */ }; From 763ce51bc32470eeb7316643f4f341e14fe4ee07 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:47 +1300 Subject: [PATCH 088/120] x86: itss: Tidy up bind() for of-platdata-inst With the standard of-platdata we must fix up driver_data manually. With of-platadata-inst this is not necessary, since it is added to the device by dtoc. Update the code to handle this. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/cpu/intel_common/itss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/cpu/intel_common/itss.c b/arch/x86/cpu/intel_common/itss.c index 588a512ecd..1eff030983 100644 --- a/arch/x86/cpu/intel_common/itss.c +++ b/arch/x86/cpu/intel_common/itss.c @@ -153,8 +153,9 @@ static int route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num) static int itss_bind(struct udevice *dev) { - /* This is not set with of-platdata, so set it manually */ - if (CONFIG_IS_ENABLED(OF_PLATDATA)) + /* This is not set with basic of-platdata, so set it manually */ + if (CONFIG_IS_ENABLED(OF_PLATDATA) && + !CONFIG_IS_ENABLED(OF_PLATDATA_INST)) dev->driver_data = X86_IRQT_ITSS; return 0; From 1d3daaa681bf834d3ec438f288657c0d3c6da112 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:48 +1300 Subject: [PATCH 089/120] x86: Support a fake PCI device with of-platdata-inst With TPL we don't need full PCI support and it adds to code size. Instead, a simple_bus driver is good enough to be able to read and write the PCI config and do a little basic setup. So at present there are two drivers in U-Boot called pci_x86. One is in UCLASS_PCI, used in SPL and U-Boot proper. The other is in UCLASS_SIMPLE_BUS and used only in TPL. Add a tag to tell dtoc about this, so it knows which one to use when generating the devices and uclasses. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/lib/tpl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/lib/tpl.c b/arch/x86/lib/tpl.c index ac6b6efbcc..b3e5f9c913 100644 --- a/arch/x86/lib/tpl.c +++ b/arch/x86/lib/tpl.c @@ -150,5 +150,6 @@ U_BOOT_DRIVER(pci_x86) = { .name = "pci_x86", .id = UCLASS_SIMPLE_BUS, .of_match = of_match_ptr(tpl_fake_pci_ids), + DM_PHASE(tpl) }; #endif From 5384ea96ce2c55b042316e4d6a2b3968660c0d40 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:49 +1300 Subject: [PATCH 090/120] x86: Don't include reset driver in SPL We don't normally need this driver in TPL/SPL, so drop it for now. It can be enabled by individual boards if needed. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/dts/reset.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/dts/reset.dtsi b/arch/x86/dts/reset.dtsi index 555d0dd960..f2ba2fb5e8 100644 --- a/arch/x86/dts/reset.dtsi +++ b/arch/x86/dts/reset.dtsi @@ -1,6 +1,6 @@ / { reset: reset { compatible = "x86,reset"; - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; }; }; From 26c69078c92cca3b86bc423d673113b524ed5454 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:50 +1300 Subject: [PATCH 091/120] x86: coral: Drop ACPI properties from of-platdata We don't use these in TPL or SPL, so drop them. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- configs/chromebook_coral_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig index 0811ffd79d..e3756cf79c 100644 --- a/configs/chromebook_coral_defconfig +++ b/configs/chromebook_coral_defconfig @@ -73,6 +73,7 @@ CONFIG_MAC_PARTITION=y CONFIG_ISO_PARTITION=y CONFIG_EFI_PARTITION=y # CONFIG_SPL_EFI_PARTITION is not set +CONFIG_OF_SPL_REMOVE_PROPS="clocks clock-names interrupt-parent interrupts linux-name acpi,name acpi,path u-boot,acpi-dsdt-order u-boot,acpi-ssdt-order" CONFIG_ENV_OVERWRITE=y CONFIG_REGMAP=y CONFIG_SYSCON=y From 541b2adf080bf9299161fe9af29659633e03d7db Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:51 +1300 Subject: [PATCH 092/120] x86: coral: Drop TPM and ACPI interrupts from TPL These devices are not actually built in TPL but are currently active in the TPL devicetree. For of-platdata-inst this means that we will try to generate devices for them, which fails. Update them to be active only in U-Boot proper. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/dts/chromebook_coral.dts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts index 2ffe3b423c..d109a38759 100644 --- a/arch/x86/dts/chromebook_coral.dts +++ b/arch/x86/dts/chromebook_coral.dts @@ -100,7 +100,7 @@ clk: clock { compatible = "intel,apl-clk"; #clock-cells = <1>; - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; }; cpus { @@ -141,7 +141,7 @@ }; acpi_gpe: general-purpose-events { - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; reg = ; compatible = "intel,acpi-gpe"; interrupt-controller; @@ -423,7 +423,7 @@ compatible = "intel,apl-i2c", "snps,designware-i2c-pci"; reg = <0x0200b210 0 0 0 0>; early-regs = ; - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; #address-cells = <1>; #size-cells = <0>; clock-frequency = <400000>; @@ -434,7 +434,7 @@ tpm: tpm@50 { reg = <0x50>; compatible = "google,cr50"; - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; u-boot,i2c-offset-len = <0>; ready-gpios = <&gpio_n 28 GPIO_ACTIVE_LOW>; interrupts-extended = <&acpi_gpe GPIO_28_IRQ @@ -1233,5 +1233,5 @@ &rtc { #address-cells = <1>; #size-cells = <0>; - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; }; From 98ed0ae2cc12d4e7f5895673264f99de2e28317e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 15 Mar 2021 17:25:52 +1300 Subject: [PATCH 093/120] x86: apl: Use read-only SPL and new of-platdata With Apollo Lake, SPL is placed in read-only memory. Set this new option so that OF_PLATDATA_INST can be used. Signed-off-by: Simon Glass Signed-off-by: Simon Glass --- arch/x86/cpu/apollolake/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/cpu/apollolake/Kconfig b/arch/x86/cpu/apollolake/Kconfig index 590fe31dc4..b3ce053173 100644 --- a/arch/x86/cpu/apollolake/Kconfig +++ b/arch/x86/cpu/apollolake/Kconfig @@ -21,6 +21,8 @@ config INTEL_APOLLOLAKE select INTEL_GMA_SWSMISCI select ACPI_GNVS_EXTERNAL select TPL_OF_PLATDATA_PARENT + select TPL_OF_PLATDATA_INST + select TPL_READ_ONLY imply ENABLE_MRC_CACHE imply AHCI_PCI imply SCSI From 862ddf91002c31dcca7bd8f2712a98b71de01de8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:51 +1300 Subject: [PATCH 094/120] binman: Allow extracting to current directory Extracting files to the current directory is not normally a very friendly thing to do, but it can be warranted, e.g. in a new temporary dir. At present binman reports an error when such an attempt is made. Fix it. Signed-off-by: Simon Glass --- tools/binman/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/binman/control.py b/tools/binman/control.py index 1952b2abf4..9c0cafeafc 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -241,7 +241,7 @@ def ExtractEntries(image_fname, output_fname, outdir, entry_paths, # If this entry has children, create a directory for it and put its # data in a file called 'root' in that directory if entry.GetEntries(): - if not os.path.exists(fname): + if fname and not os.path.exists(fname): os.makedirs(fname) fname = os.path.join(fname, 'root') tout.Notice("Write entry '%s' size %x to '%s'" % From a01d1a25bcd7b22bdb16a8fd0583df0d41db3054 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:52 +1300 Subject: [PATCH 095/120] binman: Document ExpandEntries() in the base class Move the documentation to the base method as it is with other methods. Also update it a little while we are here. Signed-off-by: Simon Glass --- tools/binman/entry.py | 11 +++++++++++ tools/binman/etype/section.py | 6 ------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index d58a730f3d..507760e2a8 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -211,6 +211,17 @@ class Entry(object): return {} def ExpandEntries(self): + """Expand out entries which produce other entries + + Some entries generate subnodes automatically, from which sub-entries + are then created. This method allows those to be added to the binman + definition for the current image. An entry which implements this method + should call state.AddSubnode() to add a subnode and can add properties + with state.AddString(), etc. + + An example is 'files', which produces a section containing a list of + files. + """ pass def AddMissingProperties(self, have_image_pos): diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 1ceadef13f..2103919b0c 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -126,12 +126,6 @@ class Entry_section(Entry): return True def ExpandEntries(self): - """Expand out any entries which have calculated sub-entries - - Some entries are expanded out at runtime, e.g. 'files', which produces - a section containing a list of files. Process these entries so that - this information is added to the device tree. - """ super().ExpandEntries() for entry in self._entries.values(): entry.ExpandEntries() From 4ce4077a41e33cc1cf0b7eb441957014cfa9a718 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:53 +1300 Subject: [PATCH 096/120] binman: Update entry help for files-align Regenerate the entry documentation, which step was missed when the files-align feature was added. Fixes: 6eb9932668f ("binman: Support alignment of files") Signed-off-by: Simon Glass --- tools/binman/README.entries | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 999b77690f..7cca030409 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -302,6 +302,7 @@ Properties / Entry arguments: - files-compress: Compression algorithm to use: none: No compression lz4: Use lz4 compression (via 'lz4' command-line utility) + - files-align: Align each file to the given alignment This entry reads a number of files and places each in a separate sub-entry within this entry. To access these you need to enable device-tree updates From adc59eaff7040f46d91b019048b34448e7eff325 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:54 +1300 Subject: [PATCH 097/120] binman: Tidy up underscores in entry documentation Several entries currently use an underscore in the entry-type name, but in fact a hyphen is used. Update the docs to fix this as it might be confusing. Also simplify the 'filename' comment and fix the 'operation' typo. Signed-off-by: Simon Glass --- tools/binman/README.entries | 23 ++++++++++----------- tools/binman/etype/blob.py | 2 +- tools/binman/etype/u_boot.py | 2 +- tools/binman/etype/u_boot_dtb_with_ucode.py | 4 ++-- tools/binman/etype/u_boot_nodtb.py | 6 +++--- tools/binman/etype/u_boot_spl_nodtb.py | 7 +++---- tools/binman/etype/u_boot_with_ucode_ptr.py | 2 +- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 7cca030409..3fbc06d926 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -39,7 +39,7 @@ Properties / Entry arguments: This entry reads data from a file and places it in the entry. The default filename is often specified specified by the subclass. See for -example the 'u_boot' entry which provides the filename 'u-boot.bin'. +example the 'u-boot' entry which provides the filename 'u-boot.bin'. If compression is enabled, an extra 'uncomp-size' property is written to the node (if enabled with -u) which provides the uncompressed size of the @@ -840,7 +840,7 @@ Properties / Entry arguments: This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. The binary typically includes a device tree -blob at the end of it. Use u_boot_nodtb if you want to package the device +blob at the end of it. Use u-boot-nodtb if you want to package the device tree separately. U-Boot can access binman symbols at runtime. See: @@ -876,9 +876,9 @@ See Entry_u_boot_ucode for full details of the three entries involved in this process. This entry provides the U-Boot device-tree file, which contains the microcode. If the microcode is not being collated into one place then the offset and size of the microcode is recorded by this entry, -for use by u_boot_with_ucode_ptr. If it is being collated, then this +for use by u-boot-with-ucode_ptr. If it is being collated, then this entry deletes the microcode from the device tree (to save space) and makes -it available to u_boot_ucode. +it available to u-boot-ucode. @@ -920,12 +920,12 @@ Entry: u-boot-nodtb: U-Boot flat binary without device tree appended -------------------------------------------------------------------- Properties / Entry arguments: - - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin') + - filename: Filename to include (default 'u-boot-nodtb.bin') This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. It does not include a device tree blob at -the end of it so normally cannot work without it. You can add a u_boot_dtb -entry after this one, or use a u_boot entry instead (which contains both +the end of it so normally cannot work without it. You can add a u-boot-dtb +entry after this one, or use a u-boot entry instead (which contains both U-Boot and the device tree). @@ -1000,13 +1000,12 @@ Entry: u-boot-spl-nodtb: SPL binary without device tree appended ---------------------------------------------------------------- Properties / Entry arguments: - - filename: Filename of spl/u-boot-spl-nodtb.bin (default - 'spl/u-boot-spl-nodtb.bin') + - filename: Filename to include (default 'spl/u-boot-spl-nodtb.bin') This is the U-Boot SPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming SPL needs -a device tree to operation on your platform. You can add a u_boot_spl_dtb -entry after this one, or use a u_boot_spl entry instead (which contains +a device tree to operate on your platform. You can add a u-boot-spl-dtb +entry after this one, or use a u-boot-spl entry instead (which contains both SPL and the device tree). @@ -1148,7 +1147,7 @@ Properties / Entry arguments: See Entry_u_boot_ucode for full details of the three entries involved in this process. This entry updates U-Boot with the offset and size of the microcode, to allow early x86 boot code to find it without doing anything -complicated. Otherwise it is the same as the u_boot entry. +complicated. Otherwise it is the same as the u-boot entry. diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index 81756c326d..e609a8b253 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -24,7 +24,7 @@ class Entry_blob(Entry): This entry reads data from a file and places it in the entry. The default filename is often specified specified by the subclass. See for - example the 'u_boot' entry which provides the filename 'u-boot.bin'. + example the 'u-boot' entry which provides the filename 'u-boot.bin'. If compression is enabled, an extra 'uncomp-size' property is written to the node (if enabled with -u) which provides the uncompressed size of the diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py index 4767197e13..de783b2677 100644 --- a/tools/binman/etype/u_boot.py +++ b/tools/binman/etype/u_boot.py @@ -16,7 +16,7 @@ class Entry_u_boot(Entry_blob): This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. The binary typically includes a device tree - blob at the end of it. Use u_boot_nodtb if you want to package the device + blob at the end of it. Use u-boot-nodtb if you want to package the device tree separately. U-Boot can access binman symbols at runtime. See: diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py index 66a9db55ca..554b3b2e00 100644 --- a/tools/binman/etype/u_boot_dtb_with_ucode.py +++ b/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -19,9 +19,9 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob_dtb): this process. This entry provides the U-Boot device-tree file, which contains the microcode. If the microcode is not being collated into one place then the offset and size of the microcode is recorded by this entry, - for use by u_boot_with_ucode_ptr. If it is being collated, then this + for use by u-boot-with-ucode_ptr. If it is being collated, then this entry deletes the microcode from the device tree (to save space) and makes - it available to u_boot_ucode. + it available to u-boot-ucode. """ def __init__(self, section, etype, node): # Put this here to allow entry-docs and help to work without libfdt diff --git a/tools/binman/etype/u_boot_nodtb.py b/tools/binman/etype/u_boot_nodtb.py index e84df490f6..289b24fa6c 100644 --- a/tools/binman/etype/u_boot_nodtb.py +++ b/tools/binman/etype/u_boot_nodtb.py @@ -12,12 +12,12 @@ class Entry_u_boot_nodtb(Entry_blob): """U-Boot flat binary without device tree appended Properties / Entry arguments: - - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin') + - filename: Filename to include (default 'u-boot-nodtb.bin') This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. It does not include a device tree blob at - the end of it so normally cannot work without it. You can add a u_boot_dtb - entry after this one, or use a u_boot entry instead (which contains both + the end of it so normally cannot work without it. You can add a u-boot-dtb + entry after this one, or use a u-boot entry instead (which contains both U-Boot and the device tree). """ def __init__(self, section, etype, node): diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index c154cfde57..41d7505491 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -12,13 +12,12 @@ class Entry_u_boot_spl_nodtb(Entry_blob): """SPL binary without device tree appended Properties / Entry arguments: - - filename: Filename of spl/u-boot-spl-nodtb.bin (default - 'spl/u-boot-spl-nodtb.bin') + - filename: Filename to include (default 'spl/u-boot-spl-nodtb.bin') This is the U-Boot SPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming SPL needs - a device tree to operation on your platform. You can add a u_boot_spl_dtb - entry after this one, or use a u_boot_spl entry instead (which contains + a device tree to operate on your platform. You can add a u-boot-spl-dtb + entry after this one, or use a u-boot-spl entry instead (which contains both SPL and the device tree). """ def __init__(self, section, etype, node): diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py index 92d2fc6853..20be22a1fd 100644 --- a/tools/binman/etype/u_boot_with_ucode_ptr.py +++ b/tools/binman/etype/u_boot_with_ucode_ptr.py @@ -26,7 +26,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob): See Entry_u_boot_ucode for full details of the three entries involved in this process. This entry updates U-Boot with the offset and size of the microcode, to allow early x86 boot code to find it without doing anything - complicated. Otherwise it is the same as the u_boot entry. + complicated. Otherwise it is the same as the u-boot entry. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) From dccdc38247a6b6e95dd4df3f58fd79bc3c785d00 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:55 +1300 Subject: [PATCH 098/120] binman: Correct the documentation for u-boot-spl-bss-pad The documentation for this entry indicates that the SPL binary is included along with the padding. It is not, so update it to correct the error. Signed-off-by: Simon Glass --- tools/binman/README.entries | 17 ++++++++++------- tools/binman/etype/u_boot_spl_bss_pad.py | 17 ++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 3fbc06d926..5c6663e2c7 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -960,13 +960,16 @@ Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region Properties / Entry arguments: None -This is similar to u_boot_spl except that padding is added after the SPL -binary to cover the BSS (Block Started by Symbol) region. This region holds -the various used by SPL. It is set to 0 by SPL when it starts up. If you -want to append data to the SPL 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. +This holds the padding added after the SPL binary to cover the BSS (Block +Started by Symbol) region. This region holds the various variables used by +SPL. It is set to 0 by SPL when it starts up. If you want to append data to +the SPL 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 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up the BSS address. diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py index df15cd24ce..18c5596bd3 100644 --- a/tools/binman/etype/u_boot_spl_bss_pad.py +++ b/tools/binman/etype/u_boot_spl_bss_pad.py @@ -18,13 +18,16 @@ class Entry_u_boot_spl_bss_pad(Entry_blob): Properties / Entry arguments: None - This is similar to u_boot_spl except that padding is added after the SPL - binary to cover the BSS (Block Started by Symbol) region. This region holds - the various used by SPL. It is set to 0 by SPL when it starts up. If you - want to append data to the SPL 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. + This holds the padding added after the SPL binary to cover the BSS (Block + Started by Symbol) region. This region holds the various variables used by + SPL. It is set to 0 by SPL when it starts up. If you want to append data to + the SPL 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 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up the BSS address. From f589882a70d0a6450527be55506ed03097de9a19 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:56 +1300 Subject: [PATCH 099/120] binman: Support symbols in u-boot-spl-nodtb Since this is an execuable we should be able insert symbol values into it. Add support for this. Use common code for this test and the original testSymbols. Use hex consistently for the values and add some more comments. Signed-off-by: Simon Glass --- tools/binman/README.entries | 9 +++++ tools/binman/etype/u_boot_spl_nodtb.py | 14 +++++++ tools/binman/ftest.py | 49 +++++++++++++++---------- tools/binman/test/053_symbols.dts | 2 +- tools/binman/test/192_symbols_nodtb.dts | 26 +++++++++++++ tools/binman/test/u_boot_binman_syms.c | 2 +- 6 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 tools/binman/test/192_symbols_nodtb.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 5c6663e2c7..1dbdd0be03 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -1011,6 +1011,15 @@ a device tree to operate on your platform. You can add a u-boot-spl-dtb entry after this one, or use a u-boot-spl entry instead (which contains both SPL and the device tree). +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 'spl/u-boot-spl' must also be available for this to work, since +binman uses that to look up symbols to write into the SPL binary. + Entry: u-boot-spl-with-ucode-ptr: U-Boot SPL with embedded microcode pointer diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index 41d7505491..dbf2f12743 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -5,6 +5,7 @@ # Entry-type module for 'u-boot-spl-nodtb.bin' # +from binman import elf from binman.entry import Entry from binman.etype.blob import Entry_blob @@ -19,9 +20,22 @@ class Entry_u_boot_spl_nodtb(Entry_blob): a device tree to operate on your platform. You can add a u-boot-spl-dtb entry after this one, or use a u-boot-spl entry instead (which contains both SPL and the device tree). + + 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 'spl/u-boot-spl' must also be available for this to work, since + binman uses that to look up symbols to write into the SPL binary. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) + self.elf_fname = 'spl/u-boot-spl' def GetDefaultFilename(self): return 'spl/u-boot-spl-nodtb.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage()) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 814e91d42e..e056601b9a 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1337,21 +1337,43 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('052_u_boot_spl_nodtb.dts') self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) - def testSymbols(self): - """Test binman can assign symbols embedded in U-Boot""" + def checkSymbols(self, dts, base_data, u_boot_offset): + """Check the image contains the expected symbol values + + Args: + dts: Device tree file to use for test + base_data: Data before and after 'u-boot' section + u_boot_offset: Offset of 'u-boot' section in image + """ elf_fname = self.ElfTestFile('u_boot_binman_syms') syms = elf.GetSymbols(elf_fname, ['binman', 'image']) addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') - self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr) + self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address, + addr) self._SetupSplElf('u_boot_binman_syms') - data = self._DoReadFile('053_symbols.dts') - sym_values = struct.pack('; + offset = <0x18>; }; u-boot-spl2 { diff --git a/tools/binman/test/192_symbols_nodtb.dts b/tools/binman/test/192_symbols_nodtb.dts new file mode 100644 index 0000000000..5c900d6070 --- /dev/null +++ b/tools/binman/test/192_symbols_nodtb.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl-nodtb { + }; + u-boot-spl-dtb { + }; + + u-boot { + offset = <0x38>; + }; + + u-boot-spl2 { + type = "u-boot-spl-nodtb"; + }; + u-boot-spl-dtb2 { + type = "u-boot-spl-dtb"; + }; + }; +}; diff --git a/tools/binman/test/u_boot_binman_syms.c b/tools/binman/test/u_boot_binman_syms.c index 4520b319f1..37fc339ce8 100644 --- a/tools/binman/test/u_boot_binman_syms.c +++ b/tools/binman/test/u_boot_binman_syms.c @@ -8,7 +8,7 @@ #define CONFIG_BINMAN #include -binman_sym_declare(unsigned long, u_boot_spl, offset); +binman_sym_declare(unsigned long, u_boot_spl_any, offset); binman_sym_declare(unsigned long long, u_boot_spl2, offset); binman_sym_declare(unsigned long, u_boot_any, image_pos); binman_sym_declare(unsigned long, u_boot_any, size); From 77a64e08e2c8238998e4f5ea014b0c9fd1043b1b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:57 +1300 Subject: [PATCH 100/120] binman: Add support for u-boot-tpl-nodtb Allow this entry type to be placed in an image. This is the TPL binary, without a devicetree appended. Signed-off-by: Simon Glass --- tools/binman/README.entries | 23 ++++++++++++ tools/binman/etype/u_boot_tpl_nodtb.py | 41 ++++++++++++++++++++++ tools/binman/ftest.py | 6 ++++ tools/binman/test/192_u_boot_tpl_nodtb.dts | 13 +++++++ 4 files changed, 83 insertions(+) create mode 100644 tools/binman/etype/u_boot_tpl_nodtb.py create mode 100644 tools/binman/test/192_u_boot_tpl_nodtb.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 1dbdd0be03..8651ba05f2 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -1089,6 +1089,29 @@ be relocated to any address for execution. +Entry: u-boot-tpl-nodtb: TPL binary without device tree appended +---------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename to include (default 'tpl/u-boot-tpl-nodtb.bin') + +This is the U-Boot TPL binary, It does not include a device tree blob at +the end of it so may not be able to work without it, assuming TPL needs +a device tree to operate on your platform. You can add a u-boot-tpl-dtb +entry after this one, or use a u-boot-tpl entry instead (which contains +both TPL and the device tree). + +TPL 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 'tpl/u-boot-tpl' must also be available for this to work, since +binman uses that to look up symbols to write into the TPL binary. + + + Entry: u-boot-tpl-with-ucode-ptr: U-Boot TPL with embedded microcode pointer ---------------------------------------------------------------------------- diff --git a/tools/binman/etype/u_boot_tpl_nodtb.py b/tools/binman/etype/u_boot_tpl_nodtb.py new file mode 100644 index 0000000000..9a47f599bf --- /dev/null +++ b/tools/binman/etype/u_boot_tpl_nodtb.py @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type module for 'u-boot-tpl-nodtb.bin' +# + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_u_boot_tpl_nodtb(Entry_blob): + """TPL binary without device tree appended + + Properties / Entry arguments: + - filename: Filename to include (default 'tpl/u-boot-tpl-nodtb.bin') + + This is the U-Boot TPL binary, It does not include a device tree blob at + the end of it so may not be able to work without it, assuming TPL needs + a device tree to operate on your platform. You can add a u-boot-tpl-dtb + entry after this one, or use a u-boot-tpl entry instead (which contains + both TPL and the device tree). + + TPL 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 'tpl/u-boot-tpl' must also be available for this to work, since + binman uses that to look up symbols to write into the TPL binary. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl-nodtb.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage()) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index e056601b9a..684e507e6e 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4256,6 +4256,12 @@ class TestFunctional(unittest.TestCase): self.assertEquals(U_BOOT_DATA, u_boot.ReadData()) + def testTplNoDtb(self): + """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created""" + data = self._DoReadFile('192_u_boot_tpl_nodtb.dts') + self.assertEqual(U_BOOT_TPL_NODTB_DATA, + data[:len(U_BOOT_TPL_NODTB_DATA)]) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/192_u_boot_tpl_nodtb.dts b/tools/binman/test/192_u_boot_tpl_nodtb.dts new file mode 100644 index 0000000000..94cef395e8 --- /dev/null +++ b/tools/binman/test/192_u_boot_tpl_nodtb.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl-nodtb { + }; + }; +}; From d26efc89b11a41d2ef402cac47a3d4a55577084b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:58 +1300 Subject: [PATCH 101/120] binman: Add support for u-boot-tpl-bss-bad This entry holds the padding between the end of of TPL binary and the end of BSS. This region must be left empty so that the devicetree can be appended correctly and remain accessible without interfering with BSS. Signed-off-by: Simon Glass --- tools/binman/README.entries | 22 ++++++++++++ tools/binman/etype/u_boot_tpl_bss_pad.py | 44 ++++++++++++++++++++++++ tools/binman/ftest.py | 16 +++++++++ tools/binman/test/193_tpl_bss_pad.dts | 19 ++++++++++ 4 files changed, 101 insertions(+) create mode 100644 tools/binman/etype/u_boot_tpl_bss_pad.py create mode 100644 tools/binman/test/193_tpl_bss_pad.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 8651ba05f2..368c976497 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -1056,6 +1056,28 @@ binman uses that to look up symbols to write into the TPL binary. +Entry: u-boot-tpl-bss-pad: U-Boot TPL binary padded with a BSS region +--------------------------------------------------------------------- + +Properties / Entry arguments: + None + +This holds the padding added after the TPL binary to cover the BSS (Block +Started by Symbol) region. This region holds the various variables used by +TPL. It is set to 0 by TPL when it starts up. If you want to append data to +the TPL 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 'tpl/u-boot-tpl' must also be available for this to work, since +binman uses that to look up the BSS address. + + + Entry: u-boot-tpl-dtb: U-Boot TPL device tree --------------------------------------------- diff --git a/tools/binman/etype/u_boot_tpl_bss_pad.py b/tools/binman/etype/u_boot_tpl_bss_pad.py new file mode 100644 index 0000000000..521b24a384 --- /dev/null +++ b/tools/binman/etype/u_boot_tpl_bss_pad.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type module for BSS padding for tpl/u-boot-tpl.bin. This padding +# can be added after the TPL binary to ensure that anything concatenated +# to it will appear to TPL 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_tpl_bss_pad(Entry_blob): + """U-Boot TPL binary padded with a BSS region + + Properties / Entry arguments: + None + + This holds the padding added after the TPL binary to cover the BSS (Block + Started by Symbol) region. This region holds the various variables used by + TPL. It is set to 0 by TPL when it starts up. If you want to append data to + the TPL 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 'tpl/u-boot-tpl' 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.GetInputFilename('tpl/u-boot-tpl') + bss_size = elf.GetSymbolAddress(fname, '__bss_size') + if not bss_size: + self.Raise('Expected __bss_size symbol in tpl/u-boot-tpl') + self.SetContents(tools.GetBytes(0, bss_size)) + return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 684e507e6e..d1bf64ce27 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4262,6 +4262,22 @@ class TestFunctional(unittest.TestCase): self.assertEqual(U_BOOT_TPL_NODTB_DATA, data[:len(U_BOOT_TPL_NODTB_DATA)]) + def testTplBssPad(self): + """Test that we can pad TPL's BSS with zeros""" + # ELF file with a '__bss_size' symbol + self._SetupTplElf() + data = self._DoReadFile('193_tpl_bss_pad.dts') + self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA, + data) + + def testTplBssPadMissing(self): + """Test that a missing symbol is detected""" + self._SetupTplElf('u_boot_ucode_ptr') + with self.assertRaises(ValueError) as e: + self._DoReadFile('193_tpl_bss_pad.dts') + self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl', + str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/193_tpl_bss_pad.dts b/tools/binman/test/193_tpl_bss_pad.dts new file mode 100644 index 0000000000..f5c2db0646 --- /dev/null +++ b/tools/binman/test/193_tpl_bss_pad.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl { + }; + + u-boot-tpl-bss-pad { + }; + + u-boot { + }; + }; +}; From 30e1b0944ffc7c5ca5bae9344fe3f21c66b3f86d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:24:59 +1300 Subject: [PATCH 102/120] binman: Drop unnecessary 'type' property in tests A few tests declare a type when this can be inferred from the node name. Drop these lines, since it might cause confusion. Signed-off-by: Simon Glass --- tools/binman/test/149_symbols_tpl.dts | 1 - tools/binman/test/155_symbols_tpl_x86.dts | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/binman/test/149_symbols_tpl.dts b/tools/binman/test/149_symbols_tpl.dts index dfc84af5e7..0a4ab3f1fa 100644 --- a/tools/binman/test/149_symbols_tpl.dts +++ b/tools/binman/test/149_symbols_tpl.dts @@ -21,7 +21,6 @@ section { u-boot-tpl { - type = "u-boot-tpl"; }; }; }; diff --git a/tools/binman/test/155_symbols_tpl_x86.dts b/tools/binman/test/155_symbols_tpl_x86.dts index 72ca447447..9d7dc51b3d 100644 --- a/tools/binman/test/155_symbols_tpl_x86.dts +++ b/tools/binman/test/155_symbols_tpl_x86.dts @@ -24,7 +24,6 @@ section { u-boot-tpl { - type = "u-boot-tpl"; }; }; }; From 7697170e780bb13e7ace7035d8995190f79fc2f3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:00 +1300 Subject: [PATCH 103/120] binman: Use the fake SPL/TPL only if requested At present we always use the main devicetree for SPL/TPL as well when setting up the state. But this it not needed if there is a real devicetree for SPL or TPL. In fact it confuses things since we cannot distinguish between one being provided and using the fake one. Update the code to create the fakes only when requested. Put the mapping in a constant so we can use it elsewhere. Rename 'other_fname' to 'fname' while we are here since there is nothing 'other' about it. Signed-off-by: Simon Glass --- tools/binman/state.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tools/binman/state.py b/tools/binman/state.py index bb3e36ea7a..cef5f660bb 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -13,6 +13,12 @@ import os from patman import tools from patman import tout +# Map an dtb etype to its expected filename +DTB_TYPE_FNAME = { + 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb', + 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb', + } + # Records the device-tree files known to binman, keyed by entry type (e.g. # 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by # binman. They have been copied to .out files. @@ -178,19 +184,20 @@ def Prepare(images, dtb): output_fdt_info.clear() fdt_path_prefix = '' output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None] - output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None] - output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None] - if not use_fake_dtb: + if use_fake_dtb: + for etype, fname in DTB_TYPE_FNAME.items(): + output_fdt_info[etype] = [dtb, fname, None] + else: fdt_set = {} for image in images.values(): fdt_set.update(image.GetFdts()) for etype, other in fdt_set.items(): - entry, other_fname = other - infile = tools.GetInputFilename(other_fname) - other_fname_dtb = fdt_util.EnsureCompiled(infile) + entry, fname = other + infile = tools.GetInputFilename(fname) + fname_dtb = fdt_util.EnsureCompiled(infile) out_fname = tools.GetOutputFilename('%s.out' % - os.path.split(other_fname)[1]) - tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb)) + os.path.split(fname)[1]) + tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) other_dtb = fdt.FdtScan(out_fname) output_fdt_info[etype] = [other_dtb, out_fname, entry] From cb8bebbde09c762cc09d2d1a387dc6b3c4434853 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:01 +1300 Subject: [PATCH 104/120] binman: Drop unnecessary field in output_fdt_info At present we store an entry as the third field in output_fdt_info[]. This is only used to get the type of the entry. Of course multiple entries may have this same type. Also the entry type is the key to this dict, so we can use that instead. Drop the field and update GetUpdateNodes() to suit. Improve the comment for output_fdt_info a little while here. Signed-off-by: Simon Glass --- tools/binman/state.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tools/binman/state.py b/tools/binman/state.py index cef5f660bb..053b4fe73f 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -23,11 +23,10 @@ DTB_TYPE_FNAME = { # 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by # binman. They have been copied to .out files. # -# key: entry type +# key: entry type (e.g. 'u-boot-dtb) # value: tuple: # Fdt object # Filename -# Entry object, or None if not known output_fdt_info = {} # Prefix to add to an fdtmap path to turn it into a path to the /binman node @@ -124,11 +123,11 @@ def UpdateFdtContents(etype, data): etype: Entry type (e.g. 'u-boot-dtb') data: Data to replace the DTB with """ - dtb, fname, entry = output_fdt_info[etype] + dtb, fname = output_fdt_info[etype] dtb_fname = dtb.GetFilename() tools.WriteFile(dtb_fname, data) dtb = fdt.FdtScan(dtb_fname) - output_fdt_info[etype] = [dtb, fname, entry] + output_fdt_info[etype] = [dtb, fname] def SetEntryArgs(args): """Set the value of the entry args @@ -183,10 +182,10 @@ def Prepare(images, dtb): main_dtb = dtb output_fdt_info.clear() fdt_path_prefix = '' - output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None] + output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb'] if use_fake_dtb: for etype, fname in DTB_TYPE_FNAME.items(): - output_fdt_info[etype] = [dtb, fname, None] + output_fdt_info[etype] = [dtb, fname] else: fdt_set = {} for image in images.values(): @@ -199,7 +198,7 @@ def Prepare(images, dtb): os.path.split(fname)[1]) tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) other_dtb = fdt.FdtScan(out_fname) - output_fdt_info[etype] = [other_dtb, out_fname, entry] + output_fdt_info[etype] = [other_dtb, out_fname] def PrepareFromLoadedData(image): """Get device tree files ready for use with a loaded image @@ -222,7 +221,7 @@ def PrepareFromLoadedData(image): tout.Info('Preparing device trees') output_fdt_info.clear() fdt_path_prefix = '' - output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None] + output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb'] main_dtb = None tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name) for etype, value in image.GetFdts().items(): @@ -240,7 +239,7 @@ def PrepareFromLoadedData(image): if 'multiple-images' in image_node.props: image_node = dtb.GetNode('/binman/%s' % image.image_node) fdt_path_prefix = image_node.path - output_fdt_info[etype] = [dtb, None, entry] + output_fdt_info[etype] = [dtb, None] tout.Info(" FDT path prefix '%s'" % fdt_path_prefix) @@ -275,12 +274,11 @@ def GetUpdateNodes(node, for_repack=False): is node, SPL and TPL) """ yield node - for dtb, fname, entry in output_fdt_info.values(): + for entry_type, (dtb, fname) in output_fdt_info.items(): if dtb != node.GetFdt(): - if for_repack and entry.etype != 'u-boot-dtb': + if for_repack and entry_type != 'u-boot-dtb': continue other_node = dtb.GetNode(fdt_path_prefix + node.path) - #print(' try', fdt_path_prefix + node.path, other_node) if other_node: yield other_node From adb67bb94ee16d6284a13320c757460c31149c73 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:02 +1300 Subject: [PATCH 105/120] binman: Move the comment for GetFdts() to the base class Like with other methods this comment should be in the base class. Move it. Signed-off-by: Simon Glass --- tools/binman/entry.py | 2 +- tools/binman/etype/blob_dtb.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 507760e2a8..be9419584a 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -205,7 +205,7 @@ class Entry(object): Dict: key: Filename from this entry (without the path) value: Tuple: - Fdt object for this dtb, or None if not available + Entry object for this dtb Filename of file containing this dtb """ return {} diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py index 724647a7bb..3ce7511f6f 100644 --- a/tools/binman/etype/blob_dtb.py +++ b/tools/binman/etype/blob_dtb.py @@ -44,15 +44,6 @@ class Entry_blob_dtb(Entry_blob): return None def GetFdts(self): - """Get the device trees used by this entry - - Returns: - Dict: - key: Filename from this entry (without the path) - value: Tuple: - Fdt object for this dtb, or None if not available - Filename of file containing this dtb - """ fname = self.GetDefaultFilename() return {self.GetFdtEtype(): [self, fname]} From 5187b806175a357b9e3dca247a1cb8f62a927c03 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:03 +1300 Subject: [PATCH 106/120] binman: Use standard filenames for SPL/TPL devicetree At present, before any entry expansion is done (such as a 'files' entry expanding out to individual entries for each file it contains), we check the binman definition (i.e. '/binman' node) to find out what devicetree files are used in the images. This is a pain, since the definition may change during expansion. For example if there is no u-boot-spl-dtb entry in the definition at the start, we assume that the SPL devicetree is not used. But if an entry later expands to include this, then we don't notice. In fact the flexibility provided by the current approach of checking the definition is not really useful. We know that we can have SPL and TPL devicetrees. We know the pathname to each, so we can simply check if the files are present. If they are present, we can prepare them and update them regardless of whether they are actually used. If they are not present, we cannot prepare/update them anyway, i.e. an error will be generated. Simplify state.Prepare() so it uses a hard-coded list of devicetree files. Note that state.PrepareFromLoadedData() is left untouched, since in that case we have a complete definition from the loaded file, but cannot of course rely on the devicetree files that created it still being present. So in that case we still check the image defitions. Signed-off-by: Simon Glass --- tools/binman/state.py | 21 ++++++++++----------- tools/patman/tools.py | 8 ++++++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tools/binman/state.py b/tools/binman/state.py index 053b4fe73f..84f606b188 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -188,17 +188,16 @@ def Prepare(images, dtb): output_fdt_info[etype] = [dtb, fname] else: fdt_set = {} - for image in images.values(): - fdt_set.update(image.GetFdts()) - for etype, other in fdt_set.items(): - entry, fname = other - infile = tools.GetInputFilename(fname) - fname_dtb = fdt_util.EnsureCompiled(infile) - out_fname = tools.GetOutputFilename('%s.out' % - os.path.split(fname)[1]) - tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) - other_dtb = fdt.FdtScan(out_fname) - output_fdt_info[etype] = [other_dtb, out_fname] + for etype, fname in DTB_TYPE_FNAME.items(): + infile = tools.GetInputFilename(fname, allow_missing=True) + if infile and os.path.exists(infile): + fname_dtb = fdt_util.EnsureCompiled(infile) + out_fname = tools.GetOutputFilename('%s.out' % + os.path.split(fname)[1]) + tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) + other_dtb = fdt.FdtScan(out_fname) + output_fdt_info[etype] = [other_dtb, out_fname] + def PrepareFromLoadedData(image): """Get device tree files ready for use with a loaded image diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 10997e4386..e5f391b7aa 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -130,8 +130,12 @@ def GetInputFilename(fname, allow_missing=False): allow_missing: True if the filename can be missing Returns: - The full path of the filename, within the input directory, or - None on error + fname, if indir is None; + full path of the filename, within the input directory; + None, if file is missing and allow_missing is True + + Raises: + ValueError if file is missing and allow_missing is False """ if not indir or fname[:1] == '/': return fname From b35fb179364decef1f564b5667b99af65dd0402a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:04 +1300 Subject: [PATCH 107/120] binman: Allow using an an 'expanded' entry type As the first step in supporting expanded entries, add a way for binman to automatically select an 'expanded' version of an entry type, if requested. This is controlled by a class method. Signed-off-by: Simon Glass --- tools/binman/entry.py | 60 ++++++++++++++++++++++++++++++++------ tools/binman/entry_test.py | 12 ++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index be9419584a..1cfa024a9f 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -102,22 +102,30 @@ class Entry(object): self.allow_missing = False @staticmethod - def Lookup(node_path, etype): + def Lookup(node_path, etype, expanded): """Look up the entry class for a node. Args: node_node: Path name of Node object containing information about the entry to create (used for errors) etype: Entry type to use + expanded: Use the expanded version of etype Returns: - The entry class object if found, else None + The entry class object if found, else None if not found and expanded + is True + + Raise: + ValueError if expanded is False and the class is not found """ # Convert something like 'u-boot@0' to 'u_boot' since we are only # interested in the type. module_name = etype.replace('-', '_') + if '@' in module_name: module_name = module_name.split('@')[0] + if expanded: + module_name += '_expanded' module = modules.get(module_name) # Also allow entry-type modules to be brought in from the etype directory. @@ -127,6 +135,8 @@ class Entry(object): try: module = importlib.import_module('binman.etype.' + module_name) except ImportError as e: + if expanded: + return None raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" % (etype, node_path, module_name, e)) modules[module_name] = module @@ -135,21 +145,31 @@ class Entry(object): return getattr(module, 'Entry_%s' % module_name) @staticmethod - def Create(section, node, etype=None): + def Create(section, node, etype=None, expanded=False): """Create a new entry for a node. Args: - section: Section object containing this node - node: Node object containing information about the entry to - create - etype: Entry type to use, or None to work it out (used for tests) + section: Section object containing this node + node: Node object containing information about the entry to + create + etype: Entry type to use, or None to work it out (used for tests) + expanded: True to use expanded versions of entries, where available Returns: A new Entry object of the correct type (a subclass of Entry) """ if not etype: etype = fdt_util.GetString(node, 'type', node.name) - obj = Entry.Lookup(node.path, etype) + obj = Entry.Lookup(node.path, etype, expanded) + if obj and expanded: + # Check whether to use the expanded entry + new_etype = etype + '-expanded' + if obj.UseExpanded(node, etype, new_etype): + etype = new_etype + else: + obj = None + if not obj: + obj = Entry.Lookup(node.path, etype, False) # Call its constructor to get the object we want. return obj(section, etype, node) @@ -648,7 +668,7 @@ features to produce new behaviours. modules.remove('_testing') missing = [] for name in modules: - module = Entry.Lookup('WriteDocs', name) + module = Entry.Lookup('WriteDocs', name, False) docs = getattr(module, '__doc__') if test_missing == name: docs = None @@ -907,3 +927,25 @@ features to produce new behaviours. self.uncomp_size = len(indata) data = tools.Compress(indata, self.compress) return data + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + """Check whether to use an expanded entry type + + This is called by Entry.Create() when it finds an expanded version of + an entry type (e.g. 'u-boot-expanded'). If this method returns True then + it will be used (e.g. in place of 'u-boot'). If it returns False, it is + ignored. + + Args: + node: Node object containing information about the entry to + create + etype: Original entry type being used + new_etype: New entry type proposed + + Returns: + True to use this entry type, False to use the original one + """ + tout.Info("Node '%s': etype '%s': %s selected" % + (node.path, etype, new_etype)) + return True diff --git a/tools/binman/entry_test.py b/tools/binman/entry_test.py index 80802f33de..c3d5f3eef4 100644 --- a/tools/binman/entry_test.py +++ b/tools/binman/entry_test.py @@ -87,6 +87,18 @@ class TestEntry(unittest.TestCase): base = entry.Entry.Create(None, self.GetNode(), 'blob-dtb') self.assertIsNone(base.ReadChildData(base)) + def testExpandedEntry(self): + """Test use of an expanded entry when available""" + base = entry.Entry.Create(None, self.GetNode()) + self.assertEqual('u-boot', base.etype) + + expanded = entry.Entry.Create(None, self.GetNode(), expanded=True) + self.assertEqual('u-boot-expanded', expanded.etype) + + with self.assertRaises(ValueError) as e: + entry.Entry.Create(None, self.GetNode(), 'missing', expanded=True) + self.assertIn("Unknown entry type 'missing' in node '/binman/u-boot'", + str(e.exception)) if __name__ == "__main__": unittest.main() From 63aeaeb46346b215bff38ec85f29634ef82e375e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:05 +1300 Subject: [PATCH 108/120] binman: Allow a way to select expanded entries Add a new command-line option to disable expanded entries. This is needed for most tests, since it is much easier to 'factor out' this function into a separate test and keep the existing packing tests simple. Add the option and select it by default from tests. Signed-off-by: Simon Glass --- tools/binman/cmdline.py | 3 +++ tools/binman/ftest.py | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index c007d0a036..0c0f48951f 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -56,6 +56,9 @@ controlled by a description in the board device tree.''' default=False, help='Output a map file for each image') build_parser.add_argument('-M', '--allow-missing', action='store_true', default=False, help='Allow external blobs to be missing') + build_parser.add_argument('-n', '--no-expanded', action='store_true', + help="Don't use 'expanded' versions of entries where available; " + "normally 'u-boot' becomes 'u-boot-expanded', for example") build_parser.add_argument('-O', '--outdir', type=str, action='store', help='Path to directory to use for intermediate and ' 'output files') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d1bf64ce27..9182584417 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -305,7 +305,8 @@ class TestFunctional(unittest.TestCase): def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, entry_args=None, images=None, use_real_dtb=False, - verbosity=None, allow_missing=False, extra_indirs=None): + use_expanded=False, verbosity=None, allow_missing=False, + extra_indirs=None): """Run binman with a given test file Args: @@ -322,6 +323,8 @@ class TestFunctional(unittest.TestCase): the u-boot-dtb entry. Normally this is not needed and the test contents (the U_BOOT_DTB_DATA string) can be used. But in some test we need the real contents. + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' verbosity: Verbosity level to use (0-3, None=don't set it) allow_missing: Set the '--allow-missing' flag so that missing external binaries just produce a warning instead of an error @@ -344,6 +347,8 @@ class TestFunctional(unittest.TestCase): args.append('-u') if not use_real_dtb: args.append('--fake-dtb') + if not use_expanded: + args.append('--no-expanded') if entry_args: for arg, value in entry_args.items(): args.append('-a%s=%s' % (arg, value)) @@ -403,9 +408,9 @@ class TestFunctional(unittest.TestCase): dtb.Pack() return dtb.GetContents() - def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False, - update_dtb=False, entry_args=None, reset_dtbs=True, - extra_indirs=None): + def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False, + map=False, update_dtb=False, entry_args=None, + reset_dtbs=True, extra_indirs=None): """Run binman and return the resulting image This runs binman with a given test file and then reads the resulting @@ -420,6 +425,8 @@ class TestFunctional(unittest.TestCase): the u-boot-dtb entry. Normally this is not needed and the test contents (the U_BOOT_DTB_DATA string) can be used. But in some test we need the real contents. + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' map: True to output map files for the images update_dtb: Update the offset and size of each entry in the device tree before packing it into the image @@ -454,7 +461,7 @@ class TestFunctional(unittest.TestCase): try: retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb, entry_args=entry_args, use_real_dtb=use_real_dtb, - extra_indirs=extra_indirs) + use_expanded=use_expanded, extra_indirs=extra_indirs) self.assertEqual(0, retcode) out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out') @@ -652,7 +659,7 @@ class TestFunctional(unittest.TestCase): """Test that we can run it with a specific board""" self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb') TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) - result = self._DoBinman('build', '-b', 'sandbox') + result = self._DoBinman('build', '-n', '-b', 'sandbox') self.assertEqual(0, result) def testNeedBoard(self): From 0b6023ee41add24ebb9df215ceaec0a803d6c552 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:06 +1300 Subject: [PATCH 109/120] binman: Plumb expanded entries through fully Add support for this feature in the control, image and section modules, so that expanded entries will be selected by default. So far there are no expanded entry types, so this is a nop. Signed-off-by: Simon Glass --- tools/binman/control.py | 24 ++++++++++++++++++------ tools/binman/etype/section.py | 3 ++- tools/binman/image.py | 17 ++++++++++++++++- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/tools/binman/control.py b/tools/binman/control.py index 9c0cafeafc..9709aa9a2b 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -28,7 +28,7 @@ images = OrderedDict() # value: Text for the help missing_blob_help = {} -def _ReadImageDesc(binman_node): +def _ReadImageDesc(binman_node, use_expanded): """Read the image descriptions from the /binman node This normally produces a single Image object called 'image'. But if @@ -36,15 +36,17 @@ def _ReadImageDesc(binman_node): Args: binman_node: Node object of the /binman node + use_expanded: True if the FDT will be updated with the entry information Returns: OrderedDict of Image objects, each of which describes an image """ images = OrderedDict() if 'multiple-images' in binman_node.props: for node in binman_node.subnodes: - images[node.name] = Image(node.name, node) + images[node.name] = Image(node.name, node, + use_expanded=use_expanded) else: - images['image'] = Image('image', binman_node) + images['image'] = Image('image', binman_node, use_expanded=use_expanded) return images def _FindBinmanNode(dtb): @@ -399,7 +401,7 @@ def ReplaceEntries(image_fname, input_fname, indir, entry_paths, return image -def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt): +def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): """Prepare the images to be processed and select the device tree This function: @@ -413,6 +415,9 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt): dtb_fname: Filename of the device tree file to use (.dts or .dtb) selected_images: List of images to output, or None for all update_fdt: True to update the FDT wth entry offsets, etc. + use_expanded: True to use expanded versions of entries, if available. + So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This + is needed if update_fdt is True (although tests may disable it) Returns: OrderedDict of images: @@ -438,7 +443,7 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt): raise ValueError("Device tree '%s' does not have a 'binman' " "node" % dtb_fname) - images = _ReadImageDesc(node) + images = _ReadImageDesc(node, use_expanded) if select_images: skip = [] @@ -611,6 +616,13 @@ def Binman(args): elf.debug = args.debug cbfs_util.VERBOSE = args.verbosity > 2 state.use_fake_dtb = args.fake_dtb + + # Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc. + # When running tests this can be disabled using this flag. When not + # updating the FDT in image, it is not needed by binman, but we use it + # for consistency, so that the images look the same to U-Boot at + # runtime. + use_expanded = not args.no_expanded try: tools.SetInputDirs(args.indir) tools.PrepareOutputDir(args.outdir, args.preserve) @@ -618,7 +630,7 @@ def Binman(args): state.SetEntryArgs(args.entry_arg) images = PrepareImagesAndDtbs(dtb_fname, args.image, - args.update_fdt) + args.update_fdt, use_expanded) missing = False for image in images.values(): missing |= ProcessImage(image, args.update_fdt, args.map, diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 2103919b0c..2f862bddf0 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -84,7 +84,8 @@ class Entry_section(Entry): for node in self._node.subnodes: if node.name.startswith('hash') or node.name.startswith('signature'): continue - entry = Entry.Create(self, node) + entry = Entry.Create(self, node, + expanded=self.GetImage().use_expanded) entry.ReadNode() entry.SetPrefix(self._name_prefix) self._entries[node.name] = entry diff --git a/tools/binman/image.py b/tools/binman/image.py index e949435241..10778f47fe 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -47,9 +47,23 @@ class Image(section.Entry_section): exception). This should be used if the Image is being loaded from a file rather than generated. In that case we obviously don't need the entry arguments since the contents already exists. + use_expanded: True if we are updating the FDT wth entry offsets, etc. + and should use the expanded versions of the U-Boot entries. + Any entry type that includes a devicetree must put it in a + separate entry so that it will be updated. For example. 'u-boot' + normally just picks up 'u-boot.bin' which includes the + devicetree, but this is not updateable, since it comes into + binman as one piece and binman doesn't know that it is actually + an executable followed by a devicetree. Of course it could be + taught this, but then when reading an image (e.g. 'binman ls') + it may need to be able to split the devicetree out of the image + in order to determine the location of things. Instead we choose + to ignore 'u-boot-bin' in this case, and build it ourselves in + binman with 'u-boot-dtb.bin' and 'u-boot.dtb'. See + Entry_u_boot_expanded and Entry_blob_phase for details. """ def __init__(self, name, node, copy_to_orig=True, test=False, - ignore_missing=False): + ignore_missing=False, use_expanded=False): super().__init__(None, 'section', node, test=test) self.copy_to_orig = copy_to_orig self.name = 'main-section' @@ -59,6 +73,7 @@ class Image(section.Entry_section): self.fdtmap_data = None self.allow_repack = False self._ignore_missing = ignore_missing + self.use_expanded = use_expanded if not test: self.ReadNode() From 06684927289fb0ad0856fa897bbee10de61137e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:07 +1300 Subject: [PATCH 110/120] binman: Automatically expand phase binaries into sections When creating an entry, check for an expanded version of that entry, then use it instead. This allows, for example use of: u-boot { }; instead of having to write out in full: u-boot { type = "section"; u-boot-nodtb { }; u-boot-dtb { }; }; Add an implementaion of this and associated documentation. Signed-off-by: Simon Glass --- tools/binman/README | 77 ++++++++++ tools/binman/README.entries | 96 +++++++++++- tools/binman/etype/blob_phase.py | 51 +++++++ tools/binman/etype/u_boot.py | 8 +- tools/binman/etype/u_boot_expanded.py | 24 +++ tools/binman/etype/u_boot_nodtb.py | 4 +- tools/binman/etype/u_boot_spl.py | 3 + tools/binman/etype/u_boot_spl_expanded.py | 45 ++++++ tools/binman/etype/u_boot_spl_nodtb.py | 5 +- tools/binman/etype/u_boot_tpl.py | 3 + tools/binman/etype/u_boot_tpl_expanded.py | 45 ++++++ tools/binman/etype/u_boot_tpl_nodtb.py | 5 +- tools/binman/ftest.py | 174 ++++++++++++++++++++++ tools/binman/state.py | 19 ++- tools/binman/test/194_fdt_incl.dts | 17 +++ tools/binman/test/195_fdt_incl_tpl.dts | 13 ++ 16 files changed, 571 insertions(+), 18 deletions(-) create mode 100644 tools/binman/etype/blob_phase.py create mode 100644 tools/binman/etype/u_boot_expanded.py create mode 100644 tools/binman/etype/u_boot_spl_expanded.py create mode 100644 tools/binman/etype/u_boot_tpl_expanded.py create mode 100644 tools/binman/test/194_fdt_incl.dts create mode 100644 tools/binman/test/195_fdt_incl_tpl.dts diff --git a/tools/binman/README b/tools/binman/README index 45f0a0c2cd..1de703cc65 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -840,6 +840,83 @@ of the image) can be used to point to the FDT map. See fdtmap and image-header entries for more information. +Expanded entries +---------------- + +Binman automatically replaces 'u-boot' with an expanded version of that, i.e. +'u-boot-expanded'. This means that when you write: + + u-boot { + }; + +you actually get: + + u-boot { + type = "u-boot-expanded'; + }; + +which in turn expands to: + + u-boot { + type = "section"; + + u-boot-nodtb { + }; + + u-boot-dtb { + }; + }; + +U-Boot's various phase binaries actually comprise two or three pieces. +For example, u-boot.bin has the executable followed by a devicetree. + +With binman we want to be able to update that devicetree with full image +information so that it is accessible to the executable. This is tricky +if it is not clear where the devicetree starts. + +The above feature ensures that the devicetree is clearly separated from the +U-Boot executable and can be updated separately by binman as needed. It can be +disabled with the --no-expanded flag if required. + +The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion +includes the BSS padding, so for example: + + spl { + type = "u-boot-spl" + }; + +you actually get: + + spl { + type = "u-boot-expanded'; + }; + +which in turn expands to: + + spl { + type = "section"; + + u-boot-spl-nodtb { + }; + + u-boot-spl-bss-pad { + }; + + u-boot-spl-dtb { + }; + }; + + +Of course we should not expand SPL if it has no devicetree. Also if the BSS +padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), +the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned +entry type is controlled by the UseExpanded() method. In the SPL case it checks +the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. + +For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All +entry args are provided by the U-Boot Makefile. + + Compression ----------- diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 368c976497..0d9105e41c 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -87,6 +87,15 @@ See cros_ec_rw for an example of this. +Entry: blob-phase: Section that holds a phase binary +---------------------------------------------------- + +This is a base class that should not normally be used directly. It is used +when converting a 'u-boot' entry automatically into a 'u-boot-expanded' +entry; similarly for SPL. + + + Entry: cbfs: Entry containing a Coreboot Filesystem (CBFS) ---------------------------------------------------------- @@ -840,8 +849,7 @@ Properties / Entry arguments: This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. The binary typically includes a device tree -blob at the end of it. Use u-boot-nodtb if you want to package the device -tree separately. +blob at the end of it. U-Boot can access binman symbols at runtime. See: @@ -849,6 +857,9 @@ U-Boot can access binman symbols at runtime. See: in the binman README for more information. +Note that this entry is automatically replaced with u-boot-expanded unless +--no-expanded is used. + Entry: u-boot-dtb: U-Boot device tree @@ -902,6 +913,21 @@ Properties / Entry arguments: +Entry: u-boot-expanded: U-Boot flat binary broken out into its component parts +------------------------------------------------------------------------------ + +This is a section containing the U-Boot binary and a devicetree. Using this +entry type automatically creates this section, with the following entries +in it: + + u-boot-nodtb + 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. + + + Entry: u-boot-img: U-Boot legacy image -------------------------------------- @@ -925,8 +951,8 @@ Properties / Entry arguments: This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. It does not include a device tree blob at the end of it so normally cannot work without it. You can add a u-boot-dtb -entry after this one, or use a u-boot entry instead (which contains both -U-Boot and the device tree). +entry after this one, or use a u-boot entry instead, normally expands to a +section containing u-boot and u-boot-dtb @@ -952,6 +978,9 @@ in the binman README for more information. The ELF file 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up symbols to write into the SPL binary. +Note that this entry is automatically replaced with u-boot-spl-expanded +unless --no-expanded is used. + Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region @@ -999,6 +1028,29 @@ be relocated to any address for execution. +Entry: u-boot-spl-expanded: U-Boot SPL flat binary broken out into its component parts +-------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - spl-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-spl-nodtb + u-boot-spl-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 'spl-dtb' entryarg. If +this is non-empty (and not 'n' or '0') then this expanded entry is selected. + + + Entry: u-boot-spl-nodtb: SPL binary without device tree appended ---------------------------------------------------------------- @@ -1008,8 +1060,9 @@ Properties / Entry arguments: This is the U-Boot SPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming SPL needs a device tree to operate on your platform. You can add a u-boot-spl-dtb -entry after this one, or use a u-boot-spl entry instead (which contains -both SPL and the device tree). +entry after this one, or use a u-boot-spl entry instead' which normally +expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and +u-boot-spl-dtb SPL can access binman symbols at runtime. See: @@ -1054,6 +1107,9 @@ in the binman README for more information. The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since binman uses that to look up symbols to write into the TPL binary. +Note that this entry is automatically replaced with u-boot-tpl-expanded +unless --no-expanded is used. + Entry: u-boot-tpl-bss-pad: U-Boot TPL binary padded with a BSS region @@ -1111,6 +1167,29 @@ be relocated to any address for execution. +Entry: u-boot-tpl-expanded: U-Boot TPL flat binary broken out into its component parts +-------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - tpl-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-tpl-nodtb + u-boot-tpl-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 'tpl-dtb' entryarg. If +this is non-empty (and not 'n' or '0') then this expanded entry is selected. + + + Entry: u-boot-tpl-nodtb: TPL binary without device tree appended ---------------------------------------------------------------- @@ -1120,8 +1199,9 @@ Properties / Entry arguments: This is the U-Boot TPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming TPL needs a device tree to operate on your platform. You can add a u-boot-tpl-dtb -entry after this one, or use a u-boot-tpl entry instead (which contains -both TPL and the device tree). +entry after this one, or use a u-boot-tpl entry instead, which normally +expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and +u-boot-tpl-dtb TPL can access binman symbols at runtime. See: diff --git a/tools/binman/etype/blob_phase.py b/tools/binman/etype/blob_phase.py new file mode 100644 index 0000000000..54ca54c50c --- /dev/null +++ b/tools/binman/etype/blob_phase.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type base class for U-Boot or SPL binary with devicetree +# + +from binman.etype.section import Entry_section + +class Entry_blob_phase(Entry_section): + """Section that holds a phase binary + + This is a base class that should not normally be used directly. It is used + when converting a 'u-boot' entry automatically into a 'u-boot-expanded' + entry; similarly for SPL. + """ + def __init__(self, section, etype, node, root_fname, dtb_file, bss_pad): + """Set up a new blob for a phase + + This holds an executable for a U-Boot phase, optional BSS padding and + a devicetree + + Args: + section: entry_Section object for this entry's parent + etype: Type of object + node: Node defining this entry + root_fname: Root filename for the binary ('u-boot', + 'spl/u-boot-spl', etc.) + dtb_file: Name of devicetree file ('u-boot.dtb', u-boot-spl.dtb', + etc.) + bss_pad: True to add BSS padding before the devicetree + """ + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self.root_fname = root_fname + self.dtb_file = dtb_file + self.bss_pad = bss_pad + + def ExpandEntries(self): + """Create the subnodes""" + names = [self.root_fname + '-nodtb', self.root_fname + '-dtb'] + if self.bss_pad: + names.insert(1, self.root_fname + '-bss-pad') + for name in names: + subnode = state.AddSubnode(self._node, name) + + # Read entries again, now that we have some + self._ReadEntries() diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py index de783b2677..d2eaba6d4a 100644 --- a/tools/binman/etype/u_boot.py +++ b/tools/binman/etype/u_boot.py @@ -2,7 +2,7 @@ # Copyright (c) 2016 Google, Inc # Written by Simon Glass # -# Entry-type module for U-Boot binary +# Entry-type module for the expanded U-Boot binary # from binman.entry import Entry @@ -16,14 +16,16 @@ class Entry_u_boot(Entry_blob): This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. The binary typically includes a device tree - blob at the end of it. Use u-boot-nodtb if you want to package the device - tree separately. + blob at the end of it. U-Boot can access binman symbols at runtime. See: 'Access to binman entry offsets at run time (fdt)' in the binman README for more information. + + Note that this entry is automatically replaced with u-boot-expanded unless + --no-expanded is used. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_expanded.py b/tools/binman/etype/u_boot_expanded.py new file mode 100644 index 0000000000..8797824c9f --- /dev/null +++ b/tools/binman/etype/u_boot_expanded.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type module for U-Boot binary +# + +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_expanded(Entry_blob_phase): + """U-Boot flat binary broken out into its component parts + + This is a section containing the U-Boot binary and a devicetree. Using this + entry type automatically creates this section, with the following entries + in it: + + u-boot-nodtb + 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. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'u-boot', 'u-boot-dtb', False) diff --git a/tools/binman/etype/u_boot_nodtb.py b/tools/binman/etype/u_boot_nodtb.py index 289b24fa6c..347ba7dc69 100644 --- a/tools/binman/etype/u_boot_nodtb.py +++ b/tools/binman/etype/u_boot_nodtb.py @@ -17,8 +17,8 @@ class Entry_u_boot_nodtb(Entry_blob): This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. It does not include a device tree blob at the end of it so normally cannot work without it. You can add a u-boot-dtb - entry after this one, or use a u-boot entry instead (which contains both - U-Boot and the device tree). + entry after this one, or use a u-boot entry instead, normally expands to a + section containing u-boot and u-boot-dtb """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index d66e46140b..1c39f98251 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -30,6 +30,9 @@ class Entry_u_boot_spl(Entry_blob): The ELF file 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up symbols to write into the SPL binary. + + Note that this entry is automatically replaced with u-boot-spl-expanded + unless --no-expanded is used. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_spl_expanded.py b/tools/binman/etype/u_boot_spl_expanded.py new file mode 100644 index 0000000000..8e138e6a62 --- /dev/null +++ b/tools/binman/etype/u_boot_spl_expanded.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type module for expanded U-Boot SPL binary +# + +from patman import tout + +from binman import state +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_spl_expanded(Entry_blob_phase): + """U-Boot SPL flat binary broken out into its component parts + + Properties / Entry arguments: + - spl-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-spl-nodtb + u-boot-spl-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 'spl-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('spl-bss-pad') + super().__init__(section, etype, node, 'u-boot-spl', 'u-boot-spl-dtb', + bss_pad) + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + val = state.GetEntryArgBool('spl-dtb') + tout.DoOutput(tout.INFO if val else tout.DETAIL, + "Node '%s': etype '%s': %s %sselected" % + (node.path, etype, new_etype, '' if val else 'not ')) + return val diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index dbf2f12743..316b38172e 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -18,8 +18,9 @@ class Entry_u_boot_spl_nodtb(Entry_blob): This is the U-Boot SPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming SPL needs a device tree to operate on your platform. You can add a u-boot-spl-dtb - entry after this one, or use a u-boot-spl entry instead (which contains - both SPL and the device tree). + entry after this one, or use a u-boot-spl entry instead' which normally + expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and + u-boot-spl-dtb SPL can access binman symbols at runtime. See: diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py index 02287ab327..95d9bd6b20 100644 --- a/tools/binman/etype/u_boot_tpl.py +++ b/tools/binman/etype/u_boot_tpl.py @@ -30,6 +30,9 @@ class Entry_u_boot_tpl(Entry_blob): The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since binman uses that to look up symbols to write into the TPL binary. + + Note that this entry is automatically replaced with u-boot-tpl-expanded + unless --no-expanded is used. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_tpl_expanded.py b/tools/binman/etype/u_boot_tpl_expanded.py new file mode 100644 index 0000000000..15cdac4655 --- /dev/null +++ b/tools/binman/etype/u_boot_tpl_expanded.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass +# +# Entry-type module for expanded U-Boot TPL binary +# + +from patman import tout + +from binman import state +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_tpl_expanded(Entry_blob_phase): + """U-Boot TPL flat binary broken out into its component parts + + Properties / Entry arguments: + - tpl-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-tpl-nodtb + u-boot-tpl-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 'tpl-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('tpl-bss-pad') + super().__init__(section, etype, node, 'u-boot-tpl', 'u-boot-tpl-dtb', + bss_pad) + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + val = state.GetEntryArgBool('tpl-dtb') + tout.DoOutput(tout.INFO if val else tout.DETAIL, + "Node '%s': etype '%s': %s %sselected" % + (node.path, etype, new_etype, '' if val else 'not ')) + return val diff --git a/tools/binman/etype/u_boot_tpl_nodtb.py b/tools/binman/etype/u_boot_tpl_nodtb.py index 9a47f599bf..98f3853f45 100644 --- a/tools/binman/etype/u_boot_tpl_nodtb.py +++ b/tools/binman/etype/u_boot_tpl_nodtb.py @@ -18,8 +18,9 @@ class Entry_u_boot_tpl_nodtb(Entry_blob): This is the U-Boot TPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming TPL needs a device tree to operate on your platform. You can add a u-boot-tpl-dtb - entry after this one, or use a u-boot-tpl entry instead (which contains - both TPL and the device tree). + entry after this one, or use a u-boot-tpl entry instead, which normally + expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and + u-boot-tpl-dtb TPL can access binman symbols at runtime. See: diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 9182584417..432c463b58 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4285,6 +4285,180 @@ class TestFunctional(unittest.TestCase): self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl', str(e.exception)) + def checkDtbSizes(self, data, pad_len, start): + """Check the size arguments in a dtb embedded in an image + + Args: + data: The image data + pad_len: Length of the pad section in the image, in bytes + start: Start offset of the devicetree to examine, within the image + + Returns: + Size of the devicetree in bytes + """ + dtb_data = data[start:] + dtb = fdt.Fdt.FromData(dtb_data) + fdt_size = dtb.GetFdtObj().totalsize() + dtb.Scan() + props = self._GetPropTree(dtb, 'size') + self.assertEqual({ + 'size': len(data), + 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len, + 'u-boot-spl/u-boot-spl-dtb:size': 801, + 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA), + 'u-boot-spl:size': 860, + 'u-boot-tpl:size': len(U_BOOT_TPL_DATA), + 'u-boot/u-boot-dtb:size': 781, + 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA), + 'u-boot:size': 827, + }, props) + return fdt_size + + def testExpanded(self): + """Test that an expanded entry type is selected when needed""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + entry_args = { + 'spl-dtb': '1', + 'spl-bss-pad': 'y', + 'tpl-dtb': '', + } + self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(3, len(entries)) + + # First, u-boot, which should be expanded into u-boot-nodtb and dtb + self.assertIn('u-boot', entries) + entry = entries['u-boot'] + self.assertEqual('u-boot-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-nodtb', subent) + self.assertIn('u-boot-dtb', subent) + + # Second, u-boot-spl, which should be expanded into three parts + self.assertIn('u-boot-spl', entries) + entry = entries['u-boot-spl'] + self.assertEqual('u-boot-spl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(3, len(subent)) + self.assertIn('u-boot-spl-nodtb', subent) + self.assertIn('u-boot-spl-bss-pad', subent) + self.assertIn('u-boot-spl-dtb', subent) + + # Third, u-boot-tpl, which should be not be expanded, since TPL has no + # devicetree + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl', entry.etype) + self.assertEqual(None, entry.GetEntries()) + + def testExpandedTpl(self): + """Test that an expanded entry type is selected for TPL when needed""" + self._SetupTplElf() + + entry_args = { + 'tpl-bss-pad': 'y', + 'tpl-dtb': 'y', + } + self._DoReadFileDtb('195_fdt_incl_tpl.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-tpl, which be expanded + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(3, len(subent)) + self.assertIn('u-boot-tpl-nodtb', subent) + self.assertIn('u-boot-tpl-bss-pad', subent) + self.assertIn('u-boot-tpl-dtb', subent) + + def testExpandedNoPad(self): + """Test an expanded entry without BSS pad enabled""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + entry_args = { + 'spl-dtb': 'something', + 'spl-bss-pad': 'n', + 'tpl-dtb': '', + } + self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + + # Just check u-boot-spl, which should be expanded into two parts + self.assertIn('u-boot-spl', entries) + entry = entries['u-boot-spl'] + self.assertEqual('u-boot-spl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-spl-nodtb', subent) + self.assertIn('u-boot-spl-dtb', subent) + + def testExpandedTplNoPad(self): + """Test that an expanded entry type with padding disabled in TPL""" + self._SetupTplElf() + + entry_args = { + 'tpl-bss-pad': '', + 'tpl-dtb': 'y', + } + self._DoReadFileDtb('195_fdt_incl_tpl.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-tpl, which be expanded + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-tpl-nodtb', subent) + self.assertIn('u-boot-tpl-dtb', subent) + + def testFdtInclude(self): + """Test that an Fdt is update within all binaries""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + self.maxDiff = None + entry_args = { + 'spl-dtb': '1', + 'spl-bss-pad': 'y', + 'tpl-dtb': '', + } + # Build the image. It includes two separate devicetree binaries, each + # with their own contents, but all contain the binman definition. + data = self._DoReadFileDtb( + '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True, + update_dtb=True, entry_args=entry_args)[0] + pad_len = 10 + + # Check the U-Boot dtb + start = len(U_BOOT_NODTB_DATA) + fdt_size = self.checkDtbSizes(data, pad_len, start) + + # Now check SPL + start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len + fdt_size = self.checkDtbSizes(data, pad_len, start) + + # TPL has no devicetree + start += fdt_size + len(U_BOOT_TPL_DATA) + self.assertEqual(len(data), start) if __name__ == "__main__": unittest.main() diff --git a/tools/binman/state.py b/tools/binman/state.py index 84f606b188..dfb1760455 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -141,12 +141,16 @@ def SetEntryArgs(args): global entry_args entry_args = {} + tout.Debug('Processing entry args:') if args: for arg in args: m = re.match('([^=]*)=(.*)', arg) if not m: raise ValueError("Invalid entry arguemnt '%s'" % arg) - entry_args[m.group(1)] = m.group(2) + name, value = m.groups() + tout.Debug(' %20s = %s' % (name, value)) + entry_args[name] = value + tout.Debug('Processing entry args done') def GetEntryArg(name): """Get the value of an entry argument @@ -159,6 +163,19 @@ def GetEntryArg(name): """ return entry_args.get(name) +def GetEntryArgBool(name): + """Get the value of an entry argument as a boolean + + Args: + name: Name of argument to retrieve + + Returns: + False if the entry argument is consider False (empty, '0' or 'n'), else + True + """ + val = GetEntryArg(name) + return val and val not in ['n', '0'] + def Prepare(images, dtb): """Get device tree files ready for use diff --git a/tools/binman/test/194_fdt_incl.dts b/tools/binman/test/194_fdt_incl.dts new file mode 100644 index 0000000000..b14c8ff8f5 --- /dev/null +++ b/tools/binman/test/194_fdt_incl.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-spl { + }; + u-boot-tpl { + }; + }; +}; diff --git a/tools/binman/test/195_fdt_incl_tpl.dts b/tools/binman/test/195_fdt_incl_tpl.dts new file mode 100644 index 0000000000..3756ac4fc4 --- /dev/null +++ b/tools/binman/test/195_fdt_incl_tpl.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl { + }; + }; +}; From cfee2b26b698faf5d7abdf82c91148aa07a9d301 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:08 +1300 Subject: [PATCH 111/120] Makefile: Pass new entry args to binman To support the use of 'expanded' entries, binman needs to be told whether SPL and TPL have a devicetree and whether they need BSS padding. Add these to the Makefile. Signed-off-by: Simon Glass --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index a790be6226..5cfbfa9949 100644 --- a/Makefile +++ b/Makefile @@ -1332,6 +1332,11 @@ u-boot.ldr: u-boot # Use 'make BINMAN_DEBUG=1' to enable debugging # Use 'make BINMAN_VERBOSE=3' to set vebosity level default_dt := $(if $(DEVICE_TREE),$(DEVICE_TREE),$(CONFIG_DEFAULT_DEVICE_TREE)) + +# Tell binman whether we have a devicetree for SPL and TPL +have_spl_dt := $(if $(CONFIG_SPL_OF_PLATDATA),,$(CONFIG_SPL_OF_CONTROL)) +have_tpl_dt := $(if $(CONFIG_TPL_OF_PLATDATA),,$(CONFIG_TPL_OF_CONTROL)) + quiet_cmd_binman = BINMAN $@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ --toolpath $(objtree)/tools \ @@ -1342,6 +1347,9 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a atf-bl31-path=${BL31} \ -a default-dt=$(default_dt) \ -a scp-path=$(SCP) \ + -a spl-bss-pad=$(if $(CONFIG_SPL_SEPARATE_BSS),,1) \ + -a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \ + -a spl-dtb=$(have_spl_dt) -a tpl-dtb=$(have_tpl_dt) \ $(BINMAN_$(@F)) OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex From cb88ec7cab4b034fafc622d8e795e72f49258639 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:09 +1300 Subject: [PATCH 112/120] x86: Make use of binman expanded entries We don't need to spell out the separate pieces of U-Boot phase binaries anymore. Revert to using the simple entry and let binman do the expansion itself as needed. Signed-off-by: Simon Glass --- arch/x86/dts/u-boot.dtsi | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi index bf92f45f2d..50134b2fe0 100644 --- a/arch/x86/dts/u-boot.dtsi +++ b/arch/x86/dts/u-boot.dtsi @@ -38,20 +38,11 @@ }; #endif spl { - type = "section"; + type = "u-boot-spl"; offset = ; - u-boot-spl { - }; - u-boot-spl-dtb { - }; }; u-boot { - type = "section"; offset = ; - u-boot-nodtb { - }; - u-boot-dtb { - }; }; #elif defined(CONFIG_SPL) u-boot-spl-with-ucode-ptr { From cad7b6b2519a275d79085bbdff0227492cd8ee48 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:10 +1300 Subject: [PATCH 113/120] x86: dts: Drop unused CONFIG_SPL This cannot be used since the previous #elif has already dealt with SPL. Drop it. Signed-off-by: Simon Glass --- arch/x86/dts/u-boot.dtsi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi index 50134b2fe0..ca84d18ad9 100644 --- a/arch/x86/dts/u-boot.dtsi +++ b/arch/x86/dts/u-boot.dtsi @@ -55,11 +55,7 @@ offset = ; }; #else -# ifdef CONFIG_SPL - u-boot { - offset = ; - }; -# elif defined(CONFIG_HAVE_MICROCODE) +# ifdef CONFIG_HAVE_MICROCODE /* If there is no SPL then we need to put microcode in U-Boot */ u-boot-with-ucode-ptr { offset = ; From d1ceeeff6c2ee1e55b7140654c8d6de44b60dab6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:11 +1300 Subject: [PATCH 114/120] doc: Move UEFI under develop/ Much of the content here is useful only for development. Move it under that section. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- doc/arch/x86.rst | 4 ++-- doc/develop/index.rst | 1 + doc/{ => develop}/uefi/index.rst | 4 ++++ doc/{ => develop}/uefi/iscsi.rst | 0 doc/{ => develop}/uefi/u-boot_on_efi.rst | 0 doc/{ => develop}/uefi/uefi.rst | 0 doc/index.rst | 12 ------------ 7 files changed, 7 insertions(+), 14 deletions(-) rename doc/{ => develop}/uefi/index.rst (51%) rename doc/{ => develop}/uefi/iscsi.rst (100%) rename doc/{ => develop}/uefi/u-boot_on_efi.rst (100%) rename doc/{ => develop}/uefi/uefi.rst (100%) diff --git a/doc/arch/x86.rst b/doc/arch/x86.rst index cc307aa8d5..2ebfed871b 100644 --- a/doc/arch/x86.rst +++ b/doc/arch/x86.rst @@ -709,8 +709,8 @@ to load a 'u-boot-payload.efi', see below test logs on QEMU. No controllers found Hit any key to stop autoboot: 0 -See :doc:`../uefi/u-boot_on_efi` and :doc:`../uefi/uefi` for details of -EFI support in U-Boot. +See :doc:`../develop/uefi/u-boot_on_efi` and :doc:`../develop/uefi/uefi` for +details of EFI support in U-Boot. Chain-loading ------------- diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 41c0ba1ebd..84914bb47b 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -13,6 +13,7 @@ Implementation global_data logging menus + uefi/index version Debugging diff --git a/doc/uefi/index.rst b/doc/develop/uefi/index.rst similarity index 51% rename from doc/uefi/index.rst rename to doc/develop/uefi/index.rst index b790a91f17..7e65dbc5d5 100644 --- a/doc/uefi/index.rst +++ b/doc/develop/uefi/index.rst @@ -3,6 +3,10 @@ Unified Extensible Firmware (UEFI) ================================== +U-Boot provides an implementation of the UEFI API allowing to run UEFI +compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself +can be run an UEFI payload. + .. toctree:: :maxdepth: 2 diff --git a/doc/uefi/iscsi.rst b/doc/develop/uefi/iscsi.rst similarity index 100% rename from doc/uefi/iscsi.rst rename to doc/develop/uefi/iscsi.rst diff --git a/doc/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst similarity index 100% rename from doc/uefi/u-boot_on_efi.rst rename to doc/develop/uefi/u-boot_on_efi.rst diff --git a/doc/uefi/uefi.rst b/doc/develop/uefi/uefi.rst similarity index 100% rename from doc/uefi/uefi.rst rename to doc/develop/uefi/uefi.rst diff --git a/doc/index.rst b/doc/index.rst index 4c44955d67..366963813a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -38,18 +38,6 @@ want to contribute to U-Boot. develop/index -Unified Extensible Firmware (UEFI) ----------------------------------- - -U-Boot provides an implementation of the UEFI API allowing to run UEFI -compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself -can be run an UEFI payload. - -.. toctree:: - :maxdepth: 2 - - uefi/index - Driver-Model documentation -------------------------- From 5ce319133b2364e3283c3cde7a269681ff8431af Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:12 +1300 Subject: [PATCH 115/120] doc: Move driver model docs under develop/ These docs are useful for developers, not users. Move them under that section. Suggested-by: Heinrich Schuchardt Signed-off-by: Simon Glass --- doc/{ => develop}/driver-model/bind.rst | 0 doc/{ => develop}/driver-model/debugging.rst | 0 doc/{ => develop}/driver-model/design.rst | 0 doc/{ => develop}/driver-model/ethernet.rst | 0 doc/{ => develop}/driver-model/fdt-fixup.rst | 0 doc/{ => develop}/driver-model/fs_firmware_loader.rst | 0 doc/{ => develop}/driver-model/i2c-howto.rst | 0 doc/{ => develop}/driver-model/index.rst | 4 ++++ doc/{ => develop}/driver-model/livetree.rst | 0 doc/{ => develop}/driver-model/migration.rst | 0 doc/{ => develop}/driver-model/of-plat.rst | 0 doc/{ => develop}/driver-model/pci-info.rst | 0 doc/{ => develop}/driver-model/pmic-framework.rst | 0 .../driver-model/remoteproc-framework.rst | 0 doc/{ => develop}/driver-model/serial-howto.rst | 0 doc/{ => develop}/driver-model/soc-framework.rst | 0 doc/{ => develop}/driver-model/spi-howto.rst | 0 doc/{ => develop}/driver-model/usb-info.rst | 0 doc/develop/index.rst | 1 + doc/index.rst | 11 ----------- 20 files changed, 5 insertions(+), 11 deletions(-) rename doc/{ => develop}/driver-model/bind.rst (100%) rename doc/{ => develop}/driver-model/debugging.rst (100%) rename doc/{ => develop}/driver-model/design.rst (100%) rename doc/{ => develop}/driver-model/ethernet.rst (100%) rename doc/{ => develop}/driver-model/fdt-fixup.rst (100%) rename doc/{ => develop}/driver-model/fs_firmware_loader.rst (100%) rename doc/{ => develop}/driver-model/i2c-howto.rst (100%) rename doc/{ => develop}/driver-model/index.rst (68%) rename doc/{ => develop}/driver-model/livetree.rst (100%) rename doc/{ => develop}/driver-model/migration.rst (100%) rename doc/{ => develop}/driver-model/of-plat.rst (100%) rename doc/{ => develop}/driver-model/pci-info.rst (100%) rename doc/{ => develop}/driver-model/pmic-framework.rst (100%) rename doc/{ => develop}/driver-model/remoteproc-framework.rst (100%) rename doc/{ => develop}/driver-model/serial-howto.rst (100%) rename doc/{ => develop}/driver-model/soc-framework.rst (100%) rename doc/{ => develop}/driver-model/spi-howto.rst (100%) rename doc/{ => develop}/driver-model/usb-info.rst (100%) diff --git a/doc/driver-model/bind.rst b/doc/develop/driver-model/bind.rst similarity index 100% rename from doc/driver-model/bind.rst rename to doc/develop/driver-model/bind.rst diff --git a/doc/driver-model/debugging.rst b/doc/develop/driver-model/debugging.rst similarity index 100% rename from doc/driver-model/debugging.rst rename to doc/develop/driver-model/debugging.rst diff --git a/doc/driver-model/design.rst b/doc/develop/driver-model/design.rst similarity index 100% rename from doc/driver-model/design.rst rename to doc/develop/driver-model/design.rst diff --git a/doc/driver-model/ethernet.rst b/doc/develop/driver-model/ethernet.rst similarity index 100% rename from doc/driver-model/ethernet.rst rename to doc/develop/driver-model/ethernet.rst diff --git a/doc/driver-model/fdt-fixup.rst b/doc/develop/driver-model/fdt-fixup.rst similarity index 100% rename from doc/driver-model/fdt-fixup.rst rename to doc/develop/driver-model/fdt-fixup.rst diff --git a/doc/driver-model/fs_firmware_loader.rst b/doc/develop/driver-model/fs_firmware_loader.rst similarity index 100% rename from doc/driver-model/fs_firmware_loader.rst rename to doc/develop/driver-model/fs_firmware_loader.rst diff --git a/doc/driver-model/i2c-howto.rst b/doc/develop/driver-model/i2c-howto.rst similarity index 100% rename from doc/driver-model/i2c-howto.rst rename to doc/develop/driver-model/i2c-howto.rst diff --git a/doc/driver-model/index.rst b/doc/develop/driver-model/index.rst similarity index 68% rename from doc/driver-model/index.rst rename to doc/develop/driver-model/index.rst index c9faf0a591..fd4575db9b 100644 --- a/doc/driver-model/index.rst +++ b/doc/develop/driver-model/index.rst @@ -3,6 +3,10 @@ Driver Model ============ +The following holds information on the U-Boot device driver framework: +driver-model, including the design details of itself and several driver +subsystems + .. toctree:: :maxdepth: 2 diff --git a/doc/driver-model/livetree.rst b/doc/develop/driver-model/livetree.rst similarity index 100% rename from doc/driver-model/livetree.rst rename to doc/develop/driver-model/livetree.rst diff --git a/doc/driver-model/migration.rst b/doc/develop/driver-model/migration.rst similarity index 100% rename from doc/driver-model/migration.rst rename to doc/develop/driver-model/migration.rst diff --git a/doc/driver-model/of-plat.rst b/doc/develop/driver-model/of-plat.rst similarity index 100% rename from doc/driver-model/of-plat.rst rename to doc/develop/driver-model/of-plat.rst diff --git a/doc/driver-model/pci-info.rst b/doc/develop/driver-model/pci-info.rst similarity index 100% rename from doc/driver-model/pci-info.rst rename to doc/develop/driver-model/pci-info.rst diff --git a/doc/driver-model/pmic-framework.rst b/doc/develop/driver-model/pmic-framework.rst similarity index 100% rename from doc/driver-model/pmic-framework.rst rename to doc/develop/driver-model/pmic-framework.rst diff --git a/doc/driver-model/remoteproc-framework.rst b/doc/develop/driver-model/remoteproc-framework.rst similarity index 100% rename from doc/driver-model/remoteproc-framework.rst rename to doc/develop/driver-model/remoteproc-framework.rst diff --git a/doc/driver-model/serial-howto.rst b/doc/develop/driver-model/serial-howto.rst similarity index 100% rename from doc/driver-model/serial-howto.rst rename to doc/develop/driver-model/serial-howto.rst diff --git a/doc/driver-model/soc-framework.rst b/doc/develop/driver-model/soc-framework.rst similarity index 100% rename from doc/driver-model/soc-framework.rst rename to doc/develop/driver-model/soc-framework.rst diff --git a/doc/driver-model/spi-howto.rst b/doc/develop/driver-model/spi-howto.rst similarity index 100% rename from doc/driver-model/spi-howto.rst rename to doc/develop/driver-model/spi-howto.rst diff --git a/doc/driver-model/usb-info.rst b/doc/develop/driver-model/usb-info.rst similarity index 100% rename from doc/driver-model/usb-info.rst rename to doc/develop/driver-model/usb-info.rst diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 84914bb47b..444df67957 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -10,6 +10,7 @@ Implementation :maxdepth: 1 commands + driver-model/index global_data logging menus diff --git a/doc/index.rst b/doc/index.rst index 366963813a..f7aada966b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -38,17 +38,6 @@ want to contribute to U-Boot. develop/index -Driver-Model documentation --------------------------- - -The following holds information on the U-Boot device driver framework: -driver-model, including the design details of itself and several driver -subsystems. - -.. toctree:: - :maxdepth: 2 - - driver-model/index U-Boot API documentation ------------------------ From 61adb2d2474eb72ea05365ef81e5c6d7e5f61441 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:13 +1300 Subject: [PATCH 116/120] binman: doc: Add documentation to htmldocs Add a link to binman's documentation and adjust the files so that it is accessible. Use the name README.rst so it is easy to discover when binman is installed without U-Boot. Signed-off-by: Simon Glass --- doc/develop/index.rst | 8 + doc/develop/package/binman.rst | 1 + doc/develop/package/index.rst | 19 ++ doc/usage/fit.rst | 8 + doc/usage/index.rst | 1 + tools/binman/README.rst | 1 + tools/binman/{README => binman.rst} | 478 ++++++++++++++-------------- tools/binman/control.py | 2 +- tools/binman/ftest.py | 4 +- tools/binman/index.rst | 9 + tools/binman/setup.py | 2 +- 11 files changed, 291 insertions(+), 242 deletions(-) create mode 120000 doc/develop/package/binman.rst create mode 100644 doc/develop/package/index.rst create mode 100644 doc/usage/fit.rst create mode 120000 tools/binman/README.rst rename tools/binman/{README => binman.rst} (73%) create mode 100644 tools/binman/index.rst diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 444df67957..3edffbc637 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -26,6 +26,14 @@ Debugging crash_dumps trace +Packaging +--------- + +.. toctree:: + :maxdepth: 1 + + package/index + Testing ------- diff --git a/doc/develop/package/binman.rst b/doc/develop/package/binman.rst new file mode 120000 index 0000000000..2e26e84b7d --- /dev/null +++ b/doc/develop/package/binman.rst @@ -0,0 +1 @@ +../../../tools/binman/binman.rst \ No newline at end of file diff --git a/doc/develop/package/index.rst b/doc/develop/package/index.rst new file mode 100644 index 0000000000..9374be2e62 --- /dev/null +++ b/doc/develop/package/index.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Package U-Boot +============== + +U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging +images that it it reads and boots. Documentation about FIT is available at +doc/uImage.FIT + +U-Boot also provides binman for cases not covered by FIT. Examples include +initial execution (since FIT itself does not have an executable header) and +dealing with device boundaries, such as the read-only/read-write separation in +SPI flash. + + +.. toctree:: + :maxdepth: 2 + + binman diff --git a/doc/usage/fit.rst b/doc/usage/fit.rst new file mode 100644 index 0000000000..7037434057 --- /dev/null +++ b/doc/usage/fit.rst @@ -0,0 +1,8 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Flat Image Tree (FIT) +===================== + +U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging +images that it it reads and boots. Documentation about FIT is available at +doc/uImage.FIT diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 637b73ccab..35c515f8b5 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -5,6 +5,7 @@ Use U-Boot :maxdepth: 1 fdt_overlays + fit netconsole partitions diff --git a/tools/binman/README.rst b/tools/binman/README.rst new file mode 120000 index 0000000000..b734f544b7 --- /dev/null +++ b/tools/binman/README.rst @@ -0,0 +1 @@ +binman.rst \ No newline at end of file diff --git a/tools/binman/README b/tools/binman/binman.rst similarity index 73% rename from tools/binman/README rename to tools/binman/binman.rst index 1de703cc65..fd6308b6e4 100644 --- a/tools/binman/README +++ b/tools/binman/binman.rst @@ -1,5 +1,5 @@ -# SPDX-License-Identifier: GPL-2.0+ -# Copyright (c) 2016 Google, Inc +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2016 Google, Inc Introduction ------------ @@ -67,18 +67,19 @@ standard format, we can support making valid images for any board without manual effort, lots of READMEs, etc. Benefits: -- Each binary can have its own build system and tool chain without creating -any dependencies between them -- Avoids the need for a single-shot build: individual parts can be updated -and brought in as needed -- Provides for a standard image description available in the build and at -run-time -- SoC-specific image-signing tools can be accommodated -- Avoids cluttering the U-Boot build system with image-building code -- The image description is automatically available at run-time in U-Boot, -SPL. It can be made available to other software also -- The image description is easily readable (it's a text file in device-tree -format) and permits flexible packing of binaries + + - Each binary can have its own build system and tool chain without creating + any dependencies between them + - Avoids the need for a single-shot build: individual parts can be updated + and brought in as needed + - Provides for a standard image description available in the build and at + run-time + - SoC-specific image-signing tools can be accommodated + - Avoids cluttering the U-Boot build system with image-building code + - The image description is automatically available at run-time in U-Boot, + SPL. It can be made available to other software also + - The image description is easily readable (it's a text file in device-tree + format) and permits flexible packing of binaries Terminology @@ -144,14 +145,14 @@ build system. Consider sunxi. It has the following steps: -1. It uses a custom mksunxiboot tool to build an SPL image called -sunxi-spl.bin. This should probably move into mkimage. + #. It uses a custom mksunxiboot tool to build an SPL image called + sunxi-spl.bin. This should probably move into mkimage. -2. It uses mkimage to package U-Boot into a legacy image file (so that it can -hold the load and execution address) called u-boot.img. + #. It uses mkimage to package U-Boot into a legacy image file (so that it can + hold the load and execution address) called u-boot.img. -3. It builds a final output image called u-boot-sunxi-with-spl.bin which -consists of sunxi-spl.bin, some padding and u-boot.img. + #. It builds a final output image called u-boot-sunxi-with-spl.bin which + consists of sunxi-spl.bin, some padding and u-boot.img. Binman is intended to replace the last step. The U-Boot build system builds u-boot.bin and sunxi-spl.bin. Binman can then take over creation of @@ -180,22 +181,22 @@ the configuration of the Intel-format descriptor. Running binman -------------- -First install prerequisites, e.g. +First install prerequisites, e.g:: - sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ - liblz4-tool + sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ + liblz4-tool -Type: +Type:: - binman build -b + binman build -b to build an image for a board. The board name is the same name used when configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). Binman assumes that the input files for the build are in ../b/. -Or you can specify this explicitly: +Or you can specify this explicitly:: - binman build -I + binman build -I where is the build directory containing the output of the U-Boot build. @@ -212,12 +213,12 @@ Enabling binman for a board --------------------------- At present binman is invoked from a rule in the main Makefile. Typically you -will have a rule like: +will have a rule like:: -ifneq ($(CONFIG_ARCH_),) -u-boot-.bin: checkbinman FORCE - $(call if_changed,binman) -endif + ifneq ($(CONFIG_ARCH_),) + u-boot-.bin: checkbinman FORCE + $(call if_changed,binman) + endif This assumes that u-boot-.bin is a target, and is the final file that you need to produce. You can make it a target by adding it to INPUTS-y @@ -233,18 +234,18 @@ Image description format ------------------------ The binman node is called 'binman'. An example image description is shown -below: +below:: - binman { - filename = "u-boot-sunxi-with-spl.bin"; - pad-byte = <0xff>; - blob { - filename = "spl/sunxi-spl.bin"; - }; - u-boot { - offset = ; - }; - }; + binman { + filename = "u-boot-sunxi-with-spl.bin"; + pad-byte = <0xff>; + blob { + filename = "spl/sunxi-spl.bin"; + }; + u-boot { + offset = ; + }; + }; This requests binman to create an image file called u-boot-sunxi-with-spl.bin @@ -270,184 +271,184 @@ use any unique name, with the 'type' property providing the type. The attributes supported for entries are described below. offset: - This sets the offset of an entry within the image or section containing - it. The first byte of the image is normally at offset 0. If 'offset' is - not provided, binman sets it to the end of the previous region, or the - start of the image's entry area (normally 0) if there is no previous - region. + This sets the offset of an entry within the image or section containing + it. The first byte of the image is normally at offset 0. If 'offset' is + not provided, binman sets it to the end of the previous region, or the + start of the image's entry area (normally 0) if there is no previous + region. align: - This sets the alignment of the entry. The entry offset is adjusted - so that the entry starts on an aligned boundary within the containing - section or image. For example 'align = <16>' means that the entry will - start on a 16-byte boundary. This may mean that padding is added before - the entry. The padding is part of the containing section but is not - included in the entry, meaning that an empty space may be created before - the entry starts. Alignment should be a power of 2. If 'align' is not - provided, no alignment is performed. + This sets the alignment of the entry. The entry offset is adjusted + so that the entry starts on an aligned boundary within the containing + section or image. For example 'align = <16>' means that the entry will + start on a 16-byte boundary. This may mean that padding is added before + the entry. The padding is part of the containing section but is not + included in the entry, meaning that an empty space may be created before + the entry starts. Alignment should be a power of 2. If 'align' is not + provided, no alignment is performed. size: - This sets the size of the entry. The contents will be padded out to - this size. If this is not provided, it will be set to the size of the - contents. + This sets the size of the entry. The contents will be padded out to + this size. If this is not provided, it will be set to the size of the + contents. pad-before: - 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 - to offset the entry contents a little. 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 starts with the padding bytes, so may grow. Defaults to 0. + 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 + to offset the entry contents a little. 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 starts with the padding bytes, so may grow. Defaults to 0. pad-after: - Padding after the contents of the entry. Normally this is 0, meaning - that the entry ends at the last byte of content (unless adjusted by - other properties). This allows room to be created in the image for - this entry to expand later. 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. + Padding after the contents of the entry. Normally this is 0, meaning + that the entry ends at the last byte of content (unless adjusted by + other properties). This allows room to be created in the image for + this entry to expand later. 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. align-size: - This sets the alignment of the entry size. For example, to ensure - that the size of an entry is a multiple of 64 bytes, set this to 64. - 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 is that the entry ends with the padding - bytes, so may grow. If 'align-size' is not provided, no alignment is - performed. + This sets the alignment of the entry size. For example, to ensure + that the size of an entry is a multiple of 64 bytes, set this to 64. + 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 is that the entry ends with the padding + bytes, so may grow. If 'align-size' is not provided, no alignment is + performed. align-end: - This sets the alignment of the end of an entry with respect to the - containing section. Some entries require that they end on an alignment - boundary, regardless of where they start. This does not move the start - of the entry, so the contents of the entry will still start at the - beginning. But there may be padding at the end. 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 - is that the entry ends with the padding bytes, so may grow. - If 'align-end' is not provided, no alignment is performed. + This sets the alignment of the end of an entry with respect to the + containing section. Some entries require that they end on an alignment + boundary, regardless of where they start. This does not move the start + of the entry, so the contents of the entry will still start at the + beginning. But there may be padding at the end. 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 + is that the entry ends with the padding bytes, so may grow. + If 'align-end' is not provided, no alignment is performed. filename: - For 'blob' types this provides the filename containing the binary to - put into the entry. If binman knows about the entry type (like - u-boot-bin), then there is no need to specify this. + For 'blob' types this provides the filename containing the binary to + put into the entry. If binman knows about the entry type (like + u-boot-bin), then there is no need to specify this. type: - Sets the type of an entry. This defaults to the entry name, but it is - possible to use any name, and then add (for example) 'type = "u-boot"' - to specify the type. + Sets the type of an entry. This defaults to the entry name, but it is + possible to use any name, and then add (for example) 'type = "u-boot"' + to specify the type. offset-unset: - Indicates that the offset of this entry should not be set by placing - it immediately after the entry before. Instead, is set by another - entry which knows where this entry should go. When this boolean - property is present, binman will give an error if another entry does - not set the offset (with the GetOffsets() method). + Indicates that the offset of this entry should not be set by placing + it immediately after the entry before. Instead, is set by another + entry which knows where this entry should go. When this boolean + property is present, binman will give an error if another entry does + not set the offset (with the GetOffsets() method). image-pos: - This cannot be set on entry (or at least it is ignored if it is), but - with the -u option, binman will set it to the absolute image position - for each entry. This makes it easy to find out exactly where the entry - ended up in the image, regardless of parent sections, etc. + This cannot be set on entry (or at least it is ignored if it is), but + with the -u option, binman will set it to the absolute image position + for each entry. This makes it easy to find out exactly where the entry + ended up in the image, regardless of parent sections, etc. expand-size: - Expand the size of this entry to fit available space. This space is only - limited by the size of the image/section and the position of the next - entry. + Expand the size of this entry to fit available space. This space is only + limited by the size of the image/section and the position of the next + entry. compress: - Sets the compression algortihm to use (for blobs only). See the entry - documentation for details. + Sets the compression algortihm to use (for blobs only). See the entry + documentation for details. missing-msg: - Sets the tag of the message to show if this entry is missing. This is - used for external blobs. When they are missing it is helpful to show - information about what needs to be fixed. See missing-blob-help for the - message for each tag. + Sets the tag of the message to show if this entry is missing. This is + used for external blobs. When they are missing it is helpful to show + information about what needs to be fixed. See missing-blob-help for the + message for each tag. The attributes supported for images and sections are described below. Several are similar to those for entries. size: - Sets the image size in bytes, for example 'size = <0x100000>' for a - 1MB image. + Sets the image size in bytes, for example 'size = <0x100000>' for a + 1MB image. offset: - This is similar to 'offset' in entries, setting the offset of a section - within the image or section containing it. The first byte of the section - is normally at offset 0. If 'offset' is not provided, binman sets it to - the end of the previous region, or the start of the image's entry area - (normally 0) if there is no previous region. + This is similar to 'offset' in entries, setting the offset of a section + within the image or section containing it. The first byte of the section + is normally at offset 0. If 'offset' is not provided, binman sets it to + the end of the previous region, or the start of the image's entry area + (normally 0) if there is no previous region. align-size: - This sets the alignment of the image size. For example, to ensure - that the image ends on a 512-byte boundary, use 'align-size = <512>'. - If 'align-size' is not provided, no alignment is performed. + This sets the alignment of the image size. For example, to ensure + that the image ends on a 512-byte boundary, use 'align-size = <512>'. + If 'align-size' is not provided, no alignment is performed. pad-before: - This sets the padding before the image entries. The first entry will - be positioned after the padding. This defaults to 0. + This sets the padding before the image entries. The first entry will + be positioned after the padding. This defaults to 0. pad-after: - This sets the padding after the image entries. The padding will be - placed after the last entry. This defaults to 0. + This sets the padding after the image entries. The padding will be + placed after the last entry. This defaults to 0. pad-byte: - This specifies the pad byte to use when padding in the image. It - defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. + This specifies the pad byte to use when padding in the image. It + defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. filename: - This specifies the image filename. It defaults to 'image.bin'. + This specifies the image filename. It defaults to 'image.bin'. sort-by-offset: - This causes binman to reorder the entries as needed to make sure they - are in increasing positional order. This can be used when your entry - order may not match the positional order. A common situation is where - the 'offset' properties are set by CONFIG options, so their ordering is - not known a priori. + This causes binman to reorder the entries as needed to make sure they + are in increasing positional order. This can be used when your entry + order may not match the positional order. A common situation is where + the 'offset' properties are set by CONFIG options, so their ordering is + not known a priori. - This is a boolean property so needs no value. To enable it, add a - line 'sort-by-offset;' to your description. + This is a boolean property so needs no value. To enable it, add a + line 'sort-by-offset;' to your description. multiple-images: - Normally only a single image is generated. To create more than one - image, put this property in the binman node. For example, this will - create image1.bin containing u-boot.bin, and image2.bin containing - both spl/u-boot-spl.bin and u-boot.bin: + Normally only a single image is generated. To create more than one + image, put this property in the binman node. For example, this will + create image1.bin containing u-boot.bin, and image2.bin containing + both spl/u-boot-spl.bin and u-boot.bin:: - binman { - multiple-images; - image1 { - u-boot { - }; - }; + binman { + multiple-images; + image1 { + u-boot { + }; + }; - image2 { - spl { - }; - u-boot { - }; - }; - }; + image2 { + spl { + }; + u-boot { + }; + }; + }; end-at-4gb: - For x86 machines the ROM offsets start just before 4GB and extend - up so that the image finished at the 4GB boundary. This boolean - option can be enabled to support this. The image size must be - provided so that binman knows when the image should start. For an - 8MB ROM, the offset of the first entry would be 0xfff80000 with - this option, instead of 0 without this option. + For x86 machines the ROM offsets start just before 4GB and extend + up so that the image finished at the 4GB boundary. This boolean + option can be enabled to support this. The image size must be + provided so that binman knows when the image should start. For an + 8MB ROM, the offset of the first entry would be 0xfff80000 with + this option, instead of 0 without this option. skip-at-start: - This property specifies the entry offset of the first entry. + This property specifies the entry offset of the first entry. - For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry - offset of the first entry. It can be 0xeff40000 or 0xfff40000 for - nor flash boot, 0x201000 for sd boot etc. + For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry + offset of the first entry. It can be 0xeff40000 or 0xfff40000 for + nor flash boot, 0x201000 for sd boot etc. - 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + - Image size != 4gb. + 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + + Image size != 4gb. Examples of the above options can be found in the tests. See the tools/binman/test directory. @@ -470,23 +471,23 @@ This feature provides a way of creating hierarchical images. For example here is an example image with two copies of U-Boot. One is read-only (ro), intended to be written only in the factory. Another is read-write (rw), so that it can be upgraded in the field. The sizes are fixed so that the ro/rw boundary is known -and can be programmed: +and can be programmed:: - binman { - section@0 { - read-only; - name-prefix = "ro-"; - size = <0x100000>; - u-boot { - }; - }; - section@1 { - name-prefix = "rw-"; - size = <0x100000>; - u-boot { - }; - }; - }; + binman { + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x100000>; + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x100000>; + u-boot { + }; + }; + }; This image could be placed into a SPI flash chip, with the protection boundary set at 1MB. @@ -494,14 +495,14 @@ set at 1MB. A few special properties are provided for sections: read-only: - Indicates that this section is read-only. This has no impact on binman's - operation, but his property can be read at run time. + Indicates that this section is read-only. This has no impact on binman's + operation, but his property can be read at run time. name-prefix: - This string is prepended to all the names of the binaries in the - section. In the example above, the 'u-boot' binaries which actually be - renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to - distinguish binaries with otherwise identical names. + This string is prepended to all the names of the binaries in the + section. In the example above, the 'u-boot' binaries which actually be + renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to + distinguish binaries with otherwise identical names. Image Properties @@ -510,21 +511,21 @@ Image Properties Image nodes act like sections but also have a few extra properties: filename: - Output filename for the image. This defaults to image.bin (or in the - case of multiple images .bin where is the name of - the image node. + Output filename for the image. This defaults to image.bin (or in the + case of multiple images .bin where is the name of + the image node. allow-repack: - Create an image that can be repacked. With this option it is possible - to change anything in the image after it is created, including updating - the position and size of image components. By default this is not - permitted since it is not possibly to know whether this might violate a - constraint in the image description. For example, if a section has to - increase in size to hold a larger binary, that might cause the section - to fall out of its allow region (e.g. read-only portion of flash). + Create an image that can be repacked. With this option it is possible + to change anything in the image after it is created, including updating + the position and size of image components. By default this is not + permitted since it is not possibly to know whether this might violate a + constraint in the image description. For example, if a section has to + increase in size to hold a larger binary, that might cause the section + to fall out of its allow region (e.g. read-only portion of flash). - Adding this property causes the original offset and size values in the - image description to be stored in the FDT and fdtmap. + Adding this property causes the original offset and size values in the + image description to be stored in the FDT and fdtmap. Entry Documentation @@ -533,14 +534,14 @@ Entry Documentation For details on the various entry types supported by binman and how to use them, see README.entries. This is generated from the source code using: - binman entry-docs >tools/binman/README.entries + binman entry-docs >tools/binman/README.entries Listing images -------------- It is possible to list the entries in an existing firmware image created by -binman, provided that there is an 'fdtmap' entry in the image. For example: +binman, provided that there is an 'fdtmap' entry in the image. For example:: $ binman ls -i image.bin Name Image-pos Size Entry-type Offset Uncomp-size @@ -559,7 +560,7 @@ This shows the hierarchy of the image, the position, size and type of each entry, the offset of each entry within its parent and the uncompressed size if the entry is compressed. -It is also possible to list just some files in an image, e.g. +It is also possible to list just some files in an image, e.g.:: $ binman ls -i image.bin section/cbfs Name Image-pos Size Entry-type Offset Uncomp-size @@ -568,7 +569,7 @@ It is also possible to list just some files in an image, e.g. u-boot 138 4 u-boot 38 u-boot-dtb 180 108 u-boot-dtb 80 3b5 -or with wildcards: +or with wildcards:: $ binman ls -i image.bin "*cb*" "*head*" Name Image-pos Size Entry-type Offset Uncomp-size @@ -583,22 +584,22 @@ Extracting files from images ---------------------------- You can extract files from an existing firmware image created by binman, -provided that there is an 'fdtmap' entry in the image. For example: +provided that there is an 'fdtmap' entry in the image. For example:: $ binman extract -i image.bin section/cbfs/u-boot which will write the uncompressed contents of that entry to the file 'u-boot' in the current directory. You can also extract to a particular file, in this case -u-boot.bin: +u-boot.bin:: $ binman extract -i image.bin section/cbfs/u-boot -f u-boot.bin It is possible to extract all files into a destination directory, which will -put files in subdirectories matching the entry hierarchy: +put files in subdirectories matching the entry hierarchy:: $ binman extract -i image.bin -O outdir -or just a selection: +or just a selection:: $ binman extract -i image.bin "*u-boot*" -O outdir @@ -616,18 +617,18 @@ to the that entry, compressing if necessary. If the entry size changes, you must add the 'allow-repack' property to the original image before generating it (see above), otherwise you will get an error. -You can also use a particular file, in this case u-boot.bin: +You can also use a particular file, in this case u-boot.bin:: $ binman replace -i image.bin section/cbfs/u-boot -f u-boot.bin It is possible to replace all files from a source directory which uses the same -hierarchy as the entries: +hierarchy as the entries:: $ binman replace -i image.bin -I indir Files that are missing will generate a warning. -You can also replace just a selection of entries: +You can also replace just a selection of entries:: $ binman replace -i image.bin "*u-boot*" -I indir @@ -656,15 +657,15 @@ Hashing Entries --------------- It is possible to ask binman to hash the contents of an entry and write that -value back to the device-tree node. For example: +value back to the device-tree node. For example:: - binman { - u-boot { - hash { - algo = "sha256"; - }; - }; - }; + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; Here, a new 'value' property will be written to the 'hash' node containing the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole @@ -759,7 +760,7 @@ a common header. You can then put the binman node (and anything else that is specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header file. -Binman will search for the following files in arch//dts: +Binman will search for the following files in arch//dts:: -u-boot.dtsi where is the base name of the .dts file -u-boot.dtsi @@ -770,7 +771,7 @@ Binman will search for the following files in arch//dts: U-Boot will only use the first one that it finds. If you need to include a more general file you can do that from the more specific file using #include. If you are having trouble figuring out what is going on, you can uncomment -the 'warning' line in scripts/Makefile.lib to see what it has found: +the 'warning' line in scripts/Makefile.lib to see what it has found:: # Uncomment for debugging # This shows all the files that were considered and the one that we chose. @@ -786,13 +787,13 @@ is useful to be able to find the location of U-Boot so that it can be executed when SPL is finished. Binman allows you to declare symbols in the SPL image which are filled in -with their correct values during the build. For example: +with their correct values during the build. For example:: binman_sym_declare(ulong, u_boot_any, image_pos); declares a ulong value which will be assigned to the image-pos of any U-Boot image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. -You can access this value with something like: +You can access this value with something like:: ulong u_boot_offset = binman_sym(ulong, u_boot_any, image_pos); @@ -844,18 +845,18 @@ Expanded entries ---------------- Binman automatically replaces 'u-boot' with an expanded version of that, i.e. -'u-boot-expanded'. This means that when you write: +'u-boot-expanded'. This means that when you write:: u-boot { }; -you actually get: +you actually get:: u-boot { type = "u-boot-expanded'; }; -which in turn expands to: +which in turn expands to:: u-boot { type = "section"; @@ -879,19 +880,19 @@ U-Boot executable and can be updated separately by binman as needed. It can be disabled with the --no-expanded flag if required. The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion -includes the BSS padding, so for example: +includes the BSS padding, so for example:: spl { type = "u-boot-spl" }; -you actually get: +you actually get:: spl { type = "u-boot-expanded'; }; -which in turn expands to: +which in turn expands to:: spl { type = "section"; @@ -921,7 +922,7 @@ Compression ----------- Binman support compression for 'blob' entries (those of type 'blob' and -derivatives). To enable this for an entry, add a 'compress' property: +derivatives). To enable this for an entry, add a 'compress' property:: blob { filename = "datafile"; @@ -946,7 +947,7 @@ Map files --------- The -m option causes binman to output a .map file for each image that it -generates. This shows the offset and size of each entry. For example: +generates. This shows the offset and size of each entry. For example:: Offset Size Name 00000000 00000028 main-section @@ -969,11 +970,11 @@ Sometimes it is useful to pass binman the value of an entry property from the command line. For example some entries need access to files and it is not always convenient to put these filenames in the image definition (device tree). -The-a option supports this: +The-a option supports this:: -a= -where +where:: is the property to set is the value to set it to @@ -1004,7 +1005,7 @@ Code coverage Binman is a critical tool and is designed to be very testable. Entry implementations target 100% test coverage. Run 'binman test -T' to check this. -To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): +To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):: $ sudo apt-get install python-coverage python3-coverage python-pytest @@ -1015,7 +1016,7 @@ Concurrent tests Binman tries to run tests concurrently. This means that the tests make use of all available CPUs to run. - To enable this: + To enable this:: $ sudo apt-get install python-subunit python3-subunit @@ -1038,11 +1039,11 @@ Binman's tests have been written under the assumption that they'll be run on a x86-like host and there hasn't been an attempt to make them portable yet. However, it's possible to run the tests by cross-compiling to x86. -To install an x86 cross-compiler on Debian-type distributions (e.g. Ubuntu): +To install an x86 cross-compiler on Debian-type distributions (e.g. Ubuntu):: $ sudo apt-get install gcc-x86-64-linux-gnu -Then, you can run the tests under cross-compilation: +Then, you can run the tests under cross-compilation:: $ CROSS_COMPILE=x86_64-linux-gnu- binman test -T @@ -1078,13 +1079,13 @@ the DTC environment variable. This can be useful when the system dtc is too old. To enable a full backtrace and other debugging features in binman, pass -BINMAN_DEBUG=1 to your build: +BINMAN_DEBUG=1 to your build:: make qemu-x86_defconfig make BINMAN_DEBUG=1 To enable verbose logging from binman, base BINMAN_VERBOSE to your build, which -adds a -v option to the call to binman: +adds a -v option to the call to binman:: make qemu-x86_defconfig make BINMAN_VERBOSE=5 @@ -1124,6 +1125,7 @@ To do ----- Some ideas: + - Use of-platdata to make the information available to code that is unable to use device tree (such as a very small SPL image) - Allow easy building of images by specifying just the board name diff --git a/tools/binman/control.py b/tools/binman/control.py index 9709aa9a2b..f57e34daaa 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -569,7 +569,7 @@ def Binman(args): if not pager: pager = 'more' fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), - 'README') + 'README.rst') command.Run(pager, fname) return 0 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 432c463b58..81c213a908 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -631,7 +631,7 @@ class TestFunctional(unittest.TestCase): def testFullHelp(self): """Test that the full help is displayed with -H""" result = self._RunBinman('-H') - help_file = os.path.join(self._binman_dir, 'README') + help_file = os.path.join(self._binman_dir, 'README.rst') # Remove possible extraneous strings extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' gothelp = result.stdout.replace(extra, '') @@ -644,7 +644,7 @@ class TestFunctional(unittest.TestCase): try: command.test_result = command.CommandResult() result = self._DoBinman('-H') - help_file = os.path.join(self._binman_dir, 'README') + help_file = os.path.join(self._binman_dir, 'README.rst') finally: command.test_result = None diff --git a/tools/binman/index.rst b/tools/binman/index.rst new file mode 100644 index 0000000000..6eef7b5d05 --- /dev/null +++ b/tools/binman/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Binman +====== + +.. toctree:: + :maxdepth: 2 + + README diff --git a/tools/binman/setup.py b/tools/binman/setup.py index fe408ed691..2dad43d493 100644 --- a/tools/binman/setup.py +++ b/tools/binman/setup.py @@ -7,6 +7,6 @@ setup(name='binman', scripts=['binman'], packages=['binman', 'binman.etype'], package_dir={'binman': ''}, - package_data={'binman': ['README', 'README.entries']}, + package_data={'binman': ['README.rst', 'README.entries']}, classifiers=['Environment :: Console', 'Topic :: Software Development :: Embedded Systems']) From 072026e7bb4cf90c376b774c22b3baba17cc72bf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:14 +1300 Subject: [PATCH 117/120] binman: Rearrange documentation into headings Collect the material into different top-level headings to make it easier to read. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 523 ++++++++++++++++++++-------------------- 1 file changed, 266 insertions(+), 257 deletions(-) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index fd6308b6e4..1cdc9de752 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -2,7 +2,7 @@ .. Copyright (c) 2016 Google, Inc Introduction ------------- +============ Firmware often consists of several components which must be packaged together. For example, we may have SPL, U-Boot, a device tree and an environment area @@ -137,6 +137,9 @@ the boundaries between building input files (mkimage) and packaging then into a final image (binman). +Using binman +============ + Example use of binman in U-Boot ------------------------------- @@ -230,8 +233,111 @@ You can use other, more specific CONFIG options - see 'Automatic .dtsi inclusion' below. +Access to binman entry offsets at run time (symbols) +---------------------------------------------------- + +Binman assembles images and determines where each entry is placed in the image. +This information may be useful to U-Boot at run time. For example, in SPL it +is useful to be able to find the location of U-Boot so that it can be executed +when SPL is finished. + +Binman allows you to declare symbols in the SPL image which are filled in +with their correct values during the build. For example:: + + binman_sym_declare(ulong, u_boot_any, image_pos); + +declares a ulong value which will be assigned to the image-pos of any U-Boot +image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. +You can access this value with something like:: + + ulong u_boot_offset = binman_sym(ulong, u_boot_any, image_pos); + +Thus u_boot_offset will be set to the image-pos of U-Boot in memory, assuming +that the whole image has been loaded, or is available in flash. You can then +jump to that address to start U-Boot. + +At present this feature is only supported in SPL and TPL. In principle it is +possible to fill in such symbols in U-Boot proper, as well, but a future C +library is planned for this instead, to read from the device tree. + +As well as image-pos, it is possible to read the size of an entry and its +offset (which is the start position of the entry within its parent). + +A small technical note: Binman automatically adds the base address of the image +(i.e. __image_copy_start) to the value of the image-pos symbol, so that when the +image is loaded to its linked address, the value will be correct and actually +point into the image. + +For example, say SPL is at the start of the image and linked to start at address +80108000. If U-Boot's image-pos is 0x8000 then binman will write an image-pos +for U-Boot of 80110000 into the SPL binary, since it assumes the image is loaded +to 80108000, with SPL at 80108000 and U-Boot at 80110000. + +For x86 devices (with the end-at-4gb property) this base address is not added +since it is assumed that images are XIP and the offsets already include the +address. + + +Access to binman entry offsets at run time (fdt) +------------------------------------------------ + +Binman can update the U-Boot FDT to include the final position and size of +each entry in the images it processes. The option to enable this is -u and it +causes binman to make sure that the 'offset', 'image-pos' and 'size' properties +are set correctly for every entry. Since it is not necessary to specify these in +the image definition, binman calculates the final values and writes these to +the device tree. These can be used by U-Boot at run-time to find the location +of each entry. + +Alternatively, an FDT map entry can be used to add a special FDT containing +just the information about the image. This is preceded by a magic string so can +be located anywhere in the image. An image header (typically at the start or end +of the image) can be used to point to the FDT map. See fdtmap and image-header +entries for more information. + + +Map files +--------- + +The -m option causes binman to output a .map file for each image that it +generates. This shows the offset and size of each entry. For example:: + + Offset Size Name + 00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 u-boot + 00000010 00000010 section@1 + 00000000 00000004 u-boot + +This shows a hierarchical image with two sections, each with a single entry. The +offsets of the sections are absolute hex byte offsets within the image. The +offsets of the entries are relative to their respective sections. The size of +each entry is also shown, in bytes (hex). The indentation shows the entries +nested inside their sections. + + +Passing command-line arguments to entries +----------------------------------------- + +Sometimes it is useful to pass binman the value of an entry property from the +command line. For example some entries need access to files and it is not +always convenient to put these filenames in the image definition (device tree). + +The-a option supports this:: + + -a= + +where:: + + is the property to set + is the value to set it to + +Not all properties can be provided this way. Only some entries support it, +typically for filenames. + + Image description format ------------------------- +======================== The binman node is called 'binman'. An example image description is shown below:: @@ -528,6 +634,157 @@ allow-repack: image description to be stored in the FDT and fdtmap. +Hashing Entries +--------------- + +It is possible to ask binman to hash the contents of an entry and write that +value back to the device-tree node. For example:: + + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; + +Here, a new 'value' property will be written to the 'hash' node containing +the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole +sections can be hased if desired, by adding the 'hash' node to the section. + +The has value can be chcked at runtime by hashing the data actually read and +comparing this has to the value in the device tree. + + +Expanded entries +---------------- + +Binman automatically replaces 'u-boot' with an expanded version of that, i.e. +'u-boot-expanded'. This means that when you write:: + + u-boot { + }; + +you actually get:: + + u-boot { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + u-boot { + type = "section"; + + u-boot-nodtb { + }; + + u-boot-dtb { + }; + }; + +U-Boot's various phase binaries actually comprise two or three pieces. +For example, u-boot.bin has the executable followed by a devicetree. + +With binman we want to be able to update that devicetree with full image +information so that it is accessible to the executable. This is tricky +if it is not clear where the devicetree starts. + +The above feature ensures that the devicetree is clearly separated from the +U-Boot executable and can be updated separately by binman as needed. It can be +disabled with the --no-expanded flag if required. + +The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion +includes the BSS padding, so for example:: + + spl { + type = "u-boot-spl" + }; + +you actually get:: + + spl { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + spl { + type = "section"; + + u-boot-spl-nodtb { + }; + + u-boot-spl-bss-pad { + }; + + u-boot-spl-dtb { + }; + }; + +Of course we should not expand SPL if it has no devicetree. Also if the BSS +padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), +the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned +entry type is controlled by the UseExpanded() method. In the SPL case it checks +the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. + +For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All +entry args are provided by the U-Boot Makefile. + + +Compression +----------- + +Binman support compression for 'blob' entries (those of type 'blob' and +derivatives). To enable this for an entry, add a 'compress' property:: + + blob { + filename = "datafile"; + compress = "lz4"; + }; + +The entry will then contain the compressed data, using the 'lz4' compression +algorithm. Currently this is the only one that is supported. The uncompressed +size is written to the node in an 'uncomp-size' property, if -u is used. + +Compression is also supported for sections. In that case the entire section is +compressed in one block, including all its contents. This means that accessing +an entry from the section required decompressing the entire section. Also, the +size of a section indicates the space that it consumes in its parent section +(and typically the image). With compression, the section may contain more data, +and the uncomp-size property indicates that, as above. The contents of the +section is compressed first, before any padding is added. This ensures that the +padding itself is not compressed, which would be a waste of time. + + +Automatic .dtsi inclusion +------------------------- + +It is sometimes inconvenient to add a 'binman' node to the .dts file for each +board. This can be done by using #include to bring in a common file. Another +approach supported by the U-Boot build system is to automatically include +a common header. You can then put the binman node (and anything else that is +specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header +file. + +Binman will search for the following files in arch//dts:: + + -u-boot.dtsi where is the base name of the .dts file + -u-boot.dtsi + -u-boot.dtsi + -u-boot.dtsi + u-boot.dtsi + +U-Boot will only use the first one that it finds. If you need to include a +more general file you can do that from the more specific file using #include. +If you are having trouble figuring out what is going on, you can uncomment +the 'warning' line in scripts/Makefile.lib to see what it has found:: + + # Uncomment for debugging + # This shows all the files that were considered and the one that we chose. + # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) + + Entry Documentation ------------------- @@ -537,6 +794,9 @@ see README.entries. This is generated from the source code using: binman entry-docs >tools/binman/README.entries +Managing images +=============== + Listing images -------------- @@ -653,27 +913,9 @@ by increasing the -v/--verbosity from the default of 1: You can use BINMAN_VERBOSE=5 (for example) when building to select this. -Hashing Entries ---------------- - -It is possible to ask binman to hash the contents of an entry and write that -value back to the device-tree node. For example:: - - binman { - u-boot { - hash { - algo = "sha256"; - }; - }; - }; - -Here, a new 'value' property will be written to the 'hash' node containing -the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole -sections can be hased if desired, by adding the 'hash' node to the section. - -The has value can be chcked at runtime by hashing the data actually read and -comparing this has to the value in the device tree. +Technical details +================= Order of image creation ----------------------- @@ -750,239 +992,6 @@ what happens in this stage. final step. -Automatic .dtsi inclusion -------------------------- - -It is sometimes inconvenient to add a 'binman' node to the .dts file for each -board. This can be done by using #include to bring in a common file. Another -approach supported by the U-Boot build system is to automatically include -a common header. You can then put the binman node (and anything else that is -specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header -file. - -Binman will search for the following files in arch//dts:: - - -u-boot.dtsi where is the base name of the .dts file - -u-boot.dtsi - -u-boot.dtsi - -u-boot.dtsi - u-boot.dtsi - -U-Boot will only use the first one that it finds. If you need to include a -more general file you can do that from the more specific file using #include. -If you are having trouble figuring out what is going on, you can uncomment -the 'warning' line in scripts/Makefile.lib to see what it has found:: - - # Uncomment for debugging - # This shows all the files that were considered and the one that we chose. - # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) - - -Access to binman entry offsets at run time (symbols) ----------------------------------------------------- - -Binman assembles images and determines where each entry is placed in the image. -This information may be useful to U-Boot at run time. For example, in SPL it -is useful to be able to find the location of U-Boot so that it can be executed -when SPL is finished. - -Binman allows you to declare symbols in the SPL image which are filled in -with their correct values during the build. For example:: - - binman_sym_declare(ulong, u_boot_any, image_pos); - -declares a ulong value which will be assigned to the image-pos of any U-Boot -image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. -You can access this value with something like:: - - ulong u_boot_offset = binman_sym(ulong, u_boot_any, image_pos); - -Thus u_boot_offset will be set to the image-pos of U-Boot in memory, assuming -that the whole image has been loaded, or is available in flash. You can then -jump to that address to start U-Boot. - -At present this feature is only supported in SPL and TPL. In principle it is -possible to fill in such symbols in U-Boot proper, as well, but a future C -library is planned for this instead, to read from the device tree. - -As well as image-pos, it is possible to read the size of an entry and its -offset (which is the start position of the entry within its parent). - -A small technical note: Binman automatically adds the base address of the image -(i.e. __image_copy_start) to the value of the image-pos symbol, so that when the -image is loaded to its linked address, the value will be correct and actually -point into the image. - -For example, say SPL is at the start of the image and linked to start at address -80108000. If U-Boot's image-pos is 0x8000 then binman will write an image-pos -for U-Boot of 80110000 into the SPL binary, since it assumes the image is loaded -to 80108000, with SPL at 80108000 and U-Boot at 80110000. - -For x86 devices (with the end-at-4gb property) this base address is not added -since it is assumed that images are XIP and the offsets already include the -address. - - -Access to binman entry offsets at run time (fdt) ------------------------------------------------- - -Binman can update the U-Boot FDT to include the final position and size of -each entry in the images it processes. The option to enable this is -u and it -causes binman to make sure that the 'offset', 'image-pos' and 'size' properties -are set correctly for every entry. Since it is not necessary to specify these in -the image definition, binman calculates the final values and writes these to -the device tree. These can be used by U-Boot at run-time to find the location -of each entry. - -Alternatively, an FDT map entry can be used to add a special FDT containing -just the information about the image. This is preceded by a magic string so can -be located anywhere in the image. An image header (typically at the start or end -of the image) can be used to point to the FDT map. See fdtmap and image-header -entries for more information. - - -Expanded entries ----------------- - -Binman automatically replaces 'u-boot' with an expanded version of that, i.e. -'u-boot-expanded'. This means that when you write:: - - u-boot { - }; - -you actually get:: - - u-boot { - type = "u-boot-expanded'; - }; - -which in turn expands to:: - - u-boot { - type = "section"; - - u-boot-nodtb { - }; - - u-boot-dtb { - }; - }; - -U-Boot's various phase binaries actually comprise two or three pieces. -For example, u-boot.bin has the executable followed by a devicetree. - -With binman we want to be able to update that devicetree with full image -information so that it is accessible to the executable. This is tricky -if it is not clear where the devicetree starts. - -The above feature ensures that the devicetree is clearly separated from the -U-Boot executable and can be updated separately by binman as needed. It can be -disabled with the --no-expanded flag if required. - -The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion -includes the BSS padding, so for example:: - - spl { - type = "u-boot-spl" - }; - -you actually get:: - - spl { - type = "u-boot-expanded'; - }; - -which in turn expands to:: - - spl { - type = "section"; - - u-boot-spl-nodtb { - }; - - u-boot-spl-bss-pad { - }; - - u-boot-spl-dtb { - }; - }; - - -Of course we should not expand SPL if it has no devicetree. Also if the BSS -padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), -the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned -entry type is controlled by the UseExpanded() method. In the SPL case it checks -the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. - -For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All -entry args are provided by the U-Boot Makefile. - - -Compression ------------ - -Binman support compression for 'blob' entries (those of type 'blob' and -derivatives). To enable this for an entry, add a 'compress' property:: - - blob { - filename = "datafile"; - compress = "lz4"; - }; - -The entry will then contain the compressed data, using the 'lz4' compression -algorithm. Currently this is the only one that is supported. The uncompressed -size is written to the node in an 'uncomp-size' property, if -u is used. - -Compression is also supported for sections. In that case the entire section is -compressed in one block, including all its contents. This means that accessing -an entry from the section required decompressing the entire section. Also, the -size of a section indicates the space that it consumes in its parent section -(and typically the image). With compression, the section may contain more data, -and the uncomp-size property indicates that, as above. The contents of the -section is compressed first, before any padding is added. This ensures that the -padding itself is not compressed, which would be a waste of time. - - -Map files ---------- - -The -m option causes binman to output a .map file for each image that it -generates. This shows the offset and size of each entry. For example:: - - Offset Size Name - 00000000 00000028 main-section - 00000000 00000010 section@0 - 00000000 00000004 u-boot - 00000010 00000010 section@1 - 00000000 00000004 u-boot - -This shows a hierarchical image with two sections, each with a single entry. The -offsets of the sections are absolute hex byte offsets within the image. The -offsets of the entries are relative to their respective sections. The size of -each entry is also shown, in bytes (hex). The indentation shows the entries -nested inside their sections. - - -Passing command-line arguments to entries ------------------------------------------ - -Sometimes it is useful to pass binman the value of an entry property from the -command line. For example some entries need access to files and it is not -always convenient to put these filenames in the image definition (device tree). - -The-a option supports this:: - - -a= - -where:: - - is the property to set - is the value to set it to - -Not all properties can be provided this way. Only some entries support it, -typically for filenames. - - External tools -------------- @@ -1050,8 +1059,8 @@ Then, you can run the tests under cross-compilation:: You can also use gcc-i686-linux-gnu similar to the above. -Advanced Features / Technical docs ----------------------------------- +Writing new entries and debugging +--------------------------------- The behaviour of entries is defined by the Entry class. All other entries are a subclass of this. An important subclass is Entry_blob which takes binary From 6bc4309be763e2541f45b8246f21cd65d39402ef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:15 +1300 Subject: [PATCH 118/120] binman: Incorporate entry documentation Update this to avoid sphinx warnings and incorporate it into the new documentaiton tree. Signed-off-by: Simon Glass --- tools/binman/cmdline.py | 2 +- tools/binman/{README.entries => entries.rst} | 79 +++++++++++--------- tools/binman/etype/cbfs.py | 10 +-- tools/binman/etype/fdtmap.py | 28 +++---- tools/binman/etype/fit.py | 19 +++-- tools/binman/etype/intel_ifwi.py | 8 +- tools/binman/etype/mkimage.py | 2 +- tools/binman/etype/section.py | 6 +- tools/binman/etype/text.py | 6 +- tools/binman/setup.py | 2 +- 10 files changed, 86 insertions(+), 76 deletions(-) rename tools/binman/{README.entries => entries.rst} (97%) diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 0c0f48951f..95f9ba27fb 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -69,7 +69,7 @@ controlled by a description in the board device tree.''' default=False, help='Update the binman node with offset/size info') entry_parser = subparsers.add_parser('entry-docs', - help='Write out entry documentation (see README.entries)') + help='Write out entry documentation (see entries.rst)') list_parser = subparsers.add_parser('ls', help='List files in an image') list_parser.add_argument('-i', '--image', type=str, required=True, diff --git a/tools/binman/README.entries b/tools/binman/entries.rst similarity index 97% rename from tools/binman/README.entries rename to tools/binman/entries.rst index 0d9105e41c..2d79850c64 100644 --- a/tools/binman/README.entries +++ b/tools/binman/entries.rst @@ -106,7 +106,7 @@ is not used, it supports compression and storing ELF files. CBFS is used by coreboot as its way of orgnanising SPI-flash contents. -The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.: +The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:: cbfs { size = <0x100000>; @@ -122,7 +122,7 @@ This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb. Note that the size is required since binman does not support calculating it. The contents of each entry is just what binman would normally provide if it were not a CBFS node. A blob type can be used to import arbitrary files as -with the second subnode below: +with the second subnode below:: cbfs { size = <0x100000>; @@ -168,7 +168,7 @@ cbfs-type: This is an ELF file that has been loaded (i.e. mapped to memory), so appears in the CBFS as a flat binary. The input file must be an ELF image, for example this puts "u-boot" (the ELF image) into a 'stage' - entry: + entry:: cbfs { size = <0x100000>; @@ -178,7 +178,7 @@ cbfs-type: }; }; - You can use your own ELF file with something like: + You can use your own ELF file with something like:: cbfs { size = <0x100000>; @@ -211,7 +211,7 @@ not support other file types (e.g. payload), adding multiple files (like the particular offset in the CBFS and a few other things. Of course binman can create images containing multiple CBFSs, simply by -defining these in the binman config: +defining these in the binman config:: binman { @@ -279,24 +279,24 @@ sizes are included. Note that the -u option must be provided to ensure that binman updates the FDT with the position of each entry. -Example output for a simple image with U-Boot and an FDT map: +Example output for a simple image with U-Boot and an FDT map:: -/ { - image-name = "binman"; - size = <0x00000112>; - image-pos = <0x00000000>; - offset = <0x00000000>; - u-boot { - size = <0x00000004>; + / { + image-name = "binman"; + size = <0x00000112>; image-pos = <0x00000000>; offset = <0x00000000>; + u-boot { + size = <0x00000004>; + image-pos = <0x00000000>; + offset = <0x00000000>; + }; + fdtmap { + size = <0x0000010e>; + image-pos = <0x00000004>; + offset = <0x00000004>; + }; }; - fdtmap { - size = <0x0000010e>; - image-pos = <0x00000004>; - offset = <0x00000004>; - }; -}; If allow-repack is used then 'orig-offset' and 'orig-size' properties are added as necessary. See the binman README. @@ -344,7 +344,7 @@ input provided. Nodes for the FIT should be written out in the binman configuration just as they would be in a file passed to mkimage. -For example, this creates an image containing a FIT with U-Boot SPL: +For example, this creates an image containing a FIT with U-Boot SPL:: binman { fit { @@ -374,7 +374,7 @@ that you want to generates nodes for two files: file1.dtb and file2.dtb The fit,fdt-list property (see above) indicates that of-list should be used. If the property is missing you will get an error. -Then add a 'generator node', a node with a name starting with '@': +Then add a 'generator node', a node with a name starting with '@':: images { @fdt-SEQ { @@ -389,7 +389,7 @@ files. All the properties you specify will be included in the node. This node acts like a template to generate the nodes. The generator node itself does not appear in the output - it is replaced with what binman generates. -You can create config nodes in a similar way: +You can create config nodes in a similar way:: configurations { default = "@config-DEFAULT-SEQ"; @@ -406,8 +406,10 @@ each of your two files. Available substitutions for '@' nodes are: - SEQ Sequence number of the generated fdt (1, 2, ...) - NAME Name of the dtb as provided (i.e. without adding '.dtb') +SEQ: + Sequence number of the generated fdt (1, 2, ...) +NAME + Name of the dtb as provided (i.e. without adding '.dtb') Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. @@ -416,10 +418,11 @@ The 'default' property, if present, will be automatically set to the name if of configuration whose devicetree matches the 'default-dt' entry argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'. -Available substitutions for '@' property values are: +Available substitutions for '@' property values are - DEFAULT-SEQ Sequence number of the default fdt,as provided by the - 'default-dt' entry argument +DEFAULT-SEQ: + Sequence number of the default fdt,as provided by the 'default-dt' entry + argument Properties (in the 'fit' node itself): fit,external-offset: Indicates that the contents of the FIT are external @@ -633,10 +636,10 @@ Each subnode describes an entry which is placed into the IFWFI with a given sub-partition (and optional entry name). Properties for subnodes: - ifwi-subpart - sub-parition to put this entry into, e.g. "IBBP" - ifwi-entry - entry name t use, e.g. "IBBL" - ifwi-replace - if present, indicates that the item should be replaced - in the IFWI. Otherwise it is added. + - ifwi-subpart: sub-parition to put this entry into, e.g. "IBBP" + - ifwi-entry: entry name t use, e.g. "IBBL" + - ifwi-replace: if present, indicates that the item should be replaced + in the IFWI. Otherwise it is added. See README.x86 for information about x86 binary blobs. @@ -726,7 +729,7 @@ Properties / Entry arguments: - args: Other arguments to pass The data passed to mkimage is collected from subnodes of the mkimage node, -e.g.: +e.g.:: mkimage { args = "-n test -T imximage"; @@ -766,11 +769,13 @@ This entry holds firmware for an external platform-specific coprocessor. Entry: section: Entry that contains other entries ------------------------------------------------- -Properties / Entry arguments: (see binman README for more information) +Properties / Entry arguments: (see binman README for more information): pad-byte: Pad byte to use when padding sort-by-offset: True if entries should be sorted by offset, False if - they must be in-order in the device tree description + they must be in-order in the device tree description + end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + skip-at-start: Number of bytes before the first entry starts. These effectively adjust the starting offset of entries. For example, if this is 16, then the first entry would start at 16. An entry @@ -808,7 +813,7 @@ Properties / Entry arguments: : The text to place in the entry (overrides the above mechanism). This is useful when the text is constant. -Example node: +Example node:: text { size = <50>; @@ -821,7 +826,7 @@ You can then use: and binman will insert that string into the entry. -It is also possible to put the string directly in the node: +It is also possible to put the string directly in the node:: text { size = <8>; @@ -829,7 +834,7 @@ It is also possible to put the string directly in the node: message = "a message directly in the node" }; -or just: +or just:: text { size = <8>; diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py index 6cdbaa085f..baf9c3e45b 100644 --- a/tools/binman/etype/cbfs.py +++ b/tools/binman/etype/cbfs.py @@ -22,7 +22,7 @@ class Entry_cbfs(Entry): CBFS is used by coreboot as its way of orgnanising SPI-flash contents. - The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.: + The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:: cbfs { size = <0x100000>; @@ -38,7 +38,7 @@ class Entry_cbfs(Entry): Note that the size is required since binman does not support calculating it. The contents of each entry is just what binman would normally provide if it were not a CBFS node. A blob type can be used to import arbitrary files as - with the second subnode below: + with the second subnode below:: cbfs { size = <0x100000>; @@ -84,7 +84,7 @@ class Entry_cbfs(Entry): This is an ELF file that has been loaded (i.e. mapped to memory), so appears in the CBFS as a flat binary. The input file must be an ELF image, for example this puts "u-boot" (the ELF image) into a 'stage' - entry: + entry:: cbfs { size = <0x100000>; @@ -94,7 +94,7 @@ class Entry_cbfs(Entry): }; }; - You can use your own ELF file with something like: + You can use your own ELF file with something like:: cbfs { size = <0x100000>; @@ -127,7 +127,7 @@ class Entry_cbfs(Entry): particular offset in the CBFS and a few other things. Of course binman can create images containing multiple CBFSs, simply by - defining these in the binman config: + defining these in the binman config:: binman { diff --git a/tools/binman/etype/fdtmap.py b/tools/binman/etype/fdtmap.py index 6ca88a100e..2339feeba8 100644 --- a/tools/binman/etype/fdtmap.py +++ b/tools/binman/etype/fdtmap.py @@ -53,24 +53,24 @@ class Entry_fdtmap(Entry): Note that the -u option must be provided to ensure that binman updates the FDT with the position of each entry. - Example output for a simple image with U-Boot and an FDT map: + Example output for a simple image with U-Boot and an FDT map:: - / { - image-name = "binman"; - size = <0x00000112>; - image-pos = <0x00000000>; - offset = <0x00000000>; - u-boot { - size = <0x00000004>; + / { + image-name = "binman"; + size = <0x00000112>; image-pos = <0x00000000>; offset = <0x00000000>; + u-boot { + size = <0x00000004>; + image-pos = <0x00000000>; + offset = <0x00000000>; + }; + fdtmap { + size = <0x0000010e>; + image-pos = <0x00000004>; + offset = <0x00000004>; + }; }; - fdtmap { - size = <0x0000010e>; - image-pos = <0x00000004>; - offset = <0x00000004>; - }; - }; If allow-repack is used then 'orig-offset' and 'orig-size' properties are added as necessary. See the binman README. diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 1a7cbd7cec..c4fa711b85 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -22,7 +22,7 @@ class Entry_fit(Entry): Nodes for the FIT should be written out in the binman configuration just as they would be in a file passed to mkimage. - For example, this creates an image containing a FIT with U-Boot SPL: + For example, this creates an image containing a FIT with U-Boot SPL:: binman { fit { @@ -52,7 +52,7 @@ class Entry_fit(Entry): The fit,fdt-list property (see above) indicates that of-list should be used. If the property is missing you will get an error. - Then add a 'generator node', a node with a name starting with '@': + Then add a 'generator node', a node with a name starting with '@':: images { @fdt-SEQ { @@ -67,7 +67,7 @@ class Entry_fit(Entry): node acts like a template to generate the nodes. The generator node itself does not appear in the output - it is replaced with what binman generates. - You can create config nodes in a similar way: + You can create config nodes in a similar way:: configurations { default = "@config-DEFAULT-SEQ"; @@ -84,8 +84,10 @@ class Entry_fit(Entry): Available substitutions for '@' nodes are: - SEQ Sequence number of the generated fdt (1, 2, ...) - NAME Name of the dtb as provided (i.e. without adding '.dtb') + SEQ: + Sequence number of the generated fdt (1, 2, ...) + NAME + Name of the dtb as provided (i.e. without adding '.dtb') Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. @@ -94,10 +96,11 @@ class Entry_fit(Entry): if of configuration whose devicetree matches the 'default-dt' entry argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'. - Available substitutions for '@' property values are: + Available substitutions for '@' property values are - DEFAULT-SEQ Sequence number of the default fdt,as provided by the - 'default-dt' entry argument + DEFAULT-SEQ: + Sequence number of the default fdt,as provided by the 'default-dt' entry + argument Properties (in the 'fit' node itself): fit,external-offset: Indicates that the contents of the FIT are external diff --git a/tools/binman/etype/intel_ifwi.py b/tools/binman/etype/intel_ifwi.py index 1a0e481c19..c2ede641aa 100644 --- a/tools/binman/etype/intel_ifwi.py +++ b/tools/binman/etype/intel_ifwi.py @@ -37,10 +37,10 @@ class Entry_intel_ifwi(Entry_blob_ext): sub-partition (and optional entry name). Properties for subnodes: - ifwi-subpart - sub-parition to put this entry into, e.g. "IBBP" - ifwi-entry - entry name t use, e.g. "IBBL" - ifwi-replace - if present, indicates that the item should be replaced - in the IFWI. Otherwise it is added. + - ifwi-subpart: sub-parition to put this entry into, e.g. "IBBP" + - ifwi-entry: entry name t use, e.g. "IBBL" + - ifwi-replace: if present, indicates that the item should be replaced + in the IFWI. Otherwise it is added. See README.x86 for information about x86 binary blobs. """ diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index 8fddc88118..f8c491d5d0 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -19,7 +19,7 @@ class Entry_mkimage(Entry): - args: Other arguments to pass The data passed to mkimage is collected from subnodes of the mkimage node, - e.g.: + e.g.:: mkimage { args = "-n test -T imximage"; diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 2f862bddf0..cce1500b4e 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -22,11 +22,13 @@ from patman.tools import ToHexSize class Entry_section(Entry): """Entry that contains other entries - Properties / Entry arguments: (see binman README for more information) + Properties / Entry arguments: (see binman README for more information): pad-byte: Pad byte to use when padding sort-by-offset: True if entries should be sorted by offset, False if - they must be in-order in the device tree description + they must be in-order in the device tree description + end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + skip-at-start: Number of bytes before the first entry starts. These effectively adjust the starting offset of entries. For example, if this is 16, then the first entry would start at 16. An entry diff --git a/tools/binman/etype/text.py b/tools/binman/etype/text.py index a69c2a4ec4..45dfcc401e 100644 --- a/tools/binman/etype/text.py +++ b/tools/binman/etype/text.py @@ -25,7 +25,7 @@ class Entry_text(Entry): : The text to place in the entry (overrides the above mechanism). This is useful when the text is constant. - Example node: + Example node:: text { size = <50>; @@ -38,7 +38,7 @@ class Entry_text(Entry): and binman will insert that string into the entry. - It is also possible to put the string directly in the node: + It is also possible to put the string directly in the node:: text { size = <8>; @@ -46,7 +46,7 @@ class Entry_text(Entry): message = "a message directly in the node" }; - or just: + or just:: text { size = <8>; diff --git a/tools/binman/setup.py b/tools/binman/setup.py index 2dad43d493..5ed94abdaf 100644 --- a/tools/binman/setup.py +++ b/tools/binman/setup.py @@ -7,6 +7,6 @@ setup(name='binman', scripts=['binman'], packages=['binman', 'binman.etype'], package_dir={'binman': ''}, - package_data={'binman': ['README.rst', 'README.entries']}, + package_data={'binman': ['README.rst', 'entries.rst']}, classifiers=['Environment :: Console', 'Topic :: Software Development :: Embedded Systems']) From 96d340e9f6f0a044299f78632872690687de5ae9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:16 +1300 Subject: [PATCH 119/120] binman: Drop repetitive heading for each entry Many entries start 'Entry containing a'. This looks fine in the source code but is annoying when viewed in the htmldocs table of contents. Drop these unnecessary words. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 76 ++++++++++++++--------------- tools/binman/etype/atf_bl31.py | 2 +- tools/binman/etype/blob.py | 2 +- tools/binman/etype/blob_ext.py | 2 +- tools/binman/etype/cbfs.py | 2 +- tools/binman/etype/files.py | 2 +- tools/binman/etype/fit.py | 2 +- tools/binman/etype/intel_cmc.py | 2 +- tools/binman/etype/intel_fsp.py | 2 +- tools/binman/etype/intel_fsp_m.py | 2 +- tools/binman/etype/intel_fsp_s.py | 2 +- tools/binman/etype/intel_fsp_t.py | 2 +- tools/binman/etype/intel_ifwi.py | 2 +- tools/binman/etype/intel_me.py | 2 +- tools/binman/etype/intel_mrc.py | 2 +- tools/binman/etype/intel_refcode.py | 2 +- tools/binman/etype/intel_vbt.py | 2 +- tools/binman/etype/intel_vga.py | 2 +- tools/binman/etype/mkimage.py | 2 +- tools/binman/etype/scp.py | 2 +- 20 files changed, 57 insertions(+), 57 deletions(-) diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 2d79850c64..f6faa15562 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -11,8 +11,8 @@ features to produce new behaviours. -Entry: atf-bl31: Entry containing an ARM Trusted Firmware (ATF) BL31 blob -------------------------------------------------------------------------- +Entry: atf-bl31: ARM Trusted Firmware (ATF) BL31 blob +----------------------------------------------------- Properties / Entry arguments: - atf-bl31-path: Filename of file to read into entry. This is typically @@ -25,8 +25,8 @@ about ATF. -Entry: blob: Entry containing an arbitrary binary blob ------------------------------------------------------- +Entry: blob: Arbitrary binary blob +---------------------------------- Note: This should not be used by itself. It is normally used as a parent class by other entry types. @@ -56,8 +56,8 @@ obtained from the list of available device-tree files, managed by the -Entry: blob-ext: Entry containing an externally built binary blob ------------------------------------------------------------------ +Entry: blob-ext: Externally built binary blob +--------------------------------------------- Note: This should not be used by itself. It is normally used as a parent class by other entry types. @@ -96,8 +96,8 @@ entry; similarly for SPL. -Entry: cbfs: Entry containing a Coreboot Filesystem (CBFS) ----------------------------------------------------------- +Entry: cbfs: Coreboot Filesystem (CBFS) +--------------------------------------- A CBFS provides a way to group files into a group. It has a simple directory structure and allows the position of individual files to be set, since it is @@ -303,8 +303,8 @@ added as necessary. See the binman README. -Entry: files: Entry containing a set of files ---------------------------------------------- +Entry: files: A set of files arranged in a section +-------------------------------------------------- Properties / Entry arguments: - pattern: Filename pattern to match the files to include @@ -335,8 +335,8 @@ byte value of a region. -Entry: fit: Entry containing a FIT ----------------------------------- +Entry: fit: Flat Image Tree (FIT) +--------------------------------- This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the input provided. @@ -491,8 +491,8 @@ first/last in the entry list. -Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file -------------------------------------------------------------------------- +Entry: intel-cmc: Intel Chipset Micro Code (CMC) file +----------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -544,8 +544,8 @@ This entry contains a pointer to the FIT. It is required to be at address -Entry: intel-fsp: Entry containing an Intel Firmware Support Package (FSP) file -------------------------------------------------------------------------------- +Entry: intel-fsp: Intel Firmware Support Package (FSP) file +----------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -561,8 +561,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-fsp-m: Entry containing Intel Firmware Support Package (FSP) memory init -------------------------------------------------------------------------------------- +Entry: intel-fsp-m: Intel Firmware Support Package (FSP) memory init +-------------------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -578,8 +578,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-fsp-s: Entry containing Intel Firmware Support Package (FSP) silicon init --------------------------------------------------------------------------------------- +Entry: intel-fsp-s: Intel Firmware Support Package (FSP) silicon init +--------------------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -595,8 +595,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-fsp-t: Entry containing Intel Firmware Support Package (FSP) temp ram init ---------------------------------------------------------------------------------------- +Entry: intel-fsp-t: Intel Firmware Support Package (FSP) temp ram init +---------------------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -611,8 +611,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-ifwi: Entry containing an Intel Integrated Firmware Image (IFWI) file ----------------------------------------------------------------------------------- +Entry: intel-ifwi: Intel Integrated Firmware Image (IFWI) file +-------------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry. This is either the @@ -645,8 +645,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-me: Entry containing an Intel Management Engine (ME) file ----------------------------------------------------------------------- +Entry: intel-me: Intel Management Engine (ME) file +-------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -665,8 +665,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-mrc: Entry containing an Intel Memory Reference Code (MRC) file ----------------------------------------------------------------------------- +Entry: intel-mrc: Intel Memory Reference Code (MRC) file +-------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -679,8 +679,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-refcode: Entry containing an Intel Reference Code file -------------------------------------------------------------------- +Entry: intel-refcode: Intel Reference Code file +----------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -693,8 +693,8 @@ See README.x86 for information about x86 binary blobs. -Entry: intel-vbt: Entry containing an Intel Video BIOS Table (VBT) file ------------------------------------------------------------------------ +Entry: intel-vbt: Intel Video BIOS Table (VBT) file +--------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -706,8 +706,8 @@ See README.x86 for information about Intel binary blobs. -Entry: intel-vga: Entry containing an Intel Video Graphics Adaptor (VGA) file ------------------------------------------------------------------------------ +Entry: intel-vga: Intel Video Graphics Adaptor (VGA) file +--------------------------------------------------------- Properties / Entry arguments: - filename: Filename of file to read into entry @@ -721,8 +721,8 @@ See README.x86 for information about Intel binary blobs. -Entry: mkimage: Entry containing a binary produced by mkimage -------------------------------------------------------------- +Entry: mkimage: Binary produced by mkimage +------------------------------------------ Properties / Entry arguments: - datafile: Filename for -d argument @@ -756,8 +756,8 @@ placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'. -Entry: scp: Entry containing a System Control Processor (SCP) firmware blob ---------------------------------------------------------------------------- +Entry: scp: System Control Processor (SCP) firmware blob +-------------------------------------------------------- Properties / Entry arguments: - scp-path: Filename of file to read into the entry, typically scp.bin diff --git a/tools/binman/etype/atf_bl31.py b/tools/binman/etype/atf_bl31.py index 195adc714b..163d714184 100644 --- a/tools/binman/etype/atf_bl31.py +++ b/tools/binman/etype/atf_bl31.py @@ -8,7 +8,7 @@ from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg class Entry_atf_bl31(Entry_blob_named_by_arg): - """Entry containing an ARM Trusted Firmware (ATF) BL31 blob + """ARM Trusted Firmware (ATF) BL31 blob Properties / Entry arguments: - atf-bl31-path: Filename of file to read into entry. This is typically diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index e609a8b253..018f8c9a31 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -11,7 +11,7 @@ from patman import tools from patman import tout class Entry_blob(Entry): - """Entry containing an arbitrary binary blob + """Arbitrary binary blob Note: This should not be used by itself. It is normally used as a parent class by other entry types. diff --git a/tools/binman/etype/blob_ext.py b/tools/binman/etype/blob_ext.py index e372445f30..d6b0ca17c3 100644 --- a/tools/binman/etype/blob_ext.py +++ b/tools/binman/etype/blob_ext.py @@ -13,7 +13,7 @@ from patman import tools from patman import tout class Entry_blob_ext(Entry_blob): - """Entry containing an externally built binary blob + """Externally built binary blob Note: This should not be used by itself. It is normally used as a parent class by other entry types. diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py index baf9c3e45b..1daddeb822 100644 --- a/tools/binman/etype/cbfs.py +++ b/tools/binman/etype/cbfs.py @@ -13,7 +13,7 @@ from binman.entry import Entry from dtoc import fdt_util class Entry_cbfs(Entry): - """Entry containing a Coreboot Filesystem (CBFS) + """Coreboot Filesystem (CBFS) A CBFS provides a way to group files into a group. It has a simple directory structure and allows the position of individual files to be set, since it is diff --git a/tools/binman/etype/files.py b/tools/binman/etype/files.py index 1feebd0510..5db36abef0 100644 --- a/tools/binman/etype/files.py +++ b/tools/binman/etype/files.py @@ -15,7 +15,7 @@ from patman import tools class Entry_files(Entry_section): - """Entry containing a set of files + """A set of files arranged in a section Properties / Entry arguments: - pattern: Filename pattern to match the files to include diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index c4fa711b85..6936f5736a 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -14,7 +14,7 @@ from dtoc.fdt import Fdt from patman import tools class Entry_fit(Entry): - """Entry containing a FIT + """Flat Image Tree (FIT) This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the input provided. diff --git a/tools/binman/etype/intel_cmc.py b/tools/binman/etype/intel_cmc.py index 644fa421d3..494d43c9cf 100644 --- a/tools/binman/etype/intel_cmc.py +++ b/tools/binman/etype/intel_cmc.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_cmc(Entry_blob_ext): - """Entry containing an Intel Chipset Micro Code (CMC) file + """Intel Chipset Micro Code (CMC) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_fsp.py b/tools/binman/etype/intel_fsp.py index 2ac012bce1..326cb7d09b 100644 --- a/tools/binman/etype/intel_fsp.py +++ b/tools/binman/etype/intel_fsp.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_fsp(Entry_blob_ext): - """Entry containing an Intel Firmware Support Package (FSP) file + """Intel Firmware Support Package (FSP) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_fsp_m.py b/tools/binman/etype/intel_fsp_m.py index 434b0f1856..9bcac790ed 100644 --- a/tools/binman/etype/intel_fsp_m.py +++ b/tools/binman/etype/intel_fsp_m.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_fsp_m(Entry_blob_ext): - """Entry containing Intel Firmware Support Package (FSP) memory init + """Intel Firmware Support Package (FSP) memory init Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_fsp_s.py b/tools/binman/etype/intel_fsp_s.py index 564e1228bb..1d5046d452 100644 --- a/tools/binman/etype/intel_fsp_s.py +++ b/tools/binman/etype/intel_fsp_s.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_fsp_s(Entry_blob_ext): - """Entry containing Intel Firmware Support Package (FSP) silicon init + """Intel Firmware Support Package (FSP) silicon init Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_fsp_t.py b/tools/binman/etype/intel_fsp_t.py index df0c5fbee0..80d95cc6f9 100644 --- a/tools/binman/etype/intel_fsp_t.py +++ b/tools/binman/etype/intel_fsp_t.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_fsp_t(Entry_blob_ext): - """Entry containing Intel Firmware Support Package (FSP) temp ram init + """Intel Firmware Support Package (FSP) temp ram init Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_ifwi.py b/tools/binman/etype/intel_ifwi.py index c2ede641aa..903d39bdbe 100644 --- a/tools/binman/etype/intel_ifwi.py +++ b/tools/binman/etype/intel_ifwi.py @@ -13,7 +13,7 @@ from dtoc import fdt_util from patman import tools class Entry_intel_ifwi(Entry_blob_ext): - """Entry containing an Intel Integrated Firmware Image (IFWI) file + """Intel Integrated Firmware Image (IFWI) file Properties / Entry arguments: - filename: Filename of file to read into entry. This is either the diff --git a/tools/binman/etype/intel_me.py b/tools/binman/etype/intel_me.py index a6fe5427f3..b93ebabdc9 100644 --- a/tools/binman/etype/intel_me.py +++ b/tools/binman/etype/intel_me.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_me(Entry_blob_ext): - """Entry containing an Intel Management Engine (ME) file + """Intel Management Engine (ME) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_mrc.py b/tools/binman/etype/intel_mrc.py index ccbb046519..bb8b26ff68 100644 --- a/tools/binman/etype/intel_mrc.py +++ b/tools/binman/etype/intel_mrc.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_mrc(Entry_blob_ext): - """Entry containing an Intel Memory Reference Code (MRC) file + """Intel Memory Reference Code (MRC) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_refcode.py b/tools/binman/etype/intel_refcode.py index 5ead08b2be..9112730a9a 100644 --- a/tools/binman/etype/intel_refcode.py +++ b/tools/binman/etype/intel_refcode.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_refcode(Entry_blob_ext): - """Entry containing an Intel Reference Code file + """Intel Reference Code file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_vbt.py b/tools/binman/etype/intel_vbt.py index 2a98c12368..8afd576600 100644 --- a/tools/binman/etype/intel_vbt.py +++ b/tools/binman/etype/intel_vbt.py @@ -7,7 +7,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_vbt(Entry_blob_ext): - """Entry containing an Intel Video BIOS Table (VBT) file + """Intel Video BIOS Table (VBT) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/intel_vga.py b/tools/binman/etype/intel_vga.py index a103f1ce0e..51e6465f0d 100644 --- a/tools/binman/etype/intel_vga.py +++ b/tools/binman/etype/intel_vga.py @@ -8,7 +8,7 @@ from binman.etype.blob_ext import Entry_blob_ext class Entry_intel_vga(Entry_blob_ext): - """Entry containing an Intel Video Graphics Adaptor (VGA) file + """Intel Video Graphics Adaptor (VGA) file Properties / Entry arguments: - filename: Filename of file to read into entry diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index f8c491d5d0..e9f82729ab 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -12,7 +12,7 @@ from dtoc import fdt_util from patman import tools class Entry_mkimage(Entry): - """Entry containing a binary produced by mkimage + """Binary produced by mkimage Properties / Entry arguments: - datafile: Filename for -d argument diff --git a/tools/binman/etype/scp.py b/tools/binman/etype/scp.py index 93f8787d2d..a9bee3ce8b 100644 --- a/tools/binman/etype/scp.py +++ b/tools/binman/etype/scp.py @@ -7,7 +7,7 @@ from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg class Entry_scp(Entry_blob_named_by_arg): - """Entry containing a System Control Processor (SCP) firmware blob + """System Control Processor (SCP) firmware blob Properties / Entry arguments: - scp-path: Filename of file to read into the entry, typically scp.bin From fcae6682a7929abf88d9f0b756f9d5f9725e4d14 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:17 +1300 Subject: [PATCH 120/120] binman: Update various pieces of the documentation A few sections are a little out of date now. Update them. Signed-off-by: Simon Glass --- doc/develop/package/entries.rst | 1 + tools/binman/binman.rst | 83 +++++++++++++++++---------------- 2 files changed, 45 insertions(+), 39 deletions(-) create mode 120000 doc/develop/package/entries.rst diff --git a/doc/develop/package/entries.rst b/doc/develop/package/entries.rst new file mode 120000 index 0000000000..ecedcebaad --- /dev/null +++ b/doc/develop/package/entries.rst @@ -0,0 +1 @@ +../../../tools/binman/entries.rst \ No newline at end of file diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 1cdc9de752..15314d1958 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -9,39 +9,43 @@ For example, we may have SPL, U-Boot, a device tree and an environment area grouped together and placed in MMC flash. When the system starts, it must be able to find these pieces. -So far U-Boot has not provided a way to handle creating such images in a -general way. Each SoC does what it needs to build an image, often packing or -concatenating images in the U-Boot build system. - -Binman aims to provide a mechanism for building images, from simple -SPL + U-Boot combinations, to more complex arrangements with many parts. +Building firmware should be separate from packaging it. Many of the complexities +of modern firmware build systems come from trying to do both at once. With +binman, you build all the pieces that are needed, using whatever assortment of +projects and build systems are needed, then use binman to stitch everything +together. What it does ------------ Binman reads your board's device tree and finds a node which describes the -required image layout. It uses this to work out what to place where. The -output file normally contains the device tree, so it is in principle possible -to read an image and extract its constituent parts. +required image layout. It uses this to work out what to place where. + +Binman provides a mechanism for building images, from simple SPL + U-Boot +combinations, to more complex arrangements with many parts. It also allows +users to inspect images, extract and replace binaries within them, repacking if +needed. Features -------- -So far binman is pretty simple. It supports binary blobs, such as 'u-boot', -'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can -place entries at a fixed location in the image, or fit them together with -suitable padding and alignment. It provides a way to process binaries before -they are included, by adding a Python plug-in. The device tree is available -to U-Boot at run-time so that the images can be interpreted. +Apart from basic padding, alignment and positioning features, Binman supports +hierarchical images, compression, hashing and dealing with the binary blobs +which are a sad trend in open-source firmware at present. -Binman can update the device tree with the final location of everything when it -is done. Entry positions can be provided to U-Boot SPL as run-time symbols, -avoiding device-tree code overhead. +Executable binaries can access the location of other binaries in an image by +using special linker symbols (zero-overhead but somewhat limited) or by reading +the devicetree description of the image. -Binman can also support incorporating filesystems in the image if required. -For example x86 platforms may use CBFS in some cases. +Binman is designed primarily for use with U-Boot and associated binaries such +as ARM Trusted Firmware, but it is suitable for use with other projects, such +as Zephyr. Binman also provides facilities useful in Chromium OS, such as CBFS, +vblocks and and the like. + +Binman provides a way to process binaries before they are included, by adding a +Python plug-in. Binman is intended for use with U-Boot but is designed to be general enough to be useful in other image-packaging situations. @@ -50,11 +54,11 @@ to be useful in other image-packaging situations. Motivation ---------- -Packaging of firmware is quite a different task from building the various -parts. In many cases the various binaries which go into the image come from -separate build systems. For example, ARM Trusted Firmware is used on ARMv8 -devices but is not built in the U-Boot tree. If a Linux kernel is included -in the firmware image, it is built elsewhere. +As mentioned above, packaging of firmware is quite a different task from +building the various parts. In many cases the various binaries which go into +the image come from separate build systems. For example, ARM Trusted Firmware +is used on ARMv8 devices but is not built in the U-Boot tree. If a Linux kernel +is included in the firmware image, it is built elsewhere. It is of course possible to add more and more build rules to the U-Boot build system to cover these cases. It can shell out to other Makefiles and @@ -215,17 +219,12 @@ Binman has a few other options which you can see by running 'binman -h'. Enabling binman for a board --------------------------- -At present binman is invoked from a rule in the main Makefile. Typically you -will have a rule like:: +At present binman is invoked from a rule in the main Makefile. You should be +able to enable CONFIG_BINMAN to enable this rule. - ifneq ($(CONFIG_ARCH_),) - u-boot-.bin: checkbinman FORCE - $(call if_changed,binman) - endif - -This assumes that u-boot-.bin is a target, and is the final file -that you need to produce. You can make it a target by adding it to INPUTS-y -either in the main Makefile or in a config.mk file in your arch subdirectory. +The output file is typically named image.bin and is located in the output +directory. If input files are needed to you add these to INPUTS-y either in the +main Makefile or in a config.mk file in your arch subdirectory. Once binman is executed it will pick up its instructions from a device-tree file, typically -u-boot.dtsi, where is your CONFIG_SYS_SOC value. @@ -786,12 +785,17 @@ the 'warning' line in scripts/Makefile.lib to see what it has found:: Entry Documentation -------------------- +=================== For details on the various entry types supported by binman and how to use them, -see README.entries. This is generated from the source code using: +see entries.rst which is generated from the source code using: - binman entry-docs >tools/binman/README.entries + binman entry-docs >tools/binman/entries.rst + +.. toctree:: + :maxdepth: 2 + + entries Managing images @@ -1136,7 +1140,8 @@ To do Some ideas: - Use of-platdata to make the information available to code that is unable - to use device tree (such as a very small SPL image) + to use device tree (such as a very small SPL image). For now, limited info is + available via linker symbols - Allow easy building of images by specifying just the board name - Support building an image for a board (-b) more completely, with a configurable build directory