[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v3 21/24] tools/(lib)xl: Add partial device tree support for ARM



On Tue, 13 Jan 2015, Julien Grall wrote:
> Let the user to pass additional nodes to the guest device tree. For this
> purpose, everything in the node /passthrough from the partial device tree will
> be copied into the guest device tree.
> 
> The node /aliases will be also copied to allow the user to define aliases
> which can be used by the guest kernel.
> 
> A simple partial device tree will look like:
> 
> /dts-v1/;
> 
> / {
>         #address-cells = <2>;
>         #size-cells = <2>;
> 
>         passthrough {
>             compatible = "simple-bus";
>             ranges;
>             #address-cells = <2>;
>             #size-cells = <2>;
> 
>             /* List of your nodes */
>         }
> };

It would be nice to have an example of this under tools/examples.


> Note that:
>     * The interrupt-parent proporties will be added by the toolstack in
>     the root node
>     * The properties compatible, ranges, #address-cells and #size-cells
>     in /passthrough are mandatory.
> 
> Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx>
> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
> Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
> 
> ---
>     Changes in v3:
>         - Patch added
> ---
>  docs/man/xl.cfg.pod.5       |   7 ++
>  tools/libxl/libxl_arm.c     | 253 
> ++++++++++++++++++++++++++++++++++++++++++++
>  tools/libxl/libxl_types.idl |   1 +
>  tools/libxl/xl_cmdimpl.c    |   1 +
>  4 files changed, 262 insertions(+)
> 
> diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5
> index e2f91fc..225b782 100644
> --- a/docs/man/xl.cfg.pod.5
> +++ b/docs/man/xl.cfg.pod.5
> @@ -398,6 +398,13 @@ not emulated.
>  Specify that this domain is a driver domain. This enables certain
>  features needed in order to run a driver domain.
>  
> +=item B<device_tree=PATH>
> +
> +Specify a partial device tree (compiled via the Device Tree Compiler).
> +Everything under the node "/passthrough" will be copied into the guest
> +device tree. For convenience, the node "/aliases" is also copied to allow
> +the user to defined aliases which can be used by the guest kernel.
> +
>  =back
>  
>  =head2 Devices
> diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c
> index 53177eb..619458b 100644
> --- a/tools/libxl/libxl_arm.c
> +++ b/tools/libxl/libxl_arm.c
> @@ -540,6 +540,238 @@ out:
>      }
>  }
>  
> +static bool check_overrun(uint64_t a, uint64_t b, uint32_t max)
> +{
> +    return ((a + b) > UINT_MAX || (a + b) > max);
> +}
> +
> +/* Only FDT v17 is supported */
> +#define FDT_REQUIRED_VERSION    0x11
> +
> +static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
> +{
> +    int r;
> +
> +    if (size < FDT_V17_SIZE) {
> +        LOG(ERROR, "Partial FDT is too small");
> +        return ERROR_FAIL;
> +    }
> +
> +    if (fdt_magic(fdt) != FDT_MAGIC) {
> +        LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
> +        return ERROR_FAIL;
> +    }
> +
> +    if (fdt_version(fdt) != FDT_REQUIRED_VERSION) {
> +        LOG(ERROR, "Partial FDT version not supported. Required 0x%x got 
> 0x%x",
> +            FDT_REQUIRED_VERSION, fdt_version(fdt));
> +        return ERROR_FAIL;
> +    }
> +
> +    r = fdt_check_header(fdt);
> +    if (r) {
> +        LOG(ERROR, "Failed to check the partial FDT (%d)", r);
> +        return ERROR_FAIL;
> +    }
> +
> +    /* Check if the *size and off* fields doesn't overrun the totalsize
> +     * of the partial FDT.
> +     */
> +    if (fdt_totalsize(fdt) > size) {
> +        LOG(ERROR, "Partial FDT totalsize is too big");
> +        return ERROR_FAIL;
> +    }
> +
> +    size = fdt_totalsize(fdt);
> +    if (fdt_off_dt_struct(fdt) > size ||
> +        fdt_off_dt_strings(fdt) > size ||
> +        check_overrun(fdt_off_dt_struct(fdt), fdt_size_dt_struct(fdt), size) 
> ||
> +        check_overrun(fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt), 
> size)) {
> +        LOG(ERROR, "Failed to validate the header of the partial FDT");
> +        return ERROR_FAIL;
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * Check if a string stored the strings block section is correctly
> + * nul-terminated.
> + * off_dt_strings and size_dt_strings fields have been validity-check
> + * earlier, so it's safe to use them here.
> + */
> +static bool check_string(void *fdt, int nameoffset)
> +{
> +    const char *str = fdt_string(fdt, nameoffset);
> +
> +    for (; nameoffset < fdt_size_dt_strings(fdt); nameoffset++, str++) {
> +        if (*str == '\0')
> +            return true;
> +    }
> +
> +    return false;
> +}

strnlen?


> +static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
> +                           int nodeoff)
> +{
> +    int propoff, nameoff, r;
> +    const struct fdt_property *prop;
> +
> +    for (propoff = fdt_first_property_offset(pfdt, nodeoff);
> +         propoff >= 0;
> +         propoff = fdt_next_property_offset(pfdt, propoff)) {
> +
> +        if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
> +            return -FDT_ERR_INTERNAL;
> +        }
> +
> +        /*
> +         * Libfdt doesn't perform any check on the validity of a string
> +         * stored in the strings block section. As the property name is
> +         * stored there, check it.
> +         */
> +        nameoff = fdt32_to_cpu(prop->nameoff);
> +        if (!check_string(pfdt, nameoff)) {
> +            LOG(ERROR, "The strings block section of the partial FDT is 
> malformed");
> +            return -FDT_ERR_BADSTRUCTURE;
> +        }
> +
> +        r = fdt_property(fdt, fdt_string(pfdt, nameoff),
> +                         prop->data, fdt32_to_cpu(prop->len));
> +        if (r) return r;
> +    }
> +
> +    /* FDT_ERR_NOTFOUND => There is no more properties for this node */
> +    return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
> +}
> +
> +/*
> + * Based on fdt_first_subnode and fdt_next_subnode.
> + * We can't use the one helpers provided by libfdt because:
> + *      - It has been introduced in 2013 => Doesn't work on wheezy
> + *      - The prototypes exist but the functions are not exported. Don't
> + *      ask why...
> + *      - There is no version in the header (might be better to use
> + *      configure for this purpose?)
> + */
> +static int _fdt_first_subnode(const void *fdt, int offset)
> +{
> +    int depth = 0;
> +
> +    offset = fdt_next_node(fdt, offset, &depth);
> +    if (offset < 0 || depth != 1)
> +        return -FDT_ERR_NOTFOUND;
> +
> +    return offset;
> +}
> +
> +static int _fdt_next_subnode(const void *fdt, int offset)
> +{
> +    int depth = 1;
> +
> +    /*
> +     * With respect to the parent, the depth of the next subnode will be
> +     * the same as the last.
> +     */
> +    do {
> +        offset = fdt_next_node(fdt, offset, &depth);
> +        if (offset < 0 || depth < 1)
> +            return -FDT_ERR_NOTFOUND;
> +    } while (depth > 1);
> +
> +    return offset;
> +}
> +
> +/* Limit the maxixum depth of a node because of the recursion */
> +#define MAX_DEPTH   8
> +
> +/* Copy a node from the partial device tree to the guest device tree */
> +static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
> +                     int nodeoff, int depth)
> +{
> +    int r;
> +
> +    if (depth >= MAX_DEPTH) {
> +        LOG(ERROR, "Partial FDT contains a too depth node");
> +        return -FDT_ERR_INTERNAL;
> +    }
> +
> +    r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
> +    if (r) return r;
> +
> +    r = copy_properties(gc, fdt, pfdt, nodeoff);
> +    if (r) return r;
> +
> +    for (nodeoff = _fdt_first_subnode(pfdt, nodeoff);
> +         nodeoff >= 0;
> +         nodeoff = _fdt_next_subnode(pfdt, nodeoff)) {
> +        r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
> +        if (r) return r;
> +    }
> +
> +    if (nodeoff != -FDT_ERR_NOTFOUND)
> +        return nodeoff;
> +
> +    r = fdt_end_node(fdt);
> +    if (r) return r;
> +
> +    return 0;
> +}
> +
> +static int copy_node_by_path(libxl__gc *gc, const char *path,
> +                             void *fdt, void *pfdt)
> +{
> +    int nodeoff, r;
> +    const char *name = strrchr(path, '/');
> +
> +    if (!name)
> +        return -FDT_ERR_INTERNAL;
> +
> +    name++;
> +
> +    /* The FDT function to look at node doesn't take into account the
> +     * unit (i.e anything after @) when search by name. Check if the
> +     * name exactly match.
> +     */
> +    nodeoff = fdt_path_offset(pfdt, path);
> +    if (nodeoff < 0)
> +        return nodeoff;
> +
> +    if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
> +        return -FDT_ERR_NOTFOUND;

Are we sure that the string returned by fdt_get_name is NULL terminated?


> +    r = copy_node(gc, fdt, pfdt, nodeoff, 0);
> +    if (r) return r;
> +
> +    return 0;
> +}
> +
> +/*
> + * The partial device tree is not copied entirely. Only the relevant bits are
> + * copied to the guest device tree:
> + *  - /passthrough node
> + *  - /aliases node
> + */
> +static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
> +{
> +    int r;
> +
> +    r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
> +    if (r < 0) {
> +        LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial 
> FDT");
> +        return r;
> +    }
> +
> +    r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
> +    if (r < 0 && r != -FDT_ERR_NOTFOUND) {
> +        LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
> +        return r;
> +    }
> +
> +    return 0;
> +}
> +
>  #define FDT_MAX_SIZE (1<<20)
>  
>  int libxl__arch_domain_init_hw_description(libxl__gc *gc,
> @@ -548,8 +780,10 @@ int libxl__arch_domain_init_hw_description(libxl__gc *gc,
>                                             struct xc_dom_image *dom)
>  {
>      void *fdt = NULL;
> +    void *pfdt = NULL;
>      int rc, res;
>      size_t fdt_size = 0;
> +    int pfdt_size = 0;
>  
>      const libxl_version_info *vers;
>      const struct arch_info *ainfo;
> @@ -569,6 +803,22 @@ int libxl__arch_domain_init_hw_description(libxl__gc *gc,
>          vers->xen_version_major, vers->xen_version_minor);
>      LOG(DEBUG, " - vGIC version: %s\n", 
> gicv_to_string(xc_config->gic_version));
>  
> +    if (info->device_tree) {
> +        LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
> +
> +        rc = libxl_read_file_contents(CTX, info->device_tree,
> +                                      &pfdt, &pfdt_size);
> +        if (rc) {
> +            LOGEV(ERROR, rc, "failed to read the partial device file %s",
> +                  info->device_tree);
> +            return ERROR_FAIL;
> +        }
> +        libxl__ptr_add(gc, pfdt);
> +
> +        if (check_partial_fdt(gc, pfdt, pfdt_size))
> +            return ERROR_FAIL;
> +    }
> +
>  /*
>   * Call "call" handling FDT_ERR_*. Will either:
>   * - loop back to retry_resize
> @@ -635,6 +885,9 @@ next_resize:
>          FDT( make_timer_node(gc, fdt, ainfo) );
>          FDT( make_hypervisor_node(gc, fdt, vers) );
>  
> +        if (pfdt)
> +            FDT( copy_partial_fdt(gc, fdt, pfdt) );
> +
>          FDT( fdt_end_node(fdt) );
>  
>          FDT( fdt_finish(fdt) );
> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
> index 1214d2e..5651110 100644
> --- a/tools/libxl/libxl_types.idl
> +++ b/tools/libxl/libxl_types.idl
> @@ -399,6 +399,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
>      ("kernel",           string),
>      ("cmdline",          string),
>      ("ramdisk",          string),
> +    ("device_tree",      string),
>      ("u", KeyedUnion(None, libxl_domain_type, "type",
>                  [("hvm", Struct(None, [("firmware",         string),
>                                         ("bios",             libxl_bios_type),
> diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
> index 0b02a6c..31e89e8 100644
> --- a/tools/libxl/xl_cmdimpl.c
> +++ b/tools/libxl/xl_cmdimpl.c
> @@ -1184,6 +1184,7 @@ static void parse_config_data(const char *config_source,
>  
>      xlu_cfg_replace_string (config, "kernel", &b_info->kernel, 0);
>      xlu_cfg_replace_string (config, "ramdisk", &b_info->ramdisk, 0);
> +    xlu_cfg_replace_string (config, "device_tree", &b_info->device_tree, 0);
>      b_info->cmdline = parse_cmdline(config);
>  
>      xlu_cfg_get_defbool(config, "driver_domain", &c_info->driver_domain, 0);
> -- 
> 2.1.4
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.