The backend device tree property so far always pointed at a partition.
Let's allow pointing it at GPT storage devices directly and lookup
the correct barebox state partition by the well-known type GUID:

  4778ed65-bf42-45fa-9c5b-287a1dc4aab1

The implementation is not as neat as hoped for, because lifetime for
barebox cdev and libudev udev_device are quite different and libdt
doesn't yet get the latter right. Once we make sure not to leak
libudev objects and add cdev_unref stubs to barebox, this can be
further simplified by sticking the struct udev_device into struct
cdev and determining extra information on demand.

Signed-off-by: Ahmad Fatoum <a.fat...@pengutronix.de>
---
 src/barebox-state/state.c |  26 ++++++++++
 src/dt/dt.h               |   5 ++
 src/libdt-utils.sym       |   3 ++
 src/libdt.c               | 101 ++++++++++++++++++++++++++++++++++++--
 src/linux/uuid.h          |  24 +++++++++
 src/state.h               |   4 ++
 6 files changed, 160 insertions(+), 3 deletions(-)
 create mode 100644 src/linux/uuid.h

diff --git a/src/barebox-state/state.c b/src/barebox-state/state.c
index 29e04c3d7ea8..42ce88d3f161 100644
--- a/src/barebox-state/state.c
+++ b/src/barebox-state/state.c
@@ -22,6 +22,7 @@
 #include <crc.h>
 #include <linux/err.h>
 #include <linux/list.h>
+#include <linux/uuid.h>
 
 #include <linux/mtd/mtd-abi.h>
 #include <malloc.h>
@@ -592,6 +593,8 @@ static char *cdev_to_devpath(struct cdev *cdev, off_t 
*offset, size_t *size)
 }
 #endif
 
+static guid_t barebox_state_partition_guid = BAREBOX_STATE_PARTITION_GUID;
+
 /*
  * state_new_from_node - create a new state instance from a device_node
  *
@@ -638,6 +641,29 @@ struct state *state_new_from_node(struct device_node 
*node, bool readonly)
                goto out_release_state;
        }
 
+       /* Is the backend referencing an on-disk partitonable block device? */
+       if (cdev_is_block_disk(cdev)) {
+               struct cdev *partcdev = NULL;
+
+               if (cdev_is_gpt_partitioned(cdev))
+                       partcdev = cdev_find_child_by_typeuuid(cdev, 
&barebox_state_partition_guid);
+
+               if (!partcdev) {
+                       ret = -EINVAL;
+                       goto out_release_state;
+               }
+
+               pr_debug("%s: backend GPT partition looked up via 
PartitionTypeGUID\n",
+                        node->full_name);
+
+               cdev = partcdev;
+       }
+
+       state->backend_path = cdev_to_devpath(cdev, &offset, &size);
+
+       pr_debug("%s: backend resolved to %s %lld %zu\n", node->full_name,
+                state->backend_path, (long long)offset, size);
+
        state->backend_reproducible_name = 
of_get_reproducible_name(partition_node);
 
        ret = of_property_read_string(node, "backend-type", &backend_type);
diff --git a/src/dt/dt.h b/src/dt/dt.h
index f8bd3e56efed..a4213d49606a 100644
--- a/src/dt/dt.h
+++ b/src/dt/dt.h
@@ -5,6 +5,7 @@
 #include <dt/list.h>
 #include <dt/common.h>
 #include <asm/byteorder.h>
+#include <linux/uuid.h>
 
 /* Default string compare functions */
 #define of_compat_cmp(s1, s2, l)       strcasecmp((s1), (s2))
@@ -234,6 +235,10 @@ int of_get_devicepath(struct device_node *partition_node, 
char **devnode, off_t
 struct cdev *of_cdev_find(struct device_node *partition_node);
 char *cdev_to_devpath(struct cdev *cdev, off_t *offset, size_t *size);
 
+bool cdev_is_block_disk(struct cdev *cdev);
+bool cdev_is_gpt_partitioned(struct cdev *cdev);
+struct cdev *cdev_find_child_by_typeuuid(struct cdev *cdev, guid_t *typeuuid);
+
 #define for_each_node_by_name(dn, name) \
        for (dn = of_find_node_by_name(NULL, name); dn; \
             dn = of_find_node_by_name(dn, name))
diff --git a/src/libdt-utils.sym b/src/libdt-utils.sym
index f95c74305fb0..63536224e518 100644
--- a/src/libdt-utils.sym
+++ b/src/libdt-utils.sym
@@ -36,6 +36,9 @@ global:
        of_get_devicepath;
        of_cdev_find;
        cdev_to_devpath;
+       cdev_is_block_disk;
+       cdev_is_gpt_partitioned;
+       cdev_find_child_by_typeuuid;
        of_get_next_available_child;
        of_get_parent;
        of_get_property;
diff --git a/src/libdt.c b/src/libdt.c
index 4c000919d305..af28de6f17c6 100644
--- a/src/libdt.c
+++ b/src/libdt.c
@@ -30,12 +30,15 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <libudev.h>
+#include <sys/sysmacros.h>
 #include <dt.h>
 
 struct cdev {
        char *devpath;
        off_t offset;
        size_t size;
+       bool is_gpt_partitioned;
+       bool is_block_disk;
 };
 
 static int pr_level = 5;
@@ -2230,7 +2233,7 @@ static bool region_contains(loff_t starta, loff_t enda,
 static int cdev_from_block_device(struct udev_device *dev,
                                  struct cdev *cdev)
 {
-
+       const char *devtype;
        struct udev *udev;
        struct udev_enumerate *enumerate;
        struct udev_list_entry *devices, *dev_list_entry;
@@ -2250,7 +2253,7 @@ static int cdev_from_block_device(struct udev_device *dev,
        udev_enumerate_scan_devices(enumerate);
        devices = udev_enumerate_get_list_entry(enumerate);
        udev_list_entry_foreach(dev_list_entry, devices) {
-               const char *path, *devtype;
+               const char *path;
                path = udev_list_entry_get_name(dev_list_entry);
                part = udev_device_new_from_syspath(udev, path);
                /* distinguish device (disk) from partitions */
@@ -2288,8 +2291,15 @@ static int cdev_from_block_device(struct udev_device 
*dev,
                }
        }
 
-       if (best_match)
+       if (best_match) {
                cdev->devpath = strdup(udev_device_get_devnode(best_match));
+               if (!strcmp(devtype, "disk")) {
+                       const char *part_type;
+
+                       part_type = udev_device_get_property_value(best_match, 
"ID_PART_TABLE_TYPE");
+                       cdev->is_gpt_partitioned = part_type && 
!strcmp(part_type, "gpt");
+               }
+       }
 
        udev_enumerate_unref(enumerate);
        udev_unref(udev);
@@ -2526,6 +2536,8 @@ static int __of_cdev_find(struct device_node 
*partition_node, struct cdev *cdev)
 
        cdev->offset = 0;
        cdev->size = 0;
+       cdev->is_gpt_partitioned = false;
+       cdev->is_block_disk = false;
 
        /*
         * simplest case: This nodepath can directly be translated into
@@ -2541,6 +2553,11 @@ static int __of_cdev_find(struct device_node 
*partition_node, struct cdev *cdev)
                if (!udev_parse_mtd(dev, &cdev->devpath, &cdev->size))
                        return 0;
 
+               if (!cdev_from_block_device(dev, cdev)) {
+                       cdev->is_block_disk = true;
+                       return 0;
+               }
+
                /*
                 * If we found a device but couldn't classify it above, we fall
                 * through.
@@ -2718,3 +2735,81 @@ char *cdev_to_devpath(struct cdev *cdev, off_t *offset, 
size_t *size)
        *size = cdev->size;
        return cdev->devpath;
 }
+
+static struct udev_device *udev_from_devpath(struct udev *udev,
+                                            const char *devpath)
+{
+       char syspath[64];
+       struct stat st;
+       const char *type;
+       int ret;
+
+       ret = stat(devpath, &st);
+       if (ret)
+               return NULL;
+
+       if (S_ISBLK(st.st_mode))
+               type = "block";
+       else if (S_ISCHR(st.st_mode))
+               type = "char";
+       else
+               return NULL;
+
+       if (major(st.st_rdev) == 0)
+               return NULL;
+
+       snprintf(syspath, sizeof(syspath), "/sys/dev/%s/%u:%u",
+                type, major(st.st_rdev), minor(st.st_rdev));
+
+       return udev_device_new_from_syspath(udev, syspath);
+}
+
+struct cdev *cdev_find_child_by_typeuuid(struct cdev *cdev, guid_t *typeuuid)
+{
+       struct cdev *new = NULL;
+       struct udev_device *parent, *child;
+       struct udev *udev;
+       u64 size;
+       int ret;
+
+       udev = udev_new();
+       if (!udev)
+               return NULL;
+
+       parent = udev_from_devpath(udev, cdev->devpath);
+       if (!parent)
+               goto udev_unref;
+
+       child = of_find_device_by_uuid(parent, typeuuid->str, true);
+       if (!child)
+               goto udev_unref_parent;
+
+       ret = udev_device_parse_sysattr_u64(child, "size", &size);
+       if (ret)
+               goto udev_unref_child;
+
+       new = xzalloc(sizeof(*new));
+
+       new->offset = 0;
+       new->size = size * 512;
+       new->devpath = strdup(udev_device_get_devnode(child));
+
+udev_unref_child:
+       udev_device_unref(child);
+udev_unref_parent:
+       udev_device_unref(parent);
+udev_unref:
+       udev_unref(udev);
+
+       return new;
+}
+
+bool cdev_is_block_disk(struct cdev *cdev)
+{
+       return cdev->is_block_disk;
+}
+
+bool cdev_is_gpt_partitioned(struct cdev *cdev)
+{
+       return cdev->is_gpt_partitioned;
+}
diff --git a/src/linux/uuid.h b/src/linux/uuid.h
new file mode 100644
index 000000000000..1bc7cfc94040
--- /dev/null
+++ b/src/linux/uuid.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 Pengutronix, Ahmad Fatoum <a.fat...@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _LINUX_UUID_H_
+#define _LINUX_UUID_H_
+
+#define   UUID_STRING_LEN         36
+
+/* We only do string comparisons anyway */
+typedef struct { const char str[UUID_STRING_LEN + 1]; } guid_t;
+
+#define GUID_INIT(str) { str }
+
+#endif
diff --git a/src/state.h b/src/state.h
index d98b781c2089..889df43b5780 100644
--- a/src/state.h
+++ b/src/state.h
@@ -2,6 +2,7 @@
 #define __STATE_H
 
 #include <of.h>
+#include <linux/uuid.h>
 
 struct state;
 
@@ -55,4 +56,7 @@ static inline int state_read_mac(struct state *state, const 
char *name, u8 *buf)
 
 #endif /* #if IS_ENABLED(CONFIG_STATE) / #else */
 
+#define BAREBOX_STATE_PARTITION_GUID \
+       GUID_INIT("4778ed65-bf42-45fa-9c5b-287a1dc4aab1")
+
 #endif /* __STATE_H */
-- 
2.39.2


Reply via email to