[PATCH 1/2] fastboot: Add UFS flash backend

Mattijs Korpershoek mkorpershoek at kernel.org
Mon Mar 23 11:00:50 CET 2026


On Wed, Mar 04, 2026 at 17:23, Neil Armstrong <neil.armstrong at linaro.org> wrote:

> 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...

I agree with Neil here. Can you explain why this needs to be UFS
specific or re-spin a new version to extend block support?

>
> 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