Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Marangi and committed by
Jens Axboe
2e3a191e 3ec7cb11

+124
+9
block/partitions/Kconfig
··· 270 270 Say Y here if you want to read the partition table from bootargs. 271 271 The format for the command line is just like mtdparts. 272 272 273 + config OF_PARTITION 274 + bool "Device Tree partition support" if PARTITION_ADVANCED 275 + depends on OF 276 + help 277 + Say Y here if you want to enable support for partition table 278 + defined in Device Tree. (mainly for eMMC) 279 + The format for the device tree node is just like MTD fixed-partition 280 + schema. 281 + 273 282 endmenu
+1
block/partitions/Makefile
··· 12 12 obj-$(CONFIG_MAC_PARTITION) += mac.o 13 13 obj-$(CONFIG_LDM_PARTITION) += ldm.o 14 14 obj-$(CONFIG_MSDOS_PARTITION) += msdos.o 15 + obj-$(CONFIG_OF_PARTITION) += of.o 15 16 obj-$(CONFIG_OSF_PARTITION) += osf.o 16 17 obj-$(CONFIG_SGI_PARTITION) += sgi.o 17 18 obj-$(CONFIG_SUN_PARTITION) += sun.o
+1
block/partitions/check.h
··· 62 62 int ldm_partition(struct parsed_partitions *state); 63 63 int mac_partition(struct parsed_partitions *state); 64 64 int msdos_partition(struct parsed_partitions *state); 65 + int of_partition(struct parsed_partitions *state); 65 66 int osf_partition(struct parsed_partitions *state); 66 67 int sgi_partition(struct parsed_partitions *state); 67 68 int sun_partition(struct parsed_partitions *state);
+3
block/partitions/core.c
··· 43 43 #ifdef CONFIG_CMDLINE_PARTITION 44 44 cmdline_partition, 45 45 #endif 46 + #ifdef CONFIG_OF_PARTITION 47 + of_partition, /* cmdline have priority to OF */ 48 + #endif 46 49 #ifdef CONFIG_EFI_PARTITION 47 50 efi_partition, /* this must come before msdos */ 48 51 #endif
+110
block/partitions/of.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/blkdev.h> 4 + #include <linux/major.h> 5 + #include <linux/of.h> 6 + #include <linux/string.h> 7 + #include "check.h" 8 + 9 + static int validate_of_partition(struct device_node *np, int slot) 10 + { 11 + u64 offset, size; 12 + int len; 13 + 14 + const __be32 *reg = of_get_property(np, "reg", &len); 15 + int a_cells = of_n_addr_cells(np); 16 + int s_cells = of_n_size_cells(np); 17 + 18 + /* Make sure reg len match the expected addr and size cells */ 19 + if (len / sizeof(*reg) != a_cells + s_cells) 20 + return -EINVAL; 21 + 22 + /* Validate offset conversion from bytes to sectors */ 23 + offset = of_read_number(reg, a_cells); 24 + if (offset % SECTOR_SIZE) 25 + return -EINVAL; 26 + 27 + /* Validate size conversion from bytes to sectors */ 28 + size = of_read_number(reg + a_cells, s_cells); 29 + if (!size || size % SECTOR_SIZE) 30 + return -EINVAL; 31 + 32 + return 0; 33 + } 34 + 35 + static void add_of_partition(struct parsed_partitions *state, int slot, 36 + struct device_node *np) 37 + { 38 + struct partition_meta_info *info; 39 + char tmp[sizeof(info->volname) + 4]; 40 + const char *partname; 41 + int len; 42 + 43 + const __be32 *reg = of_get_property(np, "reg", &len); 44 + int a_cells = of_n_addr_cells(np); 45 + int s_cells = of_n_size_cells(np); 46 + 47 + /* Convert bytes to sector size */ 48 + u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE; 49 + u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE; 50 + 51 + put_partition(state, slot, offset, size); 52 + 53 + if (of_property_read_bool(np, "read-only")) 54 + state->parts[slot].flags |= ADDPART_FLAG_READONLY; 55 + 56 + /* 57 + * Follow MTD label logic, search for label property, 58 + * fallback to node name if not found. 59 + */ 60 + info = &state->parts[slot].info; 61 + partname = of_get_property(np, "label", &len); 62 + if (!partname) 63 + partname = of_get_property(np, "name", &len); 64 + strscpy(info->volname, partname, sizeof(info->volname)); 65 + 66 + snprintf(tmp, sizeof(tmp), "(%s)", info->volname); 67 + strlcat(state->pp_buf, tmp, PAGE_SIZE); 68 + } 69 + 70 + int of_partition(struct parsed_partitions *state) 71 + { 72 + struct device *ddev = disk_to_dev(state->disk); 73 + struct device_node *np; 74 + int slot; 75 + 76 + struct device_node *partitions_np = of_node_get(ddev->of_node); 77 + 78 + if (!partitions_np || 79 + !of_device_is_compatible(partitions_np, "fixed-partitions")) 80 + return 0; 81 + 82 + slot = 1; 83 + /* Validate parition offset and size */ 84 + for_each_child_of_node(partitions_np, np) { 85 + if (validate_of_partition(np, slot)) { 86 + of_node_put(np); 87 + of_node_put(partitions_np); 88 + 89 + return -1; 90 + } 91 + 92 + slot++; 93 + } 94 + 95 + slot = 1; 96 + for_each_child_of_node(partitions_np, np) { 97 + if (slot >= state->limit) { 98 + of_node_put(np); 99 + break; 100 + } 101 + 102 + add_of_partition(state, slot, np); 103 + 104 + slot++; 105 + } 106 + 107 + strlcat(state->pp_buf, "\n", PAGE_SIZE); 108 + 109 + return 1; 110 + }