root/arch/riscv/kvm/vcpu_config.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2026 Qualcomm Technologies, Inc.
 */

#include <linux/kvm_host.h>
#include <asm/kvm_nacl.h>

#define KVM_HEDELEG_DEFAULT     (BIT(EXC_INST_MISALIGNED) | \
                                 BIT(EXC_INST_ILLEGAL)     | \
                                 BIT(EXC_BREAKPOINT)      | \
                                 BIT(EXC_SYSCALL)         | \
                                 BIT(EXC_INST_PAGE_FAULT) | \
                                 BIT(EXC_LOAD_PAGE_FAULT) | \
                                 BIT(EXC_STORE_PAGE_FAULT))

#define KVM_HIDELEG_DEFAULT     (BIT(IRQ_VS_SOFT)  | \
                                 BIT(IRQ_VS_TIMER) | \
                                 BIT(IRQ_VS_EXT))

void kvm_riscv_vcpu_config_init(struct kvm_vcpu *vcpu)
{
        vcpu->arch.cfg.hedeleg = KVM_HEDELEG_DEFAULT;
        vcpu->arch.cfg.hideleg = KVM_HIDELEG_DEFAULT;
}

void kvm_riscv_vcpu_config_guest_debug(struct kvm_vcpu *vcpu)
{
        struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;

        if (vcpu->guest_debug)
                cfg->hedeleg &= ~BIT(EXC_BREAKPOINT);
        else
                cfg->hedeleg |= BIT(EXC_BREAKPOINT);

        vcpu->arch.csr_dirty = true;
}

void kvm_riscv_vcpu_config_ran_once(struct kvm_vcpu *vcpu)
{
        const unsigned long *isa = vcpu->arch.isa;
        struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;

        if (riscv_isa_extension_available(isa, SVPBMT))
                cfg->henvcfg |= ENVCFG_PBMTE;

        if (riscv_isa_extension_available(isa, SSTC))
                cfg->henvcfg |= ENVCFG_STCE;

        if (riscv_isa_extension_available(isa, ZICBOM))
                cfg->henvcfg |= (ENVCFG_CBIE | ENVCFG_CBCFE);

        if (riscv_isa_extension_available(isa, ZICBOZ))
                cfg->henvcfg |= ENVCFG_CBZE;

        if (riscv_isa_extension_available(isa, SVADU) &&
            !riscv_isa_extension_available(isa, SVADE))
                cfg->henvcfg |= ENVCFG_ADUE;

        if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) {
                cfg->hstateen0 |= SMSTATEEN0_HSENVCFG;
                if (riscv_isa_extension_available(isa, SSAIA))
                        cfg->hstateen0 |= SMSTATEEN0_AIA_IMSIC |
                                          SMSTATEEN0_AIA |
                                          SMSTATEEN0_AIA_ISEL;
                if (riscv_isa_extension_available(isa, SMSTATEEN))
                        cfg->hstateen0 |= SMSTATEEN0_SSTATEEN0;
        }

        if (vcpu->guest_debug)
                cfg->hedeleg &= ~BIT(EXC_BREAKPOINT);
}

void kvm_riscv_vcpu_config_load(struct kvm_vcpu *vcpu)
{
        struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;
        void *nsh;

        if (kvm_riscv_nacl_sync_csr_available()) {
                nsh = nacl_shmem();
                nacl_csr_write(nsh, CSR_HEDELEG, cfg->hedeleg);
                nacl_csr_write(nsh, CSR_HIDELEG, cfg->hideleg);
                nacl_csr_write(nsh, CSR_HENVCFG, cfg->henvcfg);
                if (IS_ENABLED(CONFIG_32BIT))
                        nacl_csr_write(nsh, CSR_HENVCFGH, cfg->henvcfg >> 32);
                if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) {
                        nacl_csr_write(nsh, CSR_HSTATEEN0, cfg->hstateen0);
                        if (IS_ENABLED(CONFIG_32BIT))
                                nacl_csr_write(nsh, CSR_HSTATEEN0H, cfg->hstateen0 >> 32);
                }
        } else {
                csr_write(CSR_HEDELEG, cfg->hedeleg);
                csr_write(CSR_HIDELEG, cfg->hideleg);
                csr_write(CSR_HENVCFG, cfg->henvcfg);
                if (IS_ENABLED(CONFIG_32BIT))
                        csr_write(CSR_HENVCFGH, cfg->henvcfg >> 32);
                if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) {
                        csr_write(CSR_HSTATEEN0, cfg->hstateen0);
                        if (IS_ENABLED(CONFIG_32BIT))
                                csr_write(CSR_HSTATEEN0H, cfg->hstateen0 >> 32);
                }
        }
}