From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Fri, 14 Oct 2022 18:42:21 +0200 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1ojNlO-008iGc-5B for lore@lore.pengutronix.de; Fri, 14 Oct 2022 18:42:21 +0200 Received: from localhost ([127.0.0.1] helo=metis.ext.pengutronix.de) by metis.ext.pengutronix.de with esmtp (Exim 4.92) (envelope-from ) id 1ojNlD-0006Ja-6l; Fri, 14 Oct 2022 18:42:11 +0200 Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1ojNlA-0006F7-Sw; Fri, 14 Oct 2022 18:42:08 +0200 Received: from [2a0a:edc0:0:1101:1d::28] (helo=dude02.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtp (Exim 4.94.2) (envelope-from ) id 1ojNlA-001WTg-2f; Fri, 14 Oct 2022 18:42:08 +0200 Received: from mfe by dude02.red.stw.pengutronix.de with local (Exim 4.94.2) (envelope-from ) id 1ojNl8-00Fzpp-RA; Fri, 14 Oct 2022 18:42:06 +0200 From: Marco Felsch To: oss-tools@pengutronix.de Date: Fri, 14 Oct 2022 18:42:03 +0200 Message-Id: <20221014164204.3812506-14-m.felsch@pengutronix.de> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221014164204.3812506-1-m.felsch@pengutronix.de> References: <20221014164204.3812506-1-m.felsch@pengutronix.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [OSS-Tools] [PATCH dt-utils 13/14] libdt: add partition search function X-BeenThere: oss-tools@pengutronix.de X-Mailman-Version: 2.1.29 Precedence: list List-Id: Pengutronix Public Open-Source-Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mfe@pengutronix.de Sender: "OSS-Tools" X-SA-Exim-Connect-IP: 127.0.0.1 X-SA-Exim-Mail-From: oss-tools-bounces@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false From: Juergen Borleis 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 [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 --- 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 #include #include +#include #include 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 ) or 'PARTNAME' (for ) can + * be queried by: + * udevadm info -q all /dev/ + */ + + /* "partname:" use case */ + if (!isdigit(*specifier)) + return udev_enumerate_add_match_property(e, "PARTNAME", specifier); + + /* + * "partname:" 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/) + * + * 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:": restrict the scan to a partition with index + 1 + * - can be used in conjuntion with MBR or GPT partitions + * - "partname:": restrict the scan to a partition with 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 ":". Currently supported + * for are: + * + * 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