[dpdk-dev] [RFC 2/6] eal/fdt: implement FDT API for Linux
Jan Viktorin
viktorin at rehivetech.com
Sat Mar 26 02:12:34 CET 2016
The Linux FDT API implementation reads the /proc/device-tree structure. Each
FDT entry is represented by a file or directory there.
Signed-off-by: Jan Viktorin <viktorin at rehivetech.com>
---
lib/librte_eal/common/eal_common_fdt.c | 317 +++++++++++++++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 3 +
lib/librte_eal/linuxapp/eal/eal_fdt.c | 336 +++++++++++++++++++++++++++++++++
3 files changed, 656 insertions(+)
create mode 100644 lib/librte_eal/common/eal_common_fdt.c
create mode 100644 lib/librte_eal/linuxapp/eal/eal_fdt.c
diff --git a/lib/librte_eal/common/eal_common_fdt.c b/lib/librte_eal/common/eal_common_fdt.c
new file mode 100644
index 0000000..29d08c1
--- /dev/null
+++ b/lib/librte_eal/common/eal_common_fdt.c
@@ -0,0 +1,317 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 RehiveTech. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of RehiveTech nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <rte_fdt.h>
+#include <rte_debug.h>
+
+bool rte_fdt_path_is_valid(const char *name)
+{
+ size_t i;
+ size_t len = 0;
+
+ if (name == NULL)
+ return 0;
+
+ len = strlen(name);
+ if (len == 0)
+ return 0;
+
+ if (!strcmp(name, "."))
+ return 0;
+ if (!strcmp(name, ".."))
+ return 0;
+
+ for (i = 0; i < len; ++i) {
+ if (strchr("/\\", name[i]))
+ return 0;
+ }
+
+ return 1;
+}
+
+struct rte_fdt_path *rte_fdt_path_pushs(struct rte_fdt_path *base,
+ const char *top)
+{
+ struct rte_fdt_path *path;
+ size_t toplen;
+
+ RTE_VERIFY(top != NULL);
+ RTE_VERIFY(rte_fdt_path_is_valid(top));
+
+ toplen = strlen(top);
+
+ path = malloc(sizeof(*path) + toplen + 1);
+ if (path == NULL)
+ return NULL;
+
+ path->name = (char *) (path + 1);
+ memcpy(path->name, top, toplen);
+ path->name[toplen] = '\0';
+
+ path->base = base;
+ if (base != NULL)
+ base->top = path;
+
+ path->top = NULL;
+ return path;
+}
+
+struct rte_fdt_path *rte_fdt_path_pop(struct rte_fdt_path *path)
+{
+ struct rte_fdt_path *base;
+ RTE_VERIFY(path != NULL);
+
+ base = path->base;
+ free(path);
+
+ if (base != NULL)
+ base->top = NULL;
+
+ return base;
+}
+
+struct rte_fdt_path *rte_fdt_path_dup(const struct rte_fdt_path *path)
+{
+ struct rte_fdt_path *copy = NULL;
+ struct rte_fdt_path *tmp = NULL;
+ const struct rte_fdt_path *cur = path;
+
+ if (cur == NULL)
+ return NULL;
+
+ while (cur->base != NULL)
+ cur = cur->base;
+
+ /* copy all but the top most path component */
+ while (cur != path) {
+ tmp = rte_fdt_path_pushs(copy, cur->name);
+ if (tmp == NULL) {
+ rte_fdt_path_free(copy);
+ return NULL;
+ }
+
+ copy = tmp;
+ cur = cur->top;
+ }
+
+ /* copy the top most path component */
+ tmp = rte_fdt_path_pushs(copy, path->name);
+ if (tmp == NULL) {
+ rte_fdt_path_free(copy);
+ return NULL;
+ }
+
+ copy = tmp;
+ return copy;
+}
+
+struct rte_fdt_path *rte_fdt_path_free(struct rte_fdt_path *path)
+{
+ while (path != NULL)
+ path = rte_fdt_path_pop(path);
+
+ return NULL;
+}
+
+int rte_fdt_path_parse(struct rte_fdt_path **p, const char *path)
+{
+ const char *cur;
+ const char *end;
+ size_t pathlen;
+ struct rte_fdt_path *base = NULL;
+ struct rte_fdt_path *tmp = NULL;
+ char name[PATH_MAX];
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pathlen = strlen(path);
+ if (pathlen == 0) {
+ errno = EINVAL;
+ return -2;
+ }
+
+ cur = path;
+
+ if (cur[0] != '/') {
+ errno = EINVAL;
+ return -3;
+ }
+
+ /* root "/" */
+ if (cur[1] == '\0') {
+ *p = NULL;
+ return 0;
+ }
+ cur += 1;
+
+ do {
+ end = strchr(cur, '/');
+ if (end == NULL)
+ end = path + pathlen;
+
+ if (end - cur == 0)
+ break; /* strip the ending '/' */
+
+ RTE_VERIFY(end >= cur);
+ RTE_VERIFY(end - cur < PATH_MAX);
+ memcpy(name, cur, end - cur);
+ name[end - cur] = '\0';
+
+ if (!strcmp(name, "."))
+ goto next_cur;
+
+ if (!strcmp(name, "..")) {
+ if (base)
+ base = rte_fdt_path_pop(base);
+ goto next_cur;
+ }
+
+ if (!rte_fdt_path_is_valid(name))
+ goto name_invalid;
+
+ tmp = rte_fdt_path_pushs(base, name);
+ if (tmp == NULL)
+ goto push_failed;
+
+ base = tmp;
+next_cur:
+ if (*end == '\0')
+ break;
+
+ cur = end + 1;
+ } while(end != '\0');
+
+ *p = base;
+ return 0;
+
+name_invalid:
+ errno = EINVAL;
+ return -4;
+push_failed:
+ rte_fdt_path_free(base);
+ return -5;
+}
+
+/**
+ * Compute the length of the base using the delimiter '/'. An optional new top
+ * can be specified. If the top is NULL, only the base is examined. The NUL
+ * character is included.
+ */
+static size_t fdt_path_length(const struct rte_fdt_path *base, const char *top)
+{
+ size_t len = 0;
+
+ if (base == NULL && top == NULL)
+ return strlen("/") + 1;
+
+ while (base != NULL) {
+ RTE_VERIFY(base->name != NULL);
+ /* '/' + <name> */
+ len += 1 + strlen(base->name);
+ base = base->base;
+ }
+
+ if (top != NULL) {
+ /* '/' + <name> */
+ len += 1 + strlen(top);
+ }
+
+ return len + 1; /* append NUL */
+}
+
+char *rte_fdt_path_tostr(const struct rte_fdt_path *base, const char *top)
+{
+ const size_t len = fdt_path_length(base, top);
+ const struct rte_fdt_path *cur = base;
+ char *s;
+ char *p;
+
+ if (base == NULL && top == NULL)
+ return strdup("/");
+
+ s = malloc(len);
+ if (s == NULL)
+ return NULL;
+
+ if (base == NULL /* && top != NULL */) {
+ memcpy(s + 1, top, len - 2);
+ s[0] = '/';
+ s[len] = '\0';
+ return s;
+ }
+
+ /* find the bottom, the root component of the path */
+ while (cur->base != NULL)
+ cur = cur->base;
+
+ p = s;
+
+ /* copy the base from the bottom into the target string */
+ while (cur != NULL) {
+ size_t curlen = strlen(cur->name);
+
+ RTE_VERIFY(p - s + curlen + 1 < len);
+
+ p[0] = '/';
+ memcpy(p + 1, cur->name, curlen);
+ p += curlen + 1;
+
+ if (cur == base)
+ break;
+
+ cur = cur->top;
+ }
+
+ /* append top if exists */
+ if (top != NULL) {
+ size_t toplen = strlen(top);
+
+ RTE_VERIFY(p - s + toplen + 1 < len);
+
+ p[0] = '/';
+ memcpy(p + 1, top, toplen);
+ p += toplen + 1;
+ }
+
+ RTE_VERIFY(p - s + 1 == (off_t) len);
+ p[0] = '\0';
+
+ return s;
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index e109361..e4b33b5 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -61,6 +61,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_memory.c
ifeq ($(CONFIG_RTE_LIBRTE_XEN_DOM0),y)
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_xen_memory.c
endif
+SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_fdt.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_thread.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_log.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_pci.c
@@ -82,6 +83,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_timer.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_memzone.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_log.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_launch.c
+SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_fdt.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_pci.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_pci_uio.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal_common_memory.c
@@ -113,6 +115,7 @@ CFLAGS_eal_lcore.o := -D_GNU_SOURCE
CFLAGS_eal_thread.o := -D_GNU_SOURCE
CFLAGS_eal_log.o := -D_GNU_SOURCE
CFLAGS_eal_common_log.o := -D_GNU_SOURCE
+CFLAGS_eal_fdt.o := -D_GNU_SOURCE
CFLAGS_eal_hugepage_info.o := -D_GNU_SOURCE
CFLAGS_eal_pci.o := -D_GNU_SOURCE
CFLAGS_eal_pci_uio.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/eal_fdt.c b/lib/librte_eal/linuxapp/eal/eal_fdt.c
new file mode 100644
index 0000000..48c6b52
--- /dev/null
+++ b/lib/librte_eal/linuxapp/eal/eal_fdt.c
@@ -0,0 +1,336 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 RehiveTech. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of RehiveTech nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <rte_debug.h>
+#include <rte_log.h>
+#include <rte_byteorder.h>
+#include <rte_fdt.h>
+
+struct rte_fdt {
+ char *path;
+};
+
+struct rte_fdt *rte_fdt_open(const char *path)
+{
+ struct rte_fdt *fdt;
+ size_t pathlen;
+
+ if (path == NULL) {
+ path = "/proc/device-tree";
+ pathlen = strlen("/proc/device-tree");
+ } else {
+ pathlen = strlen(path);
+ }
+
+ fdt = malloc(sizeof(*fdt) + pathlen + 1);
+ if (fdt == NULL)
+ return NULL;
+
+ fdt->path = (char *) (fdt + 1);
+ memcpy(fdt->path, path, pathlen);
+ fdt->path[pathlen] = '\0';
+
+ return fdt;
+}
+
+void rte_fdt_close(struct rte_fdt *fdt)
+{
+ RTE_VERIFY(fdt != NULL);
+
+ fdt->path = NULL;
+ free(fdt);
+}
+
+static int concat_and_abspath(char path[PATH_MAX],
+ const char *p1, size_t p1len,
+ const char *p2, size_t p2len)
+{
+ char *tmppath;
+
+ tmppath = malloc(p1len + 1 + p2len + 1);
+ if (tmppath == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): failed to malloc %zu B\n", __func__,
+ p1len + 1 + p2len + 1);
+ return -1;
+ }
+
+ memcpy(tmppath, p1, p1len);
+ tmppath[p1len] = '/';
+ memcpy(tmppath + p1len + 1, p2, p2len);
+ tmppath[p1len + p2len + 1] = '\0';
+
+ if (realpath(tmppath, path) == NULL) {
+ long _e = errno;
+ RTE_LOG(ERR, EAL, "%s(): realpath has failed for '%s'\n",
+ __func__, tmppath);
+ RTE_LOG(ERR, EAL, "reason: '%s'\n", strerror(_e));
+ free(tmppath);
+ return -2;
+ }
+
+ free(tmppath);
+ return 0;
+}
+
+static int fdt_path_open(struct rte_fdt *fdt, const struct rte_fdt_path *base,
+ const char *top)
+{
+ char *relpath = rte_fdt_path_tostr(base, top);
+ char path[PATH_MAX];
+ char root[PATH_MAX];
+ int fd;
+
+ RTE_VERIFY(fdt != NULL);
+ RTE_VERIFY(relpath[0] == '/');
+
+ if (relpath == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): failed to convert "
+ "base path to string\n", __func__);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (concat_and_abspath(path, fdt->path, strlen(fdt->path),
+ relpath + 1, strlen(relpath + 1))) {
+ RTE_LOG(ERR, EAL, "%s(): failed to derive absolute path from "
+ "the root ('%s') and the FDT path ('%s')\n",
+ __func__, fdt->path, relpath);
+ free(relpath);
+ errno = ENOMEM;
+ return -2;
+ }
+ free(relpath); /* not needed anymore */
+
+ /* ensure we have the fdt->path as a real and absolute path */
+ if (realpath(fdt->path, root) == NULL) {
+ long _e = errno;
+ RTE_LOG(ERR, EAL, "%s(): realpath of '%s' has failed",
+ __func__, fdt->path);
+ errno = _e;
+ return -3;
+ }
+
+ if (strstr(path, root) != path) {
+ /* We are out of the fdt->path */
+ RTE_LOG(ERR, EAL, "%s(): attempt to access out "
+ "of the root path: '%s'\n", __func__, path);
+ errno = EACCES;
+ return -4;
+ }
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ RTE_LOG(ERR, EAL, "%s(): failed to open the FDT path '%s'\n",
+ __func__, path);
+ return -5;
+ }
+
+ return fd;
+}
+
+static ssize_t read_all(int fd, char *b, size_t bmax)
+{
+ size_t total = 0;
+
+ while (total < bmax) {
+ ssize_t rlen = read(fd, b + total, bmax - total);
+ if (rlen < 0)
+ return -1;
+
+ total += rlen;
+ }
+
+ return total;
+}
+
+static ssize_t fdt_path_read(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base, const char *top,
+ void *b, size_t bmax)
+{
+ int fd;
+ struct stat st;
+ size_t goal;
+ ssize_t ret;
+
+ if ((fd = fdt_path_open(fdt, base, top)) < 0)
+ return -1;
+
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return -2;
+ }
+
+ goal = st.st_size;
+ ret = read_all(fd, b, bmax > goal? goal : bmax);
+
+ close(fd);
+ return ret;
+}
+
+static ssize_t fdt_path_readx(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base, const char *top,
+ void *v, size_t vmax, size_t vsize)
+{
+ size_t bmax = vmax * vsize;
+ ssize_t ret;
+
+ ret = fdt_path_read(fdt, base, top, v, bmax);
+ if (ret < 0)
+ return -1;
+
+ return ret / vsize;
+}
+
+ssize_t rte_fdt_path_read(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base,
+ const char *top, char *b, size_t blen)
+{
+ return fdt_path_read(fdt, base, top, b, blen);
+}
+
+ssize_t rte_fdt_path_read32(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base, const char *top,
+ uint32_t *v, size_t vmax)
+{
+ ssize_t ret;
+ ssize_t i;
+
+ ret = fdt_path_readx(fdt, base, top, (void *) v, vmax, sizeof(*v));
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0; i < ret; ++i)
+ v[i] = rte_be_to_cpu_32(v[i]);
+
+ return ret;
+}
+
+ssize_t rte_fdt_path_read64(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base, const char *top,
+ uint64_t *v, size_t vmax)
+{
+ ssize_t ret;
+ ssize_t i;
+
+ ret = fdt_path_readx(fdt, base, top, (void *) v, vmax, sizeof(*v));
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0; i < ret; ++i)
+ v[i] = rte_be_to_cpu_64(v[i]);
+
+ return ret;
+
+}
+
+ssize_t rte_fdt_path_reads(struct rte_fdt *fdt,
+ const struct rte_fdt_path *base,
+ const char *top, char **s)
+{
+ int fd;
+ struct stat st;
+ size_t goal;
+ ssize_t ret;
+ char *b;
+
+ if ((fd = fdt_path_open(fdt, base, top)) < 0)
+ return -1;
+
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return -2;
+ }
+
+ goal = st.st_size;
+
+ b = malloc(goal + 1);
+ if (b == NULL) {
+ close(fd);
+ return -3;
+ }
+
+
+ if ((ret = read_all(fd, b, goal)) < 0) {
+ free(b);
+ close(fd);
+ return -4;
+ }
+
+ b[goal] = '\0';
+
+ close(fd);
+ *s = b;
+ return ret;
+}
+
+int rte_fdt_path_walk(struct rte_fdt *fdt, const struct rte_fdt_path *base,
+ int (*f)(struct rte_fdt *, const struct rte_fdt_path *,
+ const char *, void *), void *context)
+{
+ int fd;
+ DIR *dir;
+ struct dirent entry;
+ struct dirent *cur = NULL;
+ int ret = 0;
+
+ RTE_VERIFY(f != NULL);
+
+ if ((fd = fdt_path_open(fdt, base, NULL)) < 0)
+ return -1;
+
+ dir = fdopendir(fd);
+ if (dir == NULL) {
+ close(fd);
+ return -2;
+ }
+
+ while ((readdir_r(dir, &entry, &cur)) == 0 && cur != NULL) {
+ if (!rte_fdt_path_is_valid(cur->d_name))
+ continue;
+
+ ret = f(fdt, base, cur->d_name, context);
+ if (ret != 0)
+ break;
+ }
+
+ closedir(dir); /* calls close() */
+ return ret;
+}
--
2.7.0
More information about the dev
mailing list