[PATCH v1] mach-snapdragon: Add KVM hypervisor support

neil.armstrong at linaro.org neil.armstrong at linaro.org
Mon Apr 20 09:50:36 CEST 2026


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 at 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



More information about the U-Boot mailing list