[PATCH 1/2] fastboot: Add UFS flash backend
Neil Armstrong
neil.armstrong at linaro.org
Wed Mar 4 17:23:45 CET 2026
Hi,
On 3/3/26 07:29, Ruitong Su wrote:
> Add a fastboot backend for UFS (Universal Flash Storage) devices.
> UFS devices are accessed through the SCSI subsystem, with each UFS
> LUN exposed as a separate SCSI block device.
>
> The key feature is multi-LUN partition addressing using the syntax
> "<scsi_dev>#<partition_name>" (e.g. "0#system", "1#boot_a").
> Unqualified names fall back to the default SCSI device configured
> via CONFIG_FASTBOOT_FLASH_UFS_DEV.
I get this is a cool feature, but multi-LUN is exactly like multi-block,
so could you try to extend the block to support multiple different block
devices instead ? no need to limit this to UFS...
Thanks,
Neil
>
> The implementation reuses the existing fb_block helpers for raw
> image, sparse image, and erase operations, adding only the
> UFS-specific partition lookup and GPT handling logic on top.
>
> Partition lookup uses a quiet iteration via part_get_info() instead
> of the verbose part_get_info_by_name() / part_get_info_by_dev_and_
> name_or_num(), which avoids noisy console output during fastboot
> getvar probing (has-slot, partition-size queries).
>
> Signed-off-by: Ruitong Su <suruitong at lixiang.com>
> ---
> MAINTAINERS | 1 +
> drivers/fastboot/Kconfig | 30 ++++++-
> drivers/fastboot/Makefile | 1 +
> drivers/fastboot/fb_ufs.c | 164 ++++++++++++++++++++++++++++++++++++++
> include/fb_ufs.h | 49 ++++++++++++
> 5 files changed, 243 insertions(+), 2 deletions(-)
> create mode 100644 drivers/fastboot/fb_ufs.c
> create mode 100644 include/fb_ufs.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f8d4f6ee8b2..071102f47fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1197,6 +1197,7 @@ F: cmd/fastboot.c
> F: doc/android/fastboot*.rst
> F: include/fastboot.h
> F: include/fastboot-internal.h
> +F: include/fb_ufs.h
> F: include/net/fastboot_tcp.h
> F: include/net/fastboot_udp.h
> F: drivers/fastboot/
> diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
> index 576c3ef8a45..8569bec5d9b 100644
> --- a/drivers/fastboot/Kconfig
> +++ b/drivers/fastboot/Kconfig
> @@ -92,7 +92,7 @@ config FASTBOOT_FLASH
> bool "Enable FASTBOOT FLASH command"
> default y if ARCH_SUNXI && ( MMC || MTD_RAW_NAND )
> default y if ARCH_ROCKCHIP && MMC
> - depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS) || DM_SPI_FLASH || BLK
> + depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS) || DM_SPI_FLASH || BLK || (UFS && SCSI)
> select IMAGE_SPARSE
> help
> The fastboot protocol includes a "flash" command for writing
> @@ -124,12 +124,38 @@ config FASTBOOT_FLASH_SPI
> bool "FASTBOOT on SPI flash"
> depends on DM_SPI_FLASH
>
> +config FASTBOOT_FLASH_UFS
> + bool "FASTBOOT on UFS"
> + depends on UFS && SCSI
> + help
> + This enables support for the fastboot "flash" and "erase"
> + commands on UFS (Universal Flash Storage) devices. UFS
> + devices are accessed through the SCSI subsystem, with each
> + UFS LUN exposed as a separate SCSI block device.
> +
> + Partitions are addressed using the syntax
> + "<scsi_dev>#<partition_name>" (e.g. "0#system") to
> + disambiguate across multiple UFS LUNs. Unqualified names
> + fall back to the default SCSI device number.
> +
> config FASTBOOT_FLASH_BLOCK
> bool "FASTBOOT on block device"
> depends on BLK
>
> endchoice
>
> +config FASTBOOT_FLASH_UFS_DEV
> + int "Define FASTBOOT UFS default SCSI device number"
> + depends on FASTBOOT_FLASH_UFS
> + default 0
> + help
> + The default SCSI device number for UFS fastboot operations.
> + UFS LUNs are exposed as SCSI block devices by the UFS driver.
> + Use "scsi scan" followed by "scsi info" to discover the
> + mapping between SCSI device numbers and UFS LUNs. Typically
> + device 0 corresponds to UFS LUN 0. When the partition name
> + does not contain a "#" separator, this device is used.
> +
> config FASTBOOT_FLASH_MMC_DEV
> int "Define FASTBOOT MMC FLASH default device"
> depends on FASTBOOT_FLASH_MMC
> @@ -227,7 +253,7 @@ config FASTBOOT_FLASH_BLOCK_DEVICE_ID
>
> config FASTBOOT_GPT_NAME
> string "Target name for updating GPT"
> - depends on FASTBOOT_FLASH_MMC && EFI_PARTITION
> + depends on (FASTBOOT_FLASH_MMC || FASTBOOT_FLASH_UFS) && EFI_PARTITION
> default "gpt"
> help
> The fastboot "flash" command supports writing the downloaded
> diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile
> index a341af076d1..93a5227b737 100644
> --- a/drivers/fastboot/Makefile
> +++ b/drivers/fastboot/Makefile
> @@ -7,4 +7,5 @@ obj-$(CONFIG_FASTBOOT_FLASH_BLOCK) += fb_block.o
> # MMC reuses block implementation
> obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_block.o fb_mmc.o
> obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
> +obj-$(CONFIG_FASTBOOT_FLASH_UFS) += fb_block.o fb_ufs.o
> obj-$(CONFIG_FASTBOOT_FLASH_SPI) += fb_spi_flash.o
> diff --git a/drivers/fastboot/fb_ufs.c b/drivers/fastboot/fb_ufs.c
> new file mode 100644
> index 00000000000..1273c946491
> --- /dev/null
> +++ b/drivers/fastboot/fb_ufs.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026 Li Auto Inc.
> + *
> + * UFS (Universal Flash Storage) backend for fastboot.
> + *
> + * UFS devices in U-Boot are accessed through the SCSI subsystem, with
> + * each UFS LUN exposed as a separate SCSI block device. This backend
> + * provides UFS-aware partition lookup with multi-LUN addressing, layered
> + * on top of the generic fb_block helpers for the actual I/O.
> + *
> + * Partition addressing uses the syntax "<scsi_dev>#<partition_name>"
> + * (e.g. "0#system") to disambiguate across multiple UFS LUNs.
> + * Unqualified names fall back to CONFIG_FASTBOOT_FLASH_UFS_DEV.
> + */
> +
> +#include <blk.h>
> +#include <fastboot.h>
> +#include <fastboot-internal.h>
> +#include <fb_block.h>
> +#include <fb_ufs.h>
> +#include <image-sparse.h>
> +#include <part.h>
> +#include <vsprintf.h>
> +#include <linux/printk.h>
> +
> +static struct blk_desc *fastboot_ufs_get_dev(char *response, int dev_num)
> +{
> + struct blk_desc *dev_desc;
> +
> + dev_desc = blk_get_dev("scsi", dev_num);
> + if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
> + pr_err("invalid UFS/SCSI device %d\n", dev_num);
> + fastboot_fail("invalid UFS device", response);
> + return NULL;
> + }
> +
> + return dev_desc;
> +}
> +
> +static int fb_ufs_find_part(struct blk_desc *dev_desc, const char *name,
> + struct disk_partition *info)
> +{
> + int i;
> + int ret;
> +
> + for (i = 1; ; i++) {
> + ret = part_get_info(dev_desc, i, info);
> + if (ret)
> + return ret;
> + if (!strcmp(name, (char *)info->name))
> + return i;
> + }
> +}
> +
> +int fastboot_ufs_get_part_info(const char *part_name,
> + struct blk_desc **dev_desc,
> + struct disk_partition *part_info,
> + char *response)
> +{
> + int dev_num = CONFIG_FASTBOOT_FLASH_UFS_DEV;
> + char name_buf[32];
> + const char *name = part_name;
> + int ret;
> +
> + if (!part_name || !*part_name) {
> + fastboot_fail("partition not given", response);
> + return -ENOENT;
> + }
> +
> + /* Parse "<dev>#<name>" syntax for multi-LUN addressing */
> + if (strchr(part_name, '#')) {
> + if (sscanf(part_name, "%d#%31s", &dev_num, name_buf) != 2) {
> + fastboot_fail("invalid partition spec", response);
> + return -EINVAL;
> + }
> + name = name_buf;
> + }
> +
> + *dev_desc = blk_get_dev("scsi", dev_num);
> + if (!*dev_desc || (*dev_desc)->type == DEV_TYPE_UNKNOWN) {
> + fastboot_fail("no such device", response);
> + return -ENODEV;
> + }
> +
> + ret = fb_ufs_find_part(*dev_desc, name, part_info);
> + if (ret < 0) {
> + fastboot_fail("no such partition", response);
> + return -ENOENT;
> + }
> +
> + return ret;
> +}
> +
> +void fastboot_ufs_flash_write(const char *cmd, void *download_buffer,
> + u32 download_bytes, char *response)
> +{
> + struct blk_desc *dev_desc;
> + struct disk_partition info = {0};
> +
> +#if CONFIG_IS_ENABLED(EFI_PARTITION)
> + {
> + int dev_num = CONFIG_FASTBOOT_FLASH_UFS_DEV;
> + char op_buf[16];
> + const char *op = cmd;
> +
> + if (strchr(cmd, '#')) {
> + if (sscanf(cmd, "%d#%15s", &dev_num, op_buf) == 2)
> + op = op_buf;
> + }
> +
> + if (!strcmp(op, CONFIG_FASTBOOT_GPT_NAME)) {
> + dev_desc = fastboot_ufs_get_dev(response, dev_num);
> + if (!dev_desc)
> + return;
> +
> + printf("%s: updating GPT on scsi %d\n",
> + __func__, dev_num);
> + if (is_valid_gpt_buf(dev_desc, download_buffer)) {
> + printf("%s: invalid GPT - refusing to write\n",
> + __func__);
> + fastboot_fail("invalid GPT partition",
> + response);
> + return;
> + }
> + if (write_mbr_and_gpt_partitions(dev_desc,
> + download_buffer)) {
> + printf("%s: writing GPT partitions failed\n",
> + __func__);
> + fastboot_fail("writing GPT partitions failed",
> + response);
> + return;
> + }
> + part_init(dev_desc);
> + printf("........ success\n");
> + fastboot_okay(NULL, response);
> + return;
> + }
> + }
> +#endif
> +
> + if (fastboot_ufs_get_part_info(cmd, &dev_desc, &info, response) < 0)
> + return;
> +
> + if (is_sparse_image(download_buffer)) {
> + fastboot_block_write_sparse_image(dev_desc, &info, cmd,
> + download_buffer, response);
> + } else {
> + fastboot_block_write_raw_image(dev_desc, &info, cmd,
> + download_buffer,
> + download_bytes, response);
> + }
> +}
> +
> +void fastboot_ufs_erase(const char *cmd, char *response)
> +{
> + struct blk_desc *dev_desc;
> + struct disk_partition info;
> +
> + if (fastboot_ufs_get_part_info(cmd, &dev_desc, &info, response) < 0)
> + return;
> +
> + fastboot_block_raw_erase(dev_desc, &info, cmd, 0, response);
> +}
> diff --git a/include/fb_ufs.h b/include/fb_ufs.h
> new file mode 100644
> index 00000000000..d64d5aca81a
> --- /dev/null
> +++ b/include/fb_ufs.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2026 Li Auto Inc.
> + */
> +
> +#ifndef _FB_UFS_H_
> +#define _FB_UFS_H_
> +
> +struct blk_desc;
> +struct disk_partition;
> +
> +/**
> + * fastboot_ufs_get_part_info() - Look up partition on UFS device
> + *
> + * Supports two naming conventions:
> + * "<scsi_dev>#<partition_name>" -- explicit LUN targeting (e.g. "0#system")
> + * "<partition_name>" -- searches on default UFS SCSI device
> + *
> + * @part_name: Partition specifier (see above)
> + * @dev_desc: Pointer to returned block device descriptor
> + * @part_info: Pointer to returned partition info
> + * @response: Pointer to fastboot response buffer
> + * Return: Partition number on success, negative on error
> + */
> +int fastboot_ufs_get_part_info(const char *part_name,
> + struct blk_desc **dev_desc,
> + struct disk_partition *part_info,
> + char *response);
> +
> +/**
> + * fastboot_ufs_flash_write() - Write downloaded image to UFS partition
> + *
> + * @cmd: Partition specifier ("0#system", "0#gpt", etc.)
> + * @download_buffer: Pointer to downloaded image data
> + * @download_bytes: Size of downloaded image
> + * @response: Pointer to fastboot response buffer
> + */
> +void fastboot_ufs_flash_write(const char *cmd, void *download_buffer,
> + u32 download_bytes, char *response);
> +
> +/**
> + * fastboot_ufs_erase() - Erase a UFS partition
> + *
> + * @cmd: Partition specifier ("0#userdata", etc.)
> + * @response: Pointer to fastboot response buffer
> + */
> +void fastboot_ufs_erase(const char *cmd, char *response);
> +
> +#endif /* _FB_UFS_H_ */
More information about the U-Boot
mailing list