[PATCH v2 10/11] lib: utils: Add helper routines to populate domains from FDT
Anup Patel
Anup.Patel at wdc.com
Sat Dec 5 04:51:20 EST 2020
> -----Original Message-----
> From: Anup Patel <Anup.Patel at wdc.com>
> Sent: 04 December 2020 21:22
> To: Atish Patra <Atish.Patra at wdc.com>; Alistair Francis
> <Alistair.Francis at wdc.com>
> Cc: Anup Patel <anup at brainfault.org>; opensbi at lists.infradead.org; Anup
> Patel <Anup.Patel at wdc.com>
> Subject: [PATCH v2 10/11] lib: utils: Add helper routines to populate domains
> from FDT
>
> We add various helper routines to populate domains, iterate domains,
> iterate domain memregions, and parse HART to domain assignment from the
> FDT.
>
> These helper routines can be used by platform support code and FDT fixup
> code.
>
> Signed-off-by: Anup Patel <anup.patel at wdc.com>
> Reviewed-by: Atish Patra <atish.patra at wdc.com>
> ---
> include/sbi_utils/fdt/fdt_domain.h | 80 ++++++
> lib/utils/fdt/fdt_domain.c | 445 +++++++++++++++++++++++++++++
> lib/utils/fdt/objects.mk | 1 +
> 3 files changed, 526 insertions(+)
> create mode 100644 include/sbi_utils/fdt/fdt_domain.h
> create mode 100644 lib/utils/fdt/fdt_domain.c
>
> diff --git a/include/sbi_utils/fdt/fdt_domain.h
> b/include/sbi_utils/fdt/fdt_domain.h
> new file mode 100644
> index 0000000..3c02d56
> --- /dev/null
> +++ b/include/sbi_utils/fdt/fdt_domain.h
> @@ -0,0 +1,80 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * fdt_domain.c - Flat Device Tree Domain helper routines
> + *
> + * Copyright (c) 2020 Western Digital Corporation or its affiliates.
> + *
> + * Authors:
> + * Anup Patel <anup.patel at wdc.com>
> + */
> +
> +#ifndef __FDT_DOMAIN_H__
> +#define __FDT_DOMAIN_H__
> +
> +#include <sbi/sbi_types.h>
> +
> +struct sbi_domain;
> +
> +/**
> + * Iterate over each domains in device tree
> + *
> + * @param fdt device tree blob
> + * @param opaque private pointer for each iteration
> + * @param fn callback function for each iteration */ void
> +fdt_iterate_each_domain(void *fdt, void *opaque,
> + void (*fn)(void *fdt, int domain_offset,
> + void *opaque));
> +
> +/**
> + * Iterate over each memregion of a domain in device tree
> + *
> + * @param fdt device tree blob
> + * @param domain_offset domain DT node offset
> + * @param opaque private pointer for each iteration
> + * @param fn callback function for each iteration */ void
> +fdt_iterate_each_memregion(void *fdt, int domain_offset, void *opaque,
> + void (*fn)(void *fdt, int domain_offset,
> + int region_offset, u32
> region_access,
> + void *opaque));
> +
> +/**
> + * Fix up the domain configuration in the device tree
> + *
> + * This routine:
> + * 1. Disables MMIO devices not accessible to the coldboot HART domain
> + * 2. Removes "opensbi-domain" DT property from CPU DT nodes
> + * 3. Removes domain configuration DT node under /chosen DT node
> + *
> + * It is recommended that platform support call this function in
> + * their final_init() platform operation.
> + *
> + * @param fdt device tree blob
> + */
> +void fdt_domain_fixup(void *fdt);
> +
> +/**
> + * Get domain instance for given HART
> + *
> + * Note: Domains should be populated before using this function.
> + *
> + * @param hartid the HART for which domain instance is needed
> + *
> + * @return pointer to domain instance on success and NULL on failure
> +*/ struct sbi_domain *fdt_domain_get(u32 hartid);
> +
> +/**
> + * Populate domains from device tree
> + *
> + * It is recommended that platform support call this function in
> + * their domains_init() platform operation.
> + *
> + * @param fdt device tree blob
> + *
> + * @return 0 on success and negative error code on failure */ int
> +fdt_domains_populate(void *fdt);
> +
> +#endif /* __FDT_DOMAIN_H__ */
> diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c new file
> mode 100644 index 0000000..972a1f4
> --- /dev/null
> +++ b/lib/utils/fdt/fdt_domain.c
> @@ -0,0 +1,445 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * fdt_domain.c - Flat Device Tree Domain helper routines
> + *
> + * Copyright (c) 2020 Western Digital Corporation or its affiliates.
> + *
> + * Authors:
> + * Anup Patel <anup.patel at wdc.com>
> + */
> +
> +#include <libfdt.h>
> +#include <sbi/sbi_domain.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_hartmask.h>
> +#include <sbi/sbi_scratch.h>
> +#include <sbi_utils/fdt/fdt_domain.h>
> +#include <sbi_utils/fdt/fdt_helper.h>
> +
> +void fdt_iterate_each_domain(void *fdt, void *opaque,
> + void (*fn)(void *fdt, int domain_offset,
> + void *opaque))
> +{
> + int doffset, poffset;
> +
> + if (!fdt || !fn)
> + return;
> +
> + poffset = fdt_path_offset(fdt, "/chosen");
> + if (poffset < 0)
> + return;
> + poffset = fdt_node_offset_by_compatible(fdt, poffset,
> + "opensbi,domain,config");
> + if (poffset < 0)
> + return;
> +
> + fdt_for_each_subnode(doffset, fdt, poffset) {
> + if (fdt_node_check_compatible(fdt, doffset,
> + "opensbi,domain,instance"))
> + continue;
> +
> + fn(fdt, doffset, opaque);
> + }
> +}
> +
> +void fdt_iterate_each_memregion(void *fdt, int domain_offset, void
> *opaque,
> + void (*fn)(void *fdt, int domain_offset,
> + int region_offset, u32
> region_access,
> + void *opaque))
> +{
> + u32 i, rcount;
> + int len, region_offset;
> + const u32 *regions;
> +
> + if (!fdt || (domain_offset < 0) || !fn)
> + return;
> +
> + if (fdt_node_check_compatible(fdt, domain_offset,
> + "opensbi,domain,instance"))
> + return;
> +
> + regions = fdt_getprop(fdt, domain_offset, "regions", &len);
> + if (!regions)
> + return;
> +
> + rcount = (u32)len / (sizeof(u32) * 2);
> + for (i = 0; i < rcount; i++) {
> + region_offset = fdt_node_offset_by_phandle(fdt,
> + fdt32_to_cpu(regions[2 * i]));
> + if (region_offset < 0)
> + continue;
> +
> + if (fdt_node_check_compatible(fdt, region_offset,
> + "opensbi,domain,memregion"))
> + continue;
> +
> + fn(fdt, domain_offset, region_offset,
> + fdt32_to_cpu(regions[(2 * i) + 1]), opaque);
> + }
> +}
> +
> +struct __fixup_find_domain_offset_info {
> + const char *name;
> + int *doffset;
> +};
> +
> +static void __fixup_find_domain_offset(void *fdt, int doff, void *p) {
> + struct __fixup_find_domain_offset_info *fdo = p;
> +
> + if (sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL)))
> + return;
> +
> + *fdo->doffset = doff;
> +}
> +
> +#define DISABLE_DEVICES_MASK
> (SBI_DOMAIN_MEMREGION_READABLE | \
> + SBI_DOMAIN_MEMREGION_WRITEABLE | \
> + SBI_DOMAIN_MEMREGION_EXECUTABLE)
> +
> +static void __fixup_count_disable_devices(void *fdt, int doff, int roff,
> + u32 perm, void *p)
> +{
> + int len;
> + u32 *dcount = p;
> +
> + if (perm & DISABLE_DEVICES_MASK)
> + return;
> +
> + len = 0;
> + if (fdt_getprop(fdt, roff, "devices", &len))
> + *dcount += len / sizeof(u32);
> +}
> +
> +static void __fixup_disable_devices(void *fdt, int doff, int roff,
> + u32 raccess, void *p)
> +{
> + int i, len, coff;
> + const u32 *devices;
> +
> + if (raccess & DISABLE_DEVICES_MASK)
> + return;
> +
> + len = 0;
> + devices = fdt_getprop(fdt, roff, "devices", &len);
> + if (!devices)
> + return;
> + len = len / sizeof(u32);
> +
> + for (i = 0; i < len; i++) {
> + coff = fdt_node_offset_by_phandle(fdt,
> + fdt32_to_cpu(devices[i]));
> + if (coff < 0)
> + continue;
> +
> + fdt_setprop_string(fdt, coff, "status", "disabled");
> + }
> +}
> +
> +void fdt_domain_fixup(void *fdt)
> +{
> + u32 i, dcount;
> + int err, poffset, doffset;
> + struct sbi_domain *dom = sbi_domain_thishart_ptr();
> + struct __fixup_find_domain_offset_info fdo;
> +
> + /* Remove the domain assignment DT property from CPU DT nodes
> */
> + poffset = fdt_path_offset(fdt, "/cpus");
> + if (poffset < 0)
> + return;
> + fdt_for_each_subnode(doffset, fdt, poffset) {
> + err = fdt_parse_hart_id(fdt, doffset, &i);
> + if (err)
> + continue;
> +
> + fdt_nop_property(fdt, doffset, "opensbi-domain");
> + }
> +
> + /* Skip device disable for root domain */
> + if (!dom->index)
> + goto skip_device_disable;
> +
> + /* Find current domain DT node */
> + doffset = -1;
> + fdo.name = dom->name;
> + fdo.doffset = &doffset;
> + fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset);
> + if (doffset < 0)
> + goto skip_device_disable;
> +
> + /* Count current domain device DT nodes to be disabled */
> + dcount = 0;
> + fdt_iterate_each_memregion(fdt, doffset, &dcount,
> + __fixup_count_disable_devices);
> + if (!dcount)
> + goto skip_device_disable;
> +
> + /* Expand FDT based on device DT nodes to be disabled */
> + err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + dcount * 32);
> + if (err < 0)
> + return;
> +
> + /* Again find current domain DT node */
> + doffset = -1;
> + fdo.name = dom->name;
> + fdo.doffset = &doffset;
> + fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset);
> + if (doffset < 0)
> + goto skip_device_disable;
> +
> + /* Disable device DT nodes for current domain */
> + fdt_iterate_each_memregion(fdt, doffset, NULL,
> + __fixup_disable_devices);
> +skip_device_disable:
> +
> + /* Remove the OpenSBI domain config DT node */
> + poffset = fdt_path_offset(fdt, "/chosen");
> + if (poffset < 0)
> + return;
> + poffset = fdt_node_offset_by_compatible(fdt, poffset,
> + "opensbi,domain,config");
> + if (poffset < 0)
> + return;
> + fdt_nop_node(fdt, poffset);
> +}
> +
> +static struct sbi_domain
> *fdt_hartid_to_domain[SBI_HARTMASK_MAX_BITS];
> +
> +#define FDT_DOMAIN_MAX_COUNT 8
> +#define FDT_DOMAIN_REGION_MAX_COUNT 16
> +
> +static u32 fdt_domains_count;
> +static struct sbi_domain fdt_domains[FDT_DOMAIN_MAX_COUNT];
> +static struct sbi_hartmask fdt_masks[FDT_DOMAIN_MAX_COUNT]; static
> +struct sbi_domain_memregion
> +
> fdt_regions[FDT_DOMAIN_MAX_COUNT][FDT_DOMAIN_REGION_
> MAX_COUNT + 2];
> +
> +struct sbi_domain *fdt_domain_get(u32 hartid) {
> + if (SBI_HARTMASK_MAX_BITS <= hartid)
> + return NULL;
> + return fdt_hartid_to_domain[hartid];
> +}
> +
> +static void __fdt_parse_region(void *fdt, int domain_offset,
> + int region_offset, u32 region_access,
> + void *opaque)
> +{
> + int len;
> + u32 val32;
> + u64 val64;
> + const u32 *val;
> + u32 *region_count = opaque;
> + struct sbi_domain_memregion *region;
> +
> + /* Find next region of the domain */
> + if (FDT_DOMAIN_REGION_MAX_COUNT <= *region_count)
> + return;
> + region = &fdt_regions[fdt_domains_count][*region_count];
> +
> + /* Read "base" DT property */
> + val = fdt_getprop(fdt, region_offset, "base", &len);
> + if (!val && len >= 8)
> + return;
> + val64 = fdt32_to_cpu(val[0]);
> + val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
> + region->base = val64;
> +
> + /* Read "order" DT property */
> + val = fdt_getprop(fdt, region_offset, "order", &len);
> + if (!val && len >= 4)
> + return;
> + val32 = fdt32_to_cpu(*val);
> + if (val32 < 3 || __riscv_xlen < val32)
> + return;
> + region->order = val32;
> +
> + /* Read "mmio" DT property */
> + region->flags = region_access &
> SBI_DOMAIN_MEMREGION_ACCESS_MASK;
> + if (fdt_get_property(fdt, region_offset, "mmio", NULL))
> + region->flags |= SBI_DOMAIN_MEMREGION_MMIO;
> +
> + (*region_count)++;
> +}
> +
> +static void __fdt_parse_domain(void *fdt, int domain_offset, void
> +*opaque) {
> + u32 val32;
> + u64 val64;
> + const u32 *val;
> + struct sbi_domain *dom;
> + struct sbi_hartmask *mask;
> + int i, err, len, cpu_offset;
> + int *cold_domain_offset = opaque;
> + struct sbi_domain_memregion *regions;
> +
> + /* Sanity check on maximum domains we can handle */
> + if (FDT_DOMAIN_MAX_COUNT <= fdt_domains_count)
> + return;
> + dom = &fdt_domains[fdt_domains_count];
> + mask = &fdt_masks[fdt_domains_count];
> + regions = &fdt_regions[fdt_domains_count][0];
> +
> + /* Read DT node name */
> + sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
> + sizeof(dom->name));
> + dom->name[sizeof(dom->name) - 1] = '\0';
> +
> + /* Setup possible HARTs mask */
> + SBI_HARTMASK_INIT(mask);
> + dom->possible_harts = mask;
> + val = fdt_getprop(fdt, domain_offset, "possible-harts", &len);
> + len = len / sizeof(u32);
> + if (val && len) {
> + for (i = 0; i < len; i++) {
> + cpu_offset = fdt_node_offset_by_phandle(fdt,
> + fdt32_to_cpu(val[i]));
> + if (cpu_offset < 0)
> + continue;
> +
> + err = fdt_parse_hart_id(fdt, cpu_offset, &val32);
> + if (err)
> + continue;
> +
> + sbi_hartmask_set_hart(val32, mask);
> + }
> + }
> +
> + /* Setup memregions from DT */
> + val32 = 0;
> + sbi_memset(regions, 0,
> + sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT +
> 2));
> + dom->regions = regions;
> + fdt_iterate_each_memregion(fdt, domain_offset, &val32,
> + __fdt_parse_region);
> + sbi_domain_memregion_initfw(®ions[val32]);
> +
> + /* Read "boot-hart" DT property */
> + val32 = -1U;
> + val = fdt_getprop(fdt, domain_offset, "boot-hart", &len);
> + if (val && len >= 4) {
> + cpu_offset = fdt_node_offset_by_phandle(fdt,
> + fdt32_to_cpu(*val));
> + if (cpu_offset >= 0)
> + fdt_parse_hart_id(fdt, cpu_offset, &val32);
> + } else {
> + if (domain_offset == *cold_domain_offset)
> + val32 = current_hartid();
> + }
> + dom->boot_hartid = val32;
> +
> + /* Read "next-arg1" DT property */
> + val64 = 0;
> + val = fdt_getprop(fdt, domain_offset, "next-arg1", &len);
> + if (val && len >= 8) {
> + val64 = fdt32_to_cpu(val[0]);
> + val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
> + } else {
> + if (domain_offset == *cold_domain_offset)
> + val64 = sbi_scratch_thishart_ptr()->next_arg1;
> + }
> + dom->next_arg1 = val64;
> +
> + /* Read "next-addr" DT property */
> + val64 = 0;
> + val = fdt_getprop(fdt, domain_offset, "next-addr", &len);
> + if (val && len >= 8) {
> + val64 = fdt32_to_cpu(val[0]);
> + val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
> + } else {
> + if (domain_offset == *cold_domain_offset)
> + val64 = sbi_scratch_thishart_ptr()->next_addr;
> + }
> + dom->next_addr = val64;
> +
> + /* Read "next-mode" DT property */
> + val32 = 0x1;
> + val = fdt_getprop(fdt, domain_offset, "next-mode", &len);
> + if (val && len >= 4) {
> + val32 = fdt32_to_cpu(val[0]);
> + if (val32 != 0x0 && val32 != 0x1)
> + val32 = 0x1;
> + } else {
> + if (domain_offset == *cold_domain_offset)
> + val32 = sbi_scratch_thishart_ptr()->next_mode;
> + }
> + dom->next_mode = val32;
> +
> + /* Read "system-reset-allowed" DT property */
> + if (fdt_get_property(fdt, domain_offset,
> + "system-reset-allowed", NULL))
> + dom->system_reset_allowed = TRUE;
> + else
> + dom->system_reset_allowed = FALSE;
> +
> + /* Increment domains count */
> + fdt_domains_count++;
> +}
> +
> +int fdt_domains_populate(void *fdt)
> +{
> + const u32 *val;
> + int cold_domain_offset;
> + u32 i, hartid, cold_hartid;
> + int err, len, cpus_offset, cpu_offset, domain_offset;
> +
> + /* Sanity checks */
> + if (!fdt)
> + return SBI_EINVAL;
> +
> + /* Find /cpus DT node */
> + cpus_offset = fdt_path_offset(fdt, "/cpus");
> + if (cpus_offset < 0)
> + return cpus_offset;
> +
> + /* Find coldboot HART domain DT node offset */
> + cold_domain_offset = -1;
> + cold_hartid = current_hartid();
> + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
> + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
> + if (err)
> + continue;
> +
> + if (hartid != cold_hartid)
> + continue;
> +
> + val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
> + if (val && len >= 4)
> + cold_domain_offset =
> fdt_node_offset_by_phandle(fdt,
> +
> fdt32_to_cpu(*val));
> +
> + break;
> + }
> +
> + /* Iterate over each domain in FDT and populate details */
> + fdt_iterate_each_domain(fdt, &cold_domain_offset,
> __fdt_parse_domain);
> +
> + /* HART to domain assignment based on CPU DT nodes*/
> + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
> + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
> + if (err)
> + continue;
> +
> + if (SBI_HARTMASK_MAX_BITS <= hartid)
> + continue;
> +
> + val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
> + if (!val || len < 4)
> + continue;
> +
> + domain_offset = fdt_node_offset_by_phandle(fdt,
> +
> fdt32_to_cpu(*val));
> + if (domain_offset < 0)
> + continue;
> +
> + for (i = 0; i < fdt_domains_count; i++) {
> + if (!sbi_strcmp(fdt_domains[i].name,
> + fdt_get_name(fdt, domain_offset, NULL))) {
> + fdt_hartid_to_domain[hartid] =
> &fdt_domains[i];
> + break;
> + }
> + }
> + }
> +
> + return 0;
> +}
> diff --git a/lib/utils/fdt/objects.mk b/lib/utils/fdt/objects.mk index
> b860a83..d9f1eae 100644
> --- a/lib/utils/fdt/objects.mk
> +++ b/lib/utils/fdt/objects.mk
> @@ -4,5 +4,6 @@
> # Copyright (C) 2020 Bin Meng <bmeng.cn at gmail.com> #
>
> +libsbiutils-objs-y += fdt/fdt_domain.o
> libsbiutils-objs-y += fdt/fdt_helper.o
> libsbiutils-objs-y += fdt/fdt_fixup.o
> --
> 2.25.1
Applied this patch to the riscv/opensbi repo.
Regards,
Anup
More information about the opensbi
mailing list