[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC v2 3/8] xen/arm: Export host device-tree to hypfs
If enabled, host device-tree will be exported to hypfs and can be accessed through /devicetree path. Exported device-tree has the same format, as the device-tree exported to the sysfs by the Linux kernel. This is useful when XEN toolstack needs an access to the host device-tree. Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx> --- xen/arch/arm/Kconfig | 8 + xen/arch/arm/Makefile | 1 + xen/arch/arm/host_dtb_export.c | 307 +++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 xen/arch/arm/host_dtb_export.c diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index ecfa6822e4..895016b21e 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -33,6 +33,14 @@ config ACPI Advanced Configuration and Power Interface (ACPI) support for Xen is an alternative to device tree on ARM64. +config HOST_DTB_EXPORT + bool "Export host device tree to hypfs if enabled" + depends on ARM && HYPFS && !ACPI + ---help--- + + Export host device-tree to hypfs so toolstack can have an access for the + host device tree from Dom0. If you unsure say N. + config GICV3 bool "GICv3 driver" depends on ARM_64 && !NEW_VGIC diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 07f634508e..0a41f68f8c 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -8,6 +8,7 @@ obj-y += platforms/ endif obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_HAS_VPCI) += vpci.o +obj-$(CONFIG_HOST_DTB_EXPORT) += host_dtb_export.o obj-$(CONFIG_HAS_ALTERNATIVE) += alternative.o obj-y += bootfdt.init.o diff --git a/xen/arch/arm/host_dtb_export.c b/xen/arch/arm/host_dtb_export.c new file mode 100644 index 0000000000..794395683c --- /dev/null +++ b/xen/arch/arm/host_dtb_export.c @@ -0,0 +1,307 @@ +/* + * xen/arch/arm/host_dtb_export.c + * + * Export host device-tree to the hypfs so toolstack can access + * host device-tree from Dom0 + * + * Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx> + * Copyright (C) 2021, EPAM Systems. + * + * 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. + */ + +#include <xen/device_tree.h> +#include <xen/err.h> +#include <xen/guest_access.h> +#include <xen/hypfs.h> +#include <xen/init.h> + +#define HOST_DT_DIR "devicetree" + +static int host_dt_dir_read(const struct hypfs_entry *entry, + XEN_GUEST_HANDLE_PARAM(void) uaddr); +static unsigned int host_dt_dir_getsize(const struct hypfs_entry *entry); + +static const struct hypfs_entry *host_dt_dir_enter( + const struct hypfs_entry *entry); +static void host_dt_dir_exit(const struct hypfs_entry *entry); + +static struct hypfs_entry *host_dt_dir_findentry( + const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len); + +static const struct hypfs_funcs host_dt_dir_funcs = { + .enter = host_dt_dir_enter, + .exit = host_dt_dir_exit, + .read = host_dt_dir_read, + .write = hypfs_write_deny, + .getsize = host_dt_dir_getsize, + .findentry = host_dt_dir_findentry, +}; + +static int host_dt_prop_read(const struct hypfs_entry *entry, + XEN_GUEST_HANDLE_PARAM(void) uaddr); + +static unsigned int host_dt_prop_getsize(const struct hypfs_entry *entry); + +const struct hypfs_funcs host_dt_prop_ro_funcs = { + .enter = host_dt_dir_enter, + .exit = host_dt_dir_exit, + .read = host_dt_prop_read, + .write = hypfs_write_deny, + .getsize = host_dt_prop_getsize, + .findentry = hypfs_leaf_findentry, +}; + +static HYPFS_DIR_INIT_FUNC(dt_dir, "node_template", &host_dt_dir_funcs); + +#define HYPFS_PROPERTY_MAX_SIZE 256 +static HYPFS_VARSIZE_INIT(dt_prop, XEN_HYPFS_TYPE_BLOB, "prop_template", + HYPFS_PROPERTY_MAX_SIZE, &host_dt_prop_ro_funcs); + +static const char *get_name_from_path(const char *path) +{ + const char *name = strrchr(path, '/'); + if ( !name ) + name = path; + else + { + name++; + if ( !*name ) + name--; + } + + return name; +} + +static char *get_root_from_path(const char *path, char *name) +{ + const char *nm = strchr(path, '/'); + if ( !nm ) + nm = path + strlen(path); + else + { + if ( !*nm ) + nm--; + } + + return memcpy(name, path, nm - path); +} + +static int host_dt_dir_read(const struct hypfs_entry *entry, + XEN_GUEST_HANDLE_PARAM(void) uaddr) +{ + int ret = 0; + struct dt_device_node *node; + struct dt_device_node *child; + const struct dt_property *prop; + struct hypfs_dyndir_id *data; + + data = hypfs_get_dyndata(); + if ( !data ) + return -EINVAL; + + node = data->data; + if ( !node ) + return -EINVAL; + + dt_for_each_property_node( node, prop ) + { + ret = hypfs_read_dyndir_entry(&dt_prop.e, prop->name, + strlen(prop->name), + !prop->next && !node->child, + &uaddr); + + if ( ret ) + break; + } + + for ( child = node->child; child != NULL; child = child->sibling ) + { + const char *parsed_name = get_name_from_path(child->full_name); + data->data = child; + + ret = hypfs_read_dyndir_entry(&dt_dir.e, parsed_name, + strlen(parsed_name), + child->sibling == NULL, + &uaddr); + + if ( ret ) + break; + } + + return ret; +} + +static unsigned int host_dt_dir_getsize(const struct hypfs_entry *entry) +{ + struct dt_device_node *node; + struct dt_device_node *child; + struct hypfs_dyndir_id *data; + const struct dt_property *prop; + unsigned int size = 0; + + data = hypfs_get_dyndata(); + if ( !data ) + return -EINVAL; + + node = data->data; + if ( !node ) + return -EINVAL; + + dt_for_each_property_node( node, prop ) + { + size += hypfs_dyndir_entry_size(entry, prop->name); + } + + for ( child = node->child; child != NULL; child = child->sibling ) + { + const char *parsed_name = get_name_from_path(child->full_name); + size += hypfs_dyndir_entry_size(entry, parsed_name); + } + + return size; +} + +static DEFINE_PER_CPU(bool, data_alloc); + +static inline bool data_is_alloc(void) +{ + unsigned int cpu = smp_processor_id(); + return per_cpu(data_alloc, cpu); +} + +static inline void set_data_alloc(void) +{ + unsigned int cpu = smp_processor_id(); + ASSERT(!per_cpu(data_alloc, cpu)); + + this_cpu(data_alloc) = true; +} + +static inline void unset_data_alloc(void) +{ + this_cpu(data_alloc) = false; +} + +static const struct hypfs_entry *host_dt_dir_enter( + const struct hypfs_entry *entry) +{ + struct hypfs_dyndir_id *data; + + if ( !data_is_alloc() ) + { + data = hypfs_alloc_dyndata(struct hypfs_dyndir_id); + if ( !data ) + return ERR_PTR(-ENOMEM); + + set_data_alloc(); + } + + if ( strcmp(entry->name, HOST_DT_DIR) == 0 ) + { + data = hypfs_get_dyndata(); + data->data = dt_host; + } + + return entry; +} + +static void host_dt_dir_exit(const struct hypfs_entry *entry) +{ + if ( !data_is_alloc() ) + return; + + hypfs_free_dyndata(); + unset_data_alloc(); +} + +static struct hypfs_entry *host_dt_dir_findentry( + const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len) +{ + struct dt_device_node *node; + char root_name[HYPFS_DYNDIR_ID_NAMELEN]; + struct dt_device_node *child; + struct hypfs_dyndir_id *data; + struct dt_property *prop; + + data = hypfs_get_dyndata(); + if ( !data ) + return ERR_PTR(-EINVAL); + + node = data->data; + if ( !node ) + return ERR_PTR(-EINVAL); + + memset(root_name, 0, sizeof(root_name)); + get_root_from_path(name, root_name); + + for ( child = node->child; child != NULL; child = child->sibling ) + { + if ( strcmp(get_name_from_path(child->full_name), root_name) == 0 ) + return hypfs_gen_dyndir_entry(&dt_dir.e, + get_name_from_path(child->full_name), child); + } + + dt_for_each_property_node( node, prop ) + { + + if ( dt_property_name_is_equal(prop, root_name) ) + return hypfs_gen_dyndir_entry(&dt_prop.e, prop->name, prop); + } + + return ERR_PTR(-ENOENT); +}; + +static int host_dt_prop_read(const struct hypfs_entry *entry, + XEN_GUEST_HANDLE_PARAM(void) uaddr) +{ + const struct dt_property *prop; + struct hypfs_dyndir_id *data; + + data = hypfs_get_dyndata(); + if ( !data ) + return -EINVAL; + + prop = data->data; + if ( !prop ) + return -EINVAL; + + return copy_to_guest(uaddr, prop->value, prop->length) ? -EFAULT : 0; +} + +static unsigned int host_dt_prop_getsize(const struct hypfs_entry *entry) +{ + const struct hypfs_dyndir_id *data; + const struct dt_property *prop; + + data = hypfs_get_dyndata(); + if ( !data ) + return -EINVAL; + + prop = data->data; + if ( !prop ) + return -EINVAL; + + return prop->length; +} + +static HYPFS_DIR_INIT_FUNC(host_dt_dir, HOST_DT_DIR, &host_dt_dir_funcs); + +static int __init host_dtb_export_init(void) +{ + ASSERT(dt_host && (dt_host->sibling == NULL)); + unset_data_alloc(); + + hypfs_add_dir(&hypfs_root, &host_dt_dir, true); + hypfs_add_dyndir(&hypfs_root, &dt_dir); + return 0; +} +__initcall(host_dtb_export_init); -- 2.27.0
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |