162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2019 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Anup Patel <anup.patel@wdc.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kvm_host.h> 1362306a36Sopenharmony_ci#include <asm/csr.h> 1462306a36Sopenharmony_ci#include <asm/hwcap.h> 1562306a36Sopenharmony_ci#include <asm/sbi.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cilong kvm_arch_dev_ioctl(struct file *filp, 1862306a36Sopenharmony_ci unsigned int ioctl, unsigned long arg) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return -EINVAL; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint kvm_arch_hardware_enable(void) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long hideleg, hedeleg; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci hedeleg = 0; 2862306a36Sopenharmony_ci hedeleg |= (1UL << EXC_INST_MISALIGNED); 2962306a36Sopenharmony_ci hedeleg |= (1UL << EXC_BREAKPOINT); 3062306a36Sopenharmony_ci hedeleg |= (1UL << EXC_SYSCALL); 3162306a36Sopenharmony_ci hedeleg |= (1UL << EXC_INST_PAGE_FAULT); 3262306a36Sopenharmony_ci hedeleg |= (1UL << EXC_LOAD_PAGE_FAULT); 3362306a36Sopenharmony_ci hedeleg |= (1UL << EXC_STORE_PAGE_FAULT); 3462306a36Sopenharmony_ci csr_write(CSR_HEDELEG, hedeleg); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci hideleg = 0; 3762306a36Sopenharmony_ci hideleg |= (1UL << IRQ_VS_SOFT); 3862306a36Sopenharmony_ci hideleg |= (1UL << IRQ_VS_TIMER); 3962306a36Sopenharmony_ci hideleg |= (1UL << IRQ_VS_EXT); 4062306a36Sopenharmony_ci csr_write(CSR_HIDELEG, hideleg); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* VS should access only the time counter directly. Everything else should trap */ 4362306a36Sopenharmony_ci csr_write(CSR_HCOUNTEREN, 0x02); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci csr_write(CSR_HVIP, 0); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci kvm_riscv_aia_enable(); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid kvm_arch_hardware_disable(void) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci kvm_riscv_aia_disable(); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * After clearing the hideleg CSR, the host kernel will receive 5862306a36Sopenharmony_ci * spurious interrupts if hvip CSR has pending interrupts and the 5962306a36Sopenharmony_ci * corresponding enable bits in vsie CSR are asserted. To avoid it, 6062306a36Sopenharmony_ci * hvip CSR and vsie CSR must be cleared before clearing hideleg CSR. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci csr_write(CSR_VSIE, 0); 6362306a36Sopenharmony_ci csr_write(CSR_HVIP, 0); 6462306a36Sopenharmony_ci csr_write(CSR_HEDELEG, 0); 6562306a36Sopenharmony_ci csr_write(CSR_HIDELEG, 0); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int __init riscv_kvm_init(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int rc; 7162306a36Sopenharmony_ci const char *str; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!riscv_isa_extension_available(NULL, h)) { 7462306a36Sopenharmony_ci kvm_info("hypervisor extension not available\n"); 7562306a36Sopenharmony_ci return -ENODEV; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (sbi_spec_is_0_1()) { 7962306a36Sopenharmony_ci kvm_info("require SBI v0.2 or higher\n"); 8062306a36Sopenharmony_ci return -ENODEV; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!sbi_probe_extension(SBI_EXT_RFENCE)) { 8462306a36Sopenharmony_ci kvm_info("require SBI RFENCE extension\n"); 8562306a36Sopenharmony_ci return -ENODEV; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci kvm_riscv_gstage_mode_detect(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci kvm_riscv_gstage_vmid_detect(); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci rc = kvm_riscv_aia_init(); 9362306a36Sopenharmony_ci if (rc && rc != -ENODEV) 9462306a36Sopenharmony_ci return rc; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci kvm_info("hypervisor extension available\n"); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci switch (kvm_riscv_gstage_mode()) { 9962306a36Sopenharmony_ci case HGATP_MODE_SV32X4: 10062306a36Sopenharmony_ci str = "Sv32x4"; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci case HGATP_MODE_SV39X4: 10362306a36Sopenharmony_ci str = "Sv39x4"; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case HGATP_MODE_SV48X4: 10662306a36Sopenharmony_ci str = "Sv48x4"; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case HGATP_MODE_SV57X4: 10962306a36Sopenharmony_ci str = "Sv57x4"; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci default: 11262306a36Sopenharmony_ci return -ENODEV; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci kvm_info("using %s G-stage page table format\n", str); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits()); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (kvm_riscv_aia_available()) 11962306a36Sopenharmony_ci kvm_info("AIA available with %d guest external interrupts\n", 12062306a36Sopenharmony_ci kvm_riscv_aia_nr_hgei); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); 12362306a36Sopenharmony_ci if (rc) { 12462306a36Sopenharmony_ci kvm_riscv_aia_exit(); 12562306a36Sopenharmony_ci return rc; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_cimodule_init(riscv_kvm_init); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void __exit riscv_kvm_exit(void) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci kvm_riscv_aia_exit(); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci kvm_exit(); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_cimodule_exit(riscv_kvm_exit); 139