[PATCH 0/2] Fixes for SPI boot on SanCloud BBE Lite

These two commits fix SPI boot on the SanCloud BBE Lite and are likely also required for SPI boot on other am335x boards.
Paul Barker (2): dm: core: Fix iteration over driver_info records configs: am335x_evm: Disable SPL_OF_CONTROL
configs/am335x_boneblack_vboot_defconfig | 1 - configs/am335x_evm_spiboot_defconfig | 1 - drivers/core/lists.c | 17 ++++++----------- 3 files changed, 6 insertions(+), 13 deletions(-)
base-commit: 0e49f5c26caf9972137a474065afd4bdfe5ec062

We should only perform additional iteration steps when needed to initialize the parent of a device. Other binding errors (such as a missing driver) should not lead to additional iteration steps.
Unnecessary iteration steps can cause issues when memory is tightly constrained (such as in the TPL/SPL) since device_bind_by_name() unconditionally allocates memory for a struct udevice. On the SanCloud BBE this led to boot failure caused by memory exhaustion in the SPL when booting from SPI flash.
Signed-off-by: Paul Barker paul.barker@sancloud.com --- drivers/core/lists.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/drivers/core/lists.c b/drivers/core/lists.c index c49695b24f00..82d479564121 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -51,13 +51,13 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id) return NULL; }
-static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) +static bool bind_drivers_pass(struct udevice *parent, bool pre_reloc_only, + int *result) { 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 missing_parent = false; - int result = 0; int idx;
/* @@ -98,12 +98,12 @@ static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) drt->dev = dev; } else if (ret != -EPERM) { dm_warn("No match for driver '%s'\n", entry->name); - if (!result || ret != -ENOENT) - result = ret; + if (!*result || ret != -ENOENT) + *result = ret; } }
- return result ? result : missing_parent ? -EAGAIN : 0; + return missing_parent; }
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) @@ -117,13 +117,8 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) * always succeed on the first pass. */ for (pass = 0; pass < 10; pass++) { - int ret; - - ret = bind_drivers_pass(parent, pre_reloc_only); - if (!ret) + if (!bind_drivers_pass(parent, pre_reloc_only, &result)) break; - if (ret != -EAGAIN && !result) - result = ret; }
return result;

Hi Paul,
On Sat, 15 Oct 2022 at 03:19, Paul Barker paul.barker@sancloud.com wrote:
We should only perform additional iteration steps when needed to initialize the parent of a device. Other binding errors (such as a missing driver) should not lead to additional iteration steps.
Unnecessary iteration steps can cause issues when memory is tightly constrained (such as in the TPL/SPL) since device_bind_by_name() unconditionally allocates memory for a struct udevice. On the SanCloud BBE this led to boot failure caused by memory exhaustion in the SPL when booting from SPI flash.
Signed-off-by: Paul Barker paul.barker@sancloud.com
drivers/core/lists.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-)
Are you able to construct a test for this? See test/dm/core.c or test-fdt.c
Also, I think bind_drivers_pass() needs a function comment that describes in detail what is going on.
Would it be possible to achieve the same effect while keeping that function the same as now? See below.
diff --git a/drivers/core/lists.c b/drivers/core/lists.c index c49695b24f00..82d479564121 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -51,13 +51,13 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id) return NULL; }
-static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) +static bool bind_drivers_pass(struct udevice *parent, bool pre_reloc_only,
int *result)
{ 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 missing_parent = false;
int result = 0; int idx; /*
@@ -98,12 +98,12 @@ static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) drt->dev = dev; } else if (ret != -EPERM) { dm_warn("No match for driver '%s'\n", entry->name);
if (!result || ret != -ENOENT)
result = ret;
if (!*result || ret != -ENOENT)
*result = ret; } }
return result ? result : missing_parent ? -EAGAIN : 0;
return missing_parent;
}
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) @@ -117,13 +117,8 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) * always succeed on the first pass. */ for (pass = 0; pass < 10; pass++) {
int ret;
ret = bind_drivers_pass(parent, pre_reloc_only);
if (!ret)
if (!bind_drivers_pass(parent, pre_reloc_only, &result)) break;
if (ret != -EAGAIN && !result)
result = ret;
If there were something like this, could we drop the other changes?
if (ret) { if (ret == -EAGAIN) continue; else if (!result)
result = ret; }
} return result;
-- 2.25.1

On 15/10/2022 18:53, Simon Glass wrote:
Hi Paul,
On Sat, 15 Oct 2022 at 03:19, Paul Barker paul.barker@sancloud.com wrote:
We should only perform additional iteration steps when needed to initialize the parent of a device. Other binding errors (such as a missing driver) should not lead to additional iteration steps.
Unnecessary iteration steps can cause issues when memory is tightly constrained (such as in the TPL/SPL) since device_bind_by_name() unconditionally allocates memory for a struct udevice. On the SanCloud BBE this led to boot failure caused by memory exhaustion in the SPL when booting from SPI flash.
Signed-off-by: Paul Barker paul.barker@sancloud.com
drivers/core/lists.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-)
Are you able to construct a test for this? See test/dm/core.c or test-fdt.c
I can't see how to construct a test case for this issue. What I observed was the SPL crashing during the pre-relocation lists_bind_drivers() call due to exhaustion of the available memory. On the AM335x SoC this is 64k of on-board SRAM which is available before main DRAM is initialised. I can't see how to reproduce this issue within the u-boot test framework as significantly more memory is available when running the u-boot sandbox and so memory exhaustion does not occur. If there are other side effects than memory leaking here then I'm not familiar enough with the u-boot device model to spot them.
Each call to bind_drivers_pass() calls device_bind_by_name() for every driver_info record, with no way of checking whether a particular driver_info record has already been handled by a previous call to bind_drivers_pass(). This leaks memory as each call to device_bind_by_name() allocates a new device object.
Also, I think bind_drivers_pass() needs a function comment that describes in detail what is going on.
Agreed, I'll see what I can add when I revisit this.
Would it be possible to achieve the same effect while keeping that function the same as now? See below.
diff --git a/drivers/core/lists.c b/drivers/core/lists.c index c49695b24f00..82d479564121 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -51,13 +51,13 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id) return NULL; }
-static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) +static bool bind_drivers_pass(struct udevice *parent, bool pre_reloc_only,
int *result)
{ 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 missing_parent = false;
int result = 0; int idx; /*
@@ -98,12 +98,12 @@ static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) drt->dev = dev; } else if (ret != -EPERM) { dm_warn("No match for driver '%s'\n", entry->name);
if (!result || ret != -ENOENT)
result = ret;
if (!*result || ret != -ENOENT)
*result = ret; } }
return result ? result : missing_parent ? -EAGAIN : 0;
return missing_parent;
}
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) @@ -117,13 +117,8 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) * always succeed on the first pass. */ for (pass = 0; pass < 10; pass++) {
int ret;
ret = bind_drivers_pass(parent, pre_reloc_only);
if (!ret)
if (!bind_drivers_pass(parent, pre_reloc_only, &result)) break;
if (ret != -EAGAIN && !result)
result = ret;
If there were something like this, could we drop the other changes?
if (ret) { if (ret == -EAGAIN) continue; else if (!result)
result = ret;
}
I don't think this works exactly, as we want to break when (ret == -EAGAIN) instead of continue. However I have a variant which I think is working which I can send as a v2.
} return result;
-- 2.25.1

When CONFIG_SPL_OF_CONFIG is enabled on the BeagleBone Black and the SanCloud BBE, initialization of the I2C bus in ti_i2c_eeprom_get() fails. This prevents us from determining the board variant in enable_board_pin_mux(). As pinmux settings are not correctly configured this leads to boot failure.
Signed-off-by: Paul Barker paul.barker@sancloud.com --- configs/am335x_boneblack_vboot_defconfig | 1 - configs/am335x_evm_spiboot_defconfig | 1 - 2 files changed, 2 deletions(-)
diff --git a/configs/am335x_boneblack_vboot_defconfig b/configs/am335x_boneblack_vboot_defconfig index 66b7fb6e06d8..bac0477b1d7c 100644 --- a/configs/am335x_boneblack_vboot_defconfig +++ b/configs/am335x_boneblack_vboot_defconfig @@ -43,7 +43,6 @@ CONFIG_SYS_I2C_EEPROM_ADDR_LEN=2 # CONFIG_CMD_SETEXPR is not set CONFIG_BOOTP_DNS2=y CONFIG_OF_CONTROL=y -CONFIG_SPL_OF_CONTROL=y CONFIG_ENV_OVERWRITE=y CONFIG_ENV_IS_IN_MMC=y CONFIG_SYS_REDUNDAND_ENVIRONMENT=y diff --git a/configs/am335x_evm_spiboot_defconfig b/configs/am335x_evm_spiboot_defconfig index 3d04e6fa934a..99df51f0270a 100644 --- a/configs/am335x_evm_spiboot_defconfig +++ b/configs/am335x_evm_spiboot_defconfig @@ -42,7 +42,6 @@ CONFIG_BOOTP_DNS2=y CONFIG_CMD_MTDPARTS=y # CONFIG_SPL_EFI_PARTITION is not set CONFIG_OF_CONTROL=y -CONFIG_SPL_OF_CONTROL=y CONFIG_OF_LIST="am335x-evm am335x-bone am335x-boneblack am335x-evmsk am335x-bonegreen am335x-icev2 am335x-pocketbeagle" CONFIG_ENV_OVERWRITE=y # CONFIG_ENV_IS_IN_FAT is not set

Hi Paul,
On Sat, 15 Oct 2022 at 03:19, Paul Barker paul.barker@sancloud.com wrote:
When CONFIG_SPL_OF_CONFIG is enabled on the BeagleBone Black and the SanCloud BBE, initialization of the I2C bus in ti_i2c_eeprom_get() fails. This prevents us from determining the board variant in enable_board_pin_mux(). As pinmux settings are not correctly configured this leads to boot failure.
Signed-off-by: Paul Barker paul.barker@sancloud.com
configs/am335x_boneblack_vboot_defconfig | 1 - configs/am335x_evm_spiboot_defconfig | 1 - 2 files changed, 2 deletions(-)
Instead I think you should fix the bug, perhaps in the i2c eeprom code?
Regards, SImon
participants (2)
-
Paul Barker
-
Simon Glass