From nobody Sat Sep 21 05:52:01 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=fail(p=none dis=none) header.from=arm.com Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1699520819053723.5996438660583; Thu, 9 Nov 2023 01:06:59 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.629508.981819 (Exim 4.92) (envelope-from ) id 1r10zh-00062d-11; Thu, 09 Nov 2023 09:06:33 +0000 Received: by outflank-mailman (output) from mailman id 629508.981819; Thu, 09 Nov 2023 09:06:32 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zg-00062R-Tw; Thu, 09 Nov 2023 09:06:32 +0000 Received: by outflank-mailman (input) for mailman id 629508; Thu, 09 Nov 2023 09:06:31 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10ze-0005ED-S2 for xen-devel@lists.xenproject.org; Thu, 09 Nov 2023 09:06:31 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-sth1.inumbo.com (Halon) with ESMTP id 44697726-7edf-11ee-98da-6d05b1d4d9a1; Thu, 09 Nov 2023 10:06:28 +0100 (CET) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D52241655; Thu, 9 Nov 2023 01:07:12 -0800 (PST) Received: from e125770.cambridge.arm.com (e125770.arm.com [10.1.199.1]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2E32D3F703; Thu, 9 Nov 2023 01:06:27 -0800 (PST) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 44697726-7edf-11ee-98da-6d05b1d4d9a1 From: Luca Fancellu To: xen-devel@lists.xenproject.org Cc: Stefano Stabellini , Julien Grall , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk Subject: [PATCH v3 3/5] arm/dom0less: put dom0less feature code in a separate module Date: Thu, 9 Nov 2023 09:06:13 +0000 Message-Id: <20231109090615.3878767-4-luca.fancellu@arm.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231109090615.3878767-1-luca.fancellu@arm.com> References: <20231109090615.3878767-1-luca.fancellu@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1699520819528100001 Content-Type: text/plain; charset="utf-8" Currently the dom0less feature code is mostly inside domain_build.c and setup.c, it is a feature that may not be useful to everyone so put the code in a different compilation module in order to make it easier to disable the feature in the future. Move gic_interrupt_t in domain_build.h to use it with the function declaration, move its comment above the declaration. The following functions are now visible externally from domain_build because they are used also from the dom0less-build module: - get_allocation_size - set_interrupt - domain_fdt_begin_node - make_memory_node - make_resv_memory_node - make_hypervisor_node - make_psci_node - make_cpus_node - make_timer_node - handle_device_interrupts - construct_domain - process_shm - allocate_bank_memory The functions allocate_static_memory and assign_static_memory_11 are now externally visible, so put their declarations into domain_build.h and move the #else and stub definition in the header as well. Move is_dom0less_mode from setup.c to dom0less-build.c and make it externally visible. The function allocate_bank_memory is used only by dom0less code at the moment, but it's been decided to leave it in domain_build.c in case that in the future the dom0 code can use it. Where spotted, fix code style issues. No functional change is intended. Signed-off-by: Luca Fancellu --- Changes from v2: - move allocate_bank_memory back in domain_build.c, remove header from dom0less-build.c. --- xen/arch/arm/Makefile | 1 + xen/arch/arm/dom0less-build.c | 1018 +++++++++++++++++ xen/arch/arm/domain_build.c | 1265 +++------------------ xen/arch/arm/include/asm/dom0less-build.h | 25 + xen/arch/arm/include/asm/domain_build.h | 60 + xen/arch/arm/include/asm/setup.h | 1 - xen/arch/arm/setup.c | 33 +- 7 files changed, 1245 insertions(+), 1158 deletions(-) create mode 100644 xen/arch/arm/dom0less-build.c create mode 100644 xen/arch/arm/include/asm/dom0less-build.h diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 81c31c36fc3d..70dd7201ef30 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -15,6 +15,7 @@ obj-y +=3D cpufeature.o obj-y +=3D decode.o obj-y +=3D device.o obj-$(CONFIG_IOREQ_SERVER) +=3D dm.o +obj-y +=3D dom0less-build.init.o obj-y +=3D domain.o obj-y +=3D domain_build.init.o obj-$(CONFIG_ARCH_MAP_DOMAIN_PAGE) +=3D domain_page.o diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c new file mode 100644 index 000000000000..1ca9d39043d6 --- /dev/null +++ b/xen/arch/arm/dom0less-build.c @@ -0,0 +1,1018 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +bool __init is_dom0less_mode(void) +{ + struct bootmodules *mods =3D &bootinfo.modules; + struct bootmodule *mod; + unsigned int i; + bool dom0found =3D false; + bool domUfound =3D false; + + /* Look into the bootmodules */ + for ( i =3D 0 ; i < mods->nr_mods ; i++ ) + { + mod =3D &mods->module[i]; + /* Find if dom0 and domU kernels are present */ + if ( mod->kind =3D=3D BOOTMOD_KERNEL ) + { + if ( mod->domU =3D=3D false ) + { + dom0found =3D true; + break; + } + else + domUfound =3D true; + } + } + + /* + * If there is no dom0 kernel but at least one domU, then we are in + * dom0less mode + */ + return ( !dom0found && domUfound ); +} + +static void __init allocate_memory(struct domain *d, struct kernel_info *k= info) +{ + unsigned int i; + paddr_t bank_size; + + printk(XENLOG_INFO "Allocating mappings totalling %ldMB for %pd:\n", + /* Don't want format this as PRIpaddr (16 digit hex) */ + (unsigned long)(kinfo->unassigned_mem >> 20), d); + + kinfo->mem.nr_banks =3D 0; + bank_size =3D MIN(GUEST_RAM0_SIZE, kinfo->unassigned_mem); + if ( !allocate_bank_memory(d, kinfo, gaddr_to_gfn(GUEST_RAM0_BASE), + bank_size) ) + goto fail; + + bank_size =3D MIN(GUEST_RAM1_SIZE, kinfo->unassigned_mem); + if ( !allocate_bank_memory(d, kinfo, gaddr_to_gfn(GUEST_RAM1_BASE), + bank_size) ) + goto fail; + + if ( kinfo->unassigned_mem ) + goto fail; + + for( i =3D 0; i < kinfo->mem.nr_banks; i++ ) + { + printk(XENLOG_INFO "%pd BANK[%d] %#"PRIpaddr"-%#"PRIpaddr" (%ldMB)= \n", + d, + i, + kinfo->mem.bank[i].start, + kinfo->mem.bank[i].start + kinfo->mem.bank[i].size, + /* Don't want format this as PRIpaddr (16 digit hex) */ + (unsigned long)(kinfo->mem.bank[i].size >> 20)); + } + + return; + +fail: + panic("Failed to allocate requested domain memory." + /* Don't want format this as PRIpaddr (16 digit hex) */ + " %ldKB unallocated. Fix the VMs configurations.\n", + (unsigned long)kinfo->unassigned_mem >> 10); +} + +#ifdef CONFIG_VGICV2 +static int __init make_gicv2_domU_node(struct kernel_info *kinfo) +{ + void *fdt =3D kinfo->fdt; + int res =3D 0; + __be32 reg[(GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * 2]; + __be32 *cells; + const struct domain *d =3D kinfo->d; + + res =3D domain_fdt_begin_node(fdt, "interrupt-controller", + vgic_dist_base(&d->arch.vgic)); + if ( res ) + return res; + + res =3D fdt_property_cell(fdt, "#address-cells", 0); + if ( res ) + return res; + + res =3D fdt_property_cell(fdt, "#interrupt-cells", 3); + if ( res ) + return res; + + res =3D fdt_property(fdt, "interrupt-controller", NULL, 0); + if ( res ) + return res; + + res =3D fdt_property_string(fdt, "compatible", "arm,gic-400"); + if ( res ) + return res; + + cells =3D ®[0]; + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, + vgic_dist_base(&d->arch.vgic), GUEST_GICD_SIZE); + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, + vgic_cpu_base(&d->arch.vgic), GUEST_GICC_SIZE); + + res =3D fdt_property(fdt, "reg", reg, sizeof(reg)); + if (res) + return res; + + res =3D fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); + if (res) + return res; + + res =3D fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); + if (res) + return res; + + res =3D fdt_end_node(fdt); + + return res; +} +#endif + +#ifdef CONFIG_GICV3 +static int __init make_gicv3_domU_node(struct kernel_info *kinfo) +{ + void *fdt =3D kinfo->fdt; + int res =3D 0; + __be32 *reg, *cells; + const struct domain *d =3D kinfo->d; + unsigned int i, len =3D 0; + + res =3D domain_fdt_begin_node(fdt, "interrupt-controller", + vgic_dist_base(&d->arch.vgic)); + if ( res ) + return res; + + res =3D fdt_property_cell(fdt, "#address-cells", 0); + if ( res ) + return res; + + res =3D fdt_property_cell(fdt, "#interrupt-cells", 3); + if ( res ) + return res; + + res =3D fdt_property(fdt, "interrupt-controller", NULL, 0); + if ( res ) + return res; + + res =3D fdt_property_string(fdt, "compatible", "arm,gic-v3"); + if ( res ) + return res; + + /* reg specifies all re-distributors and Distributor. */ + len =3D (GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * + (d->arch.vgic.nr_regions + 1) * sizeof(__be32); + reg =3D xmalloc_bytes(len); + if ( reg =3D=3D NULL ) + return -ENOMEM; + cells =3D reg; + + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, + vgic_dist_base(&d->arch.vgic), GUEST_GICV3_GICD_SIZ= E); + + for ( i =3D 0; i < d->arch.vgic.nr_regions; i++ ) + dt_child_set_range(&cells, + GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, + d->arch.vgic.rdist_regions[i].base, + d->arch.vgic.rdist_regions[i].size); + + res =3D fdt_property(fdt, "reg", reg, len); + xfree(reg); + if (res) + return res; + + res =3D fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); + if (res) + return res; + + res =3D fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); + if (res) + return res; + + res =3D fdt_end_node(fdt); + + return res; +} +#endif + +static int __init make_gic_domU_node(struct kernel_info *kinfo) +{ + switch ( kinfo->d->arch.vgic.version ) + { +#ifdef CONFIG_GICV3 + case GIC_V3: + return make_gicv3_domU_node(kinfo); +#endif +#ifdef CONFIG_VGICV2 + case GIC_V2: + return make_gicv2_domU_node(kinfo); +#endif + default: + panic("Unsupported GIC version\n"); + } +} + +#ifdef CONFIG_SBSA_VUART_CONSOLE +static int __init make_vpl011_uart_node(struct kernel_info *kinfo) +{ + void *fdt =3D kinfo->fdt; + int res; + gic_interrupt_t intr; + __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS]; + __be32 *cells; + struct domain *d =3D kinfo->d; + + res =3D domain_fdt_begin_node(fdt, "sbsa-uart", d->arch.vpl011.base_ad= dr); + if ( res ) + return res; + + res =3D fdt_property_string(fdt, "compatible", "arm,sbsa-uart"); + if ( res ) + return res; + + cells =3D ®[0]; + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, + GUEST_ROOT_SIZE_CELLS, d->arch.vpl011.base_addr, + GUEST_PL011_SIZE); + + res =3D fdt_property(fdt, "reg", reg, sizeof(reg)); + if ( res ) + return res; + + set_interrupt(intr, d->arch.vpl011.virq, 0xf, DT_IRQ_TYPE_LEVEL_HIGH); + + res =3D fdt_property(fdt, "interrupts", intr, sizeof (intr)); + if ( res ) + return res; + + res =3D fdt_property_cell(fdt, "interrupt-parent", + kinfo->phandle_gic); + if ( res ) + return res; + + /* Use a default baud rate of 115200. */ + fdt_property_u32(fdt, "current-speed", 115200); + + res =3D fdt_end_node(fdt); + if ( res ) + return res; + + return 0; +} +#endif + +/* + * Scan device tree properties for passthrough specific information. + * Returns < 0 on error + * 0 on success + */ +static int __init handle_passthrough_prop(struct kernel_info *kinfo, + const struct fdt_property *xen_r= eg, + const struct fdt_property *xen_p= ath, + bool xen_force, + uint32_t address_cells, + uint32_t size_cells) +{ + const __be32 *cell; + unsigned int i, len; + struct dt_device_node *node; + int res; + paddr_t mstart, size, gstart; + + /* xen,reg specifies where to map the MMIO region */ + cell =3D (const __be32 *)xen_reg->data; + len =3D fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells)= * + sizeof(uint32_t)); + + for ( i =3D 0; i < len; i++ ) + { + device_tree_get_reg(&cell, address_cells, size_cells, + &mstart, &size); + gstart =3D dt_next_cell(address_cells, &cell); + + if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MA= SK ) + { + printk(XENLOG_ERR + "DomU passthrough config has not page aligned addresses= /sizes\n"); + return -EINVAL; + } + + res =3D iomem_permit_access(kinfo->d, paddr_to_pfn(mstart), + paddr_to_pfn(PAGE_ALIGN(mstart + size - = 1))); + if ( res ) + { + printk(XENLOG_ERR "Unable to permit to dom%d access to" + " 0x%"PRIpaddr" - 0x%"PRIpaddr"\n", + kinfo->d->domain_id, + mstart & PAGE_MASK, PAGE_ALIGN(mstart + size) - 1); + return res; + } + + res =3D map_regions_p2mt(kinfo->d, + gaddr_to_gfn(gstart), + PFN_DOWN(size), + maddr_to_mfn(mstart), + p2m_mmio_direct_dev); + if ( res < 0 ) + { + printk(XENLOG_ERR + "Failed to map %"PRIpaddr" to the guest at%"PRIpaddr"\n= ", + mstart, gstart); + return -EFAULT; + } + } + + /* + * If xen_force, we let the user assign a MMIO region with no + * associated path. + */ + if ( xen_path =3D=3D NULL ) + return xen_force ? 0 : -EINVAL; + + /* + * xen,path specifies the corresponding node in the host DT. + * Both interrupt mappings and IOMMU settings are based on it, + * as they are done based on the corresponding host DT node. + */ + node =3D dt_find_node_by_path(xen_path->data); + if ( node =3D=3D NULL ) + { + printk(XENLOG_ERR "Couldn't find node %s in host_dt!\n", + (char *)xen_path->data); + return -EINVAL; + } + + res =3D map_device_irqs_to_domain(kinfo->d, node, true, NULL); + if ( res < 0 ) + return res; + + res =3D iommu_add_dt_device(node); + if ( res < 0 ) + return res; + + /* If xen_force, we allow assignment of devices without IOMMU protecti= on. */ + if ( xen_force && !dt_device_is_protected(node) ) + return 0; + + return iommu_assign_dt_device(kinfo->d, node); +} + +static int __init handle_prop_pfdt(struct kernel_info *kinfo, + const void *pfdt, int nodeoff, + uint32_t address_cells, uint32_t size_c= ells, + bool scan_passthrough_prop) +{ + void *fdt =3D kinfo->fdt; + int propoff, nameoff, res; + const struct fdt_property *prop, *xen_reg =3D NULL, *xen_path =3D NULL; + const char *name; + bool found, xen_force =3D false; + + for ( propoff =3D fdt_first_property_offset(pfdt, nodeoff); + propoff >=3D 0; + propoff =3D fdt_next_property_offset(pfdt, propoff) ) + { + if ( !(prop =3D fdt_get_property_by_offset(pfdt, propoff, NULL)) ) + return -FDT_ERR_INTERNAL; + + found =3D false; + nameoff =3D fdt32_to_cpu(prop->nameoff); + name =3D fdt_string(pfdt, nameoff); + + if ( scan_passthrough_prop ) + { + if ( dt_prop_cmp("xen,reg", name) =3D=3D 0 ) + { + xen_reg =3D prop; + found =3D true; + } + else if ( dt_prop_cmp("xen,path", name) =3D=3D 0 ) + { + xen_path =3D prop; + found =3D true; + } + else if ( dt_prop_cmp("xen,force-assign-without-iommu", + name) =3D=3D 0 ) + { + xen_force =3D true; + found =3D true; + } + } + + /* + * Copy properties other than the ones above: xen,reg, xen,path, + * and xen,force-assign-without-iommu. + */ + if ( !found ) + { + res =3D fdt_property(fdt, name, prop->data, fdt32_to_cpu(prop-= >len)); + if ( res ) + return res; + } + } + + /* + * Only handle passthrough properties if both xen,reg and xen,path + * are present, or if xen,force-assign-without-iommu is specified. + */ + if ( xen_reg !=3D NULL && (xen_path !=3D NULL || xen_force) ) + { + res =3D handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_forc= e, + address_cells, size_cells); + if ( res < 0 ) + { + printk(XENLOG_ERR "Failed to assign device to %pd\n", kinfo->d= ); + return res; + } + } + else if ( (xen_path && !xen_reg) || (xen_reg && !xen_path && !xen_forc= e) ) + { + printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n", + kinfo->d); + return -EINVAL; + } + + /* FDT_ERR_NOTFOUND =3D> There is no more properties for this node */ + return ( propoff !=3D -FDT_ERR_NOTFOUND ) ? propoff : 0; +} + +static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pf= dt, + int nodeoff, + uint32_t address_cells, uint32_t size_cel= ls, + bool scan_passthrough_prop) +{ + int rc =3D 0; + void *fdt =3D kinfo->fdt; + int node_next; + + rc =3D fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL)); + if ( rc ) + return rc; + + rc =3D handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cell= s, + scan_passthrough_prop); + if ( rc ) + return rc; + + address_cells =3D device_tree_get_u32(pfdt, nodeoff, "#address-cells", + DT_ROOT_NODE_ADDR_CELLS_DEFAULT); + size_cells =3D device_tree_get_u32(pfdt, nodeoff, "#size-cells", + DT_ROOT_NODE_SIZE_CELLS_DEFAULT); + + node_next =3D fdt_first_subnode(pfdt, nodeoff); + while ( node_next > 0 ) + { + rc =3D scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_= cells, + scan_passthrough_prop); + if ( rc ) + return rc; + + node_next =3D fdt_next_subnode(pfdt, node_next); + } + + return fdt_end_node(fdt); +} + +static int __init check_partial_fdt(void *pfdt, size_t size) +{ + int res; + + if ( fdt_magic(pfdt) !=3D FDT_MAGIC ) + { + dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree"); + return -EINVAL; + } + + res =3D fdt_check_header(pfdt); + if ( res ) + { + dprintk(XENLOG_ERR, "Failed to check the partial FDT (%d)", res); + return -EINVAL; + } + + if ( fdt_totalsize(pfdt) > size ) + { + dprintk(XENLOG_ERR, "Partial FDT totalsize is too big"); + return -EINVAL; + } + + return 0; +} + +static int __init domain_handle_dtb_bootmodule(struct domain *d, + struct kernel_info *kinfo) +{ + void *pfdt; + int res, node_next; + + pfdt =3D ioremap_cache(kinfo->dtb_bootmodule->start, + kinfo->dtb_bootmodule->size); + if ( pfdt =3D=3D NULL ) + return -EFAULT; + + res =3D check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size); + if ( res < 0 ) + goto out; + + for ( node_next =3D fdt_first_subnode(pfdt, 0); + node_next > 0; + node_next =3D fdt_next_subnode(pfdt, node_next) ) + { + const char *name =3D fdt_get_name(pfdt, node_next, NULL); + + if ( name =3D=3D NULL ) + continue; + + /* + * Only scan /gic /aliases /passthrough, ignore the rest. + * They don't have to be parsed in order. + * + * Take the GIC phandle value from the special /gic node in the + * DTB fragment. + */ + if ( dt_node_cmp(name, "gic") =3D=3D 0 ) + { + kinfo->phandle_gic =3D fdt_get_phandle(pfdt, node_next); + continue; + } + + if ( dt_node_cmp(name, "aliases") =3D=3D 0 ) + { + res =3D scan_pfdt_node(kinfo, pfdt, node_next, + DT_ROOT_NODE_ADDR_CELLS_DEFAULT, + DT_ROOT_NODE_SIZE_CELLS_DEFAULT, + false); + if ( res ) + goto out; + continue; + } + if ( dt_node_cmp(name, "passthrough") =3D=3D 0 ) + { + res =3D scan_pfdt_node(kinfo, pfdt, node_next, + DT_ROOT_NODE_ADDR_CELLS_DEFAULT, + DT_ROOT_NODE_SIZE_CELLS_DEFAULT, + true); + if ( res ) + goto out; + continue; + } + } + + out: + iounmap(pfdt); + + return res; +} + +/* + * The max size for DT is 2MB. However, the generated DT is small (not inc= luding + * domU passthrough DT nodes whose size we account separately), 4KB are en= ough + * for now, but we might have to increase it in the future. + */ +#define DOMU_DTB_SIZE 4096 +static int __init prepare_dtb_domU(struct domain *d, struct kernel_info *k= info) +{ + int addrcells, sizecells; + int ret, fdt_size =3D DOMU_DTB_SIZE; + + kinfo->phandle_gic =3D GUEST_PHANDLE_GIC; + kinfo->gnttab_start =3D GUEST_GNTTAB_BASE; + kinfo->gnttab_size =3D GUEST_GNTTAB_SIZE; + + addrcells =3D GUEST_ROOT_ADDRESS_CELLS; + sizecells =3D GUEST_ROOT_SIZE_CELLS; + + /* Account for domU passthrough DT size */ + if ( kinfo->dtb_bootmodule ) + fdt_size +=3D kinfo->dtb_bootmodule->size; + + /* Cap to max DT size if needed */ + fdt_size =3D min(fdt_size, SZ_2M); + + kinfo->fdt =3D xmalloc_bytes(fdt_size); + if ( kinfo->fdt =3D=3D NULL ) + return -ENOMEM; + + ret =3D fdt_create(kinfo->fdt, fdt_size); + if ( ret < 0 ) + goto err; + + ret =3D fdt_finish_reservemap(kinfo->fdt); + if ( ret < 0 ) + goto err; + + ret =3D fdt_begin_node(kinfo->fdt, ""); + if ( ret < 0 ) + goto err; + + ret =3D fdt_property_cell(kinfo->fdt, "#address-cells", addrcells); + if ( ret ) + goto err; + + ret =3D fdt_property_cell(kinfo->fdt, "#size-cells", sizecells); + if ( ret ) + goto err; + + ret =3D make_chosen_node(kinfo); + if ( ret ) + goto err; + + ret =3D make_psci_node(kinfo->fdt); + if ( ret ) + goto err; + + ret =3D make_cpus_node(d, kinfo->fdt); + if ( ret ) + goto err; + + ret =3D make_memory_node(d, kinfo->fdt, addrcells, sizecells, &kinfo->= mem); + if ( ret ) + goto err; + + ret =3D make_resv_memory_node(d, kinfo->fdt, addrcells, sizecells, + &kinfo->shm_mem); + if ( ret ) + goto err; + + /* + * domain_handle_dtb_bootmodule has to be called before the rest of + * the device tree is generated because it depends on the value of + * the field phandle_gic. + */ + if ( kinfo->dtb_bootmodule ) + { + ret =3D domain_handle_dtb_bootmodule(d, kinfo); + if ( ret ) + goto err; + } + + ret =3D make_gic_domU_node(kinfo); + if ( ret ) + goto err; + + ret =3D make_timer_node(kinfo); + if ( ret ) + goto err; + + if ( kinfo->vpl011 ) + { + ret =3D -EINVAL; +#ifdef CONFIG_SBSA_VUART_CONSOLE + ret =3D make_vpl011_uart_node(kinfo); +#endif + if ( ret ) + goto err; + } + + if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS ) + { + ret =3D make_hypervisor_node(d, kinfo, addrcells, sizecells); + if ( ret ) + goto err; + } + + ret =3D fdt_end_node(kinfo->fdt); + if ( ret < 0 ) + goto err; + + ret =3D fdt_finish(kinfo->fdt); + if ( ret < 0 ) + goto err; + + return 0; + + err: + printk("Device tree generation failed (%d).\n", ret); + xfree(kinfo->fdt); + + return -EINVAL; +} + +static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb, + unsigned int smp_cpus) +{ + /* + * Keep in sync with libxl__get_required_paging_memory(). + * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M ma= p, + * plus 128 pages to cover extended regions. + */ + unsigned long memkb =3D 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128= ); + + BUILD_BUG_ON(PAGE_SIZE !=3D SZ_4K); + + return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); +} + +static int __init alloc_xenstore_evtchn(struct domain *d) +{ + evtchn_alloc_unbound_t alloc; + int rc; + + alloc.dom =3D d->domain_id; + alloc.remote_dom =3D hardware_domain->domain_id; + rc =3D evtchn_alloc_unbound(&alloc, 0); + if ( rc ) + { + printk("Failed allocating event channel for domain\n"); + return rc; + } + + d->arch.hvm.params[HVM_PARAM_STORE_EVTCHN] =3D alloc.port; + + return 0; +} + +static int __init construct_domU(struct domain *d, + const struct dt_device_node *node) +{ + struct kernel_info kinfo =3D {}; + const char *dom0less_enhanced; + int rc; + u64 mem; + u32 p2m_mem_mb; + unsigned long p2m_pages; + + rc =3D dt_property_read_u64(node, "memory", &mem); + if ( !rc ) + { + printk("Error building DomU: cannot read \"memory\" property\n"); + return -EINVAL; + } + kinfo.unassigned_mem =3D (paddr_t)mem * SZ_1K; + + rc =3D dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb= ); + /* If xen,domain-p2m-mem-mb is not specified, use the default value. */ + p2m_pages =3D rc ? + p2m_mem_mb << (20 - PAGE_SHIFT) : + domain_p2m_pages(mem, d->max_vcpus); + + spin_lock(&d->arch.paging.lock); + rc =3D p2m_set_allocation(d, p2m_pages, NULL); + spin_unlock(&d->arch.paging.lock); + if ( rc !=3D 0 ) + return rc; + + printk("*** LOADING DOMU cpus=3D%u memory=3D%#"PRIx64"KB ***\n", + d->max_vcpus, mem); + + kinfo.vpl011 =3D dt_property_read_bool(node, "vpl011"); + + rc =3D dt_property_read_string(node, "xen,enhanced", &dom0less_enhance= d); + if ( rc =3D=3D -EILSEQ || + rc =3D=3D -ENODATA || + (rc =3D=3D 0 && !strcmp(dom0less_enhanced, "enabled")) ) + { + if ( hardware_domain ) + kinfo.dom0less_feature =3D DOM0LESS_ENHANCED; + else + panic("At the moment, Xenstore support requires dom0 to be pre= sent\n"); + } + else if ( rc =3D=3D 0 && !strcmp(dom0less_enhanced, "no-xenstore") ) + kinfo.dom0less_feature =3D DOM0LESS_ENHANCED_NO_XS; + + if ( vcpu_create(d, 0) =3D=3D NULL ) + return -ENOMEM; + + d->max_pages =3D ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; + + kinfo.d =3D d; + + rc =3D kernel_probe(&kinfo, node); + if ( rc < 0 ) + return rc; + +#ifdef CONFIG_ARM_64 + /* type must be set before allocate memory */ + d->arch.type =3D kinfo.type; +#endif + if ( !dt_find_property(node, "xen,static-mem", NULL) ) + allocate_memory(d, &kinfo); + else if ( !is_domain_direct_mapped(d) ) + allocate_static_memory(d, &kinfo, node); + else + assign_static_memory_11(d, &kinfo, node); + +#ifdef CONFIG_STATIC_SHM + rc =3D process_shm(d, &kinfo, node); + if ( rc < 0 ) + return rc; +#endif + + /* + * Base address and irq number are needed when creating vpl011 device + * tree node in prepare_dtb_domU, so initialization on related variabl= es + * shall be done first. + */ + if ( kinfo.vpl011 ) + { + rc =3D domain_vpl011_init(d, NULL); + if ( rc < 0 ) + return rc; + } + + rc =3D prepare_dtb_domU(d, &kinfo); + if ( rc < 0 ) + return rc; + + rc =3D construct_domain(d, &kinfo); + if ( rc < 0 ) + return rc; + + if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE ) + { + ASSERT(hardware_domain); + rc =3D alloc_xenstore_evtchn(d); + if ( rc < 0 ) + return rc; + d->arch.hvm.params[HVM_PARAM_STORE_PFN] =3D ~0ULL; + } + + return rc; +} + +void __init create_domUs(void) +{ + struct dt_device_node *node; + const struct dt_device_node *cpupool_node, + *chosen =3D dt_find_node_by_path("/chosen"= ); + + BUG_ON(chosen =3D=3D NULL); + dt_for_each_child_node(chosen, node) + { + struct domain *d; + struct xen_domctl_createdomain d_cfg =3D { + .arch.gic_version =3D XEN_DOMCTL_CONFIG_GIC_NATIVE, + .flags =3D XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, + /* + * The default of 1023 should be sufficient for guests because + * on ARM we don't bind physical interrupts to event channels. + * The only use of the evtchn port is inter-domain communicati= ons. + * 1023 is also the default value used in libxl. + */ + .max_evtchn_port =3D 1023, + .max_grant_frames =3D -1, + .max_maptrack_frames =3D -1, + .grant_opts =3D XEN_DOMCTL_GRANT_version(opt_gnttab_max_versio= n), + }; + unsigned int flags =3D 0U; + uint32_t val; + int rc; + + if ( !dt_device_is_compatible(node, "xen,domain") ) + continue; + + if ( (max_init_domid + 1) >=3D DOMID_FIRST_RESERVED ) + panic("No more domain IDs available\n"); + + if ( dt_find_property(node, "xen,static-mem", NULL) ) + flags |=3D CDF_staticmem; + + if ( dt_property_read_bool(node, "direct-map") ) + { + if ( !(flags & CDF_staticmem) ) + panic("direct-map is not valid for domain %s without stati= c allocation.\n", + dt_node_name(node)); + + flags |=3D CDF_directmap; + } + + if ( !dt_property_read_u32(node, "cpus", &d_cfg.max_vcpus) ) + panic("Missing property 'cpus' for domain %s\n", + dt_node_name(node)); + + if ( dt_find_compatible_node(node, NULL, "multiboot,device-tree") = && + iommu_enabled ) + d_cfg.flags |=3D XEN_DOMCTL_CDF_iommu; + + if ( !dt_property_read_u32(node, "nr_spis", &d_cfg.arch.nr_spis) ) + { + int vpl011_virq =3D GUEST_VPL011_SPI; + + d_cfg.arch.nr_spis =3D gic_number_lines() - 32; + + /* + * The VPL011 virq is GUEST_VPL011_SPI, unless direct-map is + * set, in which case it'll match the hardware. + * + * Since the domain is not yet created, we can't use + * d->arch.vpl011.irq. So the logic to find the vIRQ has to + * be hardcoded. + * The logic here shall be consistent with the one in + * domain_vpl011_init(). + */ + if ( flags & CDF_directmap ) + { + vpl011_virq =3D serial_irq(SERHND_DTUART); + if ( vpl011_virq < 0 ) + panic("Error getting IRQ number for this serial port %= d\n", + SERHND_DTUART); + } + + /* + * vpl011 uses one emulated SPI. If vpl011 is requested, make + * sure that we allocate enough SPIs for it. + */ + if ( dt_property_read_bool(node, "vpl011") ) + d_cfg.arch.nr_spis =3D MAX(d_cfg.arch.nr_spis, + vpl011_virq - 32 + 1); + } + + /* Get the optional property domain-cpupool */ + cpupool_node =3D dt_parse_phandle(node, "domain-cpupool", 0); + if ( cpupool_node ) + { + int pool_id =3D btcpupools_get_domain_pool_id(cpupool_node); + if ( pool_id < 0 ) + panic("Error getting cpupool id from domain-cpupool (%d)\n= ", + pool_id); + d_cfg.cpupool_id =3D pool_id; + } + + if ( dt_property_read_u32(node, "max_grant_version", &val) ) + d_cfg.grant_opts =3D XEN_DOMCTL_GRANT_version(val); + + if ( dt_property_read_u32(node, "max_grant_frames", &val) ) + { + if ( val > INT32_MAX ) + panic("max_grant_frames (%"PRIu32") overflow\n", val); + d_cfg.max_grant_frames =3D val; + } + + if ( dt_property_read_u32(node, "max_maptrack_frames", &val) ) + { + if ( val > INT32_MAX ) + panic("max_maptrack_frames (%"PRIu32") overflow\n", val); + d_cfg.max_maptrack_frames =3D val; + } + + if ( dt_get_property(node, "sve", &val) ) + { +#ifdef CONFIG_ARM64_SVE + unsigned int sve_vl_bits; + bool ret =3D false; + + if ( !val ) + { + /* Property found with no value, means max HW VL supported= */ + ret =3D sve_domctl_vl_param(-1, &sve_vl_bits); + } + else + { + if ( dt_property_read_u32(node, "sve", &val) ) + ret =3D sve_domctl_vl_param(val, &sve_vl_bits); + else + panic("Error reading 'sve' property\n"); + } + + if ( ret ) + d_cfg.arch.sve_vl =3D sve_encode_vl(sve_vl_bits); + else + panic("SVE vector length error\n"); +#else + panic("'sve' property found, but CONFIG_ARM64_SVE not selected= \n"); +#endif + } + + /* + * The variable max_init_domid is initialized with zero, so here i= t's + * very important to use the pre-increment operator to call + * domain_create() with a domid > 0. (domid =3D=3D 0 is reserved f= or Dom0) + */ + d =3D domain_create(++max_init_domid, &d_cfg, flags); + if ( IS_ERR(d) ) + panic("Error creating domain %s (rc =3D %ld)\n", + dt_node_name(node), PTR_ERR(d)); + + d->is_console =3D true; + dt_device_set_used_by(node, d->domain_id); + + rc =3D construct_domU(d, node); + if ( rc ) + panic("Could not set up domain %s (rc =3D %d)\n", + dt_node_name(node), rc); + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 3d3a117bde7a..d61fda9f2081 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include =20 @@ -117,7 +118,7 @@ struct vcpu *__init alloc_dom0_vcpu0(struct domain *dom= 0) return vcpu_create(dom0, 0); } =20 -static unsigned int __init get_allocation_size(paddr_t size) +unsigned int __init get_allocation_size(paddr_t size) { /* * get_order_from_bytes returns the order greater than or equal to @@ -413,10 +414,8 @@ static void __init allocate_memory_11(struct domain *d, } } =20 -static bool __init allocate_bank_memory(struct domain *d, - struct kernel_info *kinfo, - gfn_t sgfn, - paddr_t tot_size) +bool __init allocate_bank_memory(struct domain *d, struct kernel_info *kin= fo, + gfn_t sgfn, paddr_t tot_size) { int res; struct page_info *pg; @@ -477,49 +476,6 @@ static bool __init allocate_bank_memory(struct domain = *d, return true; } =20 -static void __init allocate_memory(struct domain *d, struct kernel_info *k= info) -{ - unsigned int i; - paddr_t bank_size; - - printk(XENLOG_INFO "Allocating mappings totalling %ldMB for %pd:\n", - /* Don't want format this as PRIpaddr (16 digit hex) */ - (unsigned long)(kinfo->unassigned_mem >> 20), d); - - kinfo->mem.nr_banks =3D 0; - bank_size =3D MIN(GUEST_RAM0_SIZE, kinfo->unassigned_mem); - if ( !allocate_bank_memory(d, kinfo, gaddr_to_gfn(GUEST_RAM0_BASE), - bank_size) ) - goto fail; - - bank_size =3D MIN(GUEST_RAM1_SIZE, kinfo->unassigned_mem); - if ( !allocate_bank_memory(d, kinfo, gaddr_to_gfn(GUEST_RAM1_BASE), - bank_size) ) - goto fail; - - if ( kinfo->unassigned_mem ) - goto fail; - - for( i =3D 0; i < kinfo->mem.nr_banks; i++ ) - { - printk(XENLOG_INFO "%pd BANK[%d] %#"PRIpaddr"-%#"PRIpaddr" (%ldMB)= \n", - d, - i, - kinfo->mem.bank[i].start, - kinfo->mem.bank[i].start + kinfo->mem.bank[i].size, - /* Don't want format this as PRIpaddr (16 digit hex) */ - (unsigned long)(kinfo->mem.bank[i].size >> 20)); - } - - return; - -fail: - panic("Failed to allocate requested domain memory." - /* Don't want format this as PRIpaddr (16 digit hex) */ - " %ldKB unallocated. Fix the VMs configurations.\n", - (unsigned long)kinfo->unassigned_mem >> 10); -} - #ifdef CONFIG_STATIC_MEMORY static bool __init append_static_memory_to_bank(struct domain *d, struct membank *bank, @@ -599,9 +555,8 @@ static int __init parse_static_mem_prop(const struct dt= _device_node *node, } =20 /* Allocate memory from static memory as RAM for one specific domain d. */ -static void __init allocate_static_memory(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *nod= e) +void __init allocate_static_memory(struct domain *d, struct kernel_info *k= info, + const struct dt_device_node *node) { u32 addr_cells, size_cells, reg_cells; unsigned int nr_banks, gbank, bank =3D 0; @@ -705,9 +660,8 @@ static void __init allocate_static_memory(struct domain= *d, * The static memory will be directly mapped in the guest(Guest Physical * Address =3D=3D Physical Address). */ -static void __init assign_static_memory_11(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *no= de) +void __init assign_static_memory_11(struct domain *d, struct kernel_info *= kinfo, + const struct dt_device_node *node) { u32 addr_cells, size_cells, reg_cells; unsigned int nr_banks, bank =3D 0; @@ -941,8 +895,8 @@ static int __init append_shm_bank_to_domain(struct kern= el_info *kinfo, return 0; } =20 -static int __init process_shm(struct domain *d, struct kernel_info *kinfo, - const struct dt_device_node *node) +int __init process_shm(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node) { struct dt_device_node *shm_node; =20 @@ -1047,20 +1001,6 @@ static int __init process_shm(struct domain *d, stru= ct kernel_info *kinfo, return 0; } #endif /* CONFIG_STATIC_SHM */ -#else -static void __init allocate_static_memory(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *nod= e) -{ - ASSERT_UNREACHABLE(); -} - -static void __init assign_static_memory_11(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *no= de) -{ - ASSERT_UNREACHABLE(); -} #endif =20 /* @@ -1264,17 +1204,10 @@ static int __init write_properties(struct domain *d= , struct kernel_info *kinfo, return 0; } =20 -/* - * Helper to write an interrupts with the GIC format - * This code is assuming the irq is an PPI. - */ - -typedef __be32 gic_interrupt_t[3]; - -static void __init set_interrupt(gic_interrupt_t interrupt, - unsigned int irq, - unsigned int cpumask, - unsigned int level) +void __init set_interrupt(gic_interrupt_t interrupt, + unsigned int irq, + unsigned int cpumask, + unsigned int level) { __be32 *cells =3D interrupt; bool is_ppi =3D !!(irq < 32); @@ -1319,8 +1252,7 @@ static int __init fdt_property_interrupts(const struc= t kernel_info *kinfo, * unit (which contains the physical address) with name to generate a * node name. */ -static int __init domain_fdt_begin_node(void *fdt, const char *name, - uint64_t unit) +int __init domain_fdt_begin_node(void *fdt, const char *name, uint64_t uni= t) { /* * The size of the buffer to hold the longest possible string (i.e. @@ -1344,10 +1276,10 @@ static int __init domain_fdt_begin_node(void *fdt, = const char *name, return fdt_begin_node(fdt, buf); } =20 -static int __init make_memory_node(const struct domain *d, - void *fdt, - int addrcells, int sizecells, - struct meminfo *mem) +int __init make_memory_node(const struct domain *d, + void *fdt, + int addrcells, int sizecells, + struct meminfo *mem) { unsigned int i; int res, reg_size =3D addrcells + sizecells; @@ -1483,10 +1415,10 @@ static int __init make_shm_memory_node(const struct= domain *d, } #endif =20 -static int __init make_resv_memory_node(const struct domain *d, - void *fdt, - int addrcells, int sizecells, - const struct meminfo *mem) +int __init make_resv_memory_node(const struct domain *d, + void *fdt, + int addrcells, int sizecells, + const struct meminfo *mem) { int res =3D 0; /* Placeholder for reserved-memory\0 */ @@ -1819,9 +1751,9 @@ static int __init find_domU_holes(const struct kernel= _info *kinfo, return res; } =20 -static int __init make_hypervisor_node(struct domain *d, - const struct kernel_info *kinfo, - int addrcells, int sizecells) +int __init make_hypervisor_node(struct domain *d, + const struct kernel_info *kinfo, + int addrcells, int sizecells) { const char compat[] =3D "xen,xen-" XEN_VERSION_STRING "\0" @@ -1938,7 +1870,7 @@ static int __init make_hypervisor_node(struct domain = *d, return res; } =20 -static int __init make_psci_node(void *fdt) +int __init make_psci_node(void *fdt) { int res; const char compat[] =3D @@ -1974,7 +1906,7 @@ static int __init make_psci_node(void *fdt) return res; } =20 -static int __init make_cpus_node(const struct domain *d, void *fdt) +int __init make_cpus_node(const struct domain *d, void *fdt) { int res; const struct dt_device_node *cpus =3D dt_find_node_by_path("/cpus"); @@ -2172,7 +2104,7 @@ static int __init make_gic_node(const struct domain *= d, void *fdt, return res; } =20 -static int __init make_timer_node(const struct kernel_info *kinfo) +int __init make_timer_node(const struct kernel_info *kinfo) { void *fdt =3D kinfo->fdt; static const struct dt_device_match timer_ids[] __initconst =3D @@ -2490,773 +2422,161 @@ static int __init handle_node(struct domain *d, s= truct kernel_info *kinfo, return res; } =20 -#ifdef CONFIG_VGICV2 -static int __init make_gicv2_domU_node(struct kernel_info *kinfo) +static int __init prepare_dtb_hwdom(struct domain *d, struct kernel_info *= kinfo) { - void *fdt =3D kinfo->fdt; - int res =3D 0; - __be32 reg[(GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * 2]; - __be32 *cells; - const struct domain *d =3D kinfo->d; - - res =3D domain_fdt_begin_node(fdt, "interrupt-controller", - vgic_dist_base(&d->arch.vgic)); - if ( res ) - return res; - - res =3D fdt_property_cell(fdt, "#address-cells", 0); - if ( res ) - return res; + const p2m_type_t default_p2mt =3D p2m_mmio_direct_c; + const void *fdt; + int new_size; + int ret; =20 - res =3D fdt_property_cell(fdt, "#interrupt-cells", 3); - if ( res ) - return res; + ASSERT(dt_host && (dt_host->sibling =3D=3D NULL)); =20 - res =3D fdt_property(fdt, "interrupt-controller", NULL, 0); - if ( res ) - return res; + kinfo->phandle_gic =3D dt_interrupt_controller->phandle; + fdt =3D device_tree_flattened; =20 - res =3D fdt_property_string(fdt, "compatible", "arm,gic-400"); - if ( res ) - return res; + new_size =3D fdt_totalsize(fdt) + DOM0_FDT_EXTRA_SIZE; + kinfo->fdt =3D xmalloc_bytes(new_size); + if ( kinfo->fdt =3D=3D NULL ) + return -ENOMEM; =20 - cells =3D ®[0]; - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, - vgic_dist_base(&d->arch.vgic), GUEST_GICD_SIZE); - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, - vgic_cpu_base(&d->arch.vgic), GUEST_GICC_SIZE); + ret =3D fdt_create(kinfo->fdt, new_size); + if ( ret < 0 ) + goto err; =20 - res =3D fdt_property(fdt, "reg", reg, sizeof(reg)); - if (res) - return res; + fdt_finish_reservemap(kinfo->fdt); =20 - res =3D fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); - if (res) - return res; + ret =3D handle_node(d, kinfo, dt_host, default_p2mt); + if ( ret ) + goto err; =20 - res =3D fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); - if (res) - return res; + ret =3D fdt_finish(kinfo->fdt); + if ( ret < 0 ) + goto err; =20 - res =3D fdt_end_node(fdt); + return 0; =20 - return res; + err: + printk("Device tree generation failed (%d).\n", ret); + xfree(kinfo->fdt); + return -EINVAL; } -#endif =20 -#ifdef CONFIG_GICV3 -static int __init make_gicv3_domU_node(struct kernel_info *kinfo) +static void __init dtb_load(struct kernel_info *kinfo) { - void *fdt =3D kinfo->fdt; - int res =3D 0; - __be32 *reg, *cells; - const struct domain *d =3D kinfo->d; - unsigned int i, len =3D 0; - - res =3D domain_fdt_begin_node(fdt, "interrupt-controller", - vgic_dist_base(&d->arch.vgic)); - if ( res ) - return res; - - res =3D fdt_property_cell(fdt, "#address-cells", 0); - if ( res ) - return res; - - res =3D fdt_property_cell(fdt, "#interrupt-cells", 3); - if ( res ) - return res; - - res =3D fdt_property(fdt, "interrupt-controller", NULL, 0); - if ( res ) - return res; - - res =3D fdt_property_string(fdt, "compatible", "arm,gic-v3"); - if ( res ) - return res; - - /* reg specifies all re-distributors and Distributor. */ - len =3D (GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * - (d->arch.vgic.nr_regions + 1) * sizeof(__be32); - reg =3D xmalloc_bytes(len); - if ( reg =3D=3D NULL ) - return -ENOMEM; - cells =3D reg; - - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_C= ELLS, - vgic_dist_base(&d->arch.vgic), GUEST_GICV3_GICD_SIZ= E); - - for ( i =3D 0; i < d->arch.vgic.nr_regions; i++ ) - dt_child_set_range(&cells, - GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, - d->arch.vgic.rdist_regions[i].base, - d->arch.vgic.rdist_regions[i].size); - - res =3D fdt_property(fdt, "reg", reg, len); - xfree(reg); - if (res) - return res; - - res =3D fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); - if (res) - return res; - - res =3D fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); - if (res) - return res; + unsigned long left; =20 - res =3D fdt_end_node(fdt); + printk("Loading %pd DTB to 0x%"PRIpaddr"-0x%"PRIpaddr"\n", + kinfo->d, kinfo->dtb_paddr, + kinfo->dtb_paddr + fdt_totalsize(kinfo->fdt)); =20 - return res; -} -#endif + left =3D copy_to_guest_phys_flush_dcache(kinfo->d, kinfo->dtb_paddr, + kinfo->fdt, + fdt_totalsize(kinfo->fdt)); =20 -static int __init make_gic_domU_node(struct kernel_info *kinfo) -{ - switch ( kinfo->d->arch.vgic.version ) - { -#ifdef CONFIG_GICV3 - case GIC_V3: - return make_gicv3_domU_node(kinfo); -#endif -#ifdef CONFIG_VGICV2 - case GIC_V2: - return make_gicv2_domU_node(kinfo); -#endif - default: - panic("Unsupported GIC version\n"); - } + if ( left !=3D 0 ) + panic("Unable to copy the DTB to %pd memory (left =3D %lu bytes)\n= ", + kinfo->d, left); + xfree(kinfo->fdt); } =20 -#ifdef CONFIG_SBSA_VUART_CONSOLE -static int __init make_vpl011_uart_node(struct kernel_info *kinfo) +static void __init initrd_load(struct kernel_info *kinfo) { - void *fdt =3D kinfo->fdt; + const struct bootmodule *mod =3D kinfo->initrd_bootmodule; + paddr_t load_addr =3D kinfo->initrd_paddr; + paddr_t paddr, len; + int node; int res; - gic_interrupt_t intr; - __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS]; - __be32 *cells; - struct domain *d =3D kinfo->d; - - res =3D domain_fdt_begin_node(fdt, "sbsa-uart", d->arch.vpl011.base_ad= dr); - if ( res ) - return res; + __be32 val[2]; + __be32 *cellp; + void __iomem *initrd; =20 - res =3D fdt_property_string(fdt, "compatible", "arm,sbsa-uart"); - if ( res ) - return res; + if ( !mod || !mod->size ) + return; =20 - cells =3D ®[0]; - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, - GUEST_ROOT_SIZE_CELLS, d->arch.vpl011.base_addr, - GUEST_PL011_SIZE); + paddr =3D mod->start; + len =3D mod->size; =20 - res =3D fdt_property(fdt, "reg", reg, sizeof(reg)); - if ( res ) - return res; + printk("Loading %pd initrd from %"PRIpaddr" to 0x%"PRIpaddr"-0x%"PRIpa= ddr"\n", + kinfo->d, paddr, load_addr, load_addr + len); =20 - set_interrupt(intr, d->arch.vpl011.virq, 0xf, DT_IRQ_TYPE_LEVEL_HIGH); + /* Fix up linux,initrd-start and linux,initrd-end in /chosen */ + node =3D fdt_path_offset(kinfo->fdt, "/chosen"); + if ( node < 0 ) + panic("Cannot find the /chosen node\n"); =20 - res =3D fdt_property(fdt, "interrupts", intr, sizeof (intr)); + cellp =3D (__be32 *)val; + dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr); + res =3D fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-start", + val, sizeof(val)); if ( res ) - return res; + panic("Cannot fix up \"linux,initrd-start\" property\n"); =20 - res =3D fdt_property_cell(fdt, "interrupt-parent", - kinfo->phandle_gic); + cellp =3D (__be32 *)val; + dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr + len); + res =3D fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-end", + val, sizeof(val)); if ( res ) - return res; + panic("Cannot fix up \"linux,initrd-end\" property\n"); =20 - /* Use a default baud rate of 115200. */ - fdt_property_u32(fdt, "current-speed", 115200); + initrd =3D ioremap_wc(paddr, len); + if ( !initrd ) + panic("Unable to map the hwdom initrd\n"); =20 - res =3D fdt_end_node(fdt); - if ( res ) - return res; + res =3D copy_to_guest_phys_flush_dcache(kinfo->d, load_addr, + initrd, len); + if ( res !=3D 0 ) + panic("Unable to copy the initrd in the hwdom memory\n"); =20 - return 0; + iounmap(initrd); } -#endif =20 /* - * Scan device tree properties for passthrough specific information. - * Returns < 0 on error - * 0 on success + * Allocate the event channel PPIs and setup the HVM_PARAM_CALLBACK_IRQ. + * The allocated IRQ will be found in d->arch.evtchn_irq. + * + * Note that this should only be called once all PPIs used by the + * hardware domain have been registered. */ -static int __init handle_passthrough_prop(struct kernel_info *kinfo, - const struct fdt_property *xen_r= eg, - const struct fdt_property *xen_p= ath, - bool xen_force, - uint32_t address_cells, uint32_t= size_cells) +void __init evtchn_allocate(struct domain *d) { - const __be32 *cell; - unsigned int i, len; - struct dt_device_node *node; int res; - paddr_t mstart, size, gstart; + u64 val; =20 - /* xen,reg specifies where to map the MMIO region */ - cell =3D (const __be32 *)xen_reg->data; - len =3D fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells)= * - sizeof(uint32_t)); + res =3D vgic_allocate_ppi(d); + if ( res < 0 ) + panic("Unable to allocate a PPI for the event channel interrupt\n"= ); =20 - for ( i =3D 0; i < len; i++ ) - { - device_tree_get_reg(&cell, address_cells, size_cells, - &mstart, &size); - gstart =3D dt_next_cell(address_cells, &cell); + d->arch.evtchn_irq =3D res; =20 - if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MA= SK ) - { - printk(XENLOG_ERR - "DomU passthrough config has not page aligned addresse= s/sizes\n"); - return -EINVAL; - } + printk("Allocating PPI %u for event channel interrupt\n", + d->arch.evtchn_irq); =20 - res =3D iomem_permit_access(kinfo->d, paddr_to_pfn(mstart), - paddr_to_pfn(PAGE_ALIGN(mstart + size - = 1))); - if ( res ) - { - printk(XENLOG_ERR "Unable to permit to dom%d access to" - " 0x%"PRIpaddr" - 0x%"PRIpaddr"\n", - kinfo->d->domain_id, - mstart & PAGE_MASK, PAGE_ALIGN(mstart + size) - 1); - return res; - } + /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ */ + val =3D MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI, + HVM_PARAM_CALLBACK_IRQ_TYPE_MASK); + /* Active-low level-sensitive */ + val |=3D MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL, + HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK); + val |=3D d->arch.evtchn_irq; + d->arch.hvm.params[HVM_PARAM_CALLBACK_IRQ] =3D val; +} =20 - res =3D map_regions_p2mt(kinfo->d, - gaddr_to_gfn(gstart), - PFN_DOWN(size), - maddr_to_mfn(mstart), - p2m_mmio_direct_dev); - if ( res < 0 ) - { - printk(XENLOG_ERR - "Failed to map %"PRIpaddr" to the guest at%"PRIpaddr"\n= ", - mstart, gstart); - return -EFAULT; - } - } +static int __init get_evtchn_dt_property(const struct dt_device_node *np, + uint32_t *port, uint32_t *phandle) +{ + const __be32 *prop =3D NULL; + uint32_t len; =20 - /* - * If xen_force, we let the user assign a MMIO region with no - * associated path. - */ - if ( xen_path =3D=3D NULL ) - return xen_force ? 0 : -EINVAL; + prop =3D dt_get_property(np, "xen,evtchn", &len); + if ( !prop ) + { + printk(XENLOG_ERR "xen,evtchn property should not be empty.\n"); + return -EINVAL; + } =20 - /* - * xen,path specifies the corresponding node in the host DT. - * Both interrupt mappings and IOMMU settings are based on it, - * as they are done based on the corresponding host DT node. - */ - node =3D dt_find_node_by_path(xen_path->data); - if ( node =3D=3D NULL ) + if ( !len || len < dt_cells_to_size(STATIC_EVTCHN_NODE_SIZE_CELLS) ) { - printk(XENLOG_ERR "Couldn't find node %s in host_dt!\n", - (char *)xen_path->data); - return -EINVAL; - } - - res =3D map_device_irqs_to_domain(kinfo->d, node, true, NULL); - if ( res < 0 ) - return res; - - res =3D iommu_add_dt_device(node); - if ( res < 0 ) - return res; - - /* If xen_force, we allow assignment of devices without IOMMU protecti= on. */ - if ( xen_force && !dt_device_is_protected(node) ) - return 0; - - return iommu_assign_dt_device(kinfo->d, node); -} - -static int __init handle_prop_pfdt(struct kernel_info *kinfo, - const void *pfdt, int nodeoff, - uint32_t address_cells, uint32_t size_c= ells, - bool scan_passthrough_prop) -{ - void *fdt =3D kinfo->fdt; - int propoff, nameoff, res; - const struct fdt_property *prop, *xen_reg =3D NULL, *xen_path =3D NULL; - const char *name; - bool found, xen_force =3D false; - - for ( propoff =3D fdt_first_property_offset(pfdt, nodeoff); - propoff >=3D 0; - propoff =3D fdt_next_property_offset(pfdt, propoff) ) - { - if ( !(prop =3D fdt_get_property_by_offset(pfdt, propoff, NULL)) ) - return -FDT_ERR_INTERNAL; - - found =3D false; - nameoff =3D fdt32_to_cpu(prop->nameoff); - name =3D fdt_string(pfdt, nameoff); - - if ( scan_passthrough_prop ) - { - if ( dt_prop_cmp("xen,reg", name) =3D=3D 0 ) - { - xen_reg =3D prop; - found =3D true; - } - else if ( dt_prop_cmp("xen,path", name) =3D=3D 0 ) - { - xen_path =3D prop; - found =3D true; - } - else if ( dt_prop_cmp("xen,force-assign-without-iommu", - name) =3D=3D 0 ) - { - xen_force =3D true; - found =3D true; - } - } - - /* - * Copy properties other than the ones above: xen,reg, xen,path, - * and xen,force-assign-without-iommu. - */ - if ( !found ) - { - res =3D fdt_property(fdt, name, prop->data, fdt32_to_cpu(prop-= >len)); - if ( res ) - return res; - } - } - - /* - * Only handle passthrough properties if both xen,reg and xen,path - * are present, or if xen,force-assign-without-iommu is specified. - */ - if ( xen_reg !=3D NULL && (xen_path !=3D NULL || xen_force) ) - { - res =3D handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_forc= e, - address_cells, size_cells); - if ( res < 0 ) - { - printk(XENLOG_ERR "Failed to assign device to %pd\n", kinfo->d= ); - return res; - } - } - else if ( (xen_path && !xen_reg) || (xen_reg && !xen_path && !xen_forc= e) ) - { - printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n", - kinfo->d); - return -EINVAL; - } - - /* FDT_ERR_NOTFOUND =3D> There is no more properties for this node */ - return ( propoff !=3D -FDT_ERR_NOTFOUND ) ? propoff : 0; -} - -static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pf= dt, - int nodeoff, - uint32_t address_cells, uint32_t size_cel= ls, - bool scan_passthrough_prop) -{ - int rc =3D 0; - void *fdt =3D kinfo->fdt; - int node_next; - - rc =3D fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL)); - if ( rc ) - return rc; - - rc =3D handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cell= s, - scan_passthrough_prop); - if ( rc ) - return rc; - - address_cells =3D device_tree_get_u32(pfdt, nodeoff, "#address-cells", - DT_ROOT_NODE_ADDR_CELLS_DEFAULT); - size_cells =3D device_tree_get_u32(pfdt, nodeoff, "#size-cells", - DT_ROOT_NODE_SIZE_CELLS_DEFAULT); - - node_next =3D fdt_first_subnode(pfdt, nodeoff); - while ( node_next > 0 ) - { - rc =3D scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_= cells, - scan_passthrough_prop); - if ( rc ) - return rc; - - node_next =3D fdt_next_subnode(pfdt, node_next); - } - - return fdt_end_node(fdt); -} - -static int __init check_partial_fdt(void *pfdt, size_t size) -{ - int res; - - if ( fdt_magic(pfdt) !=3D FDT_MAGIC ) - { - dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree"); - return -EINVAL; - } - - res =3D fdt_check_header(pfdt); - if ( res ) - { - dprintk(XENLOG_ERR, "Failed to check the partial FDT (%d)", res); - return -EINVAL; - } - - if ( fdt_totalsize(pfdt) > size ) - { - dprintk(XENLOG_ERR, "Partial FDT totalsize is too big"); - return -EINVAL; - } - - return 0; -} - -static int __init domain_handle_dtb_bootmodule(struct domain *d, - struct kernel_info *kinfo) -{ - void *pfdt; - int res, node_next; - - pfdt =3D ioremap_cache(kinfo->dtb_bootmodule->start, - kinfo->dtb_bootmodule->size); - if ( pfdt =3D=3D NULL ) - return -EFAULT; - - res =3D check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size); - if ( res < 0 ) - goto out; - - for ( node_next =3D fdt_first_subnode(pfdt, 0);=20 - node_next > 0; - node_next =3D fdt_next_subnode(pfdt, node_next) ) - { - const char *name =3D fdt_get_name(pfdt, node_next, NULL); - - if ( name =3D=3D NULL ) - continue; - - /* - * Only scan /gic /aliases /passthrough, ignore the rest. - * They don't have to be parsed in order. - * - * Take the GIC phandle value from the special /gic node in the - * DTB fragment. - */ - if ( dt_node_cmp(name, "gic") =3D=3D 0 ) - { - kinfo->phandle_gic =3D fdt_get_phandle(pfdt, node_next); - continue; - } - - if ( dt_node_cmp(name, "aliases") =3D=3D 0 ) - { - res =3D scan_pfdt_node(kinfo, pfdt, node_next, - DT_ROOT_NODE_ADDR_CELLS_DEFAULT, - DT_ROOT_NODE_SIZE_CELLS_DEFAULT, - false); - if ( res ) - goto out; - continue; - } - if ( dt_node_cmp(name, "passthrough") =3D=3D 0 ) - { - res =3D scan_pfdt_node(kinfo, pfdt, node_next, - DT_ROOT_NODE_ADDR_CELLS_DEFAULT, - DT_ROOT_NODE_SIZE_CELLS_DEFAULT, - true); - if ( res ) - goto out; - continue; - } - } - - out: - iounmap(pfdt); - - return res; -} - -/* - * The max size for DT is 2MB. However, the generated DT is small (not inc= luding - * domU passthrough DT nodes whose size we account separately), 4KB are en= ough - * for now, but we might have to increase it in the future. - */ -#define DOMU_DTB_SIZE 4096 -static int __init prepare_dtb_domU(struct domain *d, struct kernel_info *k= info) -{ - int addrcells, sizecells; - int ret, fdt_size =3D DOMU_DTB_SIZE; - - kinfo->phandle_gic =3D GUEST_PHANDLE_GIC; - kinfo->gnttab_start =3D GUEST_GNTTAB_BASE; - kinfo->gnttab_size =3D GUEST_GNTTAB_SIZE; - - addrcells =3D GUEST_ROOT_ADDRESS_CELLS; - sizecells =3D GUEST_ROOT_SIZE_CELLS; - - /* Account for domU passthrough DT size */ - if ( kinfo->dtb_bootmodule ) - fdt_size +=3D kinfo->dtb_bootmodule->size; - - /* Cap to max DT size if needed */ - fdt_size =3D min(fdt_size, SZ_2M); - - kinfo->fdt =3D xmalloc_bytes(fdt_size); - if ( kinfo->fdt =3D=3D NULL ) - return -ENOMEM; - - ret =3D fdt_create(kinfo->fdt, fdt_size); - if ( ret < 0 ) - goto err; - - ret =3D fdt_finish_reservemap(kinfo->fdt); - if ( ret < 0 ) - goto err; - - ret =3D fdt_begin_node(kinfo->fdt, ""); - if ( ret < 0 ) - goto err; - - ret =3D fdt_property_cell(kinfo->fdt, "#address-cells", addrcells); - if ( ret ) - goto err; - - ret =3D fdt_property_cell(kinfo->fdt, "#size-cells", sizecells); - if ( ret ) - goto err; - - ret =3D make_chosen_node(kinfo); - if ( ret ) - goto err; - - ret =3D make_psci_node(kinfo->fdt); - if ( ret ) - goto err; - - ret =3D make_cpus_node(d, kinfo->fdt); - if ( ret ) - goto err; - - ret =3D make_memory_node(d, kinfo->fdt, addrcells, sizecells, &kinfo->= mem); - if ( ret ) - goto err; - - ret =3D make_resv_memory_node(d, kinfo->fdt, addrcells, sizecells, - &kinfo->shm_mem); - if ( ret ) - goto err; - - /* - * domain_handle_dtb_bootmodule has to be called before the rest of - * the device tree is generated because it depends on the value of - * the field phandle_gic. - */ - if ( kinfo->dtb_bootmodule ) - { - ret =3D domain_handle_dtb_bootmodule(d, kinfo); - if ( ret ) - goto err; - } - - ret =3D make_gic_domU_node(kinfo); - if ( ret ) - goto err; - - ret =3D make_timer_node(kinfo); - if ( ret ) - goto err; - - if ( kinfo->vpl011 ) - { - ret =3D -EINVAL; -#ifdef CONFIG_SBSA_VUART_CONSOLE - ret =3D make_vpl011_uart_node(kinfo); -#endif - if ( ret ) - goto err; - } - - if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS ) - { - ret =3D make_hypervisor_node(d, kinfo, addrcells, sizecells); - if ( ret ) - goto err; - } - - ret =3D fdt_end_node(kinfo->fdt); - if ( ret < 0 ) - goto err; - - ret =3D fdt_finish(kinfo->fdt); - if ( ret < 0 ) - goto err; - - return 0; - - err: - printk("Device tree generation failed (%d).\n", ret); - xfree(kinfo->fdt); - - return -EINVAL; -} - -static int __init prepare_dtb_hwdom(struct domain *d, struct kernel_info *= kinfo) -{ - const p2m_type_t default_p2mt =3D p2m_mmio_direct_c; - const void *fdt; - int new_size; - int ret; - - ASSERT(dt_host && (dt_host->sibling =3D=3D NULL)); - - kinfo->phandle_gic =3D dt_interrupt_controller->phandle; - fdt =3D device_tree_flattened; - - new_size =3D fdt_totalsize(fdt) + DOM0_FDT_EXTRA_SIZE; - kinfo->fdt =3D xmalloc_bytes(new_size); - if ( kinfo->fdt =3D=3D NULL ) - return -ENOMEM; - - ret =3D fdt_create(kinfo->fdt, new_size); - if ( ret < 0 ) - goto err; - - fdt_finish_reservemap(kinfo->fdt); - - ret =3D handle_node(d, kinfo, dt_host, default_p2mt); - if ( ret ) - goto err; - - ret =3D fdt_finish(kinfo->fdt); - if ( ret < 0 ) - goto err; - - return 0; - - err: - printk("Device tree generation failed (%d).\n", ret); - xfree(kinfo->fdt); - return -EINVAL; -} - -static void __init dtb_load(struct kernel_info *kinfo) -{ - unsigned long left; - - printk("Loading %pd DTB to 0x%"PRIpaddr"-0x%"PRIpaddr"\n", - kinfo->d, kinfo->dtb_paddr, - kinfo->dtb_paddr + fdt_totalsize(kinfo->fdt)); - - left =3D copy_to_guest_phys_flush_dcache(kinfo->d, kinfo->dtb_paddr, - kinfo->fdt, - fdt_totalsize(kinfo->fdt)); - - if ( left !=3D 0 ) - panic("Unable to copy the DTB to %pd memory (left =3D %lu bytes)\n= ", - kinfo->d, left); - xfree(kinfo->fdt); -} - -static void __init initrd_load(struct kernel_info *kinfo) -{ - const struct bootmodule *mod =3D kinfo->initrd_bootmodule; - paddr_t load_addr =3D kinfo->initrd_paddr; - paddr_t paddr, len; - int node; - int res; - __be32 val[2]; - __be32 *cellp; - void __iomem *initrd; - - if ( !mod || !mod->size ) - return; - - paddr =3D mod->start; - len =3D mod->size; - - printk("Loading %pd initrd from %"PRIpaddr" to 0x%"PRIpaddr"-0x%"PRIpa= ddr"\n", - kinfo->d, paddr, load_addr, load_addr + len); - - /* Fix up linux,initrd-start and linux,initrd-end in /chosen */ - node =3D fdt_path_offset(kinfo->fdt, "/chosen"); - if ( node < 0 ) - panic("Cannot find the /chosen node\n"); - - cellp =3D (__be32 *)val; - dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr); - res =3D fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-start", - val, sizeof(val)); - if ( res ) - panic("Cannot fix up \"linux,initrd-start\" property\n"); - - cellp =3D (__be32 *)val; - dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr + len); - res =3D fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-end", - val, sizeof(val)); - if ( res ) - panic("Cannot fix up \"linux,initrd-end\" property\n"); - - initrd =3D ioremap_wc(paddr, len); - if ( !initrd ) - panic("Unable to map the hwdom initrd\n"); - - res =3D copy_to_guest_phys_flush_dcache(kinfo->d, load_addr, - initrd, len); - if ( res !=3D 0 ) - panic("Unable to copy the initrd in the hwdom memory\n"); - - iounmap(initrd); -} - -/* - * Allocate the event channel PPIs and setup the HVM_PARAM_CALLBACK_IRQ. - * The allocated IRQ will be found in d->arch.evtchn_irq. - * - * Note that this should only be called once all PPIs used by the - * hardware domain have been registered. - */ -void __init evtchn_allocate(struct domain *d) -{ - int res; - u64 val; - - res =3D vgic_allocate_ppi(d); - if ( res < 0 ) - panic("Unable to allocate a PPI for the event channel interrupt\n"= ); - - d->arch.evtchn_irq =3D res; - - printk("Allocating PPI %u for event channel interrupt\n", - d->arch.evtchn_irq); - - /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ */ - val =3D MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI, - HVM_PARAM_CALLBACK_IRQ_TYPE_MASK); - /* Active-low level-sensitive */ - val |=3D MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL, - HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK); - val |=3D d->arch.evtchn_irq; - d->arch.hvm.params[HVM_PARAM_CALLBACK_IRQ] =3D val; -} - -static int __init get_evtchn_dt_property(const struct dt_device_node *np, - uint32_t *port, uint32_t *phandle) -{ - const __be32 *prop =3D NULL; - uint32_t len; - - prop =3D dt_get_property(np, "xen,evtchn", &len); - if ( !prop ) - { - printk(XENLOG_ERR "xen,evtchn property should not be empty.\n"); - return -EINVAL; - } - - if ( !len || len < dt_cells_to_size(STATIC_EVTCHN_NODE_SIZE_CELLS) ) - { - printk(XENLOG_ERR "xen,evtchn property value is not valid.\n"); + printk(XENLOG_ERR "xen,evtchn property value is not valid.\n"); return -EINVAL; } =20 @@ -3412,22 +2732,7 @@ static void __init find_gnttab_region(struct domain = *d, kinfo->gnttab_start, kinfo->gnttab_start + kinfo->gnttab_size); } =20 -static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb, - unsigned int smp_cpus) -{ - /* - * Keep in sync with libxl__get_required_paging_memory(). - * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M ma= p, - * plus 128 pages to cover extended regions. - */ - unsigned long memkb =3D 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128= ); - - BUILD_BUG_ON(PAGE_SIZE !=3D SZ_4K); - - return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); -} - -static int __init construct_domain(struct domain *d, struct kernel_info *k= info) +int __init construct_domain(struct domain *d, struct kernel_info *kinfo) { unsigned int i; struct vcpu *v =3D d->vcpu[0]; @@ -3518,296 +2823,6 @@ static int __init construct_domain(struct domain *d= , struct kernel_info *kinfo) return 0; } =20 -static int __init alloc_xenstore_evtchn(struct domain *d) -{ - evtchn_alloc_unbound_t alloc; - int rc; - - alloc.dom =3D d->domain_id; - alloc.remote_dom =3D hardware_domain->domain_id; - rc =3D evtchn_alloc_unbound(&alloc, 0); - if ( rc ) - { - printk("Failed allocating event channel for domain\n"); - return rc; - } - - d->arch.hvm.params[HVM_PARAM_STORE_EVTCHN] =3D alloc.port; - - return 0; -} - -static int __init construct_domU(struct domain *d, - const struct dt_device_node *node) -{ - struct kernel_info kinfo =3D {}; - const char *dom0less_enhanced; - int rc; - u64 mem; - u32 p2m_mem_mb; - unsigned long p2m_pages; - - rc =3D dt_property_read_u64(node, "memory", &mem); - if ( !rc ) - { - printk("Error building DomU: cannot read \"memory\" property\n"); - return -EINVAL; - } - kinfo.unassigned_mem =3D (paddr_t)mem * SZ_1K; - - rc =3D dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb= ); - /* If xen,domain-p2m-mem-mb is not specified, use the default value. */ - p2m_pages =3D rc ? - p2m_mem_mb << (20 - PAGE_SHIFT) : - domain_p2m_pages(mem, d->max_vcpus); - - spin_lock(&d->arch.paging.lock); - rc =3D p2m_set_allocation(d, p2m_pages, NULL); - spin_unlock(&d->arch.paging.lock); - if ( rc !=3D 0 ) - return rc; - - printk("*** LOADING DOMU cpus=3D%u memory=3D%#"PRIx64"KB ***\n", - d->max_vcpus, mem); - - kinfo.vpl011 =3D dt_property_read_bool(node, "vpl011"); - - rc =3D dt_property_read_string(node, "xen,enhanced", &dom0less_enhance= d); - if ( rc =3D=3D -EILSEQ || - rc =3D=3D -ENODATA || - (rc =3D=3D 0 && !strcmp(dom0less_enhanced, "enabled")) ) - { - if ( hardware_domain ) - kinfo.dom0less_feature =3D DOM0LESS_ENHANCED; - else - panic("At the moment, Xenstore support requires dom0 to be pre= sent\n"); - } - else if ( rc =3D=3D 0 && !strcmp(dom0less_enhanced, "no-xenstore") ) - kinfo.dom0less_feature =3D DOM0LESS_ENHANCED_NO_XS; - - if ( vcpu_create(d, 0) =3D=3D NULL ) - return -ENOMEM; - - d->max_pages =3D ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; - - kinfo.d =3D d; - - rc =3D kernel_probe(&kinfo, node); - if ( rc < 0 ) - return rc; - -#ifdef CONFIG_ARM_64 - /* type must be set before allocate memory */ - d->arch.type =3D kinfo.type; -#endif - if ( !dt_find_property(node, "xen,static-mem", NULL) ) - allocate_memory(d, &kinfo); - else if ( !is_domain_direct_mapped(d) ) - allocate_static_memory(d, &kinfo, node); - else - assign_static_memory_11(d, &kinfo, node); - -#ifdef CONFIG_STATIC_SHM - rc =3D process_shm(d, &kinfo, node); - if ( rc < 0 ) - return rc; -#endif - - /* - * Base address and irq number are needed when creating vpl011 device - * tree node in prepare_dtb_domU, so initialization on related variabl= es - * shall be done first. - */ - if ( kinfo.vpl011 ) - { - rc =3D domain_vpl011_init(d, NULL); - if ( rc < 0 ) - return rc; - } - - rc =3D prepare_dtb_domU(d, &kinfo); - if ( rc < 0 ) - return rc; - - rc =3D construct_domain(d, &kinfo); - if ( rc < 0 ) - return rc; - - if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE ) - { - ASSERT(hardware_domain); - rc =3D alloc_xenstore_evtchn(d); - if ( rc < 0 ) - return rc; - d->arch.hvm.params[HVM_PARAM_STORE_PFN] =3D ~0ULL; - } - - return rc; -} - -void __init create_domUs(void) -{ - struct dt_device_node *node; - const struct dt_device_node *cpupool_node, - *chosen =3D dt_find_node_by_path("/chosen"= ); - - BUG_ON(chosen =3D=3D NULL); - dt_for_each_child_node(chosen, node) - { - struct domain *d; - struct xen_domctl_createdomain d_cfg =3D { - .arch.gic_version =3D XEN_DOMCTL_CONFIG_GIC_NATIVE, - .flags =3D XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, - /* - * The default of 1023 should be sufficient for guests because - * on ARM we don't bind physical interrupts to event channels. - * The only use of the evtchn port is inter-domain communicati= ons. - * 1023 is also the default value used in libxl. - */ - .max_evtchn_port =3D 1023, - .max_grant_frames =3D -1, - .max_maptrack_frames =3D -1, - .grant_opts =3D XEN_DOMCTL_GRANT_version(opt_gnttab_max_versio= n), - }; - unsigned int flags =3D 0U; - uint32_t val; - int rc; - - if ( !dt_device_is_compatible(node, "xen,domain") ) - continue; - - if ( (max_init_domid + 1) >=3D DOMID_FIRST_RESERVED ) - panic("No more domain IDs available\n"); - - if ( dt_find_property(node, "xen,static-mem", NULL) ) - flags |=3D CDF_staticmem; - - if ( dt_property_read_bool(node, "direct-map") ) - { - if ( !(flags & CDF_staticmem) ) - panic("direct-map is not valid for domain %s without stati= c allocation.\n", - dt_node_name(node)); - - flags |=3D CDF_directmap; - } - - if ( !dt_property_read_u32(node, "cpus", &d_cfg.max_vcpus) ) - panic("Missing property 'cpus' for domain %s\n", - dt_node_name(node)); - - if ( dt_find_compatible_node(node, NULL, "multiboot,device-tree") = && - iommu_enabled ) - d_cfg.flags |=3D XEN_DOMCTL_CDF_iommu; - - if ( !dt_property_read_u32(node, "nr_spis", &d_cfg.arch.nr_spis) ) - { - int vpl011_virq =3D GUEST_VPL011_SPI; - - d_cfg.arch.nr_spis =3D gic_number_lines() - 32; - - /* - * The VPL011 virq is GUEST_VPL011_SPI, unless direct-map is - * set, in which case it'll match the hardware. - * - * Since the domain is not yet created, we can't use - * d->arch.vpl011.irq. So the logic to find the vIRQ has to - * be hardcoded. - * The logic here shall be consistent with the one in - * domain_vpl011_init(). - */ - if ( flags & CDF_directmap ) - { - vpl011_virq =3D serial_irq(SERHND_DTUART); - if ( vpl011_virq < 0 ) - panic("Error getting IRQ number for this serial port %= d\n", - SERHND_DTUART); - } - - /* - * vpl011 uses one emulated SPI. If vpl011 is requested, make - * sure that we allocate enough SPIs for it. - */ - if ( dt_property_read_bool(node, "vpl011") ) - d_cfg.arch.nr_spis =3D MAX(d_cfg.arch.nr_spis, - vpl011_virq - 32 + 1); - } - - /* Get the optional property domain-cpupool */ - cpupool_node =3D dt_parse_phandle(node, "domain-cpupool", 0); - if ( cpupool_node ) - { - int pool_id =3D btcpupools_get_domain_pool_id(cpupool_node); - if ( pool_id < 0 ) - panic("Error getting cpupool id from domain-cpupool (%d)\n= ", - pool_id); - d_cfg.cpupool_id =3D pool_id; - } - - if ( dt_property_read_u32(node, "max_grant_version", &val) ) - d_cfg.grant_opts =3D XEN_DOMCTL_GRANT_version(val); - - if ( dt_property_read_u32(node, "max_grant_frames", &val) ) - { - if ( val > INT32_MAX ) - panic("max_grant_frames (%"PRIu32") overflow\n", val); - d_cfg.max_grant_frames =3D val; - } - - if ( dt_property_read_u32(node, "max_maptrack_frames", &val) ) - { - if ( val > INT32_MAX ) - panic("max_maptrack_frames (%"PRIu32") overflow\n", val); - d_cfg.max_maptrack_frames =3D val; - } - - if ( dt_get_property(node, "sve", &val) ) - { -#ifdef CONFIG_ARM64_SVE - unsigned int sve_vl_bits; - bool ret =3D false; - - if ( !val ) - { - /* Property found with no value, means max HW VL supported= */ - ret =3D sve_domctl_vl_param(-1, &sve_vl_bits); - } - else - { - if ( dt_property_read_u32(node, "sve", &val) ) - ret =3D sve_domctl_vl_param(val, &sve_vl_bits); - else - panic("Error reading 'sve' property\n"); - } - - if ( ret ) - d_cfg.arch.sve_vl =3D sve_encode_vl(sve_vl_bits); - else - panic("SVE vector length error\n"); -#else - panic("'sve' property found, but CONFIG_ARM64_SVE not selected= \n"); -#endif - } - - /* - * The variable max_init_domid is initialized with zero, so here i= t's - * very important to use the pre-increment operator to call - * domain_create() with a domid > 0. (domid =3D=3D 0 is reserved f= or Dom0) - */ - d =3D domain_create(++max_init_domid, &d_cfg, flags); - if ( IS_ERR(d) ) - panic("Error creating domain %s (rc =3D %ld)\n", - dt_node_name(node), PTR_ERR(d)); - - d->is_console =3D true; - dt_device_set_used_by(node, d->domain_id); - - rc =3D construct_domU(d, node); - if ( rc ) - panic("Could not set up domain %s (rc =3D %d)\n", - dt_node_name(node), rc); - } -} - static int __init construct_dom0(struct domain *d) { struct kernel_info kinfo =3D {}; diff --git a/xen/arch/arm/include/asm/dom0less-build.h b/xen/arch/arm/inclu= de/asm/dom0less-build.h new file mode 100644 index 000000000000..0d28fa1bee46 --- /dev/null +++ b/xen/arch/arm/include/asm/dom0less-build.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/include/asm/dom0less-build.h + * + * Copyright (C) 2023 Arm Ltd. + */ + +#ifndef __ARM_DOM0LESS_BUILD_H_ +#define __ARM_DOM0LESS_BUILD_H_ + +#include + +void create_domUs(void); +bool is_dom0less_mode(void); + +#endif /* __ARM_DOM0LESS_BUILD_H_ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End:b + */ diff --git a/xen/arch/arm/include/asm/domain_build.h b/xen/arch/arm/include= /asm/domain_build.h index b9329c9ee032..b9ae0d170c5c 100644 --- a/xen/arch/arm/include/asm/domain_build.h +++ b/xen/arch/arm/include/asm/domain_build.h @@ -4,9 +4,47 @@ #include #include =20 +typedef __be32 gic_interrupt_t[3]; + +bool allocate_bank_memory(struct domain *d, struct kernel_info *kinfo, + gfn_t sgfn, paddr_t tot_size); +int construct_domain(struct domain *d, struct kernel_info *kinfo); +int domain_fdt_begin_node(void *fdt, const char *name, uint64_t unit); +int make_resv_memory_node(const struct domain *d, void *fdt, int addrcells, + int sizecells, const struct meminfo *mem); int make_chosen_node(const struct kernel_info *kinfo); +int make_cpus_node(const struct domain *d, void *fdt); +int make_hypervisor_node(struct domain *d, const struct kernel_info *kinfo, + int addrcells, int sizecells); +int make_memory_node(const struct domain *d, void *fdt, int addrcells, + int sizecells, struct meminfo *mem); +int make_psci_node(void *fdt); +int make_timer_node(const struct kernel_info *kinfo); void evtchn_allocate(struct domain *d); =20 +unsigned int get_allocation_size(paddr_t size); + +/* + * handle_device_interrupts retrieves the interrupts configuration from + * a device tree node and maps those interrupts to the target domain. + * + * Returns: + * < 0 error + * 0 success + */ +int handle_device_interrupts(struct domain *d, struct dt_device_node *dev, + bool need_mapping); + +/* + * Helper to write an interrupts with the GIC format + * This code is assuming the irq is an PPI. + */ +void set_interrupt(gic_interrupt_t interrupt, unsigned int irq, + unsigned int cpumask, unsigned int level); + +int process_shm(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node); + #ifndef CONFIG_ACPI static inline int prepare_acpi(struct domain *d, struct kernel_info *kinfo) { @@ -17,6 +55,28 @@ static inline int prepare_acpi(struct domain *d, struct = kernel_info *kinfo) #else int prepare_acpi(struct domain *d, struct kernel_info *kinfo); #endif + +#ifdef CONFIG_STATIC_MEMORY +void allocate_static_memory(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node); +void assign_static_memory_11(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node); +#else +static inline void allocate_static_memory(struct domain *d, + struct kernel_info *kinfo, + const struct dt_device_node *nod= e) +{ + ASSERT_UNREACHABLE(); +} + +static inline void assign_static_memory_11(struct domain *d, + struct kernel_info *kinfo, + const struct dt_device_node *no= de) +{ + ASSERT_UNREACHABLE(); +} +#endif + #endif =20 /* diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/se= tup.h index 98af6f55f5a0..39359f64b678 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -135,7 +135,6 @@ void acpi_create_efi_mmap_table(struct domain *d, =20 int acpi_make_efi_nodes(void *fdt, struct membank tbl_add[]); =20 -void create_domUs(void); void create_dom0(void); void alloc_static_evtchn(void); =20 diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 3f3a45719ccb..60b544b67b4e 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1042,38 +1043,6 @@ static void __init setup_mm(void) } #endif =20 -static bool __init is_dom0less_mode(void) -{ - struct bootmodules *mods =3D &bootinfo.modules; - struct bootmodule *mod; - unsigned int i; - bool dom0found =3D false; - bool domUfound =3D false; - - /* Look into the bootmodules */ - for ( i =3D 0 ; i < mods->nr_mods ; i++ ) - { - mod =3D &mods->module[i]; - /* Find if dom0 and domU kernels are present */ - if ( mod->kind =3D=3D BOOTMOD_KERNEL ) - { - if ( mod->domU =3D=3D false ) - { - dom0found =3D true; - break; - } - else - domUfound =3D true; - } - } - - /* - * If there is no dom0 kernel but at least one domU, then we are in - * dom0less mode - */ - return ( !dom0found && domUfound ); -} - size_t __read_mostly dcache_line_bytes; =20 /* C entry point for boot CPU */ --=20 2.34.1