[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