1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2024 Ventana Micro Systems Inc. 4 */ 5 6 #include <linux/kvm_host.h> 7 #include <linux/vmalloc.h> 8 #include <asm/kvm_nacl.h> 9 10 DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_available); 11 DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_csr_available); 12 DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_hfence_available); 13 DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_sret_available); 14 DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_autoswap_csr_available); 15 DEFINE_PER_CPU(struct kvm_riscv_nacl, kvm_riscv_nacl); 16 17 void __kvm_riscv_nacl_hfence(void *shmem, 18 unsigned long control, 19 unsigned long page_num, 20 unsigned long page_count) 21 { 22 int i, ent = -1, try_count = 5; 23 unsigned long *entp; 24 25 again: 26 for (i = 0; i < SBI_NACL_SHMEM_HFENCE_ENTRY_MAX; i++) { 27 entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i); 28 if (lelong_to_cpu(*entp) & SBI_NACL_SHMEM_HFENCE_CONFIG_PEND) 29 continue; 30 31 ent = i; 32 break; 33 } 34 35 if (ent < 0) { 36 if (try_count) { 37 nacl_sync_hfence(-1UL); 38 goto again; 39 } else { 40 pr_warn("KVM: No free entry in NACL shared memory\n"); 41 return; 42 } 43 } 44 45 entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i); 46 *entp = cpu_to_lelong(control); 47 entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PNUM(i); 48 *entp = cpu_to_lelong(page_num); 49 entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PCOUNT(i); 50 *entp = cpu_to_lelong(page_count); 51 } 52 53 int kvm_riscv_nacl_enable(void) 54 { 55 int rc; 56 struct sbiret ret; 57 struct kvm_riscv_nacl *nacl; 58 59 if (!kvm_riscv_nacl_available()) 60 return 0; 61 nacl = this_cpu_ptr(&kvm_riscv_nacl); 62 63 ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, 64 nacl->shmem_phys, 0, 0, 0, 0, 0); 65 rc = sbi_err_map_linux_errno(ret.error); 66 if (rc) 67 return rc; 68 69 return 0; 70 } 71 72 void kvm_riscv_nacl_disable(void) 73 { 74 if (!kvm_riscv_nacl_available()) 75 return; 76 77 sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, 78 SBI_SHMEM_DISABLE, SBI_SHMEM_DISABLE, 0, 0, 0, 0); 79 } 80 81 void kvm_riscv_nacl_exit(void) 82 { 83 int cpu; 84 struct kvm_riscv_nacl *nacl; 85 86 if (!kvm_riscv_nacl_available()) 87 return; 88 89 /* Allocate per-CPU shared memory */ 90 for_each_possible_cpu(cpu) { 91 nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu); 92 if (!nacl->shmem) 93 continue; 94 95 free_pages((unsigned long)nacl->shmem, 96 get_order(SBI_NACL_SHMEM_SIZE)); 97 nacl->shmem = NULL; 98 nacl->shmem_phys = 0; 99 } 100 } 101 102 static long nacl_probe_feature(long feature_id) 103 { 104 struct sbiret ret; 105 106 if (!kvm_riscv_nacl_available()) 107 return 0; 108 109 ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_PROBE_FEATURE, 110 feature_id, 0, 0, 0, 0, 0); 111 return ret.value; 112 } 113 114 int kvm_riscv_nacl_init(void) 115 { 116 int cpu; 117 struct page *shmem_page; 118 struct kvm_riscv_nacl *nacl; 119 120 if (sbi_spec_version < sbi_mk_version(1, 0) || 121 sbi_probe_extension(SBI_EXT_NACL) <= 0) 122 return -ENODEV; 123 124 /* Enable NACL support */ 125 static_branch_enable(&kvm_riscv_nacl_available); 126 127 /* Probe NACL features */ 128 if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_CSR)) 129 static_branch_enable(&kvm_riscv_nacl_sync_csr_available); 130 if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_HFENCE)) 131 static_branch_enable(&kvm_riscv_nacl_sync_hfence_available); 132 if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_SRET)) 133 static_branch_enable(&kvm_riscv_nacl_sync_sret_available); 134 if (nacl_probe_feature(SBI_NACL_FEAT_AUTOSWAP_CSR)) 135 static_branch_enable(&kvm_riscv_nacl_autoswap_csr_available); 136 137 /* Allocate per-CPU shared memory */ 138 for_each_possible_cpu(cpu) { 139 nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu); 140 141 shmem_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 142 get_order(SBI_NACL_SHMEM_SIZE)); 143 if (!shmem_page) { 144 kvm_riscv_nacl_exit(); 145 return -ENOMEM; 146 } 147 nacl->shmem = page_to_virt(shmem_page); 148 nacl->shmem_phys = page_to_phys(shmem_page); 149 } 150 151 return 0; 152 } 153