Merge patch series "Convert extension support to UCLASS and adds its support to boot flows"

Kory Maincent (TI.com) <kory.maincent@bootlin.com> says:

This series converts the extension board framework to use UCLASS as
requested by Simon Glass, then adds extension support to pxe_utils
and bootmeth_efi (not tested) to enable extension boards devicetree load
in the standard boot process.

I can't test the imx8 extension scan enabled by the
imx8mm-cl-iot-gate_defconfig as I don't have this board.
I also can't test the efi bootmeth change as I don't have such board.

Link: https://lore.kernel.org/r/20251030-feature_sysboot_extension_board-v5-0-cfb77672fc68@bootlin.com
This commit is contained in:
Tom Rini
2025-11-03 10:12:05 -06:00
22 changed files with 569 additions and 262 deletions
+9
View File
@@ -1185,6 +1185,15 @@ F: scripts/event_dump.py
F: test/common/event.c
F: test/py/tests/test_event_dump.py
EXTENSION
M: Kory Maincent <kory.maincent@bootlin.com>
S: Maintained
F: board/sunxi/chip.c
F: board/ti/common/cape_detect.c
F: boot/extension-uclass.c
F: cmd/extension_board.c
F: include/extension_board.h
FASTBOOT
M: Mattijs Korpershoek <mkorpershoek@kernel.org>
S: Maintained
@@ -249,6 +249,8 @@ int board_mmc_get_env_dev(int devno)
#define IOT_GATE_IMX8_EXT_I2C_ADDR_GPIO 0x22 /* I2C address of the GPIO
extender */
#if !IS_ENABLED(CONFIG_XPL_BUILD)
static int iot_gate_imx8_ext_id = IOT_GATE_EXT_EMPTY; /* Extension board ID */
static int iot_gate_imx8_ext_ied_id [3] = {
IOT_GATE_IMX8_CARD_ID_EMPTY,
@@ -434,9 +436,10 @@ static int iot_gate_imx8_update_ext_ied(void)
return 0;
}
int extension_board_scan(struct list_head *extension_list)
static int iot_gate_imx8_extension_board_scan(struct udevice *dev,
struct alist *extension_list)
{
struct extension *extension = NULL;
struct extension extension = {0};
int i;
int ret = 0;
@@ -446,25 +449,21 @@ int extension_board_scan(struct list_head *extension_list)
case IOT_GATE_EXT_EMPTY:
break;
case IOT_GATE_EXT_CAN:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_EXT_CAN");
break;
case IOT_GATE_EXT_IED:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_EXT_IED");
snprintf(extension->overlay, sizeof(extension->overlay),
snprintf(extension.overlay, sizeof(extension.overlay),
"imx8mm-cl-iot-gate-ied.dtbo");
break;
case IOT_GATE_EXT_POE:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_EXT_POE");
break;
case IOT_GATE_EXT_POEV2:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_EXT_POEV2");
break;
default:
@@ -472,10 +471,11 @@ int extension_board_scan(struct list_head *extension_list)
break;
}
if (extension) {
snprintf(extension->owner, sizeof(extension->owner),
if (extension.name[0]) {
snprintf(extension.owner, sizeof(extension.owner),
"Compulab");
list_add_tail(&extension->list, extension_list);
if (!alist_add(extension_list, extension))
return -ENOMEM;
ret = 1;
} else
return ret;
@@ -484,44 +484,38 @@ int extension_board_scan(struct list_head *extension_list)
iot_gate_imx8_update_ext_ied();
for (i=0; i<ARRAY_SIZE(iot_gate_imx8_ext_ied_id); i++) {
extension = NULL;
memset(&extension, 0, sizeof(extension));
switch (iot_gate_imx8_ext_ied_id[i]) {
case IOT_GATE_IMX8_CARD_ID_EMPTY:
break;
case IOT_GATE_IMX8_CARD_ID_RS_485:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_RS_485");
break;
case IOT_GATE_IMX8_CARD_ID_RS_232:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_RS_232");
break;
case IOT_GATE_IMX8_CARD_ID_CAN:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_CAN");
snprintf(extension->overlay, sizeof(extension->overlay),
snprintf(extension.overlay, sizeof(extension.overlay),
"imx8mm-cl-iot-gate-ied-can%d.dtbo", i);
break;
case IOT_GATE_IMX8_CARD_ID_TPM:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_TPM");
snprintf(extension->overlay, sizeof(extension->overlay),
snprintf(extension.overlay, sizeof(extension.overlay),
"imx8mm-cl-iot-gate-ied-tpm%d.dtbo", i);
break;
case IOT_GATE_IMX8_CARD_ID_CL420:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_CL420");
snprintf(extension->overlay, sizeof(extension->overlay),
snprintf(extension.overlay, sizeof(extension.overlay),
"imx8mm-cl-iot-gate-ied-can%d.dtbo", i);
break;
case IOT_GATE_IMX8_CARD_ID_DI4O4:
extension = calloc(1, sizeof(struct extension));
snprintf(extension->name, sizeof(extension->name),
snprintf(extension.name, sizeof(extension.name),
"IOT_GATE_IMX8_CARD_ID_DI4O4");
break;
default:
@@ -529,12 +523,13 @@ int extension_board_scan(struct list_head *extension_list)
__func__, i, iot_gate_imx8_ext_ied_id[i]);
break;
}
if (extension) {
snprintf(extension->owner, sizeof(extension->owner),
if (extension.name[0]) {
snprintf(extension.owner, sizeof(extension.owner),
"Compulab");
snprintf(extension->other, sizeof(extension->other),
snprintf(extension.other, sizeof(extension.other),
"On slot %d", i);
list_add_tail(&extension->list, extension_list);
if (!alist_add(extension_list, extension))
return -ENOMEM;
ret = ret + 1;
}
}
@@ -542,6 +537,13 @@ int extension_board_scan(struct list_head *extension_list)
return ret;
}
U_BOOT_EXTENSION(iot_gate_imx8_extension, iot_gate_imx8_extension_board_scan);
U_BOOT_DRVINFO(iot_gate_imx8_extension) = {
.name = "iot_gate_imx8_extension",
};
#endif
static int setup_mac_address(void)
{
unsigned char enetaddr[6];
+19 -10
View File
@@ -110,24 +110,33 @@ int ft_board_setup(void *fdt, struct bd_info *bd)
return fdt_add_mem_rsv(fdt, 0x00d02000, 0x4000);
}
#ifdef CONFIG_CMD_EXTENSION
int extension_board_scan(struct list_head *extension_list)
#if CONFIG_IS_ENABLED(SUPPORT_EXTENSION_SCAN) && \
!CONFIG_IS_ENABLED(XPL_BUILD)
static int sandbox_extension_board_scan(struct udevice *dev,
struct alist *extension_list)
{
struct extension *extension;
int i;
for (i = 0; i < 2; i++) {
extension = calloc(1, sizeof(struct extension));
snprintf(extension->overlay, sizeof(extension->overlay), "overlay%d.dtbo", i);
snprintf(extension->name, sizeof(extension->name), "extension board %d", i);
snprintf(extension->owner, sizeof(extension->owner), "sandbox");
snprintf(extension->version, sizeof(extension->version), "1.1");
snprintf(extension->other, sizeof(extension->other), "Fictional extension board");
list_add_tail(&extension->list, extension_list);
struct extension extension = {0};
snprintf(extension.overlay, sizeof(extension.overlay), "overlay%d.dtbo", i);
snprintf(extension.name, sizeof(extension.name), "extension board %d", i);
snprintf(extension.owner, sizeof(extension.owner), "sandbox");
snprintf(extension.version, sizeof(extension.version), "1.1");
snprintf(extension.other, sizeof(extension.other), "Fictional extension board");
if (!alist_add(extension_list, extension))
return -ENOMEM;
}
return i;
}
U_BOOT_EXTENSION(sandbox_extension, sandbox_extension_board_scan);
U_BOOT_DRVINFO(sandbox_extension) = {
.name = "sandbox_extension",
};
#endif
#ifdef CONFIG_BOARD_LATE_INIT
+1 -1
View File
@@ -11,4 +11,4 @@ obj-$(CONFIG_SUN7I_GMAC) += gmac.o
obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o
obj-$(CONFIG_MACH_SUN7I) += dram_sun5i_auto.o
obj-$(CONFIG_CHIP_DIP_SCAN) += chip.o
obj-$(CONFIG_$(PHASE_)CHIP_DIP_SCAN) += chip.o
+19 -17
View File
@@ -40,16 +40,12 @@ struct dip_w1_header {
u8 data[16]; /* user data, per-dip specific */
} __packed;
int extension_board_scan(struct list_head *extension_list)
static int sunxi_extension_board_scan(struct udevice *udev,
struct alist *extension_list)
{
struct extension *dip;
struct dip_w1_header w1_header;
struct udevice *bus, *dev;
u32 vid;
u16 pid;
int ret;
int num_dip = 0;
int ret;
sunxi_gpio_set_pull(SUNXI_GPD(2), SUNXI_GPIO_PULL_UP);
@@ -60,6 +56,11 @@ int extension_board_scan(struct list_head *extension_list)
}
for_each_w1_device(bus, &dev) {
struct extension dip = {0};
struct dip_w1_header w1_header;
u32 vid;
u16 pid;
if (w1_get_device_family(dev) != W1_FAMILY_DS2431)
continue;
@@ -82,18 +83,19 @@ int extension_board_scan(struct list_head *extension_list)
w1_header.product_name, pid,
w1_header.vendor_name, vid);
dip = calloc(1, sizeof(struct extension));
if (!dip) {
printf("Error in memory allocation\n");
return num_dip;
}
snprintf(dip->overlay, sizeof(dip->overlay), "dip-%x-%x.dtbo",
snprintf(dip.overlay, sizeof(dip.overlay), "dip-%x-%x.dtbo",
vid, pid);
strncpy(dip->name, w1_header.product_name, 32);
strncpy(dip->owner, w1_header.vendor_name, 32);
list_add_tail(&dip->list, extension_list);
strlcpy(dip.name, w1_header.product_name, sizeof(dip.name));
strlcpy(dip.owner, w1_header.vendor_name, sizeof(dip.owner));
if (!alist_add(extension_list, dip))
return -ENOMEM;
num_dip++;
}
return num_dip;
}
U_BOOT_EXTENSION(dip, sunxi_extension_board_scan);
U_BOOT_DRVINFO(dip) = {
.name = "dip",
};
+1 -1
View File
@@ -20,7 +20,7 @@ config CAPE_EEPROM_BUS_NUM
int "Cape EEPROM's I2C bus address"
range 0 8
default 2
depends on CMD_EXTENSION
depends on SUPPORT_EXTENSION_SCAN
config TI_COMMON_CMD_OPTIONS
bool "Enable cmd options on TI platforms"
+1 -1
View File
@@ -2,6 +2,6 @@
# Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
obj-${CONFIG_TI_I2C_BOARD_DETECT} += board_detect.o
obj-${CONFIG_CMD_EXTENSION} += cape_detect.o
obj-${CONFIG_$(PHASE_)SUPPORT_EXTENSION_SCAN} += cape_detect.o
obj-${CONFIG_OF_LIBFDT} += fdt_ops.o
obj-${CONFIG_ARCH_K3} += k3-ddr.o
+30 -26
View File
@@ -22,21 +22,21 @@ static void sanitize_field(char *text, size_t size)
}
}
int extension_board_scan(struct list_head *extension_list)
static int ti_extension_board_scan(struct udevice *dev,
struct alist *extension_list)
{
struct extension *cape;
struct am335x_cape_eeprom_id eeprom_header;
int num_capes = 0;
int ret, i;
struct udevice *dev;
unsigned char addr;
char process_cape_part_number[17] = {'0'};
char process_cape_version[5] = {'0'};
uint8_t cursor = 0;
int num_capes = 0;
for (addr = CAPE_EEPROM_FIRST_ADDR; addr <= CAPE_EEPROM_LAST_ADDR; addr++) {
struct am335x_cape_eeprom_id eeprom_header;
char process_cape_part_number[17] = {'0'};
char process_cape_version[5] = {'0'};
struct extension cape = {0};
struct udevice *dev;
u8 cursor = 0;
int ret, i;
ret = i2c_get_chip_for_busnum(CONFIG_CAPE_EEPROM_BUS_NUM, addr, 1, &dev);
if (ret)
continue;
@@ -59,8 +59,8 @@ int extension_board_scan(struct list_head *extension_list)
sanitize_field(eeprom_header.part_number, sizeof(eeprom_header.part_number));
/* Process cape part_number */
memset(process_cape_part_number, 0, sizeof(process_cape_part_number));
strncpy(process_cape_part_number, eeprom_header.part_number, 16);
strlcpy(process_cape_part_number, eeprom_header.part_number,
sizeof(process_cape_part_number));
/* Some capes end with '.' */
for (i = 15; i >= 0; i--) {
if (process_cape_part_number[i] == '.')
@@ -70,8 +70,8 @@ int extension_board_scan(struct list_head *extension_list)
}
/* Process cape version */
memset(process_cape_version, 0, sizeof(process_cape_version));
strncpy(process_cape_version, eeprom_header.version, 4);
strlcpy(process_cape_version, eeprom_header.version,
sizeof(process_cape_version));
for (i = 0; i < 4; i++) {
if (process_cape_version[i] == 0)
process_cape_version[i] = '0';
@@ -79,19 +79,23 @@ int extension_board_scan(struct list_head *extension_list)
printf("BeagleBone Cape: %s (0x%x)\n", eeprom_header.board_name, addr);
cape = calloc(1, sizeof(struct extension));
if (!cape) {
printf("Error in memory allocation\n");
return num_capes;
}
snprintf(cape->overlay, sizeof(cape->overlay), "%s-%s.dtbo",
snprintf(cape.overlay, sizeof(cape.overlay), "%s-%s.dtbo",
process_cape_part_number, process_cape_version);
strncpy(cape->name, eeprom_header.board_name, 32);
strncpy(cape->version, process_cape_version, 4);
strncpy(cape->owner, eeprom_header.manufacturer, 16);
list_add_tail(&cape->list, extension_list);
strlcpy(cape.name, eeprom_header.board_name,
sizeof(eeprom_header.board_name));
strlcpy(cape.version, process_cape_version,
sizeof(process_cape_version));
strlcpy(cape.owner, eeprom_header.manufacturer,
sizeof(eeprom_header.manufacturer) + 1);
if (!alist_add(extension_list, cape))
return -ENOMEM;
num_capes++;
}
return num_capes;
}
U_BOOT_EXTENSION(cape, ti_extension_board_scan);
U_BOOT_DRVINFO(cape) = {
.name = "cape",
};
+4
View File
@@ -1909,6 +1909,10 @@ endmenu
endif # OF_LIBFDT
config SUPPORT_EXTENSION_SCAN
select OF_LIBFDT_OVERLAY
bool
config USE_BOOTARGS
bool "Enable boot arguments"
help
+1
View File
@@ -9,6 +9,7 @@ obj-$(CONFIG_BOOT_RETRY) += bootretry.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o bootm_os.o
obj-$(CONFIG_CMD_BOOTZ) += bootm.o bootm_os.o
obj-$(CONFIG_CMD_BOOTI) += bootm.o bootm_os.o
obj-$(CONFIG_SUPPORT_EXTENSION_SCAN) += extension-uclass.o
obj-$(CONFIG_PXE_UTILS) += pxe_utils.o
+63 -16
View File
@@ -16,6 +16,7 @@
#include <efi.h>
#include <efi_loader.h>
#include <env.h>
#include <extension_board.h>
#include <fs.h>
#include <malloc.h>
#include <mapmem.h>
@@ -99,8 +100,11 @@ static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
static int distro_efi_try_bootflow_files(struct udevice *dev,
struct bootflow *bflow)
{
ulong fdt_addr, size, overlay_addr;
const struct extension *extension;
struct fdt_header *working_fdt;
struct blk_desc *desc = NULL;
ulong fdt_addr, size;
struct alist *extension_list;
char fname[256];
int ret, seq;
@@ -148,23 +152,66 @@ static int distro_efi_try_bootflow_files(struct udevice *dev,
return log_msg_ret("fil", -ENOMEM);
}
if (!ret) {
bflow->fdt_size = size;
bflow->fdt_addr = fdt_addr;
/*
* TODO: Apply extension overlay
*
* Here we need to load and apply the extension overlay. This is
* not implemented. See do_extension_apply(). The extension
* stuff needs an implementation in boot/extension.c so it is
* separate from the command code. Really the extension stuff
* should use the device tree and a uclass / driver interface
* rather than implementing its own list
*/
} else {
if (ret) {
log_debug("No device tree available\n");
bflow->flags |= BOOTFLOWF_USE_BUILTIN_FDT;
return 0;
}
bflow->fdt_size = size;
bflow->fdt_addr = fdt_addr;
if (!CONFIG_IS_ENABLED(SUPPORT_EXTENSION_SCAN))
return 0;
ret = extension_scan();
if (ret < 0)
return 0;
extension_list = extension_get_list();
if (!extension_list)
return 0;
working_fdt = map_sysmem(fdt_addr, 0);
if (fdt_check_header(working_fdt))
return 0;
overlay_addr = env_get_hex("extension_overlay_addr", 0);
if (!overlay_addr) {
log_debug("Environment extension_overlay_addr is missing\n");
return 0;
}
alist_for_each(extension, extension_list) {
char *overlay_file;
int len;
len = sizeof(EFI_DIRNAME) + strlen(extension->overlay);
overlay_file = calloc(1, len);
if (!overlay_file)
return -ENOMEM;
snprintf(overlay_file, len, "%s%s", EFI_DIRNAME,
extension->overlay);
ret = bootmeth_common_read_file(dev, bflow, overlay_file,
overlay_addr,
(enum bootflow_img_t)IH_TYPE_FLATDT,
&size);
if (ret) {
log_debug("Failed loading overlay %s\n", overlay_file);
free(overlay_file);
continue;
}
ret = extension_apply(working_fdt, size);
if (ret) {
log_debug("Failed applying overlay %s\n", overlay_file);
free(overlay_file);
continue;
}
bflow->fdt_size += size;
free(overlay_file);
}
return 0;
+93
View File
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2025 Köry Maincent <kory.maincent@bootlin.com>
*/
#include <alist.h>
#include <command.h>
#include <env.h>
#include <extension_board.h>
#include <fdt_support.h>
#include <malloc.h>
#include <mapmem.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass.h>
struct alist *extension_get_list(void)
{
struct udevice *dev;
if (uclass_first_device_err(UCLASS_EXTENSION, &dev))
return NULL;
return dev_get_priv(dev);
}
int extension_probe(struct udevice *dev)
{
struct alist *extension_list = dev_get_priv(dev);
alist_init_struct(extension_list, struct extension);
return 0;
}
int extension_remove(struct udevice *dev)
{
struct alist *extension_list = dev_get_priv(dev);
alist_uninit(extension_list);
return 0;
}
int extension_scan(void)
{
struct alist *extension_list = extension_get_list();
const struct extension_ops *ops;
struct udevice *dev;
int ret;
ret = uclass_first_device_err(UCLASS_EXTENSION, &dev);
if (ret)
return ret;
if (!extension_list)
return -ENODEV;
ops = extension_get_ops(dev);
alist_empty(extension_list);
return ops->scan(dev, extension_list);
}
int extension_apply(struct fdt_header *working_fdt, ulong size)
{
struct fdt_header *blob;
ulong overlay_addr;
int ret;
overlay_addr = env_get_hex("extension_overlay_addr", 0);
if (!overlay_addr) {
printf("Environment extension_overlay_addr is missing\n");
return -EINVAL;
}
fdt_shrink_to_minimum(working_fdt, size);
blob = map_sysmem(overlay_addr, 0);
if (!fdt_valid(&blob)) {
printf("Invalid overlay devicetree\n");
return -EINVAL;
}
/* Apply method prints messages on error */
ret = fdt_overlay_apply_verbose(working_fdt, blob);
if (ret)
printf("Failed to apply overlay\n");
return ret;
}
UCLASS_DRIVER(extension) = {
.name = "extension",
.id = UCLASS_EXTENSION,
};
+92
View File
@@ -10,6 +10,7 @@
#include <command.h>
#include <dm.h>
#include <env.h>
#include <extension_board.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
@@ -432,6 +433,95 @@ skip_overlay:
}
#endif
/*
* label_boot_extension - scan extension boards and load overlay associated
*/
static void label_boot_extension(struct pxe_context *ctx,
struct pxe_label *label)
{
#if CONFIG_IS_ENABLED(SUPPORT_EXTENSION_SCAN)
const struct extension *extension;
struct fdt_header *working_fdt;
struct alist *extension_list;
int ret, dir_len, len;
char *overlay_dir;
const char *slash;
ulong fdt_addr;
ret = extension_scan();
if (ret < 0)
return;
extension_list = extension_get_list();
if (!extension_list)
return;
/* Get the main fdt and map it */
fdt_addr = env_get_hex("fdt_addr_r", 0);
working_fdt = map_sysmem(fdt_addr, 0);
if (fdt_check_header(working_fdt))
return;
/* Use fdtdir for now as the overlay devicetree directory */
if (label->fdtdir) {
len = strlen(label->fdtdir);
if (!len)
slash = "./";
else if (label->fdtdir[len - 1] != '/')
slash = "/";
else
slash = "";
dir_len = strlen(label->fdtdir) + strlen(slash) + 1;
overlay_dir = calloc(1, len);
if (!overlay_dir)
return;
snprintf(overlay_dir, dir_len, "%s%s", label->fdtdir,
slash);
} else {
dir_len = 2;
snprintf(overlay_dir, dir_len, "/");
}
alist_for_each(extension, extension_list) {
char *overlay_file;
ulong size;
len = dir_len + strlen(extension->overlay);
overlay_file = calloc(1, len);
if (!overlay_file)
goto cleanup;
snprintf(overlay_file, len, "%s%s", overlay_dir,
extension->overlay);
/* Load extension overlay file */
ret = get_relfile_envaddr(ctx, overlay_file,
"extension_overlay_addr",
(enum bootflow_img_t)IH_TYPE_FLATDT,
&size);
if (ret < 0) {
printf("Failed loading overlay %s\n", overlay_file);
free(overlay_file);
continue;
}
ret = extension_apply(working_fdt, size);
if (ret) {
printf("Failed applying overlay %s\n", overlay_file);
free(overlay_file);
continue;
}
free(overlay_file);
}
cleanup:
free(overlay_dir);
#endif
}
/**
* label_boot() - Boot according to the contents of a pxe_label
*
@@ -685,6 +775,8 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label)
if (label->fdtoverlays)
label_boot_fdtoverlay(ctx, label);
#endif
label_boot_extension(ctx, label);
} else {
bootm_argv[3] = NULL;
}
-3
View File
@@ -545,9 +545,6 @@ config CMD_FDT
help
Do FDT related setup before booting into the Operating System.
config SUPPORT_EXTENSION_SCAN
bool
config CMD_EXTENSION
bool "Extension board management command"
select CMD_FDT
+87 -104
View File
@@ -4,75 +4,107 @@
* Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
*/
#include <bootdev.h>
#include <alist.h>
#include <exports.h>
#include <command.h>
#include <dm.h>
#include <env.h>
#include <malloc.h>
#include <extension_board.h>
#include <mapmem.h>
#include <linux/libfdt.h>
#include <fdt_support.h>
static LIST_HEAD(extension_list);
static int extension_apply(struct extension *extension)
static int
cmd_extension_load_overlay_from_env(const struct extension *extension,
ulong *filesize)
{
ulong size, overlay_addr;
char *overlay_cmd;
ulong extrasize, overlay_addr;
struct fdt_header *blob;
if (!working_fdt) {
printf("No FDT memory address configured. Please configure\n"
"the FDT address via \"fdt addr <address>\" command.\n");
return CMD_RET_FAILURE;
}
int ret;
overlay_cmd = env_get("extension_overlay_cmd");
if (!overlay_cmd) {
printf("Environment extension_overlay_cmd is missing\n");
return CMD_RET_FAILURE;
return -EINVAL;
}
overlay_addr = env_get_hex("extension_overlay_addr", 0);
if (!overlay_addr) {
printf("Environment extension_overlay_addr is missing\n");
return CMD_RET_FAILURE;
return -EINVAL;
}
env_set("extension_overlay_name", extension->overlay);
if (run_command(overlay_cmd, 0) != 0)
return CMD_RET_FAILURE;
ret = run_command(overlay_cmd, 0);
if (ret)
return ret;
extrasize = env_get_hex("filesize", 0);
if (!extrasize)
return CMD_RET_FAILURE;
size = env_get_hex("filesize", 0);
if (!size)
return -EINVAL;
fdt_shrink_to_minimum(working_fdt, extrasize);
*filesize = size;
return 0;
}
blob = map_sysmem(overlay_addr, 0);
if (!fdt_valid(&blob))
return CMD_RET_FAILURE;
static int cmd_extension_apply(int extension_num)
{
struct alist *extension_list = extension_get_list();
const struct extension *extension;
ulong size;
int ret;
/* apply method prints messages on error */
if (fdt_overlay_apply_verbose(working_fdt, blob))
return CMD_RET_FAILURE;
if (!extension_list)
return -ENODEV;
return CMD_RET_SUCCESS;
extension = alist_get(extension_list, extension_num,
struct extension);
if (!extension) {
printf("Wrong extension number\n");
return -ENODEV;
}
ret = cmd_extension_load_overlay_from_env(extension, &size);
if (ret)
return ret;
return extension_apply(working_fdt, size);
}
static int cmd_extension_apply_all(void)
{
struct alist *extension_list = extension_get_list();
const struct extension *extension;
int ret;
if (!extension_list)
return -ENODEV;
alist_for_each(extension, extension_list) {
ulong size;
ret = cmd_extension_load_overlay_from_env(extension, &size);
if (ret)
return ret;
ret = extension_apply(working_fdt, size);
if (ret)
return ret;
}
return 0;
}
static int do_extension_list(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
{
int i = 0;
struct alist *extension_list;
struct extension *extension;
int i = 0;
if (list_empty(&extension_list)) {
extension_list = extension_get_list();
if (!alist_get_ptr(extension_list, 0)) {
printf("No extension registered - Please run \"extension scan\"\n");
return CMD_RET_SUCCESS;
}
list_for_each_entry(extension, &extension_list, list) {
alist_for_each(extension, extension_list) {
printf("Extension %d: %s\n", i++, extension->name);
printf("\tManufacturer: \t\t%s\n", extension->owner);
printf("\tVersion: \t\t%s\n", extension->version);
@@ -82,71 +114,45 @@ static int do_extension_list(struct cmd_tbl *cmdtp, int flag,
return CMD_RET_SUCCESS;
}
static int extension_scan(bool show)
{
struct extension *extension, *next;
int extension_num;
list_for_each_entry_safe(extension, next, &extension_list, list) {
list_del(&extension->list);
free(extension);
}
extension_num = extension_board_scan(&extension_list);
if (show && extension_num >= 0)
printf("Found %d extension board(s).\n", extension_num);
/* either the number of extensions, or -ve for error */
return extension_num;
}
static int do_extension_scan(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
{
int extension_num;
extension_num = extension_scan(true);
if (extension_num < 0)
extension_num = extension_scan();
if (extension_num == -ENODEV)
extension_num = 0;
else if (extension_num < 0)
return CMD_RET_FAILURE;
printf("Found %d extension board(s).\n", extension_num);
return CMD_RET_SUCCESS;
}
static int do_extension_apply(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
{
struct extension *extension = NULL;
struct list_head *entry;
int i = 0, extension_id, ret;
int extension_id;
if (argc < 2)
return CMD_RET_USAGE;
if (strcmp(argv[1], "all") == 0) {
ret = CMD_RET_FAILURE;
list_for_each_entry(extension, &extension_list, list) {
ret = extension_apply(extension);
if (ret != CMD_RET_SUCCESS)
break;
}
} else {
extension_id = simple_strtol(argv[1], NULL, 10);
list_for_each(entry, &extension_list) {
if (i == extension_id) {
extension = list_entry(entry, struct extension, list);
break;
}
i++;
}
if (!extension) {
printf("Wrong extension number\n");
return CMD_RET_FAILURE;
}
ret = extension_apply(extension);
if (!working_fdt) {
printf("No FDT memory address configured. Please configure\n"
"the FDT address via \"fdt addr <address>\" command.\n");
return -EINVAL;
}
return ret;
if (strcmp(argv[1], "all") == 0) {
if (cmd_extension_apply_all())
return CMD_RET_FAILURE;
} else {
extension_id = simple_strtol(argv[1], NULL, 10);
if (cmd_extension_apply(extension_id))
return CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
static struct cmd_tbl cmd_extension[] = {
@@ -177,26 +183,3 @@ U_BOOT_CMD(extension, 3, 1, do_extensionops,
"extension list - lists available extension(s) board(s)\n"
"extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n"
);
static int extension_bootdev_hunt(struct bootdev_hunter *info, bool show)
{
int ret;
ret = env_set_hex("extension_overlay_addr",
env_get_hex("fdtoverlay_addr_r", 0));
if (ret)
return log_msg_ret("env", ret);
ret = extension_scan(show);
if (ret < 0)
return log_msg_ret("ext", ret);
return 0;
}
/* extensions should have a uclass - for now we use UCLASS_SIMPLE_BUS uclass */
BOOTDEV_HUNTER(extension_bootdev_hunter) = {
.prio = BOOTDEVP_1_PRE_SCAN,
.uclass = UCLASS_SIMPLE_BUS,
.hunt = extension_bootdev_hunt,
};
+17 -12
View File
@@ -25,9 +25,8 @@ Device Tree overlays depending on the detected extension boards.
The "extension" command comes with three sub-commands:
- "extension scan" makes the generic code call the board-specific
extension_board_scan() function to retrieve the list of detected
extension boards.
- "extension scan" makes the generic code call a board-specific extension
function to retrieve the list of detected extension boards.
- "extension list" allows to list the detected extension boards.
@@ -98,17 +97,23 @@ Simple extension_board_scan function example
.. code-block:: c
int extension_board_scan(struct list_head *extension_list)
static int foo_extension_board_scan(struct alist *extension_list)
{
struct extension *extension;
struct extension extension = {0};
extension = calloc(1, sizeof(struct extension));
snprintf(extension->overlay, sizeof(extension->overlay), "overlay.dtbo");
snprintf(extension->name, sizeof(extension->name), "extension board");
snprintf(extension->owner, sizeof(extension->owner), "sandbox");
snprintf(extension->version, sizeof(extension->version), "1.1");
snprintf(extension->other, sizeof(extension->other), "Extension board information");
list_add_tail(&extension->list, extension_list);
snprintf(extension.overlay, sizeof(extension.overlay), "overlay.dtbo");
snprintf(extension.name, sizeof(extension.name), "extension board");
snprintf(extension.owner, sizeof(extension.owner), "sandbox");
snprintf(extension.version, sizeof(extension.version), "1.1");
snprintf(extension.other, sizeof(extension.other), "Extension board information");
if (!alist_add(extension_list, extension))
return -ENOMEM;
return 1;
}
U_BOOT_EXTENSION(foo_extension_name, foo_extension_board_scan);
U_BOOT_DRVINFO(foo_extension_name) = {
.name = "foo_extension_name",
};
+4
View File
@@ -103,6 +103,10 @@ Environment
``fdt_addr_r``. Required to use the ``fdtoverlays`` command in
the PXE file.
``extension_overlay_addr``
Location in RAM to temporarily store extension fdt overlay(s)
before applying them to the fdt blob stored at ``fdt_addr_r``.
``pxe_label_override``
Override label to be used, if exists, instead of the default
label. This will allow consumers to choose a pxe label at
+1
View File
@@ -63,6 +63,7 @@ enum uclass_id {
UCLASS_ETH, /* Ethernet device */
UCLASS_ETH_PHY, /* Ethernet PHY device */
UCLASS_EXTCON, /* External Connector Class */
UCLASS_EXTENSION, /* Extension board */
UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */
UCLASS_FFA_EMUL, /* sandbox FF-A device emulator */
UCLASS_FIRMWARE, /* Firmware */
+74 -12
View File
@@ -7,10 +7,58 @@
#ifndef __EXTENSION_SUPPORT_H
#define __EXTENSION_SUPPORT_H
#include <alist.h>
#include <dm/device.h>
#include <linux/list.h>
#include <dm/platdata.h>
extern struct list_head extension_list;
/**
* extension_get_list - Get the extension list
* Return: The extension alist pointer, or NULL if no such list exists.
*
* The caller must not free the list.
*/
struct alist *extension_get_list(void);
/**
* extension_probe - Probe extension device
* @dev: Extension device that needs to be probed
* Return: Zero on success, negative on failure.
*/
int extension_probe(struct udevice *dev);
/**
* extension_remove - Remove extension device
* @dev: Extension device that needs to be removed
* Return: Zero on success, negative on failure.
*/
int extension_remove(struct udevice *dev);
/**
* extension_scan - Scan extension boards available.
* Return: Zero on success, negative on failure.
*/
int extension_scan(void);
/**
* extension_apply - Apply extension board overlay to the devicetree
* @working_fdt: Pointer to working flattened device tree
* @size: Size of the devicetree overlay
* Return: Zero on success, negative on failure.
*/
int extension_apply(struct fdt_header *working_fdt, ulong size);
/**
* extension - Description fields of an extension board
* @name: Name of the extension
* @owner: Owner of the extension
* @version: Version of the extension
* @overlay: Devicetree overlay name to be loaded for this extension
* @other: Other information of this extension
*/
struct extension {
struct list_head list;
char name[32];
char owner[32];
char version[32];
@@ -18,16 +66,30 @@ struct extension {
char other[32];
};
/**
* extension_board_scan - Add system-specific function to scan extension board.
* @param extension_list List of extension board information to update.
* Return: the number of extension.
*
* This function is called if CONFIG_CMD_EXTENSION is defined.
* Needs to fill the list extension_list with elements.
* Each element need to be allocated to an extension structure.
*
*/
int extension_board_scan(struct list_head *extension_list);
struct extension_ops {
/**
* scan - Add system-specific function to scan extension boards.
* @dev: extension device
* @extension_list: alist of extension to expand
* Return: The number of extension or a negative value in case of
* error.
*/
int (*scan)(struct udevice *dev, struct alist *extension_list);
};
#define extension_get_ops(dev) ((struct extension_ops *)(dev)->driver->ops)
/* Currently, only one extension driver enabled at a time is supported */
#define U_BOOT_EXTENSION(_name, _scan_func) \
U_BOOT_DRIVER(_name) = { \
.name = #_name, \
.id = UCLASS_EXTENSION, \
.probe = extension_probe, \
.remove = extension_remove, \
.ops = &(struct extension_ops) { \
.scan = _scan_func, \
}, \
.priv_auto = sizeof(struct alist), \
}
#endif /* __EXTENSION_SUPPORT_H */
+13 -19
View File
@@ -380,7 +380,6 @@ static int bootdev_test_hunter(struct unit_test_state *uts)
ut_assert_nextline("Prio Used Uclass Hunter");
ut_assert_nextlinen("----");
ut_assert_nextline(" 6 ethernet eth_bootdev");
ut_assert_nextline(" 1 simple_bus (none)");
ut_assert_nextline(" 5 ide ide_bootdev");
ut_assert_nextline(" 2 mmc mmc_bootdev");
ut_assert_nextline(" 4 nvme nvme_bootdev");
@@ -389,15 +388,15 @@ static int bootdev_test_hunter(struct unit_test_state *uts)
ut_assert_nextline(" 4 spi_flash sf_bootdev");
ut_assert_nextline(" 5 usb usb_bootdev");
ut_assert_nextline(" 4 virtio virtio_bootdev");
ut_assert_nextline("(total hunters: 10)");
ut_assert_nextline("(total hunters: 9)");
ut_assert_console_end();
ut_assertok(bootdev_hunt("usb1", false));
ut_assert_skip_to_line("Bus usb@1: 5 USB Device(s) found");
ut_assert_console_end();
/* USB is 7th in the list, so bit 8 */
ut_asserteq(BIT(8), std->hunters_used);
/* USB is 8th in the list, so bit 7 */
ut_asserteq(BIT(7), std->hunters_used);
return 0;
}
@@ -418,7 +417,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextline("Prio Used Uclass Hunter");
ut_assert_nextlinen("----");
ut_assert_nextline(" 6 ethernet eth_bootdev");
ut_assert_skip_to_line("(total hunters: 10)");
ut_assert_skip_to_line("(total hunters: 9)");
ut_assert_console_end();
/* Use the MMC hunter and see that it updates */
@@ -426,7 +425,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assertok(run_command("bootdev hunt -l", 0));
ut_assert_skip_to_line(" 5 ide ide_bootdev");
ut_assert_nextline(" 2 * mmc mmc_bootdev");
ut_assert_skip_to_line("(total hunters: 10)");
ut_assert_skip_to_line("(total hunters: 9)");
ut_assert_console_end();
/* Scan all hunters */
@@ -436,8 +435,6 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextline("Hunting with: ethernet");
/* This is the extension feature which has no uclass at present */
ut_assert_nextline("Hunting with: simple_bus");
ut_assert_nextline("Found 2 extension board(s).");
ut_assert_nextline("Hunting with: ide");
/* mmc hunter has already been used so should not run again */
@@ -457,7 +454,6 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextlinen("Prio");
ut_assert_nextlinen("----");
ut_assert_nextline(" 6 * ethernet eth_bootdev");
ut_assert_nextline(" 1 * simple_bus (none)");
ut_assert_nextline(" 5 * ide ide_bootdev");
ut_assert_nextline(" 2 * mmc mmc_bootdev");
ut_assert_nextline(" 4 * nvme nvme_bootdev");
@@ -466,7 +462,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextline(" 4 * spi_flash sf_bootdev");
ut_assert_nextline(" 5 * usb usb_bootdev");
ut_assert_nextline(" 4 * virtio virtio_bootdev");
ut_assert_nextline("(total hunters: 10)");
ut_assert_nextline("(total hunters: 9)");
ut_assert_console_end();
ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used);
@@ -490,7 +486,7 @@ static int bootdev_test_hunt_scan(struct unit_test_state *uts)
ut_assertok(bootflow_scan_first(NULL, NULL, &iter,
BOOTFLOWIF_SHOW | BOOTFLOWIF_HUNT |
BOOTFLOWIF_SKIP_GLOBAL, &bflow));
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER), std->hunters_used);
return 0;
}
@@ -650,8 +646,8 @@ static int bootdev_test_next_label(struct unit_test_state *uts)
ut_asserteq_str("scsi.id0lun0.bootdev", dev->name);
ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags);
/* SCSI is 7th in the list, so bit 6 */
ut_asserteq(BIT(MMC_HUNTER) | BIT(6), std->hunters_used);
/* SCSI is 6th in the list, so bit 5 */
ut_asserteq(BIT(MMC_HUNTER) | BIT(5), std->hunters_used);
ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
ut_assert_console_end();
@@ -661,7 +657,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts)
mflags);
/* dhcp: Ethernet is first so bit 0 */
ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used);
ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
ut_assert_console_end();
@@ -671,7 +667,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts)
mflags);
/* pxe: Ethernet is first so bit 0 */
ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used);
mflags = 123;
ut_asserteq(-ENODEV, bootdev_next_label(&iter, &dev, &mflags));
@@ -679,7 +675,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts)
ut_assert_console_end();
/* no change */
ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used);
return 0;
}
@@ -724,12 +720,10 @@ static int bootdev_test_next_prio(struct unit_test_state *uts)
ut_assertok(bootdev_next_prio(&iter, &dev));
ut_asserteq_str("mmc2.bootdev", dev->name);
ut_assert_nextline("Hunting with: simple_bus");
ut_assert_nextline("Found 2 extension board(s).");
ut_assert_nextline("Hunting with: mmc");
ut_assert_console_end();
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER), std->hunters_used);
ut_assertok(bootdev_next_prio(&iter, &dev));
ut_asserteq_str("mmc1.bootdev", dev->name);
+2 -4
View File
@@ -861,7 +861,7 @@ static int bootflow_cmd_hunt_single(struct unit_test_state *uts)
ut_assert_console_end();
/* check that the hunter was used */
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER), std->hunters_used);
return 0;
}
@@ -883,14 +883,12 @@ static int bootflow_cmd_hunt_label(struct unit_test_state *uts)
ut_assertok(run_command("bootflow scan -l mmc", 0));
/* check that the hunter was used */
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
ut_asserteq(BIT(MMC_HUNTER), std->hunters_used);
/* check that we got the mmc1 bootflow */
ut_assert_nextline("Scanning for bootflows with label 'mmc'");
ut_assert_nextlinen("Seq");
ut_assert_nextlinen("---");
ut_assert_nextline("Hunting with: simple_bus");
ut_assert_nextline("Found 2 extension board(s).");
ut_assert_nextline("Hunting with: mmc");
ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+2 -2
View File
@@ -21,8 +21,8 @@
#define TEST_VERNUM 0x00010002
enum {
MAX_HUNTER = 9,
MMC_HUNTER = 3, /* ID of MMC hunter */
MAX_HUNTER = 8,
MMC_HUNTER = 2, /* ID of MMC hunter */
};
struct unit_test_state;