public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH v1] mach-snapdragon: Add KVM hypervisor support
@ 2026-04-19 17:38 Aswin Murugan
  2026-04-20  4:32 ` Simon Glass
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Aswin Murugan @ 2026-04-19 17:38 UTC (permalink / raw)
  To: trini, casey.connolly, neil.armstrong, sumit.garg, aswin.murugan,
	sughosh.ganu, gchan9527, u-boot-qcom, u-boot

Enable Linux KVM virtualization on Snapdragon SoCs.

Introduce CONFIG_QCOM_KVM_SUPPORT to select KVM or Gunyah
hypervisor modes at build time.

qcom-priv.h:
 - Add TrustZone SMC interface definitions and parameter IDs
 - Define hypervisor boot types (GUNYAH=0, KVM=1)
 - Add TCR_EL2 bit field definitions for memory config

board.c:
 - Add qcom_configure_kvm_hypervisor() with EL-aware logic
 - EL2: Perform direct SMC call for hypervisor setup
 - EL1: Save context, disable caches, run SMC, restore state,
   reconfigure TCR_EL2, re-enable caches
 - Add qcom_configure_gunyah_hypervisor() for standard flow
 - Add SCM service availability checks

Default mode remains Gunyah. Enable CONFIG_QCOM_KVM_SUPPORT to
select KVM for Linux.

Signed-off-by: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
---
 arch/arm/mach-snapdragon/Kconfig     |   7 ++
 arch/arm/mach-snapdragon/board.c     | 154 +++++++++++++++++++++++++++
 arch/arm/mach-snapdragon/qcom-priv.h |  31 ++++++
 3 files changed, 192 insertions(+)

diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig
index d3de8693b5a..2e5676945ca 100644
--- a/arch/arm/mach-snapdragon/Kconfig
+++ b/arch/arm/mach-snapdragon/Kconfig
@@ -42,4 +42,11 @@ config SYS_CONFIG_NAME
 	  Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
 	  will be used for board configuration.
 
+config QCOM_KVM_SUPPORT
+	bool "Enable KVM support for Qualcomm platforms"
+	depends on ARM64
+	help
+	  This configures the hypervisor interface during boot to support
+	  KVM virtualization instead of the default Gunyah hypervisor.
+
 endif
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index 5fb3240acc5..2926dd8ccc0 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
 {
 }
 
+/**
+ * qcom_configure_kvm_hypervisor() - Configure hypervisor for KVM guest mode
+ *
+ * Configures the hypervisor for KVM operation:
+ * - EL2 path: Direct SMC call
+ * - EL1 path: Save context, disable caches, SMC call, restore context
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int qcom_configure_kvm_hypervisor(void)
+{
+	struct arm_smccc_res res;
+	u64 current_el;
+
+	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
+	current_el = (current_el >> 2) & 0x3;
+
+	log_info("Configuring hypervisor for KVM (EL%llu)\n", current_el);
+
+	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
+		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
+		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
+		      0, 0, 0, 0, 0, &res);
+
+	if (res.a0 != 0)
+		log_debug("KVM milestone service not available (0x%lx)\n", res.a0);
+
+	if (current_el == 2) {
+		log_debug("At EL2\n");
+
+		arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
+			      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
+			      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
+			      0, 0, 0, &res);
+
+		if (res.a0 != 0) {
+			log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
+			return -EIO;
+		}
+
+		log_info("KVM hypervisor configured\n");
+		return 0;
+	}
+
+	log_debug("At EL1, saving register context\n");
+
+	u64 ttbr0_el1, tcr_el1, tcr_el2, mair_el1;
+	u64 t0sz, phys_addr_size;
+
+	/* Save EL1 system register context */
+	asm volatile("mrs %0, ttbr0_el1" : "=r" (ttbr0_el1));
+	asm volatile("mrs %0, tcr_el1" : "=r" (tcr_el1));
+	asm volatile("mrs %0, mair_el1" : "=r" (mair_el1));
+
+	t0sz = tcr_el1 & TCR_T0SZ_MASK;
+	phys_addr_size = tcr_el1 & TCR_PS_MASK;
+
+	log_debug("Saved context: TTBR0=0x%llx TCR=0x%llx MAIR=0x%llx\n",
+		  ttbr0_el1, tcr_el1, mair_el1);
+
+	icache_disable();
+	dcache_disable();
+
+	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
+		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
+		      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
+		      0, 0, 0, &res);
+
+	if (res.a0 != 0) {
+		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
+		icache_enable();
+		dcache_enable();
+		return -EIO;
+	}
+
+	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
+	current_el = (current_el >> 2) & 0x3;
+
+	asm volatile("msr ttbr0_el1, %0" : : "r" (ttbr0_el1));
+	asm volatile("isb");
+
+	if (current_el != 2) {
+		log_debug("No EL2 transition, skipping TCR_EL2 config\n");
+		icache_enable();
+		dcache_enable();
+		log_warning("KVM hypervisor configuration failed\n");
+		return 0;
+	}
+
+	/* Read current TCR_EL2 and reconfigure it */
+	asm volatile("mrs %0, tcr_el2" : "=r" (tcr_el2));
+
+	tcr_el2 &= ~(TCR_T0SZ_MASK | (0x7UL << 16));
+	tcr_el2 |= t0sz | (phys_addr_size >> TCR_PS_SHIFT);
+
+	tcr_el2 &= ~TCR_SH_ORGN_IRGN_MASK;
+	tcr_el2 |= TCR_SH_INNER_SHAREABLE |
+		   TCR_ORGN_WRITE_BACK_ALLOC |
+		   TCR_IRGN_WRITE_BACK_ALLOC;
+
+	asm volatile("msr tcr_el2, %0" : : "r" (tcr_el2));
+	asm volatile("msr mair_el1, %0" : : "r" (mair_el1));
+	asm volatile("isb");
+
+	icache_enable();
+	dcache_enable();
+
+	log_info("KVM hypervisor configured\n");
+	return 0;
+}
+
+/**
+ * qcom_configure_gunyah_hypervisor() - Configure hypervisor for Gunyah mode
+ *
+ * Configures the hypervisor for standard Gunyah operation.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int qcom_configure_gunyah_hypervisor(void)
+{
+	struct arm_smccc_res res;
+
+	log_info("Configuring hypervisor for Gunyah mode\n");
+
+	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
+		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
+		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
+		      0, 0, 0, 0, 0, &res);
+
+	if (res.a0 != 0) {
+		log_debug("Hypervisor milestone service not available (0x%lx)\n", res.a0);
+		return 0;
+	}
+
+	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
+		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
+		      0, 0, QCOM_HYP_BOOT_TYPE_GUNYAH,
+		      0, 0, 0, &res);
+
+	if (res.a0 != 0) {
+		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
+		return -EIO;
+	}
+
+	log_info("Gunyah hypervisor configured\n");
+	return 0;
+}
+
 #define KERNEL_COMP_SIZE	SZ_64M
arch/arm/mach-snapdragon/Kconfig #ifdef CONFIG_FASTBOOT_BUF_SIZE
 #define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
@@ -570,6 +718,12 @@ int board_late_init(void)
 	qcom_late_init();
 
 	qcom_show_boot_source();
+
+	if (IS_ENABLED(CONFIG_QCOM_KVM_SUPPORT))
+		qcom_configure_kvm_hypervisor();
+	else
+		qcom_configure_gunyah_hypervisor();
+
 	/* Configure the dfu_string for capsule updates */
 	qcom_configure_capsule_updates();
 
diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
index b8bf574e8bb..a5d9dec6aa7 100644
--- a/arch/arm/mach-snapdragon/qcom-priv.h
+++ b/arch/arm/mach-snapdragon/qcom-priv.h
@@ -17,6 +17,37 @@ enum qcom_boot_source {
 
 extern enum qcom_boot_source qcom_boot_source;
 
+/* TrustZone SMC definitions */
+#define TZ_SYSCALL_CREATE_SMC_ID(o, s, f) \
+	((u32)((((o) & 0x3f) << 24) | (((s) & 0xff) << 8) | ((f) & 0xff)))
+
+#define TZ_OWNER_SIP				2
+#define TZ_SVC_BOOT				1
+#define TZ_SVC_INFO				6
+#define TZ_BOOT_CMD_KVM_MILESTONE		0x21
+#define TZ_INFO_IS_SVC_AVAILABLE_CMD		0x01
+
+#define TZ_CONFIGURE_MILESTONE_SERVICE_ID \
+	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_BOOT, TZ_BOOT_CMD_KVM_MILESTONE)
+#define TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID		0x23
+
+#define TZ_INFO_IS_SVC_AVAILABLE_ID \
+	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_INFO, TZ_INFO_IS_SVC_AVAILABLE_CMD)
+#define TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID		0x1
+
+/* Hypervisor boot types */
+#define QCOM_HYP_BOOT_TYPE_GUNYAH		0
+#define QCOM_HYP_BOOT_TYPE_KVM			1
+
+/* TCR_EL2 bit field definitions */
+#define TCR_T0SZ_MASK				0x1FUL
+#define TCR_PS_MASK				(0x7UL << 32)
+#define TCR_PS_SHIFT				16
+#define TCR_SH_ORGN_IRGN_MASK			0x3F00UL
+#define TCR_SH_INNER_SHAREABLE			(3UL << 12)
+#define TCR_ORGN_WRITE_BACK_ALLOC		BIT(10)
+#define TCR_IRGN_WRITE_BACK_ALLOC		BIT(8)
+
 #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
 void qcom_configure_capsule_updates(void);
 #else
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] mach-snapdragon: Add KVM hypervisor support
  2026-04-19 17:38 [PATCH v1] mach-snapdragon: Add KVM hypervisor support Aswin Murugan
@ 2026-04-20  4:32 ` Simon Glass
  2026-04-20  7:50 ` neil.armstrong
  2026-04-20 13:45 ` Sumit Garg
  2 siblings, 0 replies; 4+ messages in thread
From: Simon Glass @ 2026-04-20  4:32 UTC (permalink / raw)
  To: aswin.murugan
  Cc: trini, casey.connolly, neil.armstrong, sumit.garg, sughosh.ganu,
	gchan9527, u-boot-qcom, u-boot

Hi Aswin,

On 2026-04-19T17:38:29, Aswin Murugan <aswin.murugan@oss.qualcomm.com> wrote:
> mach-snapdragon: Add KVM hypervisor support
>
> Enable Linux KVM virtualization on Snapdragon SoCs.
>
> Introduce CONFIG_QCOM_KVM_SUPPORT to select KVM or Gunyah
> hypervisor modes at build time.
>
> qcom-priv.h:
>  - Add TrustZone SMC interface definitions and parameter IDs
>  - Define hypervisor boot types (GUNYAH=0, KVM=1)
>  - Add TCR_EL2 bit field definitions for memory config
>
> board.c:
>  - Add qcom_configure_kvm_hypervisor() with EL-aware logic
>  - EL2: Perform direct SMC call for hypervisor setup
>  - EL1: Save context, disable caches, run SMC, restore state,
>    reconfigure TCR_EL2, re-enable caches
>  - Add qcom_configure_gunyah_hypervisor() for standard flow
>  - Add SCM service availability checks
>
> [...]
>
> arch/arm/mach-snapdragon/Kconfig     |   7 ++
>  arch/arm/mach-snapdragon/board.c     | 154 +++++++++++++++++++++++++++++++++++
>  arch/arm/mach-snapdragon/qcom-priv.h |  31 +++++++
>  3 files changed, 192 insertions(+)

> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> @@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
> +     log_debug("At EL1, saving register context\n");
> +
> +     u64 ttbr0_el1, tcr_el1, tcr_el2, mair_el1;
> +     u64 t0sz, phys_addr_size;

Please can you move these declarations to the top of the function
alongside res and current_el.

> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> @@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
> +     if (current_el != 2) {
> +             log_debug("No EL2 transition, skipping TCR_EL2 config\n");
> +             icache_enable();
> +             dcache_enable();
> +             log_warning("KVM hypervisor configuration failed\n");
> +             return 0;
> +     }

This logs a warning saying configuration failed but returns 0
(success). If the EL2 transition truly failed, shouldn't it an error?
If it is intentionally a soft failure, the log message should clarify
why.

> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> @@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
> +     arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
> +                   TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
> +                   TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +                   0, 0, 0, 0, 0, &res);
> +
> +     if (res.a0 != 0)
> +             log_debug("KVM milestone service not available (0x%lx)\n", res.a0);

Just to check: if the service is not available, should the function
continue anyway? In qcom_configure_gunyah_hypervisor() you return
early when the service check fails, but here you continue regardless.
A brief comment explaining why would help.

> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -17,6 +17,37 @@ extern enum qcom_boot_source qcom_boot_source;
> +/* TCR_EL2 bit field definitions */
> +#define TCR_T0SZ_MASK                                0x1FUL
> +#define TCR_PS_MASK                          (0x7UL << 32)
> +#define TCR_PS_SHIFT                         16
> +#define TCR_SH_ORGN_IRGN_MASK                        0x3F00UL
> +#define TCR_SH_INNER_SHAREABLE                       (3UL << 12)
> +#define TCR_ORGN_WRITE_BACK_ALLOC            BIT(10)
> +#define TCR_IRGN_WRITE_BACK_ALLOC            BIT(8)

arch/arm/include/asm/armv8/mmu.h already defines TCR_IRGN_WBWA,
TCR_ORGN_WBWA, and TCR_SHARED_INNER with the same values. Please can
you reuse those existing definitions rather than duplicating them
here.

Regards,
Simon

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] mach-snapdragon: Add KVM hypervisor support
  2026-04-19 17:38 [PATCH v1] mach-snapdragon: Add KVM hypervisor support Aswin Murugan
  2026-04-20  4:32 ` Simon Glass
@ 2026-04-20  7:50 ` neil.armstrong
  2026-04-20 13:45 ` Sumit Garg
  2 siblings, 0 replies; 4+ messages in thread
From: neil.armstrong @ 2026-04-20  7:50 UTC (permalink / raw)
  To: Aswin Murugan, trini, casey.connolly, sumit.garg, sughosh.ganu,
	gchan9527, u-boot-qcom, u-boot

Hi,

On 4/19/26 19:38, Aswin Murugan wrote:
> Enable Linux KVM virtualization on Snapdragon SoCs.
> 
> Introduce CONFIG_QCOM_KVM_SUPPORT to select KVM or Gunyah
> hypervisor modes at build time.

Please stop using KVW when disabling Gunyah, KVM is a possible hypervisor when running EL2,
but is completely optional. I know internally you refer to KVM when not using Gunyah,
but this make 0 sense at all to user this term when writing patches on the mailing-list.

Just rewrite like this: add support for disabling Gunyah or add support for booting into EL2

> 
> qcom-priv.h:
>   - Add TrustZone SMC interface definitions and parameter IDs
>   - Define hypervisor boot types (GUNYAH=0, KVM=1)
>   - Add TCR_EL2 bit field definitions for memory config
> 
> board.c:
>   - Add qcom_configure_kvm_hypervisor() with EL-aware logic
>   - EL2: Perform direct SMC call for hypervisor setup
>   - EL1: Save context, disable caches, run SMC, restore state,
>     reconfigure TCR_EL2, re-enable caches
>   - Add qcom_configure_gunyah_hypervisor() for standard flow
>   - Add SCM service availability checks

No need to to a summary of the patch, if it changes to much stuff (which seems to be the case here)
just split it into multiple small changes, and explain WHY instead of WHAT in the commit message.

> 
> Default mode remains Gunyah. Enable CONFIG_QCOM_KVM_SUPPORT to
> select KVM for Linux.

Neil

> 
> Signed-off-by: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
> ---
>   arch/arm/mach-snapdragon/Kconfig     |   7 ++
>   arch/arm/mach-snapdragon/board.c     | 154 +++++++++++++++++++++++++++
>   arch/arm/mach-snapdragon/qcom-priv.h |  31 ++++++
>   3 files changed, 192 insertions(+)
> 
> diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig
> index d3de8693b5a..2e5676945ca 100644
> --- a/arch/arm/mach-snapdragon/Kconfig
> +++ b/arch/arm/mach-snapdragon/Kconfig
> @@ -42,4 +42,11 @@ config SYS_CONFIG_NAME
>   	  Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
>   	  will be used for board configuration.
>   
> +config QCOM_KVM_SUPPORT
> +	bool "Enable KVM support for Qualcomm platforms"
> +	depends on ARM64
> +	help
> +	  This configures the hypervisor interface during boot to support
> +	  KVM virtualization instead of the default Gunyah hypervisor.
> +
>   endif
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index 5fb3240acc5..2926dd8ccc0 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
>   {
>   }
>   
> +/**
> + * qcom_configure_kvm_hypervisor() - Configure hypervisor for KVM guest mode
> + *
> + * Configures the hypervisor for KVM operation:
> + * - EL2 path: Direct SMC call
> + * - EL1 path: Save context, disable caches, SMC call, restore context
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int qcom_configure_kvm_hypervisor(void)
> +{
> +	struct arm_smccc_res res;
> +	u64 current_el;
> +
> +	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
> +	current_el = (current_el >> 2) & 0x3;
> +
> +	log_info("Configuring hypervisor for KVM (EL%llu)\n", current_el);
> +
> +	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
> +		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	if (res.a0 != 0)
> +		log_debug("KVM milestone service not available (0x%lx)\n", res.a0);
> +
> +	if (current_el == 2) {
> +		log_debug("At EL2\n");
> +
> +		arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +			      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +			      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
> +			      0, 0, 0, &res);
> +
> +		if (res.a0 != 0) {
> +			log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +			return -EIO;
> +		}
> +
> +		log_info("KVM hypervisor configured\n");
> +		return 0;
> +	}
> +
> +	log_debug("At EL1, saving register context\n");
> +
> +	u64 ttbr0_el1, tcr_el1, tcr_el2, mair_el1;
> +	u64 t0sz, phys_addr_size;
> +
> +	/* Save EL1 system register context */
> +	asm volatile("mrs %0, ttbr0_el1" : "=r" (ttbr0_el1));
> +	asm volatile("mrs %0, tcr_el1" : "=r" (tcr_el1));
> +	asm volatile("mrs %0, mair_el1" : "=r" (mair_el1));
> +
> +	t0sz = tcr_el1 & TCR_T0SZ_MASK;
> +	phys_addr_size = tcr_el1 & TCR_PS_MASK;
> +
> +	log_debug("Saved context: TTBR0=0x%llx TCR=0x%llx MAIR=0x%llx\n",
> +		  ttbr0_el1, tcr_el1, mair_el1);
> +
> +	icache_disable();
> +	dcache_disable();
> +
> +	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +		      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
> +		      0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +		icache_enable();
> +		dcache_enable();
> +		return -EIO;
> +	}
> +
> +	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
> +	current_el = (current_el >> 2) & 0x3;
> +
> +	asm volatile("msr ttbr0_el1, %0" : : "r" (ttbr0_el1));
> +	asm volatile("isb");
> +
> +	if (current_el != 2) {
> +		log_debug("No EL2 transition, skipping TCR_EL2 config\n");
> +		icache_enable();
> +		dcache_enable();
> +		log_warning("KVM hypervisor configuration failed\n");
> +		return 0;
> +	}
> +
> +	/* Read current TCR_EL2 and reconfigure it */
> +	asm volatile("mrs %0, tcr_el2" : "=r" (tcr_el2));
> +
> +	tcr_el2 &= ~(TCR_T0SZ_MASK | (0x7UL << 16));
> +	tcr_el2 |= t0sz | (phys_addr_size >> TCR_PS_SHIFT);
> +
> +	tcr_el2 &= ~TCR_SH_ORGN_IRGN_MASK;
> +	tcr_el2 |= TCR_SH_INNER_SHAREABLE |
> +		   TCR_ORGN_WRITE_BACK_ALLOC |
> +		   TCR_IRGN_WRITE_BACK_ALLOC;
> +
> +	asm volatile("msr tcr_el2, %0" : : "r" (tcr_el2));
> +	asm volatile("msr mair_el1, %0" : : "r" (mair_el1));
> +	asm volatile("isb");
> +
> +	icache_enable();
> +	dcache_enable();
> +
> +	log_info("KVM hypervisor configured\n");
> +	return 0;
> +}
> +
> +/**
> + * qcom_configure_gunyah_hypervisor() - Configure hypervisor for Gunyah mode
> + *
> + * Configures the hypervisor for standard Gunyah operation.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int qcom_configure_gunyah_hypervisor(void)
> +{
> +	struct arm_smccc_res res;
> +
> +	log_info("Configuring hypervisor for Gunyah mode\n");
> +
> +	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
> +		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_debug("Hypervisor milestone service not available (0x%lx)\n", res.a0);
> +		return 0;
> +	}
> +
> +	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +		      0, 0, QCOM_HYP_BOOT_TYPE_GUNYAH,
> +		      0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +		return -EIO;
> +	}
> +
> +	log_info("Gunyah hypervisor configured\n");
> +	return 0;
> +}
> +
>   #define KERNEL_COMP_SIZE	SZ_64M
> arch/arm/mach-snapdragon/Kconfig #ifdef CONFIG_FASTBOOT_BUF_SIZE
>   #define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
> @@ -570,6 +718,12 @@ int board_late_init(void)
>   	qcom_late_init();
>   
>   	qcom_show_boot_source();
> +
> +	if (IS_ENABLED(CONFIG_QCOM_KVM_SUPPORT))
> +		qcom_configure_kvm_hypervisor();
> +	else
> +		qcom_configure_gunyah_hypervisor();
> +
>   	/* Configure the dfu_string for capsule updates */
>   	qcom_configure_capsule_updates();
>   
> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> index b8bf574e8bb..a5d9dec6aa7 100644
> --- a/arch/arm/mach-snapdragon/qcom-priv.h
> +++ b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -17,6 +17,37 @@ enum qcom_boot_source {
>   
>   extern enum qcom_boot_source qcom_boot_source;
>   
> +/* TrustZone SMC definitions */
> +#define TZ_SYSCALL_CREATE_SMC_ID(o, s, f) \
> +	((u32)((((o) & 0x3f) << 24) | (((s) & 0xff) << 8) | ((f) & 0xff)))
> +
> +#define TZ_OWNER_SIP				2
> +#define TZ_SVC_BOOT				1
> +#define TZ_SVC_INFO				6
> +#define TZ_BOOT_CMD_KVM_MILESTONE		0x21
> +#define TZ_INFO_IS_SVC_AVAILABLE_CMD		0x01
> +
> +#define TZ_CONFIGURE_MILESTONE_SERVICE_ID \
> +	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_BOOT, TZ_BOOT_CMD_KVM_MILESTONE)
> +#define TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID		0x23
> +
> +#define TZ_INFO_IS_SVC_AVAILABLE_ID \
> +	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_INFO, TZ_INFO_IS_SVC_AVAILABLE_CMD)
> +#define TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID		0x1
> +
> +/* Hypervisor boot types */
> +#define QCOM_HYP_BOOT_TYPE_GUNYAH		0
> +#define QCOM_HYP_BOOT_TYPE_KVM			1
> +
> +/* TCR_EL2 bit field definitions */
> +#define TCR_T0SZ_MASK				0x1FUL
> +#define TCR_PS_MASK				(0x7UL << 32)
> +#define TCR_PS_SHIFT				16
> +#define TCR_SH_ORGN_IRGN_MASK			0x3F00UL
> +#define TCR_SH_INNER_SHAREABLE			(3UL << 12)
> +#define TCR_ORGN_WRITE_BACK_ALLOC		BIT(10)
> +#define TCR_IRGN_WRITE_BACK_ALLOC		BIT(8)
> +
>   #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
>   void qcom_configure_capsule_updates(void);
>   #else


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] mach-snapdragon: Add KVM hypervisor support
  2026-04-19 17:38 [PATCH v1] mach-snapdragon: Add KVM hypervisor support Aswin Murugan
  2026-04-20  4:32 ` Simon Glass
  2026-04-20  7:50 ` neil.armstrong
@ 2026-04-20 13:45 ` Sumit Garg
  2 siblings, 0 replies; 4+ messages in thread
From: Sumit Garg @ 2026-04-20 13:45 UTC (permalink / raw)
  To: Aswin Murugan
  Cc: trini, casey.connolly, neil.armstrong, sughosh.ganu, gchan9527,
	u-boot-qcom, u-boot

On Sun, Apr 19, 2026 at 11:08:29PM +0530, Aswin Murugan wrote:
> Enable Linux KVM virtualization on Snapdragon SoCs.
> 
> Introduce CONFIG_QCOM_KVM_SUPPORT to select KVM or Gunyah
> hypervisor modes at build time.
> 
> qcom-priv.h:
>  - Add TrustZone SMC interface definitions and parameter IDs
>  - Define hypervisor boot types (GUNYAH=0, KVM=1)
>  - Add TCR_EL2 bit field definitions for memory config
> 
> board.c:
>  - Add qcom_configure_kvm_hypervisor() with EL-aware logic
>  - EL2: Perform direct SMC call for hypervisor setup
>  - EL1: Save context, disable caches, run SMC, restore state,
>    reconfigure TCR_EL2, re-enable caches

Please don't switch EL1 -> EL2 in the middle of U-Boot execution which
just adds these expensive MMU operations during boot.

Rather than that just follow the upstream edk2 example to switch from
EL1 -> EL2 on the first platform specific init code code sequence. Have
a look at this reference [1]. Surely you can keep the switch under a
config option. This will make U-Boot to properly execute either in EL1
or EL2.

[1] https://github.com/tianocore/edk2-platforms/blob/master/Silicon/Qualcomm/KodiakPkg/Library/KodiakLib/KodiakHelper.S

-Sumit

>  - Add qcom_configure_gunyah_hypervisor() for standard flow
>  - Add SCM service availability checks
> 
> Default mode remains Gunyah. Enable CONFIG_QCOM_KVM_SUPPORT to
> select KVM for Linux.
> 
> Signed-off-by: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
> ---
>  arch/arm/mach-snapdragon/Kconfig     |   7 ++
>  arch/arm/mach-snapdragon/board.c     | 154 +++++++++++++++++++++++++++
>  arch/arm/mach-snapdragon/qcom-priv.h |  31 ++++++
>  3 files changed, 192 insertions(+)
> 
> diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig
> index d3de8693b5a..2e5676945ca 100644
> --- a/arch/arm/mach-snapdragon/Kconfig
> +++ b/arch/arm/mach-snapdragon/Kconfig
> @@ -42,4 +42,11 @@ config SYS_CONFIG_NAME
>  	  Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
>  	  will be used for board configuration.
>  
> +config QCOM_KVM_SUPPORT
> +	bool "Enable KVM support for Qualcomm platforms"
> +	depends on ARM64
> +	help
> +	  This configures the hypervisor interface during boot to support
> +	  KVM virtualization instead of the default Gunyah hypervisor.
> +
>  endif
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index 5fb3240acc5..2926dd8ccc0 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -510,6 +510,154 @@ void __weak qcom_late_init(void)
>  {
>  }
>  
> +/**
> + * qcom_configure_kvm_hypervisor() - Configure hypervisor for KVM guest mode
> + *
> + * Configures the hypervisor for KVM operation:
> + * - EL2 path: Direct SMC call
> + * - EL1 path: Save context, disable caches, SMC call, restore context
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int qcom_configure_kvm_hypervisor(void)
> +{
> +	struct arm_smccc_res res;
> +	u64 current_el;
> +
> +	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
> +	current_el = (current_el >> 2) & 0x3;
> +
> +	log_info("Configuring hypervisor for KVM (EL%llu)\n", current_el);
> +
> +	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
> +		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	if (res.a0 != 0)
> +		log_debug("KVM milestone service not available (0x%lx)\n", res.a0);
> +
> +	if (current_el == 2) {
> +		log_debug("At EL2\n");
> +
> +		arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +			      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +			      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
> +			      0, 0, 0, &res);
> +
> +		if (res.a0 != 0) {
> +			log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +			return -EIO;
> +		}
> +
> +		log_info("KVM hypervisor configured\n");
> +		return 0;
> +	}
> +
> +	log_debug("At EL1, saving register context\n");
> +
> +	u64 ttbr0_el1, tcr_el1, tcr_el2, mair_el1;
> +	u64 t0sz, phys_addr_size;
> +
> +	/* Save EL1 system register context */
> +	asm volatile("mrs %0, ttbr0_el1" : "=r" (ttbr0_el1));
> +	asm volatile("mrs %0, tcr_el1" : "=r" (tcr_el1));
> +	asm volatile("mrs %0, mair_el1" : "=r" (mair_el1));
> +
> +	t0sz = tcr_el1 & TCR_T0SZ_MASK;
> +	phys_addr_size = tcr_el1 & TCR_PS_MASK;
> +
> +	log_debug("Saved context: TTBR0=0x%llx TCR=0x%llx MAIR=0x%llx\n",
> +		  ttbr0_el1, tcr_el1, mair_el1);
> +
> +	icache_disable();
> +	dcache_disable();
> +
> +	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +		      0, 0, QCOM_HYP_BOOT_TYPE_KVM,
> +		      0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +		icache_enable();
> +		dcache_enable();
> +		return -EIO;
> +	}
> +
> +	asm volatile("mrs %0, CurrentEL" : "=r" (current_el));
> +	current_el = (current_el >> 2) & 0x3;
> +
> +	asm volatile("msr ttbr0_el1, %0" : : "r" (ttbr0_el1));
> +	asm volatile("isb");
> +
> +	if (current_el != 2) {
> +		log_debug("No EL2 transition, skipping TCR_EL2 config\n");
> +		icache_enable();
> +		dcache_enable();
> +		log_warning("KVM hypervisor configuration failed\n");
> +		return 0;
> +	}
> +
> +	/* Read current TCR_EL2 and reconfigure it */
> +	asm volatile("mrs %0, tcr_el2" : "=r" (tcr_el2));
> +
> +	tcr_el2 &= ~(TCR_T0SZ_MASK | (0x7UL << 16));
> +	tcr_el2 |= t0sz | (phys_addr_size >> TCR_PS_SHIFT);
> +
> +	tcr_el2 &= ~TCR_SH_ORGN_IRGN_MASK;
> +	tcr_el2 |= TCR_SH_INNER_SHAREABLE |
> +		   TCR_ORGN_WRITE_BACK_ALLOC |
> +		   TCR_IRGN_WRITE_BACK_ALLOC;
> +
> +	asm volatile("msr tcr_el2, %0" : : "r" (tcr_el2));
> +	asm volatile("msr mair_el1, %0" : : "r" (mair_el1));
> +	asm volatile("isb");
> +
> +	icache_enable();
> +	dcache_enable();
> +
> +	log_info("KVM hypervisor configured\n");
> +	return 0;
> +}
> +
> +/**
> + * qcom_configure_gunyah_hypervisor() - Configure hypervisor for Gunyah mode
> + *
> + * Configures the hypervisor for standard Gunyah operation.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int qcom_configure_gunyah_hypervisor(void)
> +{
> +	struct arm_smccc_res res;
> +
> +	log_info("Configuring hypervisor for Gunyah mode\n");
> +
> +	arm_smccc_smc(TZ_INFO_IS_SVC_AVAILABLE_ID,
> +		      TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_debug("Hypervisor milestone service not available (0x%lx)\n", res.a0);
> +		return 0;
> +	}
> +
> +	arm_smccc_smc(TZ_CONFIGURE_MILESTONE_SERVICE_ID,
> +		      TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID,
> +		      0, 0, QCOM_HYP_BOOT_TYPE_GUNYAH,
> +		      0, 0, 0, &res);
> +
> +	if (res.a0 != 0) {
> +		log_err("Hypervisor configuration failed: 0x%lx\n", res.a0);
> +		return -EIO;
> +	}
> +
> +	log_info("Gunyah hypervisor configured\n");
> +	return 0;
> +}
> +
>  #define KERNEL_COMP_SIZE	SZ_64M
> arch/arm/mach-snapdragon/Kconfig #ifdef CONFIG_FASTBOOT_BUF_SIZE
>  #define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
> @@ -570,6 +718,12 @@ int board_late_init(void)
>  	qcom_late_init();
>  
>  	qcom_show_boot_source();
> +
> +	if (IS_ENABLED(CONFIG_QCOM_KVM_SUPPORT))
> +		qcom_configure_kvm_hypervisor();
> +	else
> +		qcom_configure_gunyah_hypervisor();
> +
>  	/* Configure the dfu_string for capsule updates */
>  	qcom_configure_capsule_updates();
>  
> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> index b8bf574e8bb..a5d9dec6aa7 100644
> --- a/arch/arm/mach-snapdragon/qcom-priv.h
> +++ b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -17,6 +17,37 @@ enum qcom_boot_source {
>  
>  extern enum qcom_boot_source qcom_boot_source;
>  
> +/* TrustZone SMC definitions */
> +#define TZ_SYSCALL_CREATE_SMC_ID(o, s, f) \
> +	((u32)((((o) & 0x3f) << 24) | (((s) & 0xff) << 8) | ((f) & 0xff)))
> +
> +#define TZ_OWNER_SIP				2
> +#define TZ_SVC_BOOT				1
> +#define TZ_SVC_INFO				6
> +#define TZ_BOOT_CMD_KVM_MILESTONE		0x21
> +#define TZ_INFO_IS_SVC_AVAILABLE_CMD		0x01
> +
> +#define TZ_CONFIGURE_MILESTONE_SERVICE_ID \
> +	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_BOOT, TZ_BOOT_CMD_KVM_MILESTONE)
> +#define TZ_CONFIGURE_MILESTONE_SERVICE_PARAM_ID		0x23
> +
> +#define TZ_INFO_IS_SVC_AVAILABLE_ID \
> +	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_INFO, TZ_INFO_IS_SVC_AVAILABLE_CMD)
> +#define TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID		0x1
> +
> +/* Hypervisor boot types */
> +#define QCOM_HYP_BOOT_TYPE_GUNYAH		0
> +#define QCOM_HYP_BOOT_TYPE_KVM			1
> +
> +/* TCR_EL2 bit field definitions */
> +#define TCR_T0SZ_MASK				0x1FUL
> +#define TCR_PS_MASK				(0x7UL << 32)
> +#define TCR_PS_SHIFT				16
> +#define TCR_SH_ORGN_IRGN_MASK			0x3F00UL
> +#define TCR_SH_INNER_SHAREABLE			(3UL << 12)
> +#define TCR_ORGN_WRITE_BACK_ALLOC		BIT(10)
> +#define TCR_IRGN_WRITE_BACK_ALLOC		BIT(8)
> +
>  #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
>  void qcom_configure_capsule_updates(void);
>  #else
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-04-20 13:45 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-19 17:38 [PATCH v1] mach-snapdragon: Add KVM hypervisor support Aswin Murugan
2026-04-20  4:32 ` Simon Glass
2026-04-20  7:50 ` neil.armstrong
2026-04-20 13:45 ` Sumit Garg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox