From patchwork Thu Nov 9 09:06:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Fancellu X-Patchwork-Id: 13450816 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 003B1C0018C for ; Thu, 9 Nov 2023 09:06:42 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.629507.981809 (Exim 4.92) (envelope-from ) id 1r10zf-0005m5-Nn; Thu, 09 Nov 2023 09:06:31 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 629507.981809; Thu, 09 Nov 2023 09:06:31 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zf-0005lw-L8; Thu, 09 Nov 2023 09:06:31 +0000 Received: by outflank-mailman (input) for mailman id 629507; Thu, 09 Nov 2023 09:06:30 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10ze-0005V5-ED for xen-devel@lists.xenproject.org; Thu, 09 Nov 2023 09:06:30 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-flk1.inumbo.com (Halon) with ESMTP id 4305d64e-7edf-11ee-9b0e-b553b5be7939; Thu, 09 Nov 2023 10:06:26 +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 413F11477; Thu, 9 Nov 2023 01:07:10 -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 C30813F703; Thu, 9 Nov 2023 01:06:24 -0800 (PST) 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: 4305d64e-7edf-11ee-9b0e-b553b5be7939 From: Luca Fancellu To: xen-devel@lists.xenproject.org Cc: Stefano Stabellini , Julien Grall , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk , Julien Grall Subject: [PATCH v3 1/5] arm/gicv2: make GICv2 driver and vGICv2 optional Date: Thu, 9 Nov 2023 09:06:11 +0000 Message-Id: <20231109090615.3878767-2-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 Introduce Kconfig GICV2 to be able to compile the GICv2 driver only when needed, the option is active by default. Introduce Kconfig VGICV2 that compiles the Generic Interrupt Controller v2 emulation for domains, it is required only when using GICv2 driver, otherwise using the GICv3 driver it is optional and can be deselected if the user doesn't want to offer the v2 emulation to domains or maybe its GICv3 hardware can't offer the GICv2 compatible mode. Signed-off-by: Luca Fancellu Reviewed-by: Julien Grall Reviewed-by: Michal Orzel --- Changes from v2: - No changes --- xen/arch/arm/Kconfig | 19 +++++++++++++++++++ xen/arch/arm/Makefile | 4 ++-- xen/arch/arm/domain_build.c | 4 ++++ xen/arch/arm/gic-v3.c | 4 ++++ xen/arch/arm/vgic.c | 2 ++ xen/arch/arm/vgic/Makefile | 4 ++-- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 2939db429b78..0045a3762d86 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -80,6 +80,14 @@ config ARM_EFI UEFI firmware. A UEFI stub is provided to allow Xen to be booted as an EFI application. +config GICV2 + bool "GICv2 driver" + default y + select VGICV2 + help + Driver for the ARM Generic Interrupt Controller v2. + If unsure, say Y + config GICV3 bool "GICv3 driver" depends on !NEW_VGIC @@ -99,11 +107,22 @@ config OVERLAY_DTB help Dynamic addition/removal of Xen device tree nodes using a dtbo. +config VGICV2 + bool "vGICv2 interface for domains" + default y + help + Allow Xen to expose a Generic Interrupt Controller version 2 like to Xen + domains. This can be configured at the domain creation. + This option is mandatory when using GICv2. + For GICv3, this allows domain to use GICv2 when the hardware supports it. + If unsure say Y. + config HVM def_bool y config NEW_VGIC bool "Use new VGIC implementation" + select GICV2 ---help--- This is an alternative implementation of the ARM GIC interrupt diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 7bf07e992046..81c31c36fc3d 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -22,7 +22,7 @@ obj-y += domctl.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += efi/ obj-y += gic.o -obj-y += gic-v2.o +obj-$(CONFIG_GICV2) += gic-v2.o obj-$(CONFIG_GICV3) += gic-v3.o obj-$(CONFIG_HAS_ITS) += gic-v3-its.o obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o @@ -57,7 +57,7 @@ obj-$(CONFIG_NEW_VGIC) += vgic/ ifneq ($(CONFIG_NEW_VGIC),y) obj-y += gic-vgic.o obj-y += vgic.o -obj-y += vgic-v2.o +obj-$(CONFIG_VGICV2) += vgic-v2.o obj-$(CONFIG_GICV3) += vgic-v3.o obj-$(CONFIG_HAS_ITS) += vgic-v3-its.o endif diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 49792dd590ee..3d3a117bde7a 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -2490,6 +2490,7 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo, return res; } +#ifdef CONFIG_VGICV2 static int __init make_gicv2_domU_node(struct kernel_info *kinfo) { void *fdt = kinfo->fdt; @@ -2541,6 +2542,7 @@ static int __init make_gicv2_domU_node(struct kernel_info *kinfo) return res; } +#endif #ifdef CONFIG_GICV3 static int __init make_gicv3_domU_node(struct kernel_info *kinfo) @@ -2616,8 +2618,10 @@ static int __init make_gic_domU_node(struct kernel_info *kinfo) 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"); } diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c index 172ff8c005ff..292abefd8731 100644 --- a/xen/arch/arm/gic-v3.c +++ b/xen/arch/arm/gic-v3.c @@ -1334,6 +1334,7 @@ static paddr_t __initdata dbase = INVALID_PADDR; static paddr_t __initdata vbase = INVALID_PADDR, vsize = 0; static paddr_t __initdata cbase = INVALID_PADDR, csize = 0; +#ifdef CONFIG_VGICV2 /* If the GICv3 supports GICv2, initialize it */ static void __init gicv3_init_v2(void) { @@ -1359,6 +1360,9 @@ static void __init gicv3_init_v2(void) vgic_v2_setup_hw(dbase, cbase, csize, vbase, 0); } +#else +static inline void gicv3_init_v2(void) { } +#endif static void __init gicv3_ioremap_distributor(paddr_t dist_paddr) { diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index f6b49766f97a..c04fc4f83f96 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -96,10 +96,12 @@ int domain_vgic_register(struct domain *d, unsigned int *mmio_count) return -ENODEV; break; #endif +#ifdef CONFIG_VGICV2 case GIC_V2: if ( vgic_v2_init(d, mmio_count) ) return -ENODEV; break; +#endif default: printk(XENLOG_G_ERR "d%d: Unknown vGIC version %u\n", d->domain_id, d->arch.vgic.version); diff --git a/xen/arch/arm/vgic/Makefile b/xen/arch/arm/vgic/Makefile index 806826948e20..60cbf7f2f94a 100644 --- a/xen/arch/arm/vgic/Makefile +++ b/xen/arch/arm/vgic/Makefile @@ -1,5 +1,5 @@ obj-y += vgic.o -obj-y += vgic-v2.o +obj-$(CONFIG_VGICV2) += vgic-v2.o obj-y += vgic-mmio.o -obj-y += vgic-mmio-v2.o +obj-$(CONFIG_VGICV2) += vgic-mmio-v2.o obj-y += vgic-init.o From patchwork Thu Nov 9 09:06:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Fancellu X-Patchwork-Id: 13450814 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 299CDC4332F for ; Thu, 9 Nov 2023 09:06:41 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.629506.981799 (Exim 4.92) (envelope-from ) id 1r10ze-0005Vn-J2; Thu, 09 Nov 2023 09:06:30 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 629506.981799; Thu, 09 Nov 2023 09:06:30 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10ze-0005Vf-DX; Thu, 09 Nov 2023 09:06:30 +0000 Received: by outflank-mailman (input) for mailman id 629506; Thu, 09 Nov 2023 09:06:29 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zd-0005V5-P9 for xen-devel@lists.xenproject.org; Thu, 09 Nov 2023 09:06:29 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-flk1.inumbo.com (Halon) with ESMTP id 43ba1a51-7edf-11ee-9b0e-b553b5be7939; Thu, 09 Nov 2023 10:06:27 +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 6E25115BF; Thu, 9 Nov 2023 01:07:11 -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 F1B963F703; Thu, 9 Nov 2023 01:06:25 -0800 (PST) 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: 43ba1a51-7edf-11ee-9b0e-b553b5be7939 From: Luca Fancellu To: xen-devel@lists.xenproject.org Cc: Stefano Stabellini , Julien Grall , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk , Julien Grall Subject: [PATCH v3 2/5] xen/arm: Add asm/domain.h include to kernel.h Date: Thu, 9 Nov 2023 09:06:12 +0000 Message-Id: <20231109090615.3878767-3-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 The 'enum domain_type' is defined by 'asm/domain.h' which is not included (directly or indirectly) by 'asm/kernel.h'. This currently doesn't break the compilation because asm/domain.h will included by the user of 'kernel.h'. But it would be better to avoid relying on it. So add the include in 'asm/domain.h'. Signed-off-by: Luca Fancellu Acked-by: Julien Grall --- Changes from v2: - add Ack-by Julien Changes from v1: - Rephrased commit message (Julien) --- xen/arch/arm/include/asm/kernel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/xen/arch/arm/include/asm/kernel.h b/xen/arch/arm/include/asm/kernel.h index 4617cdc83bac..0a23e86c2d37 100644 --- a/xen/arch/arm/include/asm/kernel.h +++ b/xen/arch/arm/include/asm/kernel.h @@ -7,6 +7,7 @@ #define __ARCH_ARM_KERNEL_H__ #include +#include #include /* From patchwork Thu Nov 9 09:06:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Fancellu X-Patchwork-Id: 13450818 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 0D042C4332F for ; Thu, 9 Nov 2023 09:06:47 +0000 (UTC) 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 X-Outflank-Mailman: Message body and most headers restored to incoming version 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-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 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 += cpufeature.o obj-y += decode.o obj-y += device.o obj-$(CONFIG_IOREQ_SERVER) += dm.o +obj-y += dom0less-build.init.o obj-y += domain.o obj-y += domain_build.init.o obj-$(CONFIG_ARCH_MAP_DOMAIN_PAGE) += 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 = &bootinfo.modules; + struct bootmodule *mod; + unsigned int i; + bool dom0found = false; + bool domUfound = false; + + /* Look into the bootmodules */ + for ( i = 0 ; i < mods->nr_mods ; i++ ) + { + mod = &mods->module[i]; + /* Find if dom0 and domU kernels are present */ + if ( mod->kind == BOOTMOD_KERNEL ) + { + if ( mod->domU == false ) + { + dom0found = true; + break; + } + else + domUfound = 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 *kinfo) +{ + 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 = 0; + bank_size = 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 = 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 = 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 = kinfo->fdt; + int res = 0; + __be32 reg[(GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * 2]; + __be32 *cells; + const struct domain *d = kinfo->d; + + res = domain_fdt_begin_node(fdt, "interrupt-controller", + vgic_dist_base(&d->arch.vgic)); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "#address-cells", 0); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "#interrupt-cells", 3); + if ( res ) + return res; + + res = fdt_property(fdt, "interrupt-controller", NULL, 0); + if ( res ) + return res; + + res = fdt_property_string(fdt, "compatible", "arm,gic-400"); + if ( res ) + return res; + + cells = ®[0]; + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, + vgic_dist_base(&d->arch.vgic), GUEST_GICD_SIZE); + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, + vgic_cpu_base(&d->arch.vgic), GUEST_GICC_SIZE); + + res = fdt_property(fdt, "reg", reg, sizeof(reg)); + if (res) + return res; + + res = fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); + if (res) + return res; + + res = fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); + if (res) + return res; + + res = fdt_end_node(fdt); + + return res; +} +#endif + +#ifdef CONFIG_GICV3 +static int __init make_gicv3_domU_node(struct kernel_info *kinfo) +{ + void *fdt = kinfo->fdt; + int res = 0; + __be32 *reg, *cells; + const struct domain *d = kinfo->d; + unsigned int i, len = 0; + + res = domain_fdt_begin_node(fdt, "interrupt-controller", + vgic_dist_base(&d->arch.vgic)); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "#address-cells", 0); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "#interrupt-cells", 3); + if ( res ) + return res; + + res = fdt_property(fdt, "interrupt-controller", NULL, 0); + if ( res ) + return res; + + res = fdt_property_string(fdt, "compatible", "arm,gic-v3"); + if ( res ) + return res; + + /* reg specifies all re-distributors and Distributor. */ + len = (GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * + (d->arch.vgic.nr_regions + 1) * sizeof(__be32); + reg = xmalloc_bytes(len); + if ( reg == NULL ) + return -ENOMEM; + cells = reg; + + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, + vgic_dist_base(&d->arch.vgic), GUEST_GICV3_GICD_SIZE); + + for ( i = 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 = fdt_property(fdt, "reg", reg, len); + xfree(reg); + if (res) + return res; + + res = fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); + if (res) + return res; + + res = fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); + if (res) + return res; + + res = 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 = kinfo->fdt; + int res; + gic_interrupt_t intr; + __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS]; + __be32 *cells; + struct domain *d = kinfo->d; + + res = domain_fdt_begin_node(fdt, "sbsa-uart", d->arch.vpl011.base_addr); + if ( res ) + return res; + + res = fdt_property_string(fdt, "compatible", "arm,sbsa-uart"); + if ( res ) + return res; + + cells = ®[0]; + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, + GUEST_ROOT_SIZE_CELLS, d->arch.vpl011.base_addr, + GUEST_PL011_SIZE); + + res = 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 = fdt_property(fdt, "interrupts", intr, sizeof (intr)); + if ( res ) + return res; + + res = 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 = 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_reg, + const struct fdt_property *xen_path, + 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 = (const __be32 *)xen_reg->data; + len = fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells) * + sizeof(uint32_t)); + + for ( i = 0; i < len; i++ ) + { + device_tree_get_reg(&cell, address_cells, size_cells, + &mstart, &size); + gstart = dt_next_cell(address_cells, &cell); + + if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MASK ) + { + printk(XENLOG_ERR + "DomU passthrough config has not page aligned addresses/sizes\n"); + return -EINVAL; + } + + res = 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 = 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 == 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 = dt_find_node_by_path(xen_path->data); + if ( node == NULL ) + { + printk(XENLOG_ERR "Couldn't find node %s in host_dt!\n", + (char *)xen_path->data); + return -EINVAL; + } + + res = map_device_irqs_to_domain(kinfo->d, node, true, NULL); + if ( res < 0 ) + return res; + + res = iommu_add_dt_device(node); + if ( res < 0 ) + return res; + + /* If xen_force, we allow assignment of devices without IOMMU protection. */ + 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_cells, + bool scan_passthrough_prop) +{ + void *fdt = kinfo->fdt; + int propoff, nameoff, res; + const struct fdt_property *prop, *xen_reg = NULL, *xen_path = NULL; + const char *name; + bool found, xen_force = false; + + 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; + + found = false; + nameoff = fdt32_to_cpu(prop->nameoff); + name = fdt_string(pfdt, nameoff); + + if ( scan_passthrough_prop ) + { + if ( dt_prop_cmp("xen,reg", name) == 0 ) + { + xen_reg = prop; + found = true; + } + else if ( dt_prop_cmp("xen,path", name) == 0 ) + { + xen_path = prop; + found = true; + } + else if ( dt_prop_cmp("xen,force-assign-without-iommu", + name) == 0 ) + { + xen_force = true; + found = true; + } + } + + /* + * Copy properties other than the ones above: xen,reg, xen,path, + * and xen,force-assign-without-iommu. + */ + if ( !found ) + { + res = 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 != NULL && (xen_path != NULL || xen_force) ) + { + res = handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_force, + 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_force) ) + { + printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n", + kinfo->d); + return -EINVAL; + } + + /* FDT_ERR_NOTFOUND => There is no more properties for this node */ + return ( propoff != -FDT_ERR_NOTFOUND ) ? propoff : 0; +} + +static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pfdt, + int nodeoff, + uint32_t address_cells, uint32_t size_cells, + bool scan_passthrough_prop) +{ + int rc = 0; + void *fdt = kinfo->fdt; + int node_next; + + rc = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL)); + if ( rc ) + return rc; + + rc = handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells, + scan_passthrough_prop); + if ( rc ) + return rc; + + address_cells = device_tree_get_u32(pfdt, nodeoff, "#address-cells", + DT_ROOT_NODE_ADDR_CELLS_DEFAULT); + size_cells = device_tree_get_u32(pfdt, nodeoff, "#size-cells", + DT_ROOT_NODE_SIZE_CELLS_DEFAULT); + + node_next = fdt_first_subnode(pfdt, nodeoff); + while ( node_next > 0 ) + { + rc = scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_cells, + scan_passthrough_prop); + if ( rc ) + return rc; + + node_next = 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) != FDT_MAGIC ) + { + dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree"); + return -EINVAL; + } + + res = 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 = ioremap_cache(kinfo->dtb_bootmodule->start, + kinfo->dtb_bootmodule->size); + if ( pfdt == NULL ) + return -EFAULT; + + res = check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size); + if ( res < 0 ) + goto out; + + for ( node_next = fdt_first_subnode(pfdt, 0); + node_next > 0; + node_next = fdt_next_subnode(pfdt, node_next) ) + { + const char *name = fdt_get_name(pfdt, node_next, NULL); + + if ( name == 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") == 0 ) + { + kinfo->phandle_gic = fdt_get_phandle(pfdt, node_next); + continue; + } + + if ( dt_node_cmp(name, "aliases") == 0 ) + { + res = 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") == 0 ) + { + res = 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 including + * domU passthrough DT nodes whose size we account separately), 4KB are enough + * 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 *kinfo) +{ + int addrcells, sizecells; + int ret, fdt_size = DOMU_DTB_SIZE; + + kinfo->phandle_gic = GUEST_PHANDLE_GIC; + kinfo->gnttab_start = GUEST_GNTTAB_BASE; + kinfo->gnttab_size = GUEST_GNTTAB_SIZE; + + addrcells = GUEST_ROOT_ADDRESS_CELLS; + sizecells = GUEST_ROOT_SIZE_CELLS; + + /* Account for domU passthrough DT size */ + if ( kinfo->dtb_bootmodule ) + fdt_size += kinfo->dtb_bootmodule->size; + + /* Cap to max DT size if needed */ + fdt_size = min(fdt_size, SZ_2M); + + kinfo->fdt = xmalloc_bytes(fdt_size); + if ( kinfo->fdt == NULL ) + return -ENOMEM; + + ret = fdt_create(kinfo->fdt, fdt_size); + if ( ret < 0 ) + goto err; + + ret = fdt_finish_reservemap(kinfo->fdt); + if ( ret < 0 ) + goto err; + + ret = fdt_begin_node(kinfo->fdt, ""); + if ( ret < 0 ) + goto err; + + ret = fdt_property_cell(kinfo->fdt, "#address-cells", addrcells); + if ( ret ) + goto err; + + ret = fdt_property_cell(kinfo->fdt, "#size-cells", sizecells); + if ( ret ) + goto err; + + ret = make_chosen_node(kinfo); + if ( ret ) + goto err; + + ret = make_psci_node(kinfo->fdt); + if ( ret ) + goto err; + + ret = make_cpus_node(d, kinfo->fdt); + if ( ret ) + goto err; + + ret = make_memory_node(d, kinfo->fdt, addrcells, sizecells, &kinfo->mem); + if ( ret ) + goto err; + + ret = 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 = domain_handle_dtb_bootmodule(d, kinfo); + if ( ret ) + goto err; + } + + ret = make_gic_domU_node(kinfo); + if ( ret ) + goto err; + + ret = make_timer_node(kinfo); + if ( ret ) + goto err; + + if ( kinfo->vpl011 ) + { + ret = -EINVAL; +#ifdef CONFIG_SBSA_VUART_CONSOLE + ret = make_vpl011_uart_node(kinfo); +#endif + if ( ret ) + goto err; + } + + if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS ) + { + ret = make_hypervisor_node(d, kinfo, addrcells, sizecells); + if ( ret ) + goto err; + } + + ret = fdt_end_node(kinfo->fdt); + if ( ret < 0 ) + goto err; + + ret = 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 map, + * plus 128 pages to cover extended regions. + */ + unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128); + + BUILD_BUG_ON(PAGE_SIZE != 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 = d->domain_id; + alloc.remote_dom = hardware_domain->domain_id; + rc = 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] = alloc.port; + + return 0; +} + +static int __init construct_domU(struct domain *d, + const struct dt_device_node *node) +{ + struct kernel_info kinfo = {}; + const char *dom0less_enhanced; + int rc; + u64 mem; + u32 p2m_mem_mb; + unsigned long p2m_pages; + + rc = dt_property_read_u64(node, "memory", &mem); + if ( !rc ) + { + printk("Error building DomU: cannot read \"memory\" property\n"); + return -EINVAL; + } + kinfo.unassigned_mem = (paddr_t)mem * SZ_1K; + + rc = 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 = rc ? + p2m_mem_mb << (20 - PAGE_SHIFT) : + domain_p2m_pages(mem, d->max_vcpus); + + spin_lock(&d->arch.paging.lock); + rc = p2m_set_allocation(d, p2m_pages, NULL); + spin_unlock(&d->arch.paging.lock); + if ( rc != 0 ) + return rc; + + printk("*** LOADING DOMU cpus=%u memory=%#"PRIx64"KB ***\n", + d->max_vcpus, mem); + + kinfo.vpl011 = dt_property_read_bool(node, "vpl011"); + + rc = dt_property_read_string(node, "xen,enhanced", &dom0less_enhanced); + if ( rc == -EILSEQ || + rc == -ENODATA || + (rc == 0 && !strcmp(dom0less_enhanced, "enabled")) ) + { + if ( hardware_domain ) + kinfo.dom0less_feature = DOM0LESS_ENHANCED; + else + panic("At the moment, Xenstore support requires dom0 to be present\n"); + } + else if ( rc == 0 && !strcmp(dom0less_enhanced, "no-xenstore") ) + kinfo.dom0less_feature = DOM0LESS_ENHANCED_NO_XS; + + if ( vcpu_create(d, 0) == NULL ) + return -ENOMEM; + + d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; + + kinfo.d = d; + + rc = kernel_probe(&kinfo, node); + if ( rc < 0 ) + return rc; + +#ifdef CONFIG_ARM_64 + /* type must be set before allocate memory */ + d->arch.type = 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 = 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 variables + * shall be done first. + */ + if ( kinfo.vpl011 ) + { + rc = domain_vpl011_init(d, NULL); + if ( rc < 0 ) + return rc; + } + + rc = prepare_dtb_domU(d, &kinfo); + if ( rc < 0 ) + return rc; + + rc = construct_domain(d, &kinfo); + if ( rc < 0 ) + return rc; + + if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE ) + { + ASSERT(hardware_domain); + rc = alloc_xenstore_evtchn(d); + if ( rc < 0 ) + return rc; + d->arch.hvm.params[HVM_PARAM_STORE_PFN] = ~0ULL; + } + + return rc; +} + +void __init create_domUs(void) +{ + struct dt_device_node *node; + const struct dt_device_node *cpupool_node, + *chosen = dt_find_node_by_path("/chosen"); + + BUG_ON(chosen == NULL); + dt_for_each_child_node(chosen, node) + { + struct domain *d; + struct xen_domctl_createdomain d_cfg = { + .arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE, + .flags = 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 communications. + * 1023 is also the default value used in libxl. + */ + .max_evtchn_port = 1023, + .max_grant_frames = -1, + .max_maptrack_frames = -1, + .grant_opts = XEN_DOMCTL_GRANT_version(opt_gnttab_max_version), + }; + unsigned int flags = 0U; + uint32_t val; + int rc; + + if ( !dt_device_is_compatible(node, "xen,domain") ) + continue; + + if ( (max_init_domid + 1) >= DOMID_FIRST_RESERVED ) + panic("No more domain IDs available\n"); + + if ( dt_find_property(node, "xen,static-mem", NULL) ) + flags |= CDF_staticmem; + + if ( dt_property_read_bool(node, "direct-map") ) + { + if ( !(flags & CDF_staticmem) ) + panic("direct-map is not valid for domain %s without static allocation.\n", + dt_node_name(node)); + + flags |= 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 |= XEN_DOMCTL_CDF_iommu; + + if ( !dt_property_read_u32(node, "nr_spis", &d_cfg.arch.nr_spis) ) + { + int vpl011_virq = GUEST_VPL011_SPI; + + d_cfg.arch.nr_spis = 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 = 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 = MAX(d_cfg.arch.nr_spis, + vpl011_virq - 32 + 1); + } + + /* Get the optional property domain-cpupool */ + cpupool_node = dt_parse_phandle(node, "domain-cpupool", 0); + if ( cpupool_node ) + { + int pool_id = 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 = pool_id; + } + + if ( dt_property_read_u32(node, "max_grant_version", &val) ) + d_cfg.grant_opts = 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 = 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 = val; + } + + if ( dt_get_property(node, "sve", &val) ) + { +#ifdef CONFIG_ARM64_SVE + unsigned int sve_vl_bits; + bool ret = false; + + if ( !val ) + { + /* Property found with no value, means max HW VL supported */ + ret = sve_domctl_vl_param(-1, &sve_vl_bits); + } + else + { + if ( dt_property_read_u32(node, "sve", &val) ) + ret = sve_domctl_vl_param(val, &sve_vl_bits); + else + panic("Error reading 'sve' property\n"); + } + + if ( ret ) + d_cfg.arch.sve_vl = 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 it's + * very important to use the pre-increment operator to call + * domain_create() with a domid > 0. (domid == 0 is reserved for Dom0) + */ + d = domain_create(++max_init_domid, &d_cfg, flags); + if ( IS_ERR(d) ) + panic("Error creating domain %s (rc = %ld)\n", + dt_node_name(node), PTR_ERR(d)); + + d->is_console = true; + dt_device_set_used_by(node, d->domain_id); + + rc = construct_domU(d, node); + if ( rc ) + panic("Could not set up domain %s (rc = %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 @@ -117,7 +118,7 @@ struct vcpu *__init alloc_dom0_vcpu0(struct domain *dom0) return vcpu_create(dom0, 0); } -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, } } -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 *kinfo, + 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; } -static void __init allocate_memory(struct domain *d, struct kernel_info *kinfo) -{ - 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 = 0; - bank_size = 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 = 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 = 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, } /* 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 *node) +void __init allocate_static_memory(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node) { u32 addr_cells, size_cells, reg_cells; unsigned int nr_banks, gbank, bank = 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 == Physical Address). */ -static void __init assign_static_memory_11(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *node) +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 = 0; @@ -941,8 +895,8 @@ static int __init append_shm_bank_to_domain(struct kernel_info *kinfo, return 0; } -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; @@ -1047,20 +1001,6 @@ static int __init process_shm(struct domain *d, struct 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 *node) -{ - ASSERT_UNREACHABLE(); -} - -static void __init assign_static_memory_11(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *node) -{ - ASSERT_UNREACHABLE(); -} #endif /* @@ -1264,17 +1204,10 @@ static int __init write_properties(struct domain *d, struct kernel_info *kinfo, return 0; } -/* - * 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 = interrupt; bool is_ppi = !!(irq < 32); @@ -1319,8 +1252,7 @@ static int __init fdt_property_interrupts(const struct 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 unit) { /* * 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); } -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 = addrcells + sizecells; @@ -1483,10 +1415,10 @@ static int __init make_shm_memory_node(const struct domain *d, } #endif -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 = 0; /* Placeholder for reserved-memory\0 */ @@ -1819,9 +1751,9 @@ static int __init find_domU_holes(const struct kernel_info *kinfo, return res; } -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[] = "xen,xen-" XEN_VERSION_STRING "\0" @@ -1938,7 +1870,7 @@ static int __init make_hypervisor_node(struct domain *d, return res; } -static int __init make_psci_node(void *fdt) +int __init make_psci_node(void *fdt) { int res; const char compat[] = @@ -1974,7 +1906,7 @@ static int __init make_psci_node(void *fdt) return res; } -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 = dt_find_node_by_path("/cpus"); @@ -2172,7 +2104,7 @@ static int __init make_gic_node(const struct domain *d, void *fdt, return res; } -static int __init make_timer_node(const struct kernel_info *kinfo) +int __init make_timer_node(const struct kernel_info *kinfo) { void *fdt = kinfo->fdt; static const struct dt_device_match timer_ids[] __initconst = @@ -2490,773 +2422,161 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo, return res; } -#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 = kinfo->fdt; - int res = 0; - __be32 reg[(GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * 2]; - __be32 *cells; - const struct domain *d = kinfo->d; - - res = domain_fdt_begin_node(fdt, "interrupt-controller", - vgic_dist_base(&d->arch.vgic)); - if ( res ) - return res; - - res = fdt_property_cell(fdt, "#address-cells", 0); - if ( res ) - return res; + const p2m_type_t default_p2mt = p2m_mmio_direct_c; + const void *fdt; + int new_size; + int ret; - res = fdt_property_cell(fdt, "#interrupt-cells", 3); - if ( res ) - return res; + ASSERT(dt_host && (dt_host->sibling == NULL)); - res = fdt_property(fdt, "interrupt-controller", NULL, 0); - if ( res ) - return res; + kinfo->phandle_gic = dt_interrupt_controller->phandle; + fdt = device_tree_flattened; - res = fdt_property_string(fdt, "compatible", "arm,gic-400"); - if ( res ) - return res; + new_size = fdt_totalsize(fdt) + DOM0_FDT_EXTRA_SIZE; + kinfo->fdt = xmalloc_bytes(new_size); + if ( kinfo->fdt == NULL ) + return -ENOMEM; - cells = ®[0]; - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, - vgic_dist_base(&d->arch.vgic), GUEST_GICD_SIZE); - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, - vgic_cpu_base(&d->arch.vgic), GUEST_GICC_SIZE); + ret = fdt_create(kinfo->fdt, new_size); + if ( ret < 0 ) + goto err; - res = fdt_property(fdt, "reg", reg, sizeof(reg)); - if (res) - return res; + fdt_finish_reservemap(kinfo->fdt); - res = fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); - if (res) - return res; + ret = handle_node(d, kinfo, dt_host, default_p2mt); + if ( ret ) + goto err; - res = fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); - if (res) - return res; + ret = fdt_finish(kinfo->fdt); + if ( ret < 0 ) + goto err; - res = fdt_end_node(fdt); + return 0; - return res; + err: + printk("Device tree generation failed (%d).\n", ret); + xfree(kinfo->fdt); + return -EINVAL; } -#endif -#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 = kinfo->fdt; - int res = 0; - __be32 *reg, *cells; - const struct domain *d = kinfo->d; - unsigned int i, len = 0; - - res = domain_fdt_begin_node(fdt, "interrupt-controller", - vgic_dist_base(&d->arch.vgic)); - if ( res ) - return res; - - res = fdt_property_cell(fdt, "#address-cells", 0); - if ( res ) - return res; - - res = fdt_property_cell(fdt, "#interrupt-cells", 3); - if ( res ) - return res; - - res = fdt_property(fdt, "interrupt-controller", NULL, 0); - if ( res ) - return res; - - res = fdt_property_string(fdt, "compatible", "arm,gic-v3"); - if ( res ) - return res; - - /* reg specifies all re-distributors and Distributor. */ - len = (GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS) * - (d->arch.vgic.nr_regions + 1) * sizeof(__be32); - reg = xmalloc_bytes(len); - if ( reg == NULL ) - return -ENOMEM; - cells = reg; - - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, - vgic_dist_base(&d->arch.vgic), GUEST_GICV3_GICD_SIZE); - - for ( i = 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 = fdt_property(fdt, "reg", reg, len); - xfree(reg); - if (res) - return res; - - res = fdt_property_cell(fdt, "linux,phandle", kinfo->phandle_gic); - if (res) - return res; - - res = fdt_property_cell(fdt, "phandle", kinfo->phandle_gic); - if (res) - return res; + unsigned long left; - res = 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)); - return res; -} -#endif + left = copy_to_guest_phys_flush_dcache(kinfo->d, kinfo->dtb_paddr, + kinfo->fdt, + fdt_totalsize(kinfo->fdt)); -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 != 0 ) + panic("Unable to copy the DTB to %pd memory (left = %lu bytes)\n", + kinfo->d, left); + xfree(kinfo->fdt); } -#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 = kinfo->fdt; + const struct bootmodule *mod = kinfo->initrd_bootmodule; + paddr_t load_addr = 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 = kinfo->d; - - res = domain_fdt_begin_node(fdt, "sbsa-uart", d->arch.vpl011.base_addr); - if ( res ) - return res; + __be32 val[2]; + __be32 *cellp; + void __iomem *initrd; - res = fdt_property_string(fdt, "compatible", "arm,sbsa-uart"); - if ( res ) - return res; + if ( !mod || !mod->size ) + return; - cells = ®[0]; - dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, - GUEST_ROOT_SIZE_CELLS, d->arch.vpl011.base_addr, - GUEST_PL011_SIZE); + paddr = mod->start; + len = mod->size; - res = fdt_property(fdt, "reg", reg, sizeof(reg)); - if ( res ) - return res; + printk("Loading %pd initrd from %"PRIpaddr" to 0x%"PRIpaddr"-0x%"PRIpaddr"\n", + kinfo->d, paddr, load_addr, load_addr + len); - 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 = fdt_path_offset(kinfo->fdt, "/chosen"); + if ( node < 0 ) + panic("Cannot find the /chosen node\n"); - res = fdt_property(fdt, "interrupts", intr, sizeof (intr)); + cellp = (__be32 *)val; + dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr); + res = 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"); - res = fdt_property_cell(fdt, "interrupt-parent", - kinfo->phandle_gic); + cellp = (__be32 *)val; + dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr + len); + res = 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"); - /* Use a default baud rate of 115200. */ - fdt_property_u32(fdt, "current-speed", 115200); + initrd = ioremap_wc(paddr, len); + if ( !initrd ) + panic("Unable to map the hwdom initrd\n"); - res = fdt_end_node(fdt); - if ( res ) - return res; + res = copy_to_guest_phys_flush_dcache(kinfo->d, load_addr, + initrd, len); + if ( res != 0 ) + panic("Unable to copy the initrd in the hwdom memory\n"); - return 0; + iounmap(initrd); } -#endif /* - * 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_reg, - const struct fdt_property *xen_path, - 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; - /* xen,reg specifies where to map the MMIO region */ - cell = (const __be32 *)xen_reg->data; - len = fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells) * - sizeof(uint32_t)); + res = vgic_allocate_ppi(d); + if ( res < 0 ) + panic("Unable to allocate a PPI for the event channel interrupt\n"); - for ( i = 0; i < len; i++ ) - { - device_tree_get_reg(&cell, address_cells, size_cells, - &mstart, &size); - gstart = dt_next_cell(address_cells, &cell); + d->arch.evtchn_irq = res; - if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MASK ) - { - printk(XENLOG_ERR - "DomU passthrough config has not page aligned addresses/sizes\n"); - return -EINVAL; - } + printk("Allocating PPI %u for event channel interrupt\n", + d->arch.evtchn_irq); - res = 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 = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI, + HVM_PARAM_CALLBACK_IRQ_TYPE_MASK); + /* Active-low level-sensitive */ + val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL, + HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK); + val |= d->arch.evtchn_irq; + d->arch.hvm.params[HVM_PARAM_CALLBACK_IRQ] = val; +} - res = 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 = NULL; + uint32_t len; - /* - * If xen_force, we let the user assign a MMIO region with no - * associated path. - */ - if ( xen_path == NULL ) - return xen_force ? 0 : -EINVAL; + prop = dt_get_property(np, "xen,evtchn", &len); + if ( !prop ) + { + printk(XENLOG_ERR "xen,evtchn property should not be empty.\n"); + return -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 = dt_find_node_by_path(xen_path->data); - if ( node == 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 = map_device_irqs_to_domain(kinfo->d, node, true, NULL); - if ( res < 0 ) - return res; - - res = iommu_add_dt_device(node); - if ( res < 0 ) - return res; - - /* If xen_force, we allow assignment of devices without IOMMU protection. */ - 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_cells, - bool scan_passthrough_prop) -{ - void *fdt = kinfo->fdt; - int propoff, nameoff, res; - const struct fdt_property *prop, *xen_reg = NULL, *xen_path = NULL; - const char *name; - bool found, xen_force = false; - - 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; - - found = false; - nameoff = fdt32_to_cpu(prop->nameoff); - name = fdt_string(pfdt, nameoff); - - if ( scan_passthrough_prop ) - { - if ( dt_prop_cmp("xen,reg", name) == 0 ) - { - xen_reg = prop; - found = true; - } - else if ( dt_prop_cmp("xen,path", name) == 0 ) - { - xen_path = prop; - found = true; - } - else if ( dt_prop_cmp("xen,force-assign-without-iommu", - name) == 0 ) - { - xen_force = true; - found = true; - } - } - - /* - * Copy properties other than the ones above: xen,reg, xen,path, - * and xen,force-assign-without-iommu. - */ - if ( !found ) - { - res = 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 != NULL && (xen_path != NULL || xen_force) ) - { - res = handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_force, - 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_force) ) - { - printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n", - kinfo->d); - return -EINVAL; - } - - /* FDT_ERR_NOTFOUND => There is no more properties for this node */ - return ( propoff != -FDT_ERR_NOTFOUND ) ? propoff : 0; -} - -static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pfdt, - int nodeoff, - uint32_t address_cells, uint32_t size_cells, - bool scan_passthrough_prop) -{ - int rc = 0; - void *fdt = kinfo->fdt; - int node_next; - - rc = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL)); - if ( rc ) - return rc; - - rc = handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells, - scan_passthrough_prop); - if ( rc ) - return rc; - - address_cells = device_tree_get_u32(pfdt, nodeoff, "#address-cells", - DT_ROOT_NODE_ADDR_CELLS_DEFAULT); - size_cells = device_tree_get_u32(pfdt, nodeoff, "#size-cells", - DT_ROOT_NODE_SIZE_CELLS_DEFAULT); - - node_next = fdt_first_subnode(pfdt, nodeoff); - while ( node_next > 0 ) - { - rc = scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_cells, - scan_passthrough_prop); - if ( rc ) - return rc; - - node_next = 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) != FDT_MAGIC ) - { - dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree"); - return -EINVAL; - } - - res = 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 = ioremap_cache(kinfo->dtb_bootmodule->start, - kinfo->dtb_bootmodule->size); - if ( pfdt == NULL ) - return -EFAULT; - - res = check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size); - if ( res < 0 ) - goto out; - - for ( node_next = fdt_first_subnode(pfdt, 0); - node_next > 0; - node_next = fdt_next_subnode(pfdt, node_next) ) - { - const char *name = fdt_get_name(pfdt, node_next, NULL); - - if ( name == 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") == 0 ) - { - kinfo->phandle_gic = fdt_get_phandle(pfdt, node_next); - continue; - } - - if ( dt_node_cmp(name, "aliases") == 0 ) - { - res = 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") == 0 ) - { - res = 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 including - * domU passthrough DT nodes whose size we account separately), 4KB are enough - * 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 *kinfo) -{ - int addrcells, sizecells; - int ret, fdt_size = DOMU_DTB_SIZE; - - kinfo->phandle_gic = GUEST_PHANDLE_GIC; - kinfo->gnttab_start = GUEST_GNTTAB_BASE; - kinfo->gnttab_size = GUEST_GNTTAB_SIZE; - - addrcells = GUEST_ROOT_ADDRESS_CELLS; - sizecells = GUEST_ROOT_SIZE_CELLS; - - /* Account for domU passthrough DT size */ - if ( kinfo->dtb_bootmodule ) - fdt_size += kinfo->dtb_bootmodule->size; - - /* Cap to max DT size if needed */ - fdt_size = min(fdt_size, SZ_2M); - - kinfo->fdt = xmalloc_bytes(fdt_size); - if ( kinfo->fdt == NULL ) - return -ENOMEM; - - ret = fdt_create(kinfo->fdt, fdt_size); - if ( ret < 0 ) - goto err; - - ret = fdt_finish_reservemap(kinfo->fdt); - if ( ret < 0 ) - goto err; - - ret = fdt_begin_node(kinfo->fdt, ""); - if ( ret < 0 ) - goto err; - - ret = fdt_property_cell(kinfo->fdt, "#address-cells", addrcells); - if ( ret ) - goto err; - - ret = fdt_property_cell(kinfo->fdt, "#size-cells", sizecells); - if ( ret ) - goto err; - - ret = make_chosen_node(kinfo); - if ( ret ) - goto err; - - ret = make_psci_node(kinfo->fdt); - if ( ret ) - goto err; - - ret = make_cpus_node(d, kinfo->fdt); - if ( ret ) - goto err; - - ret = make_memory_node(d, kinfo->fdt, addrcells, sizecells, &kinfo->mem); - if ( ret ) - goto err; - - ret = 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 = domain_handle_dtb_bootmodule(d, kinfo); - if ( ret ) - goto err; - } - - ret = make_gic_domU_node(kinfo); - if ( ret ) - goto err; - - ret = make_timer_node(kinfo); - if ( ret ) - goto err; - - if ( kinfo->vpl011 ) - { - ret = -EINVAL; -#ifdef CONFIG_SBSA_VUART_CONSOLE - ret = make_vpl011_uart_node(kinfo); -#endif - if ( ret ) - goto err; - } - - if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS ) - { - ret = make_hypervisor_node(d, kinfo, addrcells, sizecells); - if ( ret ) - goto err; - } - - ret = fdt_end_node(kinfo->fdt); - if ( ret < 0 ) - goto err; - - ret = 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 = p2m_mmio_direct_c; - const void *fdt; - int new_size; - int ret; - - ASSERT(dt_host && (dt_host->sibling == NULL)); - - kinfo->phandle_gic = dt_interrupt_controller->phandle; - fdt = device_tree_flattened; - - new_size = fdt_totalsize(fdt) + DOM0_FDT_EXTRA_SIZE; - kinfo->fdt = xmalloc_bytes(new_size); - if ( kinfo->fdt == NULL ) - return -ENOMEM; - - ret = fdt_create(kinfo->fdt, new_size); - if ( ret < 0 ) - goto err; - - fdt_finish_reservemap(kinfo->fdt); - - ret = handle_node(d, kinfo, dt_host, default_p2mt); - if ( ret ) - goto err; - - ret = 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 = copy_to_guest_phys_flush_dcache(kinfo->d, kinfo->dtb_paddr, - kinfo->fdt, - fdt_totalsize(kinfo->fdt)); - - if ( left != 0 ) - panic("Unable to copy the DTB to %pd memory (left = %lu bytes)\n", - kinfo->d, left); - xfree(kinfo->fdt); -} - -static void __init initrd_load(struct kernel_info *kinfo) -{ - const struct bootmodule *mod = kinfo->initrd_bootmodule; - paddr_t load_addr = 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 = mod->start; - len = mod->size; - - printk("Loading %pd initrd from %"PRIpaddr" to 0x%"PRIpaddr"-0x%"PRIpaddr"\n", - kinfo->d, paddr, load_addr, load_addr + len); - - /* Fix up linux,initrd-start and linux,initrd-end in /chosen */ - node = fdt_path_offset(kinfo->fdt, "/chosen"); - if ( node < 0 ) - panic("Cannot find the /chosen node\n"); - - cellp = (__be32 *)val; - dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr); - res = fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-start", - val, sizeof(val)); - if ( res ) - panic("Cannot fix up \"linux,initrd-start\" property\n"); - - cellp = (__be32 *)val; - dt_set_cell(&cellp, ARRAY_SIZE(val), load_addr + len); - res = fdt_setprop_inplace(kinfo->fdt, node, "linux,initrd-end", - val, sizeof(val)); - if ( res ) - panic("Cannot fix up \"linux,initrd-end\" property\n"); - - initrd = ioremap_wc(paddr, len); - if ( !initrd ) - panic("Unable to map the hwdom initrd\n"); - - res = copy_to_guest_phys_flush_dcache(kinfo->d, load_addr, - initrd, len); - if ( res != 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 = vgic_allocate_ppi(d); - if ( res < 0 ) - panic("Unable to allocate a PPI for the event channel interrupt\n"); - - d->arch.evtchn_irq = 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 = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI, - HVM_PARAM_CALLBACK_IRQ_TYPE_MASK); - /* Active-low level-sensitive */ - val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL, - HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK); - val |= d->arch.evtchn_irq; - d->arch.hvm.params[HVM_PARAM_CALLBACK_IRQ] = val; -} - -static int __init get_evtchn_dt_property(const struct dt_device_node *np, - uint32_t *port, uint32_t *phandle) -{ - const __be32 *prop = NULL; - uint32_t len; - - prop = 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; } @@ -3412,22 +2732,7 @@ static void __init find_gnttab_region(struct domain *d, kinfo->gnttab_start, kinfo->gnttab_start + kinfo->gnttab_size); } -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 map, - * plus 128 pages to cover extended regions. - */ - unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128); - - BUILD_BUG_ON(PAGE_SIZE != SZ_4K); - - return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); -} - -static int __init construct_domain(struct domain *d, struct kernel_info *kinfo) +int __init construct_domain(struct domain *d, struct kernel_info *kinfo) { unsigned int i; struct vcpu *v = d->vcpu[0]; @@ -3518,296 +2823,6 @@ static int __init construct_domain(struct domain *d, struct kernel_info *kinfo) return 0; } -static int __init alloc_xenstore_evtchn(struct domain *d) -{ - evtchn_alloc_unbound_t alloc; - int rc; - - alloc.dom = d->domain_id; - alloc.remote_dom = hardware_domain->domain_id; - rc = 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] = alloc.port; - - return 0; -} - -static int __init construct_domU(struct domain *d, - const struct dt_device_node *node) -{ - struct kernel_info kinfo = {}; - const char *dom0less_enhanced; - int rc; - u64 mem; - u32 p2m_mem_mb; - unsigned long p2m_pages; - - rc = dt_property_read_u64(node, "memory", &mem); - if ( !rc ) - { - printk("Error building DomU: cannot read \"memory\" property\n"); - return -EINVAL; - } - kinfo.unassigned_mem = (paddr_t)mem * SZ_1K; - - rc = 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 = rc ? - p2m_mem_mb << (20 - PAGE_SHIFT) : - domain_p2m_pages(mem, d->max_vcpus); - - spin_lock(&d->arch.paging.lock); - rc = p2m_set_allocation(d, p2m_pages, NULL); - spin_unlock(&d->arch.paging.lock); - if ( rc != 0 ) - return rc; - - printk("*** LOADING DOMU cpus=%u memory=%#"PRIx64"KB ***\n", - d->max_vcpus, mem); - - kinfo.vpl011 = dt_property_read_bool(node, "vpl011"); - - rc = dt_property_read_string(node, "xen,enhanced", &dom0less_enhanced); - if ( rc == -EILSEQ || - rc == -ENODATA || - (rc == 0 && !strcmp(dom0less_enhanced, "enabled")) ) - { - if ( hardware_domain ) - kinfo.dom0less_feature = DOM0LESS_ENHANCED; - else - panic("At the moment, Xenstore support requires dom0 to be present\n"); - } - else if ( rc == 0 && !strcmp(dom0less_enhanced, "no-xenstore") ) - kinfo.dom0less_feature = DOM0LESS_ENHANCED_NO_XS; - - if ( vcpu_create(d, 0) == NULL ) - return -ENOMEM; - - d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; - - kinfo.d = d; - - rc = kernel_probe(&kinfo, node); - if ( rc < 0 ) - return rc; - -#ifdef CONFIG_ARM_64 - /* type must be set before allocate memory */ - d->arch.type = 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 = 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 variables - * shall be done first. - */ - if ( kinfo.vpl011 ) - { - rc = domain_vpl011_init(d, NULL); - if ( rc < 0 ) - return rc; - } - - rc = prepare_dtb_domU(d, &kinfo); - if ( rc < 0 ) - return rc; - - rc = construct_domain(d, &kinfo); - if ( rc < 0 ) - return rc; - - if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE ) - { - ASSERT(hardware_domain); - rc = alloc_xenstore_evtchn(d); - if ( rc < 0 ) - return rc; - d->arch.hvm.params[HVM_PARAM_STORE_PFN] = ~0ULL; - } - - return rc; -} - -void __init create_domUs(void) -{ - struct dt_device_node *node; - const struct dt_device_node *cpupool_node, - *chosen = dt_find_node_by_path("/chosen"); - - BUG_ON(chosen == NULL); - dt_for_each_child_node(chosen, node) - { - struct domain *d; - struct xen_domctl_createdomain d_cfg = { - .arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE, - .flags = 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 communications. - * 1023 is also the default value used in libxl. - */ - .max_evtchn_port = 1023, - .max_grant_frames = -1, - .max_maptrack_frames = -1, - .grant_opts = XEN_DOMCTL_GRANT_version(opt_gnttab_max_version), - }; - unsigned int flags = 0U; - uint32_t val; - int rc; - - if ( !dt_device_is_compatible(node, "xen,domain") ) - continue; - - if ( (max_init_domid + 1) >= DOMID_FIRST_RESERVED ) - panic("No more domain IDs available\n"); - - if ( dt_find_property(node, "xen,static-mem", NULL) ) - flags |= CDF_staticmem; - - if ( dt_property_read_bool(node, "direct-map") ) - { - if ( !(flags & CDF_staticmem) ) - panic("direct-map is not valid for domain %s without static allocation.\n", - dt_node_name(node)); - - flags |= 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 |= XEN_DOMCTL_CDF_iommu; - - if ( !dt_property_read_u32(node, "nr_spis", &d_cfg.arch.nr_spis) ) - { - int vpl011_virq = GUEST_VPL011_SPI; - - d_cfg.arch.nr_spis = 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 = 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 = MAX(d_cfg.arch.nr_spis, - vpl011_virq - 32 + 1); - } - - /* Get the optional property domain-cpupool */ - cpupool_node = dt_parse_phandle(node, "domain-cpupool", 0); - if ( cpupool_node ) - { - int pool_id = 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 = pool_id; - } - - if ( dt_property_read_u32(node, "max_grant_version", &val) ) - d_cfg.grant_opts = 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 = 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 = val; - } - - if ( dt_get_property(node, "sve", &val) ) - { -#ifdef CONFIG_ARM64_SVE - unsigned int sve_vl_bits; - bool ret = false; - - if ( !val ) - { - /* Property found with no value, means max HW VL supported */ - ret = sve_domctl_vl_param(-1, &sve_vl_bits); - } - else - { - if ( dt_property_read_u32(node, "sve", &val) ) - ret = sve_domctl_vl_param(val, &sve_vl_bits); - else - panic("Error reading 'sve' property\n"); - } - - if ( ret ) - d_cfg.arch.sve_vl = 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 it's - * very important to use the pre-increment operator to call - * domain_create() with a domid > 0. (domid == 0 is reserved for Dom0) - */ - d = domain_create(++max_init_domid, &d_cfg, flags); - if ( IS_ERR(d) ) - panic("Error creating domain %s (rc = %ld)\n", - dt_node_name(node), PTR_ERR(d)); - - d->is_console = true; - dt_device_set_used_by(node, d->domain_id); - - rc = construct_domU(d, node); - if ( rc ) - panic("Could not set up domain %s (rc = %d)\n", - dt_node_name(node), rc); - } -} - static int __init construct_dom0(struct domain *d) { struct kernel_info kinfo = {}; diff --git a/xen/arch/arm/include/asm/dom0less-build.h b/xen/arch/arm/include/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 +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); +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 *node) +{ + ASSERT_UNREACHABLE(); +} + +static inline void assign_static_memory_11(struct domain *d, + struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + ASSERT_UNREACHABLE(); +} +#endif + #endif /* diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.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, int acpi_make_efi_nodes(void *fdt, struct membank tbl_add[]); -void create_domUs(void); void create_dom0(void); void alloc_static_evtchn(void); 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 -static bool __init is_dom0less_mode(void) -{ - struct bootmodules *mods = &bootinfo.modules; - struct bootmodule *mod; - unsigned int i; - bool dom0found = false; - bool domUfound = false; - - /* Look into the bootmodules */ - for ( i = 0 ; i < mods->nr_mods ; i++ ) - { - mod = &mods->module[i]; - /* Find if dom0 and domU kernels are present */ - if ( mod->kind == BOOTMOD_KERNEL ) - { - if ( mod->domU == false ) - { - dom0found = true; - break; - } - else - domUfound = 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; /* C entry point for boot CPU */ From patchwork Thu Nov 9 09:06:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Fancellu X-Patchwork-Id: 13450817 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8B1E4C04E83 for ; Thu, 9 Nov 2023 09:06:48 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.629510.981839 (Exim 4.92) (envelope-from ) id 1r10zj-0006ZX-0D; Thu, 09 Nov 2023 09:06:35 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 629510.981839; Thu, 09 Nov 2023 09:06:34 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zi-0006ZK-Rc; Thu, 09 Nov 2023 09:06:34 +0000 Received: by outflank-mailman (input) for mailman id 629510; Thu, 09 Nov 2023 09:06:33 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zh-0005V5-AM for xen-devel@lists.xenproject.org; Thu, 09 Nov 2023 09:06:33 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-flk1.inumbo.com (Halon) with ESMTP id 452a1a9e-7edf-11ee-9b0e-b553b5be7939; Thu, 09 Nov 2023 10:06:29 +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 1135A12FC; Thu, 9 Nov 2023 01:07:14 -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 945953F703; Thu, 9 Nov 2023 01:06:28 -0800 (PST) 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: 452a1a9e-7edf-11ee-9b0e-b553b5be7939 From: Luca Fancellu To: xen-devel@lists.xenproject.org Cc: Stefano Stabellini , Julien Grall , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk Subject: [PATCH v3 4/5] xen/arm: Move static memory build code in separate modules Date: Thu, 9 Nov 2023 09:06:14 +0000 Message-Id: <20231109090615.3878767-5-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 Move static memory and static shared memory code in separate modules so that they are included only when the corresponding feature is enabled, doing that we modularise the features and we remove some ifdefs from the code to improve readability. Move process_shm_node function from bootfdt module and make it externally visible. A static inline helper called process_shm_chosen is introduced, it will call the process_shm function for the '/chosen' node, and will be used by the function construct_dom0 instead of using directly process_shm, allowing some #ifdef to be removed. No functional changes are intended. Signed-off-by: Luca Fancellu --- Changes from v2: - no changes --- xen/arch/arm/Makefile | 2 + xen/arch/arm/bootfdt.c | 161 +----- xen/arch/arm/dom0less-build.c | 4 +- xen/arch/arm/domain_build.c | 614 +--------------------- xen/arch/arm/include/asm/dom0less-build.h | 2 - xen/arch/arm/include/asm/domain_build.h | 24 - xen/arch/arm/include/asm/static-memory.h | 50 ++ xen/arch/arm/include/asm/static-shmem.h | 72 +++ xen/arch/arm/setup.c | 25 +- xen/arch/arm/static-memory.c | 294 +++++++++++ xen/arch/arm/static-shmem.c | 515 ++++++++++++++++++ 11 files changed, 939 insertions(+), 824 deletions(-) create mode 100644 xen/arch/arm/include/asm/static-memory.h create mode 100644 xen/arch/arm/include/asm/static-shmem.h create mode 100644 xen/arch/arm/static-memory.c create mode 100644 xen/arch/arm/static-shmem.c diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 70dd7201ef30..89ef0c9075b5 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -50,6 +50,8 @@ obj-y += setup.o obj-y += shutdown.o obj-y += smp.o obj-y += smpboot.o +obj-$(CONFIG_STATIC_MEMORY) += static-memory.init.o +obj-$(CONFIG_STATIC_SHM) += static-shmem.init.o obj-y += sysctl.o obj-y += time.o obj-y += traps.o diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c index 2673ad17a1e1..fcf851b4c99b 100644 --- a/xen/arch/arm/bootfdt.c +++ b/xen/arch/arm/bootfdt.c @@ -15,6 +15,7 @@ #include #include #include +#include static bool __init device_tree_node_matches(const void *fdt, int node, const char *match) @@ -402,166 +403,6 @@ static int __init process_domain_node(const void *fdt, int node, MEMBANK_STATIC_DOMAIN); } -#ifdef CONFIG_STATIC_SHM -static int __init process_shm_node(const void *fdt, int node, - uint32_t address_cells, uint32_t size_cells) -{ - const struct fdt_property *prop, *prop_id, *prop_role; - const __be32 *cell; - paddr_t paddr, gaddr, size; - struct meminfo *mem = &bootinfo.reserved_mem; - unsigned int i; - int len; - bool owner = false; - const char *shm_id; - - if ( address_cells < 1 || size_cells < 1 ) - { - printk("fdt: invalid #address-cells or #size-cells for static shared memory node.\n"); - return -EINVAL; - } - - /* - * "xen,shm-id" property holds an arbitrary string with a strict limit - * on the number of characters, MAX_SHM_ID_LENGTH - */ - prop_id = fdt_get_property(fdt, node, "xen,shm-id", NULL); - if ( !prop_id ) - return -ENOENT; - shm_id = (const char *)prop_id->data; - if ( strnlen(shm_id, MAX_SHM_ID_LENGTH) == MAX_SHM_ID_LENGTH ) - { - printk("fdt: invalid xen,shm-id %s, it must be limited to %u characters\n", - shm_id, MAX_SHM_ID_LENGTH); - return -EINVAL; - } - - /* - * "role" property is optional and if it is defined explicitly, - * it must be either `owner` or `borrower`. - */ - prop_role = fdt_get_property(fdt, node, "role", NULL); - if ( prop_role ) - { - if ( !strcmp(prop_role->data, "owner") ) - owner = true; - else if ( strcmp(prop_role->data, "borrower") ) - { - printk("fdt: invalid `role` property for static shared memory node.\n"); - return -EINVAL; - } - } - - /* - * xen,shared-mem = ; - * Memory region starting from physical address #paddr of #size shall - * be mapped to guest physical address #gaddr as static shared memory - * region. - */ - prop = fdt_get_property(fdt, node, "xen,shared-mem", &len); - if ( !prop ) - return -ENOENT; - - if ( len != dt_cells_to_size(address_cells + size_cells + address_cells) ) - { - if ( len == dt_cells_to_size(size_cells + address_cells) ) - printk("fdt: host physical address must be chosen by users at the moment.\n"); - - printk("fdt: invalid `xen,shared-mem` property.\n"); - return -EINVAL; - } - - cell = (const __be32 *)prop->data; - device_tree_get_reg(&cell, address_cells, address_cells, &paddr, &gaddr); - size = dt_next_cell(size_cells, &cell); - - if ( !size ) - { - printk("fdt: the size for static shared memory region can not be zero\n"); - return -EINVAL; - } - - for ( i = 0; i < mem->nr_banks; i++ ) - { - /* - * Meet the following check: - * 1) The shm ID matches and the region exactly match - * 2) The shm ID doesn't match and the region doesn't overlap - * with an existing one - */ - if ( paddr == mem->bank[i].start && size == mem->bank[i].size ) - { - if ( strncmp(shm_id, mem->bank[i].shm_id, MAX_SHM_ID_LENGTH) == 0 ) - break; - else - { - printk("fdt: xen,shm-id %s does not match for all the nodes using the same region.\n", - shm_id); - return -EINVAL; - } - } - else - { - paddr_t end = paddr + size; - paddr_t bank_end = mem->bank[i].start + mem->bank[i].size; - - if ( (end <= paddr) || (bank_end <= mem->bank[i].start) ) - { - printk("fdt: static shared memory region %s overflow\n", shm_id); - return -EINVAL; - } - - if ( check_reserved_regions_overlap(paddr, size) ) - return -EINVAL; - else - { - if ( strcmp(shm_id, mem->bank[i].shm_id) != 0 ) - continue; - else - { - printk("fdt: different shared memory region could not share the same shm ID %s\n", - shm_id); - return -EINVAL; - } - } - } - } - - if ( i == mem->nr_banks ) - { - if ( i < NR_MEM_BANKS ) - { - /* Static shared memory shall be reserved from any other use. */ - safe_strcpy(mem->bank[mem->nr_banks].shm_id, shm_id); - mem->bank[mem->nr_banks].start = paddr; - mem->bank[mem->nr_banks].size = size; - mem->bank[mem->nr_banks].type = MEMBANK_STATIC_DOMAIN; - mem->nr_banks++; - } - else - { - printk("Warning: Max number of supported memory regions reached.\n"); - return -ENOSPC; - } - } - /* - * keep a count of the number of borrowers, which later may be used - * to calculate the reference count. - */ - if ( !owner ) - mem->bank[i].nr_shm_borrowers++; - - return 0; -} -#else -static int __init process_shm_node(const void *fdt, int node, - uint32_t address_cells, uint32_t size_cells) -{ - printk("CONFIG_STATIC_SHM must be enabled for parsing static shared memory nodes\n"); - return -EINVAL; -} -#endif - static int __init early_scan_node(const void *fdt, int node, const char *name, int depth, u32 address_cells, u32 size_cells, diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c index 1ca9d39043d6..d39cbd969aca 100644 --- a/xen/arch/arm/dom0less-build.c +++ b/xen/arch/arm/dom0less-build.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include bool __init is_dom0less_mode(void) { @@ -806,11 +808,9 @@ static int __init construct_domU(struct domain *d, else assign_static_memory_11(d, &kinfo, node); -#ifdef CONFIG_STATIC_SHM rc = process_shm(d, &kinfo, node); if ( rc < 0 ) return rc; -#endif /* * Base address and irq number are needed when creating vpl011 device diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index d61fda9f2081..c2c17f075450 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -476,533 +477,6 @@ bool __init allocate_bank_memory(struct domain *d, struct kernel_info *kinfo, return true; } -#ifdef CONFIG_STATIC_MEMORY -static bool __init append_static_memory_to_bank(struct domain *d, - struct membank *bank, - mfn_t smfn, - paddr_t size) -{ - int res; - unsigned int nr_pages = PFN_DOWN(size); - gfn_t sgfn; - - /* - * For direct-mapped domain, the GFN match the MFN. - * Otherwise, this is inferred on what has already been allocated - * in the bank. - */ - if ( !is_domain_direct_mapped(d) ) - sgfn = gaddr_to_gfn(bank->start + bank->size); - else - sgfn = gaddr_to_gfn(mfn_to_maddr(smfn)); - - res = guest_physmap_add_pages(d, sgfn, smfn, nr_pages); - if ( res ) - { - dprintk(XENLOG_ERR, "Failed to map pages to DOMU: %d", res); - return false; - } - - bank->size = bank->size + size; - - return true; -} - -static mfn_t __init acquire_static_memory_bank(struct domain *d, - const __be32 **cell, - u32 addr_cells, u32 size_cells, - paddr_t *pbase, paddr_t *psize) -{ - mfn_t smfn; - int res; - - device_tree_get_reg(cell, addr_cells, size_cells, pbase, psize); - ASSERT(IS_ALIGNED(*pbase, PAGE_SIZE) && IS_ALIGNED(*psize, PAGE_SIZE)); - if ( PFN_DOWN(*psize) > UINT_MAX ) - { - printk(XENLOG_ERR "%pd: static memory size too large: %#"PRIpaddr, - d, *psize); - return INVALID_MFN; - } - - smfn = maddr_to_mfn(*pbase); - res = acquire_domstatic_pages(d, smfn, PFN_DOWN(*psize), 0); - if ( res ) - { - printk(XENLOG_ERR - "%pd: failed to acquire static memory: %d.\n", d, res); - return INVALID_MFN; - } - - return smfn; -} - -static int __init parse_static_mem_prop(const struct dt_device_node *node, - u32 *addr_cells, u32 *size_cells, - int *length, const __be32 **cell) -{ - const struct dt_property *prop; - - prop = dt_find_property(node, "xen,static-mem", NULL); - - *addr_cells = dt_n_addr_cells(node); - *size_cells = dt_n_size_cells(node); - - *cell = (const __be32 *)prop->value; - *length = prop->length; - - return 0; -} - -/* Allocate memory from static memory as RAM for one specific domain d. */ -void __init allocate_static_memory(struct domain *d, struct kernel_info *kinfo, - const struct dt_device_node *node) -{ - u32 addr_cells, size_cells, reg_cells; - unsigned int nr_banks, gbank, bank = 0; - const uint64_t rambase[] = GUEST_RAM_BANK_BASES; - const uint64_t ramsize[] = GUEST_RAM_BANK_SIZES; - const __be32 *cell; - u64 tot_size = 0; - paddr_t pbase, psize, gsize; - mfn_t smfn; - int length; - - if ( parse_static_mem_prop(node, &addr_cells, &size_cells, &length, &cell) ) - goto fail; - reg_cells = addr_cells + size_cells; - - /* - * The static memory will be mapped in the guest at the usual guest memory - * addresses (GUEST_RAM0_BASE, GUEST_RAM1_BASE) defined by - * xen/include/public/arch-arm.h. - */ - gbank = 0; - gsize = ramsize[gbank]; - kinfo->mem.bank[gbank].start = rambase[gbank]; - nr_banks = length / (reg_cells * sizeof (u32)); - - for ( ; bank < nr_banks; bank++ ) - { - smfn = acquire_static_memory_bank(d, &cell, addr_cells, size_cells, - &pbase, &psize); - if ( mfn_eq(smfn, INVALID_MFN) ) - goto fail; - - printk(XENLOG_INFO "%pd: STATIC BANK[%u] %#"PRIpaddr"-%#"PRIpaddr"\n", - d, bank, pbase, pbase + psize); - - while ( 1 ) - { - /* Map as much as possible the static range to the guest bank */ - if ( !append_static_memory_to_bank(d, &kinfo->mem.bank[gbank], smfn, - min(psize, gsize)) ) - goto fail; - - /* - * The current physical bank is fully mapped. - * Handle the next physical bank. - */ - if ( gsize >= psize ) - { - gsize = gsize - psize; - break; - } - /* - * When current guest bank is not enough to map, exhaust - * the current one and seek to the next. - * Before seeking to the next, check if we still have available - * guest bank. - */ - else if ( (gbank + 1) >= GUEST_RAM_BANKS ) - { - printk(XENLOG_ERR "Exhausted all possible guest banks.\n"); - goto fail; - } - else - { - psize = psize - gsize; - smfn = mfn_add(smfn, gsize >> PAGE_SHIFT); - /* Update to the next guest bank. */ - gbank++; - gsize = ramsize[gbank]; - kinfo->mem.bank[gbank].start = rambase[gbank]; - } - } - - tot_size += psize; - } - - kinfo->mem.nr_banks = ++gbank; - - kinfo->unassigned_mem -= tot_size; - /* - * The property 'memory' should match the amount of memory given to the - * guest. - * Currently, it is only possible to either acquire static memory or let - * Xen allocate. *Mixing* is not supported. - */ - if ( kinfo->unassigned_mem ) - { - printk(XENLOG_ERR - "Size of \"memory\" property doesn't match up with the sum-up of \"xen,static-mem\". Unsupported configuration.\n"); - goto fail; - } - - return; - - fail: - panic("Failed to allocate requested static memory for domain %pd.\n", d); -} - -/* - * Allocate static memory as RAM for one specific domain d. - * The static memory will be directly mapped in the guest(Guest Physical - * Address == Physical Address). - */ -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 = 0; - const __be32 *cell; - paddr_t pbase, psize; - mfn_t smfn; - int length; - - if ( parse_static_mem_prop(node, &addr_cells, &size_cells, &length, &cell) ) - { - printk(XENLOG_ERR - "%pd: failed to parse \"xen,static-mem\" property.\n", d); - goto fail; - } - reg_cells = addr_cells + size_cells; - nr_banks = length / (reg_cells * sizeof(u32)); - - if ( nr_banks > NR_MEM_BANKS ) - { - printk(XENLOG_ERR - "%pd: exceed max number of supported guest memory banks.\n", d); - goto fail; - } - - for ( ; bank < nr_banks; bank++ ) - { - smfn = acquire_static_memory_bank(d, &cell, addr_cells, size_cells, - &pbase, &psize); - if ( mfn_eq(smfn, INVALID_MFN) ) - goto fail; - - printk(XENLOG_INFO "%pd: STATIC BANK[%u] %#"PRIpaddr"-%#"PRIpaddr"\n", - d, bank, pbase, pbase + psize); - - /* One guest memory bank is matched with one physical memory bank. */ - kinfo->mem.bank[bank].start = pbase; - if ( !append_static_memory_to_bank(d, &kinfo->mem.bank[bank], - smfn, psize) ) - goto fail; - - kinfo->unassigned_mem -= psize; - } - - kinfo->mem.nr_banks = nr_banks; - - /* - * The property 'memory' should match the amount of memory given to - * the guest. - * Currently, it is only possible to either acquire static memory or - * let Xen allocate. *Mixing* is not supported. - */ - if ( kinfo->unassigned_mem != 0 ) - { - printk(XENLOG_ERR - "Size of \"memory\" property doesn't match up with the sum-up of \"xen,static-mem\".\n"); - goto fail; - } - - return; - - fail: - panic("Failed to assign requested static memory for direct-map domain %pd.\n", - d); -} - -#ifdef CONFIG_STATIC_SHM -static int __init acquire_nr_borrower_domain(struct domain *d, - paddr_t pbase, paddr_t psize, - unsigned long *nr_borrowers) -{ - unsigned int bank; - - /* Iterate reserved memory to find requested shm bank. */ - for ( bank = 0 ; bank < bootinfo.reserved_mem.nr_banks; bank++ ) - { - paddr_t bank_start = bootinfo.reserved_mem.bank[bank].start; - paddr_t bank_size = bootinfo.reserved_mem.bank[bank].size; - - if ( (pbase == bank_start) && (psize == bank_size) ) - break; - } - - if ( bank == bootinfo.reserved_mem.nr_banks ) - return -ENOENT; - - *nr_borrowers = bootinfo.reserved_mem.bank[bank].nr_shm_borrowers; - - return 0; -} - -/* - * This function checks whether the static shared memory region is - * already allocated to dom_io. - */ -static bool __init is_shm_allocated_to_domio(paddr_t pbase) -{ - struct page_info *page; - struct domain *d; - - page = maddr_to_page(pbase); - d = page_get_owner_and_reference(page); - if ( d == NULL ) - return false; - put_page(page); - - if ( d != dom_io ) - { - printk(XENLOG_ERR - "shm memory node has already been allocated to a specific owner %pd, Please check your configuration\n", - d); - return false; - } - - return true; -} - -static mfn_t __init acquire_shared_memory_bank(struct domain *d, - paddr_t pbase, paddr_t psize) -{ - mfn_t smfn; - unsigned long nr_pfns; - int res; - - /* - * Pages of statically shared memory shall be included - * into domain_tot_pages(). - */ - nr_pfns = PFN_DOWN(psize); - if ( (UINT_MAX - d->max_pages) < nr_pfns ) - { - printk(XENLOG_ERR "%pd: Over-allocation for d->max_pages: %lu.\n", - d, nr_pfns); - return INVALID_MFN; - } - d->max_pages += nr_pfns; - - smfn = maddr_to_mfn(pbase); - res = acquire_domstatic_pages(d, smfn, nr_pfns, 0); - if ( res ) - { - printk(XENLOG_ERR - "%pd: failed to acquire static memory: %d.\n", d, res); - d->max_pages -= nr_pfns; - return INVALID_MFN; - } - - return smfn; -} - -static int __init assign_shared_memory(struct domain *d, - uint32_t addr_cells, uint32_t size_cells, - paddr_t pbase, paddr_t psize, - paddr_t gbase) -{ - mfn_t smfn; - int ret = 0; - unsigned long nr_pages, nr_borrowers, i; - struct page_info *page; - - printk("%pd: allocate static shared memory BANK %#"PRIpaddr"-%#"PRIpaddr".\n", - d, pbase, pbase + psize); - - smfn = acquire_shared_memory_bank(d, pbase, psize); - if ( mfn_eq(smfn, INVALID_MFN) ) - return -EINVAL; - - /* - * DOMID_IO is not auto-translated (i.e. it sees RAM 1:1). So we do not need - * to create mapping in the P2M. - */ - nr_pages = PFN_DOWN(psize); - if ( d != dom_io ) - { - ret = guest_physmap_add_pages(d, gaddr_to_gfn(gbase), smfn, - PFN_DOWN(psize)); - if ( ret ) - { - printk(XENLOG_ERR "Failed to map shared memory to %pd.\n", d); - return ret; - } - } - - /* - * Get the right amount of references per page, which is the number of - * borrower domains. - */ - ret = acquire_nr_borrower_domain(d, pbase, psize, &nr_borrowers); - if ( ret ) - return ret; - - /* - * Instead of letting borrower domain get a page ref, we add as many - * additional reference as the number of borrowers when the owner - * is allocated, since there is a chance that owner is created - * after borrower. - * So if the borrower is created first, it will cause adding pages - * in the P2M without reference. - */ - page = mfn_to_page(smfn); - for ( i = 0; i < nr_pages; i++ ) - { - if ( !get_page_nr(page + i, d, nr_borrowers) ) - { - printk(XENLOG_ERR - "Failed to add %lu references to page %"PRI_mfn".\n", - nr_borrowers, mfn_x(smfn) + i); - goto fail; - } - } - - return 0; - - fail: - while ( --i >= 0 ) - put_page_nr(page + i, nr_borrowers); - return ret; -} - -static int __init append_shm_bank_to_domain(struct kernel_info *kinfo, - paddr_t start, paddr_t size, - const char *shm_id) -{ - if ( kinfo->shm_mem.nr_banks >= NR_MEM_BANKS ) - return -ENOMEM; - - kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].start = start; - kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].size = size; - safe_strcpy(kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].shm_id, shm_id); - kinfo->shm_mem.nr_banks++; - - return 0; -} - -int __init process_shm(struct domain *d, struct kernel_info *kinfo, - const struct dt_device_node *node) -{ - struct dt_device_node *shm_node; - - dt_for_each_child_node(node, shm_node) - { - const struct dt_property *prop; - const __be32 *cells; - uint32_t addr_cells, size_cells; - paddr_t gbase, pbase, psize; - int ret = 0; - unsigned int i; - const char *role_str; - const char *shm_id; - bool owner_dom_io = true; - - if ( !dt_device_is_compatible(shm_node, "xen,domain-shared-memory-v1") ) - continue; - - /* - * xen,shared-mem = ; - * TODO: pbase is optional. - */ - addr_cells = dt_n_addr_cells(shm_node); - size_cells = dt_n_size_cells(shm_node); - prop = dt_find_property(shm_node, "xen,shared-mem", NULL); - BUG_ON(!prop); - cells = (const __be32 *)prop->value; - device_tree_get_reg(&cells, addr_cells, addr_cells, &pbase, &gbase); - psize = dt_read_paddr(cells, size_cells); - if ( !IS_ALIGNED(pbase, PAGE_SIZE) || !IS_ALIGNED(gbase, PAGE_SIZE) ) - { - printk("%pd: physical address 0x%"PRIpaddr", or guest address 0x%"PRIpaddr" is not suitably aligned.\n", - d, pbase, gbase); - return -EINVAL; - } - if ( !IS_ALIGNED(psize, PAGE_SIZE) ) - { - printk("%pd: size 0x%"PRIpaddr" is not suitably aligned\n", - d, psize); - return -EINVAL; - } - - for ( i = 0; i < PFN_DOWN(psize); i++ ) - if ( !mfn_valid(mfn_add(maddr_to_mfn(pbase), i)) ) - { - printk("%pd: invalid physical address 0x%"PRI_mfn"\n", - d, mfn_x(mfn_add(maddr_to_mfn(pbase), i))); - return -EINVAL; - } - - /* - * "role" property is optional and if it is defined explicitly, - * then the owner domain is not the default "dom_io" domain. - */ - if ( dt_property_read_string(shm_node, "role", &role_str) == 0 ) - owner_dom_io = false; - - if ( dt_property_read_string(shm_node, "xen,shm-id", &shm_id) ) - { - printk("%pd: invalid \"xen,shm-id\" property", d); - return -EINVAL; - } - BUG_ON((strlen(shm_id) <= 0) || (strlen(shm_id) >= MAX_SHM_ID_LENGTH)); - - /* - * DOMID_IO is a fake domain and is not described in the Device-Tree. - * Therefore when the owner of the shared region is DOMID_IO, we will - * only find the borrowers. - */ - if ( (owner_dom_io && !is_shm_allocated_to_domio(pbase)) || - (!owner_dom_io && strcmp(role_str, "owner") == 0) ) - { - /* - * We found the first borrower of the region, the owner was not - * specified, so they should be assigned to dom_io. - */ - ret = assign_shared_memory(owner_dom_io ? dom_io : d, - addr_cells, size_cells, - pbase, psize, gbase); - if ( ret ) - return ret; - } - - if ( owner_dom_io || (strcmp(role_str, "borrower") == 0) ) - { - /* Set up P2M foreign mapping for borrower domain. */ - ret = map_regions_p2mt(d, _gfn(PFN_UP(gbase)), PFN_DOWN(psize), - _mfn(PFN_UP(pbase)), p2m_map_foreign_rw); - if ( ret ) - return ret; - } - - /* - * Record static shared memory region info for later setting - * up shm-node in guest device tree. - */ - ret = append_shm_bank_to_domain(kinfo, gbase, psize, shm_id); - if ( ret ) - return ret; - } - - return 0; -} -#endif /* CONFIG_STATIC_SHM */ -#endif - /* * When PCI passthrough is available we want to keep the * "linux,pci-domain" in sync for every host bridge. @@ -1336,85 +810,6 @@ int __init make_memory_node(const struct domain *d, return res; } -#ifdef CONFIG_STATIC_SHM -static int __init make_shm_memory_node(const struct domain *d, - void *fdt, - int addrcells, int sizecells, - const struct meminfo *mem) -{ - unsigned int i = 0; - int res = 0; - - if ( mem->nr_banks == 0 ) - return -ENOENT; - - /* - * For each shared memory region, a range is exposed under - * the /reserved-memory node as a child node. Each range sub-node is - * named xen-shmem@
. - */ - dt_dprintk("Create xen-shmem node\n"); - - for ( ; i < mem->nr_banks; i++ ) - { - uint64_t start = mem->bank[i].start; - uint64_t size = mem->bank[i].size; - const char compat[] = "xen,shared-memory-v1"; - /* Worst case addrcells + sizecells */ - __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS]; - __be32 *cells; - unsigned int len = (addrcells + sizecells) * sizeof(__be32); - - res = domain_fdt_begin_node(fdt, "xen-shmem", mem->bank[i].start); - if ( res ) - return res; - - res = fdt_property(fdt, "compatible", compat, sizeof(compat)); - if ( res ) - return res; - - cells = reg; - dt_child_set_range(&cells, addrcells, sizecells, start, size); - - res = fdt_property(fdt, "reg", reg, len); - if ( res ) - return res; - - dt_dprintk("Shared memory bank %u: %#"PRIx64"->%#"PRIx64"\n", - i, start, start + size); - - res = fdt_property_string(fdt, "xen,id", mem->bank[i].shm_id); - if ( res ) - return res; - - /* - * TODO: - * - xen,offset: (borrower VMs only) - * 64 bit integer offset within the owner virtual machine's shared - * memory region used for the mapping in the borrower VM - */ - res = fdt_property_u64(fdt, "xen,offset", 0); - if ( res ) - return res; - - res = fdt_end_node(fdt); - if ( res ) - return res; - } - - return res; -} -#else -static int __init make_shm_memory_node(const struct domain *d, - void *fdt, - int addrcells, int sizecells, - const struct meminfo *mem) -{ - ASSERT_UNREACHABLE(); - return -EOPNOTSUPP; -} -#endif - int __init make_resv_memory_node(const struct domain *d, void *fdt, int addrcells, int sizecells, @@ -2827,9 +2222,6 @@ static int __init construct_dom0(struct domain *d) { struct kernel_info kinfo = {}; int rc; -#ifdef CONFIG_STATIC_SHM - const struct dt_device_node *chosen = dt_find_node_by_path("/chosen"); -#endif /* Sanity! */ BUG_ON(d->domain_id != 0); @@ -2864,11 +2256,9 @@ static int __init construct_dom0(struct domain *d) allocate_memory_11(d, &kinfo); find_gnttab_region(d, &kinfo); -#ifdef CONFIG_STATIC_SHM - rc = process_shm(d, &kinfo, chosen); + rc = process_shm_chosen(d, &kinfo); if ( rc < 0 ) return rc; -#endif /* Map extra GIC MMIO, irqs and other hw stuffs to dom0. */ rc = gic_map_hwdom_extra_mappings(d); diff --git a/xen/arch/arm/include/asm/dom0less-build.h b/xen/arch/arm/include/asm/dom0less-build.h index 0d28fa1bee46..d95cb6234b62 100644 --- a/xen/arch/arm/include/asm/dom0less-build.h +++ b/xen/arch/arm/include/asm/dom0less-build.h @@ -8,8 +8,6 @@ #ifndef __ARM_DOM0LESS_BUILD_H_ #define __ARM_DOM0LESS_BUILD_H_ -#include - void create_domUs(void); bool is_dom0less_mode(void); diff --git a/xen/arch/arm/include/asm/domain_build.h b/xen/arch/arm/include/asm/domain_build.h index b9ae0d170c5c..13118b5ff956 100644 --- a/xen/arch/arm/include/asm/domain_build.h +++ b/xen/arch/arm/include/asm/domain_build.h @@ -42,9 +42,6 @@ int handle_device_interrupts(struct domain *d, struct dt_device_node *dev, 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) { @@ -56,27 +53,6 @@ static inline int prepare_acpi(struct domain *d, struct kernel_info *kinfo) 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 *node) -{ - ASSERT_UNREACHABLE(); -} - -static inline void assign_static_memory_11(struct domain *d, - struct kernel_info *kinfo, - const struct dt_device_node *node) -{ - ASSERT_UNREACHABLE(); -} -#endif - #endif /* diff --git a/xen/arch/arm/include/asm/static-memory.h b/xen/arch/arm/include/asm/static-memory.h new file mode 100644 index 000000000000..0fe193b36861 --- /dev/null +++ b/xen/arch/arm/include/asm/static-memory.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/include/asm/static-memory.h + * + * Copyright (C) 2023 Arm Ltd. + */ + +#ifndef __ARM_STATIC_MEMORY_H_ +#define __ARM_STATIC_MEMORY_H_ + +#include + +#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); +void init_staticmem_pages(void); + +#else /* !CONFIG_STATIC_MEMORY */ + +static inline void allocate_static_memory(struct domain *d, + struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + ASSERT_UNREACHABLE(); +} + +static inline void assign_static_memory_11(struct domain *d, + struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + ASSERT_UNREACHABLE(); +} + +static inline void init_staticmem_pages(void) {}; + +#endif /* CONFIG_STATIC_MEMORY */ + +#endif /* __ARM_STATIC_MEMORY_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/static-shmem.h b/xen/arch/arm/include/asm/static-shmem.h new file mode 100644 index 000000000000..5a8075fbee55 --- /dev/null +++ b/xen/arch/arm/include/asm/static-shmem.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/include/asm/static-shmem.h + * + * Copyright (C) 2023 Arm Ltd. + */ + +#ifndef __ARM_STATIC_SHMEM_H_ +#define __ARM_STATIC_SHMEM_H_ + +#include + +#ifdef CONFIG_STATIC_SHM + +int make_shm_memory_node(const struct domain *d, void *fdt, int addrcells, + int sizecells, const struct meminfo *mem); + +int process_shm(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node); + +static inline int process_shm_chosen(struct domain *d, + struct kernel_info *kinfo) +{ + const struct dt_device_node *node = dt_find_node_by_path("/chosen"); + + return process_shm(d, kinfo, node); +} + +int process_shm_node(const void *fdt, int node, uint32_t address_cells, + uint32_t size_cells); + +#else /* !CONFIG_STATIC_SHM */ + +static inline int make_shm_memory_node(const struct domain *d, void *fdt, + int addrcells, int sizecells, + const struct meminfo *mem) +{ + ASSERT_UNREACHABLE(); + return -EOPNOTSUPP; +} + +static inline int process_shm(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + return 0; +} + +static inline int process_shm_chosen(struct domain *d, + struct kernel_info *kinfo) +{ + return 0; +} + +static inline int process_shm_node(const void *fdt, int node, + uint32_t address_cells, uint32_t size_cells) +{ + printk("CONFIG_STATIC_SHM must be enabled for parsing static shared memory nodes\n"); + return -EINVAL; +} + +#endif /* CONFIG_STATIC_SHM */ + +#endif /* __ARM_STATIC_SHMEM_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/setup.c b/xen/arch/arm/setup.c index 60b544b67b4e..f6019c0c42cd 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -758,30 +759,6 @@ static void __init init_pdx(void) } } -/* Static memory initialization */ -static void __init init_staticmem_pages(void) -{ -#ifdef CONFIG_STATIC_MEMORY - unsigned int bank; - - for ( bank = 0 ; bank < bootinfo.reserved_mem.nr_banks; bank++ ) - { - if ( bootinfo.reserved_mem.bank[bank].type == MEMBANK_STATIC_DOMAIN ) - { - mfn_t bank_start = _mfn(PFN_UP(bootinfo.reserved_mem.bank[bank].start)); - unsigned long bank_pages = PFN_DOWN(bootinfo.reserved_mem.bank[bank].size); - mfn_t bank_end = mfn_add(bank_start, bank_pages); - - if ( mfn_x(bank_end) <= mfn_x(bank_start) ) - return; - - unprepare_staticmem_pages(mfn_to_page(bank_start), - bank_pages, false); - } - } -#endif -} - /* * Populate the boot allocator. * If a static heap was not provided by the admin, all the RAM but the diff --git a/xen/arch/arm/static-memory.c b/xen/arch/arm/static-memory.c new file mode 100644 index 000000000000..9aecdae28846 --- /dev/null +++ b/xen/arch/arm/static-memory.c @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/static-memory.c + * + * Code related to the static memory functionality + * + * Copyright (C) 2023 Arm Ltd. + */ + +#include + +#include + +static bool __init append_static_memory_to_bank(struct domain *d, + struct membank *bank, + mfn_t smfn, + paddr_t size) +{ + int res; + unsigned int nr_pages = PFN_DOWN(size); + gfn_t sgfn; + + /* + * For direct-mapped domain, the GFN match the MFN. + * Otherwise, this is inferred on what has already been allocated + * in the bank. + */ + if ( !is_domain_direct_mapped(d) ) + sgfn = gaddr_to_gfn(bank->start + bank->size); + else + sgfn = gaddr_to_gfn(mfn_to_maddr(smfn)); + + res = guest_physmap_add_pages(d, sgfn, smfn, nr_pages); + if ( res ) + { + dprintk(XENLOG_ERR, "Failed to map pages to DOMU: %d", res); + return false; + } + + bank->size = bank->size + size; + + return true; +} + +static mfn_t __init acquire_static_memory_bank(struct domain *d, + const __be32 **cell, + u32 addr_cells, u32 size_cells, + paddr_t *pbase, paddr_t *psize) +{ + mfn_t smfn; + int res; + + device_tree_get_reg(cell, addr_cells, size_cells, pbase, psize); + ASSERT(IS_ALIGNED(*pbase, PAGE_SIZE) && IS_ALIGNED(*psize, PAGE_SIZE)); + if ( PFN_DOWN(*psize) > UINT_MAX ) + { + printk(XENLOG_ERR "%pd: static memory size too large: %#"PRIpaddr, + d, *psize); + return INVALID_MFN; + } + + smfn = maddr_to_mfn(*pbase); + res = acquire_domstatic_pages(d, smfn, PFN_DOWN(*psize), 0); + if ( res ) + { + printk(XENLOG_ERR + "%pd: failed to acquire static memory: %d.\n", d, res); + return INVALID_MFN; + } + + return smfn; +} + +static int __init parse_static_mem_prop(const struct dt_device_node *node, + u32 *addr_cells, u32 *size_cells, + int *length, const __be32 **cell) +{ + const struct dt_property *prop; + + prop = dt_find_property(node, "xen,static-mem", NULL); + + *addr_cells = dt_n_addr_cells(node); + *size_cells = dt_n_size_cells(node); + + *cell = (const __be32 *)prop->value; + *length = prop->length; + + return 0; +} + +/* Allocate memory from static memory as RAM for one specific domain d. */ +void __init allocate_static_memory(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + u32 addr_cells, size_cells, reg_cells; + unsigned int nr_banks, gbank, bank = 0; + const uint64_t rambase[] = GUEST_RAM_BANK_BASES; + const uint64_t ramsize[] = GUEST_RAM_BANK_SIZES; + const __be32 *cell; + u64 tot_size = 0; + paddr_t pbase, psize, gsize; + mfn_t smfn; + int length; + + if ( parse_static_mem_prop(node, &addr_cells, &size_cells, &length, &cell) ) + goto fail; + reg_cells = addr_cells + size_cells; + + /* + * The static memory will be mapped in the guest at the usual guest memory + * addresses (GUEST_RAM0_BASE, GUEST_RAM1_BASE) defined by + * xen/include/public/arch-arm.h. + */ + gbank = 0; + gsize = ramsize[gbank]; + kinfo->mem.bank[gbank].start = rambase[gbank]; + nr_banks = length / (reg_cells * sizeof (u32)); + + for ( ; bank < nr_banks; bank++ ) + { + smfn = acquire_static_memory_bank(d, &cell, addr_cells, size_cells, + &pbase, &psize); + if ( mfn_eq(smfn, INVALID_MFN) ) + goto fail; + + printk(XENLOG_INFO "%pd: STATIC BANK[%u] %#"PRIpaddr"-%#"PRIpaddr"\n", + d, bank, pbase, pbase + psize); + + while ( 1 ) + { + /* Map as much as possible the static range to the guest bank */ + if ( !append_static_memory_to_bank(d, &kinfo->mem.bank[gbank], smfn, + min(psize, gsize)) ) + goto fail; + + /* + * The current physical bank is fully mapped. + * Handle the next physical bank. + */ + if ( gsize >= psize ) + { + gsize = gsize - psize; + break; + } + /* + * When current guest bank is not enough to map, exhaust + * the current one and seek to the next. + * Before seeking to the next, check if we still have available + * guest bank. + */ + else if ( (gbank + 1) >= GUEST_RAM_BANKS ) + { + printk(XENLOG_ERR "Exhausted all possible guest banks.\n"); + goto fail; + } + else + { + psize = psize - gsize; + smfn = mfn_add(smfn, gsize >> PAGE_SHIFT); + /* Update to the next guest bank. */ + gbank++; + gsize = ramsize[gbank]; + kinfo->mem.bank[gbank].start = rambase[gbank]; + } + } + + tot_size += psize; + } + + kinfo->mem.nr_banks = ++gbank; + + kinfo->unassigned_mem -= tot_size; + /* + * The property 'memory' should match the amount of memory given to the + * guest. + * Currently, it is only possible to either acquire static memory or let + * Xen allocate. *Mixing* is not supported. + */ + if ( kinfo->unassigned_mem ) + { + printk(XENLOG_ERR + "Size of \"memory\" property doesn't match up with the sum-up of \"xen,static-mem\". Unsupported configuration.\n"); + goto fail; + } + + return; + + fail: + panic("Failed to allocate requested static memory for domain %pd.\n", d); +} + +/* + * Allocate static memory as RAM for one specific domain d. + * The static memory will be directly mapped in the guest(Guest Physical + * Address == Physical Address). + */ +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 = 0; + const __be32 *cell; + paddr_t pbase, psize; + mfn_t smfn; + int length; + + if ( parse_static_mem_prop(node, &addr_cells, &size_cells, &length, &cell) ) + { + printk(XENLOG_ERR + "%pd: failed to parse \"xen,static-mem\" property.\n", d); + goto fail; + } + reg_cells = addr_cells + size_cells; + nr_banks = length / (reg_cells * sizeof(u32)); + + if ( nr_banks > NR_MEM_BANKS ) + { + printk(XENLOG_ERR + "%pd: exceed max number of supported guest memory banks.\n", d); + goto fail; + } + + for ( ; bank < nr_banks; bank++ ) + { + smfn = acquire_static_memory_bank(d, &cell, addr_cells, size_cells, + &pbase, &psize); + if ( mfn_eq(smfn, INVALID_MFN) ) + goto fail; + + printk(XENLOG_INFO "%pd: STATIC BANK[%u] %#"PRIpaddr"-%#"PRIpaddr"\n", + d, bank, pbase, pbase + psize); + + /* One guest memory bank is matched with one physical memory bank. */ + kinfo->mem.bank[bank].start = pbase; + if ( !append_static_memory_to_bank(d, &kinfo->mem.bank[bank], + smfn, psize) ) + goto fail; + + kinfo->unassigned_mem -= psize; + } + + kinfo->mem.nr_banks = nr_banks; + + /* + * The property 'memory' should match the amount of memory given to + * the guest. + * Currently, it is only possible to either acquire static memory or + * let Xen allocate. *Mixing* is not supported. + */ + if ( kinfo->unassigned_mem != 0 ) + { + printk(XENLOG_ERR + "Size of \"memory\" property doesn't match up with the sum-up of \"xen,static-mem\".\n"); + goto fail; + } + + return; + + fail: + panic("Failed to assign requested static memory for direct-map domain %pd.\n", + d); +} + +/* Static memory initialization */ +void __init init_staticmem_pages(void) +{ + unsigned int bank; + + for ( bank = 0 ; bank < bootinfo.reserved_mem.nr_banks; bank++ ) + { + if ( bootinfo.reserved_mem.bank[bank].type == MEMBANK_STATIC_DOMAIN ) + { + mfn_t bank_start = _mfn(PFN_UP(bootinfo.reserved_mem.bank[bank].start)); + unsigned long bank_pages = PFN_DOWN(bootinfo.reserved_mem.bank[bank].size); + mfn_t bank_end = mfn_add(bank_start, bank_pages); + + if ( mfn_x(bank_end) <= mfn_x(bank_start) ) + return; + + unprepare_staticmem_pages(mfn_to_page(bank_start), + bank_pages, false); + } + } +} + +/* + * 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/static-shmem.c b/xen/arch/arm/static-shmem.c new file mode 100644 index 000000000000..fd826f486e8a --- /dev/null +++ b/xen/arch/arm/static-shmem.c @@ -0,0 +1,515 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/static-shmem.c + * + * Code related to the static shared memory functionality + * + * Copyright (C) 2023 Arm Ltd. + */ + +#include +#include + +#include +#include + +static int __init acquire_nr_borrower_domain(struct domain *d, + paddr_t pbase, paddr_t psize, + unsigned long *nr_borrowers) +{ + unsigned int bank; + + /* Iterate reserved memory to find requested shm bank. */ + for ( bank = 0 ; bank < bootinfo.reserved_mem.nr_banks; bank++ ) + { + paddr_t bank_start = bootinfo.reserved_mem.bank[bank].start; + paddr_t bank_size = bootinfo.reserved_mem.bank[bank].size; + + if ( (pbase == bank_start) && (psize == bank_size) ) + break; + } + + if ( bank == bootinfo.reserved_mem.nr_banks ) + return -ENOENT; + + *nr_borrowers = bootinfo.reserved_mem.bank[bank].nr_shm_borrowers; + + return 0; +} + +/* + * This function checks whether the static shared memory region is + * already allocated to dom_io. + */ +static bool __init is_shm_allocated_to_domio(paddr_t pbase) +{ + struct page_info *page; + struct domain *d; + + page = maddr_to_page(pbase); + d = page_get_owner_and_reference(page); + if ( d == NULL ) + return false; + put_page(page); + + if ( d != dom_io ) + { + printk(XENLOG_ERR + "shm memory node has already been allocated to a specific owner %pd, Please check your configuration\n", + d); + return false; + } + + return true; +} + +static mfn_t __init acquire_shared_memory_bank(struct domain *d, + paddr_t pbase, paddr_t psize) +{ + mfn_t smfn; + unsigned long nr_pfns; + int res; + + /* + * Pages of statically shared memory shall be included + * into domain_tot_pages(). + */ + nr_pfns = PFN_DOWN(psize); + if ( (UINT_MAX - d->max_pages) < nr_pfns ) + { + printk(XENLOG_ERR "%pd: Over-allocation for d->max_pages: %lu.\n", + d, nr_pfns); + return INVALID_MFN; + } + d->max_pages += nr_pfns; + + smfn = maddr_to_mfn(pbase); + res = acquire_domstatic_pages(d, smfn, nr_pfns, 0); + if ( res ) + { + printk(XENLOG_ERR + "%pd: failed to acquire static memory: %d.\n", d, res); + d->max_pages -= nr_pfns; + return INVALID_MFN; + } + + return smfn; +} + +static int __init assign_shared_memory(struct domain *d, + uint32_t addr_cells, uint32_t size_cells, + paddr_t pbase, paddr_t psize, + paddr_t gbase) +{ + mfn_t smfn; + int ret = 0; + unsigned long nr_pages, nr_borrowers, i; + struct page_info *page; + + printk("%pd: allocate static shared memory BANK %#"PRIpaddr"-%#"PRIpaddr".\n", + d, pbase, pbase + psize); + + smfn = acquire_shared_memory_bank(d, pbase, psize); + if ( mfn_eq(smfn, INVALID_MFN) ) + return -EINVAL; + + /* + * DOMID_IO is not auto-translated (i.e. it sees RAM 1:1). So we do not need + * to create mapping in the P2M. + */ + nr_pages = PFN_DOWN(psize); + if ( d != dom_io ) + { + ret = guest_physmap_add_pages(d, gaddr_to_gfn(gbase), smfn, + PFN_DOWN(psize)); + if ( ret ) + { + printk(XENLOG_ERR "Failed to map shared memory to %pd.\n", d); + return ret; + } + } + + /* + * Get the right amount of references per page, which is the number of + * borrower domains. + */ + ret = acquire_nr_borrower_domain(d, pbase, psize, &nr_borrowers); + if ( ret ) + return ret; + + /* + * Instead of letting borrower domain get a page ref, we add as many + * additional reference as the number of borrowers when the owner + * is allocated, since there is a chance that owner is created + * after borrower. + * So if the borrower is created first, it will cause adding pages + * in the P2M without reference. + */ + page = mfn_to_page(smfn); + for ( i = 0; i < nr_pages; i++ ) + { + if ( !get_page_nr(page + i, d, nr_borrowers) ) + { + printk(XENLOG_ERR + "Failed to add %lu references to page %"PRI_mfn".\n", + nr_borrowers, mfn_x(smfn) + i); + goto fail; + } + } + + return 0; + + fail: + while ( --i >= 0 ) + put_page_nr(page + i, nr_borrowers); + return ret; +} + +static int __init append_shm_bank_to_domain(struct kernel_info *kinfo, + paddr_t start, paddr_t size, + const char *shm_id) +{ + if ( kinfo->shm_mem.nr_banks >= NR_MEM_BANKS ) + return -ENOMEM; + + kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].start = start; + kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].size = size; + safe_strcpy(kinfo->shm_mem.bank[kinfo->shm_mem.nr_banks].shm_id, shm_id); + kinfo->shm_mem.nr_banks++; + + return 0; +} + +int __init process_shm(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *node) +{ + struct dt_device_node *shm_node; + + dt_for_each_child_node(node, shm_node) + { + const struct dt_property *prop; + const __be32 *cells; + uint32_t addr_cells, size_cells; + paddr_t gbase, pbase, psize; + int ret = 0; + unsigned int i; + const char *role_str; + const char *shm_id; + bool owner_dom_io = true; + + if ( !dt_device_is_compatible(shm_node, "xen,domain-shared-memory-v1") ) + continue; + + /* + * xen,shared-mem = ; + * TODO: pbase is optional. + */ + addr_cells = dt_n_addr_cells(shm_node); + size_cells = dt_n_size_cells(shm_node); + prop = dt_find_property(shm_node, "xen,shared-mem", NULL); + BUG_ON(!prop); + cells = (const __be32 *)prop->value; + device_tree_get_reg(&cells, addr_cells, addr_cells, &pbase, &gbase); + psize = dt_read_paddr(cells, size_cells); + if ( !IS_ALIGNED(pbase, PAGE_SIZE) || !IS_ALIGNED(gbase, PAGE_SIZE) ) + { + printk("%pd: physical address 0x%"PRIpaddr", or guest address 0x%"PRIpaddr" is not suitably aligned.\n", + d, pbase, gbase); + return -EINVAL; + } + if ( !IS_ALIGNED(psize, PAGE_SIZE) ) + { + printk("%pd: size 0x%"PRIpaddr" is not suitably aligned\n", + d, psize); + return -EINVAL; + } + + for ( i = 0; i < PFN_DOWN(psize); i++ ) + if ( !mfn_valid(mfn_add(maddr_to_mfn(pbase), i)) ) + { + printk("%pd: invalid physical address 0x%"PRI_mfn"\n", + d, mfn_x(mfn_add(maddr_to_mfn(pbase), i))); + return -EINVAL; + } + + /* + * "role" property is optional and if it is defined explicitly, + * then the owner domain is not the default "dom_io" domain. + */ + if ( dt_property_read_string(shm_node, "role", &role_str) == 0 ) + owner_dom_io = false; + + if ( dt_property_read_string(shm_node, "xen,shm-id", &shm_id) ) + { + printk("%pd: invalid \"xen,shm-id\" property", d); + return -EINVAL; + } + BUG_ON((strlen(shm_id) <= 0) || (strlen(shm_id) >= MAX_SHM_ID_LENGTH)); + + /* + * DOMID_IO is a fake domain and is not described in the Device-Tree. + * Therefore when the owner of the shared region is DOMID_IO, we will + * only find the borrowers. + */ + if ( (owner_dom_io && !is_shm_allocated_to_domio(pbase)) || + (!owner_dom_io && strcmp(role_str, "owner") == 0) ) + { + /* + * We found the first borrower of the region, the owner was not + * specified, so they should be assigned to dom_io. + */ + ret = assign_shared_memory(owner_dom_io ? dom_io : d, + addr_cells, size_cells, + pbase, psize, gbase); + if ( ret ) + return ret; + } + + if ( owner_dom_io || (strcmp(role_str, "borrower") == 0) ) + { + /* Set up P2M foreign mapping for borrower domain. */ + ret = map_regions_p2mt(d, _gfn(PFN_UP(gbase)), PFN_DOWN(psize), + _mfn(PFN_UP(pbase)), p2m_map_foreign_rw); + if ( ret ) + return ret; + } + + /* + * Record static shared memory region info for later setting + * up shm-node in guest device tree. + */ + ret = append_shm_bank_to_domain(kinfo, gbase, psize, shm_id); + if ( ret ) + return ret; + } + + return 0; +} + +int __init make_shm_memory_node(const struct domain *d, void *fdt, + int addrcells, int sizecells, + const struct meminfo *mem) +{ + unsigned int i = 0; + int res = 0; + + if ( mem->nr_banks == 0 ) + return -ENOENT; + + /* + * For each shared memory region, a range is exposed under + * the /reserved-memory node as a child node. Each range sub-node is + * named xen-shmem@
. + */ + dt_dprintk("Create xen-shmem node\n"); + + for ( ; i < mem->nr_banks; i++ ) + { + uint64_t start = mem->bank[i].start; + uint64_t size = mem->bank[i].size; + const char compat[] = "xen,shared-memory-v1"; + /* Worst case addrcells + sizecells */ + __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS]; + __be32 *cells; + unsigned int len = (addrcells + sizecells) * sizeof(__be32); + + res = domain_fdt_begin_node(fdt, "xen-shmem", mem->bank[i].start); + if ( res ) + return res; + + res = fdt_property(fdt, "compatible", compat, sizeof(compat)); + if ( res ) + return res; + + cells = reg; + dt_child_set_range(&cells, addrcells, sizecells, start, size); + + res = fdt_property(fdt, "reg", reg, len); + if ( res ) + return res; + + dt_dprintk("Shared memory bank %u: %#"PRIx64"->%#"PRIx64"\n", + i, start, start + size); + + res = fdt_property_string(fdt, "xen,id", mem->bank[i].shm_id); + if ( res ) + return res; + + /* + * TODO: + * - xen,offset: (borrower VMs only) + * 64 bit integer offset within the owner virtual machine's shared + * memory region used for the mapping in the borrower VM + */ + res = fdt_property_u64(fdt, "xen,offset", 0); + if ( res ) + return res; + + res = fdt_end_node(fdt); + if ( res ) + return res; + } + + return res; +} + +int __init process_shm_node(const void *fdt, int node, uint32_t address_cells, + uint32_t size_cells) +{ + const struct fdt_property *prop, *prop_id, *prop_role; + const __be32 *cell; + paddr_t paddr, gaddr, size; + struct meminfo *mem = &bootinfo.reserved_mem; + unsigned int i; + int len; + bool owner = false; + const char *shm_id; + + if ( address_cells < 1 || size_cells < 1 ) + { + printk("fdt: invalid #address-cells or #size-cells for static shared memory node.\n"); + return -EINVAL; + } + + /* + * "xen,shm-id" property holds an arbitrary string with a strict limit + * on the number of characters, MAX_SHM_ID_LENGTH + */ + prop_id = fdt_get_property(fdt, node, "xen,shm-id", NULL); + if ( !prop_id ) + return -ENOENT; + shm_id = (const char *)prop_id->data; + if ( strnlen(shm_id, MAX_SHM_ID_LENGTH) == MAX_SHM_ID_LENGTH ) + { + printk("fdt: invalid xen,shm-id %s, it must be limited to %u characters\n", + shm_id, MAX_SHM_ID_LENGTH); + return -EINVAL; + } + + /* + * "role" property is optional and if it is defined explicitly, + * it must be either `owner` or `borrower`. + */ + prop_role = fdt_get_property(fdt, node, "role", NULL); + if ( prop_role ) + { + if ( !strcmp(prop_role->data, "owner") ) + owner = true; + else if ( strcmp(prop_role->data, "borrower") ) + { + printk("fdt: invalid `role` property for static shared memory node.\n"); + return -EINVAL; + } + } + + /* + * xen,shared-mem = ; + * Memory region starting from physical address #paddr of #size shall + * be mapped to guest physical address #gaddr as static shared memory + * region. + */ + prop = fdt_get_property(fdt, node, "xen,shared-mem", &len); + if ( !prop ) + return -ENOENT; + + if ( len != dt_cells_to_size(address_cells + size_cells + address_cells) ) + { + if ( len == dt_cells_to_size(size_cells + address_cells) ) + printk("fdt: host physical address must be chosen by users at the moment.\n"); + + printk("fdt: invalid `xen,shared-mem` property.\n"); + return -EINVAL; + } + + cell = (const __be32 *)prop->data; + device_tree_get_reg(&cell, address_cells, address_cells, &paddr, &gaddr); + size = dt_next_cell(size_cells, &cell); + + if ( !size ) + { + printk("fdt: the size for static shared memory region can not be zero\n"); + return -EINVAL; + } + + for ( i = 0; i < mem->nr_banks; i++ ) + { + /* + * Meet the following check: + * 1) The shm ID matches and the region exactly match + * 2) The shm ID doesn't match and the region doesn't overlap + * with an existing one + */ + if ( paddr == mem->bank[i].start && size == mem->bank[i].size ) + { + if ( strncmp(shm_id, mem->bank[i].shm_id, MAX_SHM_ID_LENGTH) == 0 ) + break; + else + { + printk("fdt: xen,shm-id %s does not match for all the nodes using the same region.\n", + shm_id); + return -EINVAL; + } + } + else + { + paddr_t end = paddr + size; + paddr_t bank_end = mem->bank[i].start + mem->bank[i].size; + + if ( (end <= paddr) || (bank_end <= mem->bank[i].start) ) + { + printk("fdt: static shared memory region %s overflow\n", shm_id); + return -EINVAL; + } + + if ( check_reserved_regions_overlap(paddr, size) ) + return -EINVAL; + else + { + if ( strcmp(shm_id, mem->bank[i].shm_id) != 0 ) + continue; + else + { + printk("fdt: different shared memory region could not share the same shm ID %s\n", + shm_id); + return -EINVAL; + } + } + } + } + + if ( i == mem->nr_banks ) + { + if ( i < NR_MEM_BANKS ) + { + /* Static shared memory shall be reserved from any other use. */ + safe_strcpy(mem->bank[mem->nr_banks].shm_id, shm_id); + mem->bank[mem->nr_banks].start = paddr; + mem->bank[mem->nr_banks].size = size; + mem->bank[mem->nr_banks].type = MEMBANK_STATIC_DOMAIN; + mem->nr_banks++; + } + else + { + printk("Warning: Max number of supported memory regions reached.\n"); + return -ENOSPC; + } + } + /* + * keep a count of the number of borrowers, which later may be used + * to calculate the reference count. + */ + if ( !owner ) + mem->bank[i].nr_shm_borrowers++; + + return 0; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ From patchwork Thu Nov 9 09:06:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Fancellu X-Patchwork-Id: 13450815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 46C01C4167D for ; Thu, 9 Nov 2023 09:06:43 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.629509.981824 (Exim 4.92) (envelope-from ) id 1r10zh-00066N-El; Thu, 09 Nov 2023 09:06:33 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 629509.981824; Thu, 09 Nov 2023 09:06:33 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r10zh-000665-8I; Thu, 09 Nov 2023 09:06:33 +0000 Received: by outflank-mailman (input) for mailman id 629509; Thu, 09 Nov 2023 09:06:32 +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 1r10zg-0005ED-07 for xen-devel@lists.xenproject.org; Thu, 09 Nov 2023 09:06:32 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-sth1.inumbo.com (Halon) with ESMTP id 45d56ead-7edf-11ee-98da-6d05b1d4d9a1; Thu, 09 Nov 2023 10:06:31 +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 25E951477; Thu, 9 Nov 2023 01:07:15 -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 C23FC3F703; Thu, 9 Nov 2023 01:06:29 -0800 (PST) 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: 45d56ead-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 5/5] arm/dom0less: introduce Kconfig for dom0less feature Date: Thu, 9 Nov 2023 09:06:15 +0000 Message-Id: <20231109090615.3878767-6-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 Introduce a Kconfig for the dom0less feature, enabled by default, to be able to choose if the feature should be compiled or not. Provide static inline stubs when the option is disabled for the functions externally visible. Use the new Kconfig to remove dom0less DT binding from the efi-boot.h code when the Kconfig is not enabled, do the same for allocate_bank_memory inside domain_build.c that currently is used only by dom0less-build.c module, but it's kept there provisioning its usage by dom0 code. Signed-off-by: Luca Fancellu --- Changes from v2: - protect allocate_bank_memory with the new Kconfig --- xen/arch/arm/Kconfig | 9 +++++++++ xen/arch/arm/Makefile | 2 +- xen/arch/arm/domain_build.c | 2 ++ xen/arch/arm/efi/efi-boot.h | 4 ++++ xen/arch/arm/include/asm/dom0less-build.h | 12 ++++++++++++ xen/arch/arm/include/asm/domain_build.h | 2 ++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 0045a3762d86..0399f2faf734 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -88,6 +88,15 @@ config GICV2 Driver for the ARM Generic Interrupt Controller v2. If unsure, say Y +config DOM0LESS_BOOT + bool "Dom0less boot support" if EXPERT + depends on ARM + default y + help + Dom0less boot support enables Xen to create and start domU guests during + Xen boot without the need of a control domain (Dom0), which could be + present anyway. + config GICV3 bool "GICv3 driver" depends on !NEW_VGIC diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 89ef0c9075b5..5daf8f10114d 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -15,7 +15,7 @@ obj-y += cpufeature.o obj-y += decode.o obj-y += device.o obj-$(CONFIG_IOREQ_SERVER) += dm.o -obj-y += dom0less-build.init.o +obj-$(CONFIG_DOM0LESS_BOOT) += dom0less-build.init.o obj-y += domain.o obj-y += domain_build.init.o obj-$(CONFIG_ARCH_MAP_DOMAIN_PAGE) += domain_page.o diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index c2c17f075450..87eade449006 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -415,6 +415,7 @@ static void __init allocate_memory_11(struct domain *d, } } +#ifdef CONFIG_DOM0LESS_BOOT bool __init allocate_bank_memory(struct domain *d, struct kernel_info *kinfo, gfn_t sgfn, paddr_t tot_size) { @@ -476,6 +477,7 @@ bool __init allocate_bank_memory(struct domain *d, struct kernel_info *kinfo, return true; } +#endif /* * When PCI passthrough is available we want to keep the diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h index 1c3640bb65fd..689dc016d081 100644 --- a/xen/arch/arm/efi/efi-boot.h +++ b/xen/arch/arm/efi/efi-boot.h @@ -802,6 +802,7 @@ static int __init handle_module_node(const EFI_LOADED_IMAGE *loaded_image, return 1; } +#ifdef CONFIG_DOM0LESS_BOOT /* * This function checks for boot modules under the domU guest domain node * in the DT. @@ -849,6 +850,7 @@ static int __init handle_dom0less_domain_node(const EFI_LOADED_IMAGE *loaded_ima return mb_modules_found; } +#endif /* * This function checks for xen domain nodes under the /chosen node for possible @@ -876,6 +878,7 @@ static int __init efi_check_dt_boot(const EFI_LOADED_IMAGE *loaded_image) { int ret; +#ifdef CONFIG_DOM0LESS_BOOT if ( !fdt_node_check_compatible(fdt_efi, node, "xen,domain") ) { /* Found a node with compatible xen,domain; handle this node. */ @@ -884,6 +887,7 @@ static int __init efi_check_dt_boot(const EFI_LOADED_IMAGE *loaded_image) return ERROR_DT_MODULE_DOMU; } else +#endif { ret = handle_module_node(loaded_image, &dir_handle, node, addr_len, size_len, false); diff --git a/xen/arch/arm/include/asm/dom0less-build.h b/xen/arch/arm/include/asm/dom0less-build.h index d95cb6234b62..859944eece16 100644 --- a/xen/arch/arm/include/asm/dom0less-build.h +++ b/xen/arch/arm/include/asm/dom0less-build.h @@ -8,9 +8,21 @@ #ifndef __ARM_DOM0LESS_BUILD_H_ #define __ARM_DOM0LESS_BUILD_H_ +#ifdef CONFIG_DOM0LESS_BOOT + void create_domUs(void); bool is_dom0less_mode(void); +#else /* !CONFIG_DOM0LESS_BOOT */ + +static inline void create_domUs(void) {} +static inline bool is_dom0less_mode(void) +{ + return false; +} + +#endif /* CONFIG_DOM0LESS_BOOT */ + #endif /* __ARM_DOM0LESS_BUILD_H_ */ /* diff --git a/xen/arch/arm/include/asm/domain_build.h b/xen/arch/arm/include/asm/domain_build.h index 13118b5ff956..3950f0276e55 100644 --- a/xen/arch/arm/include/asm/domain_build.h +++ b/xen/arch/arm/include/asm/domain_build.h @@ -6,8 +6,10 @@ typedef __be32 gic_interrupt_t[3]; +#ifdef CONFIG_DOM0LESS_BOOT bool allocate_bank_memory(struct domain *d, struct kernel_info *kinfo, gfn_t sgfn, paddr_t tot_size); +#endif 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,