| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (c) 2024 Ventana Micro Systems Inc.
- */
- #include <linux/kvm_host.h>
- #include <linux/vmalloc.h>
- #include <asm/kvm_nacl.h>
- DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_available);
- DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_csr_available);
- DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_hfence_available);
- DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_sret_available);
- DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_autoswap_csr_available);
- DEFINE_PER_CPU(struct kvm_riscv_nacl, kvm_riscv_nacl);
- void __kvm_riscv_nacl_hfence(void *shmem,
- unsigned long control,
- unsigned long page_num,
- unsigned long page_count)
- {
- int i, ent = -1, try_count = 5;
- unsigned long *entp;
- again:
- for (i = 0; i < SBI_NACL_SHMEM_HFENCE_ENTRY_MAX; i++) {
- entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i);
- if (lelong_to_cpu(*entp) & SBI_NACL_SHMEM_HFENCE_CONFIG_PEND)
- continue;
- ent = i;
- break;
- }
- if (ent < 0) {
- if (try_count) {
- nacl_sync_hfence(-1UL);
- goto again;
- } else {
- pr_warn("KVM: No free entry in NACL shared memory\n");
- return;
- }
- }
- entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i);
- *entp = cpu_to_lelong(control);
- entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PNUM(i);
- *entp = cpu_to_lelong(page_num);
- entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PCOUNT(i);
- *entp = cpu_to_lelong(page_count);
- }
- int kvm_riscv_nacl_enable(void)
- {
- int rc;
- struct sbiret ret;
- struct kvm_riscv_nacl *nacl;
- if (!kvm_riscv_nacl_available())
- return 0;
- nacl = this_cpu_ptr(&kvm_riscv_nacl);
- ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM,
- nacl->shmem_phys, 0, 0, 0, 0, 0);
- rc = sbi_err_map_linux_errno(ret.error);
- if (rc)
- return rc;
- return 0;
- }
- void kvm_riscv_nacl_disable(void)
- {
- if (!kvm_riscv_nacl_available())
- return;
- sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM,
- SBI_SHMEM_DISABLE, SBI_SHMEM_DISABLE, 0, 0, 0, 0);
- }
- void kvm_riscv_nacl_exit(void)
- {
- int cpu;
- struct kvm_riscv_nacl *nacl;
- if (!kvm_riscv_nacl_available())
- return;
- /* Allocate per-CPU shared memory */
- for_each_possible_cpu(cpu) {
- nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu);
- if (!nacl->shmem)
- continue;
- free_pages((unsigned long)nacl->shmem,
- get_order(SBI_NACL_SHMEM_SIZE));
- nacl->shmem = NULL;
- nacl->shmem_phys = 0;
- }
- }
- static long nacl_probe_feature(long feature_id)
- {
- struct sbiret ret;
- if (!kvm_riscv_nacl_available())
- return 0;
- ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_PROBE_FEATURE,
- feature_id, 0, 0, 0, 0, 0);
- return ret.value;
- }
- int kvm_riscv_nacl_init(void)
- {
- int cpu;
- struct page *shmem_page;
- struct kvm_riscv_nacl *nacl;
- if (sbi_spec_version < sbi_mk_version(1, 0) ||
- sbi_probe_extension(SBI_EXT_NACL) <= 0)
- return -ENODEV;
- /* Enable NACL support */
- static_branch_enable(&kvm_riscv_nacl_available);
- /* Probe NACL features */
- if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_CSR))
- static_branch_enable(&kvm_riscv_nacl_sync_csr_available);
- if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_HFENCE))
- static_branch_enable(&kvm_riscv_nacl_sync_hfence_available);
- if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_SRET))
- static_branch_enable(&kvm_riscv_nacl_sync_sret_available);
- if (nacl_probe_feature(SBI_NACL_FEAT_AUTOSWAP_CSR))
- static_branch_enable(&kvm_riscv_nacl_autoswap_csr_available);
- /* Allocate per-CPU shared memory */
- for_each_possible_cpu(cpu) {
- nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu);
- shmem_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
- get_order(SBI_NACL_SHMEM_SIZE));
- if (!shmem_page) {
- kvm_riscv_nacl_exit();
- return -ENOMEM;
- }
- nacl->shmem = page_to_virt(shmem_page);
- nacl->shmem_phys = page_to_phys(shmem_page);
- }
- return 0;
- }
|