mailarchive of the pengutronix oss-tools mailing list
 help / color / mirror / Atom feed
From: Marco Felsch <m.felsch@pengutronix.de>
To: oss-tools@pengutronix.de
Cc: mfe@pengutronix.de
Subject: [OSS-Tools] [PATCH dt-utils 13/14] libdt: add partition search function
Date: Fri, 14 Oct 2022 18:42:03 +0200	[thread overview]
Message-ID: <20221014164204.3812506-14-m.felsch@pengutronix.de> (raw)
In-Reply-To: <20221014164204.3812506-1-m.felsch@pengutronix.de>

From: Juergen Borleis <jbe@pengutronix.de>

Add of_find_path() support which mimics the of_find_path() from barebox.
The purpose of this function is to get the device path specified by a
device-tree property like:

   - proptery = &mmc2, partname:0;
   - property = &of_partition;

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
[m.felsch@pengutronix.de: adapt commit message]
[m.felsch@pengutronix.de: fix of-partition use-case]
[m.felsch@pengutronix.de: use the correct udev_list_entry variable]
[m.felsch@pengutronix.de: of_find_path function behaviour with barebox]
[m.felsch@pengutronix.de: drop partuuid suport since barbeox don't support ot either]
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
 src/dt/dt.h         |   2 +
 src/libdt-utils.sym |   1 +
 src/libdt.c         | 224 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+)

diff --git a/src/dt/dt.h b/src/dt/dt.h
index 97dd855..7c27259 100644
--- a/src/dt/dt.h
+++ b/src/dt/dt.h
@@ -229,6 +229,8 @@ void of_add_memory_bank(struct device_node *node, bool dump, int r,
 
 int of_get_devicepath(struct device_node *partition_node, char **devnode, off_t *offset,
 		size_t *size);
+int of_find_path(struct device_node *node, const char *propname, char **devpath,
+		 off_t *offset, size_t *size);
 
 #define for_each_node_by_name(dn, name) \
 	for (dn = of_find_node_by_name(NULL, name); dn; \
diff --git a/src/libdt-utils.sym b/src/libdt-utils.sym
index a749317..441c8ef 100644
--- a/src/libdt-utils.sym
+++ b/src/libdt-utils.sym
@@ -34,6 +34,7 @@ global:
 	of_get_child_by_name;
 	of_get_child_count;
 	of_get_devicepath;
+	of_find_path;
 	of_get_next_available_child;
 	of_get_parent;
 	of_get_property;
diff --git a/src/libdt.c b/src/libdt.c
index f18ea90..89ef501 100644
--- a/src/libdt.c
+++ b/src/libdt.c
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <libudev.h>
+#include <limits.h>
 #include <dt.h>
 
 static int pr_level = 5;
@@ -2234,6 +2235,137 @@ out:
 	return ret;
 }
 
+/**
+ * libdt_udev_scan_restrict - Restrict a udev scan to a specific device
+ * @e:		The scan enumerater to restrict to a specific device
+ * @specifier:	Describes the device in question in more detail
+ *
+ * Since property matches are or'ed, we can only add exactly one match based on
+ * a specific property value.
+ *
+ * Note: this function isn't thread save
+ */
+static int libdt_udev_scan_restrict(struct udev_enumerate *e, const char *specifier)
+{
+	static char part_number_match[16];
+	unsigned long int part_no;
+	char *eptr;
+
+	/*
+	 * The variables: 'PARTN' (for <number>) or 'PARTNAME' (for <name>) can
+	 * be queried by:
+	 *   udevadm info -q all /dev/<partition device node>
+	 */
+
+	/* "partname:<name>" use case */
+	if (!isdigit(*specifier))
+		return udev_enumerate_add_match_property(e, "PARTNAME", specifier);
+
+	/*
+	 * "partname:<number>" use case
+	 * It's a little bit more complicated, since the bootloader
+	 * counts beginning with '0', while userland counts
+	 * beginning with '1'. Thus, we need to increase the partition
+	 * number by one here - and we need it as a string.
+	 */
+	part_no = strtoul(specifier, &eptr, 10);
+	if (part_no == 0 && specifier == eptr) {
+		fprintf(stderr, "Numerical partition count value expected - but found invalid value: '%s'\n", specifier);
+		return -EINVAL;
+	}
+	if (part_no == ULONG_MAX && errno == ERANGE) {
+		fprintf(stderr, "Numerical partition count expected - but found invalid number: '%s'\n", specifier);
+		return -ERANGE;
+	}
+
+	snprintf(part_number_match, sizeof(part_number_match), "%ld", part_no + 1);
+	udev_enumerate_add_match_property(e, "PARTN", part_number_match);
+	return 0;
+}
+
+/**
+ * device_find_blockdev_partition - Retrieve the device node path to a block device
+ * 				    (which can be a of-partition as well)
+ * @parent:		The base block device or of-partition
+ * @part_specifier:	Specifier to define a unique partition on @parent
+ * @devpath:		Return the corresponding path to the device node
+ * 			(e.g. /dev/<something>)
+ *
+ * Returns 0 on success (e.g. @devpath is valid) or a negative errno else
+ *
+ * @part_specifier is a string and can be one of:
+ * - "partname:<number>": restrict the scan to a partition with index <number> + 1
+ *   - can be used in conjuntion with MBR or GPT partitions
+ * - "partname:<name>": restrict the scan to a partition with name <name>
+ *   - can be used in conjuntion with GPT partitions
+ */
+static int device_find_blockdev_partition(struct udev_device *parent, const char *part_specifier, char **devpath)
+{
+	struct udev_list_entry *device_list, *dev_entry;
+	struct udev_enumerate *enumerate;
+	struct udev_device *part = NULL;
+	const char *devnode_path;
+	const char *sys_path;
+	struct udev *udev;
+	int ret;
+
+	udev = udev_new();
+	if (!udev) {
+		  fprintf(stderr, "Failed to create udev context when retrieving block device nodes\n");
+		  return -ENODEV;
+	}
+	/*
+	 * Create an enumation context to match exactly one device
+	 * Note: the following matches are and'ed (one per group)
+	 */
+	enumerate = udev_enumerate_new(udev);
+
+	/* We are interested only in devices related to the given parent device */
+	udev_enumerate_add_match_parent(enumerate, parent);
+	/* We are interested only in subsystem 'block' devices */
+	udev_enumerate_add_match_subsystem(enumerate, "block");
+	/* We are interested in a unique ID */
+	ret = libdt_udev_scan_restrict(enumerate, part_specifier);
+	if (ret)
+		goto leave;
+
+	udev_enumerate_scan_devices(enumerate);
+
+	/* Note: this list should contain none or max *one* device */
+	device_list = udev_enumerate_get_list_entry(enumerate);
+	if (device_list == NULL) {
+		fprintf(stderr, "No device matches the partition specifier: '%s'\n", part_specifier);
+		ret = -ENODEV;
+		goto leave;
+	}
+
+	sys_path = udev_list_entry_get_name(device_list);
+	part = udev_device_new_from_syspath(udev, sys_path);
+	devnode_path = udev_device_get_devnode(part);
+
+	/* Ensure only one match is in the list */
+	dev_entry = udev_list_entry_get_next(device_list);
+	if (dev_entry == NULL) {
+		*devpath = strdup(devnode_path);
+		ret = 0;
+	} else {
+		fprintf(stderr, "Unexpected behaviour of udev matches: a single partition device node expected, but got more than one.\n");
+		fprintf(stderr, " One match to '%s'", devnode_path);
+		udev_device_unref(part);
+		sys_path = udev_list_entry_get_name(dev_entry);
+		part = udev_device_new_from_syspath(udev, sys_path);
+		fprintf(stderr, "  and (at least) one more to '%s'\n", udev_device_get_devnode(part));
+		ret = -ENODEV;
+	}
+
+	udev_device_unref(part);
+leave:
+	udev_enumerate_unref(enumerate);
+	udev_unref(udev);
+
+	return ret;
+}
+
 /*
  * of_parse_partition - extract offset and size from a partition device_node
  *
@@ -2532,3 +2664,95 @@ int of_get_devicepath(struct device_node *partition_node, char **devpath, off_t
 
 	return -EINVAL;
 }
+
+static int libdt_partition_size_get(const char *dev_node, size_t *size)
+{
+	struct stat fstat;
+	int rc;
+
+	rc = stat(dev_node, &fstat);
+	if (rc < 0) {
+		rc = -errno;
+		fprintf(stderr, "Failed to get the size of state's partition based backend memory via '%s': %m\n", dev_node);
+		return rc;
+	}
+
+	if (fstat.st_size > SIZE_MAX) {
+		fprintf(stderr, "Partition size of '%s' too big to fit into a size_t type. Cannot continue.\n", dev_node);
+		return -ERANGE;
+	}
+
+	*size = fstat.st_size;
+	return 0;
+}
+
+/**
+ * of_find_path - Translates a path description in the devicetree to a Linux
+ *                device path
+ *
+ * @node: the node containing the property with the path description
+ * @propname: the property name of the path description
+ * @devpath: if this function returns 0 devpath will contain the path belonging
+ *           to the input path description.
+ * @offset: Returns the offset to be used inside the partition device (always 0)
+ * @size: Returns the size of the partition device
+ *
+ * paths in the devicetree have the form of a multistring property. The first
+ * string contains the full path to the physical device containing the path or
+ * a full path to a partition described by the OF partition binding.
+ * The remaining strings have the form "<type>:<options>". Currently supported
+ * for <type> are:
+ *
+ * partname:<partname> - find a partition by its partition name. For mtd
+ *                       partitions this is the label. For DOS partitions
+ *                       this is the number beginning with 0.
+ *
+ * examples:
+ *
+ * device-path = &mmc0, "partname:0";
+ * device-path = &norflash, "partname:barebox-environment";
+ * device-path = &environment_nor;
+ */
+
+int of_find_path(struct device_node *node, const char *propname, char **devpath,
+		 off_t *offset, size_t *size)
+{
+	const char partnamestr[] = "partname:";
+	struct device_node *rnode;
+	struct udev_device *udev;
+	const char *part = NULL;
+	const char *path;
+	int rc;
+
+	path = of_get_property(node, propname, NULL);
+	if (!path)
+		return -EINVAL;
+
+	rnode = of_find_node_by_path(path);
+	if (!rnode)
+		return -ENODEV;
+
+	of_property_read_string_index(node, propname, 1, &part);
+	if (part) {
+		if (!strncmp(part, partnamestr, sizeof(partnamestr) - 1)) {
+			part += sizeof(partnamestr) - 1;
+		} else {
+			pr_err("Invalid device-path: %s\n", part);
+			return -EINVAL;
+		}
+	} else {
+		/* of-partition use-case */
+		return of_get_devicepath(rnode, devpath, offset, size);
+	}
+
+	udev = of_find_device_by_node_path(rnode->full_name);
+	if (udev) {
+		*offset = 0;
+		rc = device_find_blockdev_partition(udev, part, devpath);
+		if (rc < 0)
+			return rc;
+		return libdt_partition_size_get(*devpath, size);
+	}
+
+	return -EINVAL;
+}
-- 
2.30.2




  parent reply	other threads:[~2022-10-14 16:42 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-14 16:41 [OSS-Tools] [PATCH dt-utils 00/14] Sync Barebox-State code base Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 01/14] state: Remove duplicate incudes Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 02/14] state: backend_raw: fix ignoring unpack failures Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 03/14] state: backend_storage: deal gracefully with runtime bucket corruption Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 04/14] state: treat state with all-invalid buckets as dirty Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 05/14] state: remove param member from struct state_string Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 06/14] state: remove param member from state_uint32, state_enum32, state_mac Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 07/14] state: remove unused function Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 08/14] state: propagate failure to fixup enum32 into DT Marco Felsch
2022-10-14 16:41 ` [OSS-Tools] [PATCH dt-utils 09/14] state: add SPDX-License-Identifier for files without explicit license Marco Felsch
2022-10-14 16:42 ` [OSS-Tools] [PATCH dt-utils 10/14] state: fix typos found with codespell Marco Felsch
2022-10-14 16:42 ` [OSS-Tools] [PATCH dt-utils 11/14] common: xstrdup: don't panic on xstrdup(NULL) Marco Felsch
2022-10-14 16:42 ` [OSS-Tools] [PATCH dt-utils 12/14] libdt: add of_property_write_strings support Marco Felsch
2022-10-14 16:42 ` Marco Felsch [this message]
2022-10-14 16:42 ` [OSS-Tools] [PATCH dt-utils 14/14] state: sync with barebox to support new backend type Marco Felsch
2022-10-21  7:37 ` [OSS-Tools] [PATCH dt-utils 00/14] Sync Barebox-State code base Marco Felsch
2023-06-05 13:12 ` Ahmad Fatoum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221014164204.3812506-14-m.felsch@pengutronix.de \
    --to=m.felsch@pengutronix.de \
    --cc=mfe@pengutronix.de \
    --cc=oss-tools@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox