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 <[email protected]>
---
  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_ */

Reply via email to