2021-09-27 17:10:01 +05:30
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2019 Western Digital Corporation or its affiliates .
*
* Authors :
* Anup Patel < anup . patel @ wdc . com >
*/
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/kvm_host.h>
# include <asm/csr.h>
2023-10-31 14:45:52 +08:00
# include <asm/cpufeature.h>
2021-09-27 17:10:01 +05:30
# include <asm/sbi.h>
long kvm_arch_dev_ioctl ( struct file * filp ,
unsigned int ioctl , unsigned long arg )
{
return - EINVAL ;
}
int kvm_arch_hardware_enable ( void )
{
unsigned long hideleg , hedeleg ;
hedeleg = 0 ;
hedeleg | = ( 1UL < < EXC_INST_MISALIGNED ) ;
hedeleg | = ( 1UL < < EXC_BREAKPOINT ) ;
hedeleg | = ( 1UL < < EXC_SYSCALL ) ;
hedeleg | = ( 1UL < < EXC_INST_PAGE_FAULT ) ;
hedeleg | = ( 1UL < < EXC_LOAD_PAGE_FAULT ) ;
hedeleg | = ( 1UL < < EXC_STORE_PAGE_FAULT ) ;
csr_write ( CSR_HEDELEG , hedeleg ) ;
hideleg = 0 ;
hideleg | = ( 1UL < < IRQ_VS_SOFT ) ;
hideleg | = ( 1UL < < IRQ_VS_TIMER ) ;
hideleg | = ( 1UL < < IRQ_VS_EXT ) ;
csr_write ( CSR_HIDELEG , hideleg ) ;
2023-02-07 01:55:25 -08:00
/* VS should access only the time counter directly. Everything else should trap */
csr_write ( CSR_HCOUNTEREN , 0x02 ) ;
2021-09-27 17:10:01 +05:30
csr_write ( CSR_HVIP , 0 ) ;
2023-01-10 16:44:25 +05:30
kvm_riscv_aia_enable ( ) ;
2021-09-27 17:10:01 +05:30
return 0 ;
}
void kvm_arch_hardware_disable ( void )
{
2023-01-10 16:44:25 +05:30
kvm_riscv_aia_disable ( ) ;
2021-12-27 11:05:14 +08:00
/*
* After clearing the hideleg CSR , the host kernel will receive
* spurious interrupts if hvip CSR has pending interrupts and the
* corresponding enable bits in vsie CSR are asserted . To avoid it ,
* hvip CSR and vsie CSR must be cleared before clearing hideleg CSR .
*/
csr_write ( CSR_VSIE , 0 ) ;
csr_write ( CSR_HVIP , 0 ) ;
2021-09-27 17:10:01 +05:30
csr_write ( CSR_HEDELEG , 0 ) ;
csr_write ( CSR_HIDELEG , 0 ) ;
}
2022-11-30 23:09:08 +00:00
static int __init riscv_kvm_init ( void )
2021-09-27 17:10:01 +05:30
{
2023-01-10 16:44:25 +05:30
int rc ;
2021-09-27 17:10:09 +05:30
const char * str ;
2021-09-27 17:10:01 +05:30
if ( ! riscv_isa_extension_available ( NULL , h ) ) {
kvm_info ( " hypervisor extension not available \n " ) ;
return - ENODEV ;
}
if ( sbi_spec_is_0_1 ( ) ) {
kvm_info ( " require SBI v0.2 or higher \n " ) ;
return - ENODEV ;
}
2023-04-27 18:36:26 +02:00
if ( ! sbi_probe_extension ( SBI_EXT_RFENCE ) ) {
2021-09-27 17:10:01 +05:30
kvm_info ( " require SBI RFENCE extension \n " ) ;
return - ENODEV ;
}
2022-05-09 10:43:30 +05:30
kvm_riscv_gstage_mode_detect ( ) ;
2021-09-27 17:10:09 +05:30
2022-05-09 10:43:30 +05:30
kvm_riscv_gstage_vmid_detect ( ) ;
2021-09-27 17:10:08 +05:30
2023-01-10 16:44:25 +05:30
rc = kvm_riscv_aia_init ( ) ;
if ( rc & & rc ! = - ENODEV )
return rc ;
2021-09-27 17:10:01 +05:30
kvm_info ( " hypervisor extension available \n " ) ;
2022-05-09 10:43:30 +05:30
switch ( kvm_riscv_gstage_mode ( ) ) {
2021-09-27 17:10:09 +05:30
case HGATP_MODE_SV32X4 :
str = " Sv32x4 " ;
break ;
case HGATP_MODE_SV39X4 :
str = " Sv39x4 " ;
break ;
case HGATP_MODE_SV48X4 :
str = " Sv48x4 " ;
break ;
2022-05-09 10:43:39 +05:30
case HGATP_MODE_SV57X4 :
str = " Sv57x4 " ;
break ;
2021-09-27 17:10:09 +05:30
default :
return - ENODEV ;
}
kvm_info ( " using %s G-stage page table format \n " , str ) ;
2022-05-09 10:43:30 +05:30
kvm_info ( " VMID %ld bits available \n " , kvm_riscv_gstage_vmid_bits ( ) ) ;
2021-09-27 17:10:08 +05:30
2023-01-10 16:44:25 +05:30
if ( kvm_riscv_aia_available ( ) )
2023-06-15 13:03:44 +05:30
kvm_info ( " AIA available with %d guest external interrupts \n " ,
kvm_riscv_aia_nr_hgei ) ;
2023-01-10 16:44:25 +05:30
rc = kvm_init ( sizeof ( struct kvm_vcpu ) , 0 , THIS_MODULE ) ;
if ( rc ) {
kvm_riscv_aia_exit ( ) ;
return rc ;
}
return 0 ;
2021-09-27 17:10:01 +05:30
}
module_init ( riscv_kvm_init ) ;
2022-12-07 09:16:02 +05:30
static void __exit riscv_kvm_exit ( void )
{
2023-01-10 16:44:25 +05:30
kvm_riscv_aia_exit ( ) ;
2022-12-07 09:16:02 +05:30
kvm_exit ( ) ;
}
module_exit ( riscv_kvm_exit ) ;