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