static inline int mmc_blk_part_switch(struct mmc_card *card,
unsigned int part_type);
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
-@@ -3016,6 +3023,8 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3066,6 +3073,8 @@ static int mmc_blk_probe(struct mmc_card
{
struct mmc_blk_data *md;
int ret = 0;
/*
* Check that the card supports the command class(es) we need.
-@@ -3023,7 +3032,16 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3073,7 +3082,16 @@ static int mmc_blk_probe(struct mmc_card
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
card->complete_wq = alloc_workqueue("mmc_complete",
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
-@@ -3038,6 +3056,17 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3088,6 +3106,17 @@ static int mmc_blk_probe(struct mmc_card
goto out_free;
}
--- /dev/null
+From 03cb793b26834ddca170ba87057c8f883772dd45 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:41 +0200
+Subject: [PATCH 1/5] block: add support for defining read-only partitions
+
+Add support for defining read-only partitions and complete support for
+it in the cmdline partition parser as the additional "ro" after a
+partition is scanned but never actually applied.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-2-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/blk.h | 1 +
+ block/partitions/cmdline.c | 3 +++
+ block/partitions/core.c | 3 +++
+ 3 files changed, 7 insertions(+)
+
+--- a/block/blk.h
++++ b/block/blk.h
+@@ -424,6 +424,7 @@ void blk_free_ext_minor(unsigned int min
+ #define ADDPART_FLAG_NONE 0
+ #define ADDPART_FLAG_RAID 1
+ #define ADDPART_FLAG_WHOLEDISK 2
++#define ADDPART_FLAG_READONLY 4
+ int bdev_add_partition(struct gendisk *disk, int partno, sector_t start,
+ sector_t length);
+ int bdev_del_partition(struct gendisk *disk, int partno);
+--- a/block/partitions/cmdline.c
++++ b/block/partitions/cmdline.c
+@@ -237,6 +237,9 @@ static int add_part(int slot, struct cmd
+ put_partition(state, slot, subpart->from >> 9,
+ subpart->size >> 9);
+
++ if (subpart->flags & PF_RDONLY)
++ state->parts[slot].flags |= ADDPART_FLAG_READONLY;
++
+ info = &state->parts[slot].info;
+
+ strscpy(info->volname, subpart->name, sizeof(info->volname));
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -392,6 +392,9 @@ static struct block_device *add_partitio
+ goto out_del;
+ }
+
++ if (flags & ADDPART_FLAG_READONLY)
++ bdev->bd_read_only = true;
++
+ /* everything is up and running, commence */
+ err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL);
+ if (err)
--- /dev/null
+From e5f587242b6072ffab4f4a084a459a59f3035873 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:43 +0200
+Subject: [PATCH 3/5] block: introduce add_disk_fwnode()
+
+Introduce add_disk_fwnode() as a replacement of device_add_disk() that
+permits to pass and attach a fwnode to disk dev.
+
+This variant can be useful for eMMC that might have the partition table
+for the disk defined in DT. A parser can later make use of the attached
+fwnode to parse the related table and init the hardcoded partition for
+the disk.
+
+device_add_disk() is converted to a simple wrapper of add_disk_fwnode()
+with the fwnode entry set as NULL.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-4-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/genhd.c | 28 ++++++++++++++++++++++++----
+ include/linux/blkdev.h | 3 +++
+ 2 files changed, 27 insertions(+), 4 deletions(-)
+
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -383,16 +383,18 @@ int disk_scan_partitions(struct gendisk
+ }
+
+ /**
+- * device_add_disk - add disk information to kernel list
++ * add_disk_fwnode - add disk information to kernel list with fwnode
+ * @parent: parent device for the disk
+ * @disk: per-device partitioning information
+ * @groups: Additional per-device sysfs groups
++ * @fwnode: attached disk fwnode
+ *
+ * This function registers the partitioning information in @disk
+- * with the kernel.
++ * with the kernel. Also attach a fwnode to the disk device.
+ */
+-int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
+- const struct attribute_group **groups)
++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
++ const struct attribute_group **groups,
++ struct fwnode_handle *fwnode)
+
+ {
+ struct device *ddev = disk_to_dev(disk);
+@@ -451,6 +453,8 @@ int __must_check device_add_disk(struct
+ ddev->parent = parent;
+ ddev->groups = groups;
+ dev_set_name(ddev, "%s", disk->disk_name);
++ if (fwnode)
++ device_set_node(ddev, fwnode);
+ if (!(disk->flags & GENHD_FL_HIDDEN))
+ ddev->devt = MKDEV(disk->major, disk->first_minor);
+ ret = device_add(ddev);
+@@ -552,6 +556,22 @@ out_exit_elevator:
+ elevator_exit(disk->queue);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(add_disk_fwnode);
++
++/**
++ * device_add_disk - add disk information to kernel list
++ * @parent: parent device for the disk
++ * @disk: per-device partitioning information
++ * @groups: Additional per-device sysfs groups
++ *
++ * This function registers the partitioning information in @disk
++ * with the kernel.
++ */
++int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
++ const struct attribute_group **groups)
++{
++ return add_disk_fwnode(parent, disk, groups, NULL);
++}
+ EXPORT_SYMBOL(device_add_disk);
+
+ static void blk_report_disk_dead(struct gendisk *disk, bool surprise)
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -741,6 +741,9 @@ static inline unsigned int blk_queue_dep
+ #define for_each_bio(_bio) \
+ for (; _bio; _bio = _bio->bi_next)
+
++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
++ const struct attribute_group **groups,
++ struct fwnode_handle *fwnode);
+ int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
+ const struct attribute_group **groups);
+ static inline int __must_check add_disk(struct gendisk *disk)
--- /dev/null
+From 45ff6c340ddfc2dade74d5b7a8962c778ab7042c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:44 +0200
+Subject: [PATCH 4/5] mmc: block: attach partitions fwnode if found in mmc-card
+
+Attach partitions fwnode if found in mmc-card and register disk with it.
+
+This permits block partition to reference the node and register a
+partition table defined in DT for the special case for embedded device
+that doesn't have a partition table flashed but have an hardcoded
+partition table passed from the system.
+
+JEDEC BOOT partition boot0/boot1 are supported but in DT we refer with
+the JEDEC name of boot1 and boot2 to better adhere to documentation.
+
+Also JEDEC GP partition gp0/1/2/3 are supported but in DT we refer with
+the JEDEC name of gp1/2/3/4 to better adhere to documentration.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20241002221306.4403-5-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ drivers/mmc/core/block.c | 55 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2455,6 +2455,56 @@ static inline int mmc_blk_readonly(struc
+ !(card->csd.cmdclass & CCC_BLOCK_WRITE);
+ }
+
++/*
++ * Search for a declared partitions node for the disk in mmc-card related node.
++ *
++ * This is to permit support for partition table defined in DT in special case
++ * where a partition table is not written in the disk and is expected to be
++ * passed from the running system.
++ *
++ * For the user disk, "partitions" node is searched.
++ * For the special HW disk, "partitions-" node with the appended name is used
++ * following this conversion table (to adhere to JEDEC naming)
++ * - boot0 -> partitions-boot1
++ * - boot1 -> partitions-boot2
++ * - gp0 -> partitions-gp1
++ * - gp1 -> partitions-gp2
++ * - gp2 -> partitions-gp3
++ * - gp3 -> partitions-gp4
++ */
++static struct fwnode_handle *mmc_blk_get_partitions_node(struct device *mmc_dev,
++ const char *subname)
++{
++ const char *node_name = "partitions";
++
++ if (subname) {
++ mmc_dev = mmc_dev->parent;
++
++ /*
++ * Check if we are allocating a BOOT disk boot0/1 disk.
++ * In DT we use the JEDEC naming boot1/2.
++ */
++ if (!strcmp(subname, "boot0"))
++ node_name = "partitions-boot1";
++ if (!strcmp(subname, "boot1"))
++ node_name = "partitions-boot2";
++ /*
++ * Check if we are allocating a GP disk gp0/1/2/3 disk.
++ * In DT we use the JEDEC naming gp1/2/3/4.
++ */
++ if (!strcmp(subname, "gp0"))
++ node_name = "partitions-gp1";
++ if (!strcmp(subname, "gp1"))
++ node_name = "partitions-gp2";
++ if (!strcmp(subname, "gp2"))
++ node_name = "partitions-gp3";
++ if (!strcmp(subname, "gp3"))
++ node_name = "partitions-gp4";
++ }
++
++ return device_get_named_child_node(mmc_dev, node_name);
++}
++
+ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
+ struct device *parent,
+ sector_t size,
+@@ -2463,6 +2513,7 @@ static struct mmc_blk_data *mmc_blk_allo
+ int area_type,
+ unsigned int part_type)
+ {
++ struct fwnode_handle *disk_fwnode;
+ struct mmc_blk_data *md;
+ int devidx, ret;
+ char cap_str[10];
+@@ -2568,7 +2619,9 @@ static struct mmc_blk_data *mmc_blk_allo
+ /* used in ->open, must be set before add_disk: */
+ if (area_type == MMC_BLK_DATA_AREA_MAIN)
+ dev_set_drvdata(&card->dev, md);
+- ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
++ disk_fwnode = mmc_blk_get_partitions_node(parent, subname);
++ ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups,
++ disk_fwnode);
+ if (ret)
+ goto err_put_disk;
+ return md;
--- /dev/null
+From 884555b557e5e6d41c866e2cd8d7b32f50ec974b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:45 +0200
+Subject: [PATCH 5/5] block: add support for partition table defined in OF
+
+Add support for partition table defined in Device Tree. Similar to how
+it's done with MTD, add support for defining a fixed partition table in
+device tree.
+
+A common scenario for this is fixed block (eMMC) embedded devices that
+have no MBR or GPT partition table to save storage space. Bootloader
+access the block device with absolute address of data.
+
+This is to complete the functionality with an equivalent implementation
+with providing partition table with bootargs, for case where the booargs
+can't be modified and tweaking the Device Tree is the only solution to
+have an usabe partition table.
+
+The implementation follow the fixed-partitions parser used on MTD
+devices where a "partitions" node is expected to be declared with
+"fixed-partitions" compatible in the OF node of the disk device
+(mmc-card for eMMC for example) and each child node declare a label
+and a reg with offset and size. If label is not declared, the node name
+is used as fallback. Eventually is also possible to declare the read-only
+property to flag the partition as read-only.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-6-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/partitions/Kconfig | 9 ++++
+ block/partitions/Makefile | 1 +
+ block/partitions/check.h | 1 +
+ block/partitions/core.c | 3 ++
+ block/partitions/of.c | 110 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 124 insertions(+)
+ create mode 100644 block/partitions/of.c
+
+--- a/block/partitions/Kconfig
++++ b/block/partitions/Kconfig
+@@ -270,4 +270,13 @@ config CMDLINE_PARTITION
+ Say Y here if you want to read the partition table from bootargs.
+ The format for the command line is just like mtdparts.
+
++config OF_PARTITION
++ bool "Device Tree partition support" if PARTITION_ADVANCED
++ depends on OF
++ help
++ Say Y here if you want to enable support for partition table
++ defined in Device Tree. (mainly for eMMC)
++ The format for the device tree node is just like MTD fixed-partition
++ schema.
++
+ endmenu
+--- a/block/partitions/Makefile
++++ b/block/partitions/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_CMDLINE_PARTITION) += cmdli
+ obj-$(CONFIG_MAC_PARTITION) += mac.o
+ obj-$(CONFIG_LDM_PARTITION) += ldm.o
+ obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
++obj-$(CONFIG_OF_PARTITION) += of.o
+ obj-$(CONFIG_OSF_PARTITION) += osf.o
+ obj-$(CONFIG_SGI_PARTITION) += sgi.o
+ obj-$(CONFIG_SUN_PARTITION) += sun.o
+--- a/block/partitions/check.h
++++ b/block/partitions/check.h
+@@ -62,6 +62,7 @@ int karma_partition(struct parsed_partit
+ int ldm_partition(struct parsed_partitions *state);
+ int mac_partition(struct parsed_partitions *state);
+ int msdos_partition(struct parsed_partitions *state);
++int of_partition(struct parsed_partitions *state);
+ int osf_partition(struct parsed_partitions *state);
+ int sgi_partition(struct parsed_partitions *state);
+ int sun_partition(struct parsed_partitions *state);
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -43,6 +43,9 @@ static int (*const check_part[])(struct
+ #ifdef CONFIG_CMDLINE_PARTITION
+ cmdline_partition,
+ #endif
++#ifdef CONFIG_OF_PARTITION
++ of_partition, /* cmdline have priority to OF */
++#endif
+ #ifdef CONFIG_EFI_PARTITION
+ efi_partition, /* this must come before msdos */
+ #endif
+--- /dev/null
++++ b/block/partitions/of.c
+@@ -0,0 +1,110 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/blkdev.h>
++#include <linux/major.h>
++#include <linux/of.h>
++#include <linux/string.h>
++#include "check.h"
++
++static int validate_of_partition(struct device_node *np, int slot)
++{
++ u64 offset, size;
++ int len;
++
++ const __be32 *reg = of_get_property(np, "reg", &len);
++ int a_cells = of_n_addr_cells(np);
++ int s_cells = of_n_size_cells(np);
++
++ /* Make sure reg len match the expected addr and size cells */
++ if (len / sizeof(*reg) != a_cells + s_cells)
++ return -EINVAL;
++
++ /* Validate offset conversion from bytes to sectors */
++ offset = of_read_number(reg, a_cells);
++ if (offset % SECTOR_SIZE)
++ return -EINVAL;
++
++ /* Validate size conversion from bytes to sectors */
++ size = of_read_number(reg + a_cells, s_cells);
++ if (!size || size % SECTOR_SIZE)
++ return -EINVAL;
++
++ return 0;
++}
++
++static void add_of_partition(struct parsed_partitions *state, int slot,
++ struct device_node *np)
++{
++ struct partition_meta_info *info;
++ char tmp[sizeof(info->volname) + 4];
++ const char *partname;
++ int len;
++
++ const __be32 *reg = of_get_property(np, "reg", &len);
++ int a_cells = of_n_addr_cells(np);
++ int s_cells = of_n_size_cells(np);
++
++ /* Convert bytes to sector size */
++ u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE;
++ u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE;
++
++ put_partition(state, slot, offset, size);
++
++ if (of_property_read_bool(np, "read-only"))
++ state->parts[slot].flags |= ADDPART_FLAG_READONLY;
++
++ /*
++ * Follow MTD label logic, search for label property,
++ * fallback to node name if not found.
++ */
++ info = &state->parts[slot].info;
++ partname = of_get_property(np, "label", &len);
++ if (!partname)
++ partname = of_get_property(np, "name", &len);
++ strscpy(info->volname, partname, sizeof(info->volname));
++
++ snprintf(tmp, sizeof(tmp), "(%s)", info->volname);
++ strlcat(state->pp_buf, tmp, PAGE_SIZE);
++}
++
++int of_partition(struct parsed_partitions *state)
++{
++ struct device *ddev = disk_to_dev(state->disk);
++ struct device_node *np;
++ int slot;
++
++ struct device_node *partitions_np = of_node_get(ddev->of_node);
++
++ if (!partitions_np ||
++ !of_device_is_compatible(partitions_np, "fixed-partitions"))
++ return 0;
++
++ slot = 1;
++ /* Validate parition offset and size */
++ for_each_child_of_node(partitions_np, np) {
++ if (validate_of_partition(np, slot)) {
++ of_node_put(np);
++ of_node_put(partitions_np);
++
++ return -1;
++ }
++
++ slot++;
++ }
++
++ slot = 1;
++ for_each_child_of_node(partitions_np, np) {
++ if (slot >= state->limit) {
++ of_node_put(np);
++ break;
++ }
++
++ add_of_partition(state, slot, np);
++
++ slot++;
++ }
++
++ strlcat(state->pp_buf, "\n", PAGE_SIZE);
++
++ return 1;
++}
# CONFIG_OCTEONTX2_PF is not set
# CONFIG_OCTEON_EP is not set
# CONFIG_OF_OVERLAY is not set
+CONFIG_OF_PARTITION=y
CONFIG_OF_RESERVED_MEM=y
# CONFIG_OF_UNITTEST is not set
# CONFIG_OID_REGISTRY is not set
#include "check.h"
static int (*const check_part[])(struct parsed_partitions *) = {
-@@ -292,6 +294,74 @@ static ssize_t whole_disk_show(struct de
+@@ -295,6 +297,74 @@ static ssize_t whole_disk_show(struct de
}
static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
/*
* Must be called either with open_mutex held, before a disk can be opened or
* after all disk users are gone.
-@@ -374,6 +444,8 @@ static struct block_device *add_partitio
+@@ -377,6 +447,8 @@ static struct block_device *add_partitio
goto out_put;
}
+device_initcall(blk_notifications_init);
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
-@@ -1564,4 +1564,15 @@ struct io_comp_batch {
+@@ -1567,4 +1567,15 @@ struct io_comp_batch {
#define DEFINE_IO_COMP_BATCH(name) struct io_comp_batch name = { }
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
-@@ -2463,6 +2463,7 @@ static struct mmc_blk_data *mmc_blk_allo
- int area_type,
- unsigned int part_type)
- {
-+ struct fwnode_handle *fwnode;
- struct mmc_blk_data *md;
- int devidx, ret;
- char cap_str[10];
-@@ -2559,6 +2560,12 @@ static struct mmc_blk_data *mmc_blk_allo
-
- blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled);
-
-+ fwnode = device_get_named_child_node(subname ? md->parent->parent :
-+ md->parent,
-+ subname ? subname : "block");
-+ if (fwnode)
-+ device_set_node(disk_to_dev(md->disk), fwnode);
-+
- string_get_size((u64)size, 512, STRING_UNITS_2,
- cap_str, sizeof(cap_str));
- pr_info("%s: %s %s %s%s\n",
+@@ -2620,6 +2620,10 @@ static struct mmc_blk_data *mmc_blk_allo
+ if (area_type == MMC_BLK_DATA_AREA_MAIN)
+ dev_set_drvdata(&card->dev, md);
+ disk_fwnode = mmc_blk_get_partitions_node(parent, subname);
++ if (!disk_fwnode)
++ disk_fwnode = device_get_named_child_node(subname ? md->parent->parent :
++ md->parent,
++ subname ? subname : "block");
+ ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups,
+ disk_fwnode);
+ if (ret)
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
-@@ -2516,6 +2516,7 @@ static struct mmc_blk_data *mmc_blk_allo
+@@ -2566,6 +2566,7 @@ static struct mmc_blk_data *mmc_blk_allo
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->minors = perdev_minors;
md->disk->first_minor = devidx * perdev_minors;