mirror of
https://github.com/torvalds/linux.git
synced 2026-05-04 22:43:04 -04:00
Export kernel paravirt features to user space, so that VMM can control each single paravirt feature. By default paravirt features will be the same with kvm supported features if VMM does not set it. Also a new feature KVM_FEATURE_VIRT_EXTIOI is added which can be set from user space. This feature indicates that the virt EIOINTC can route interrupts to 256 vCPUs, rather than 4 vCPUs like with real HW. Signed-off-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
173 lines
3.9 KiB
C
173 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
|
|
*/
|
|
|
|
#include <linux/kvm_host.h>
|
|
#include <asm/kvm_mmu.h>
|
|
#include <asm/kvm_vcpu.h>
|
|
|
|
const struct _kvm_stats_desc kvm_vm_stats_desc[] = {
|
|
KVM_GENERIC_VM_STATS(),
|
|
STATS_DESC_ICOUNTER(VM, pages),
|
|
STATS_DESC_ICOUNTER(VM, hugepages),
|
|
};
|
|
|
|
const struct kvm_stats_header kvm_vm_stats_header = {
|
|
.name_size = KVM_STATS_NAME_SIZE,
|
|
.num_desc = ARRAY_SIZE(kvm_vm_stats_desc),
|
|
.id_offset = sizeof(struct kvm_stats_header),
|
|
.desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
|
|
.data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
|
|
sizeof(kvm_vm_stats_desc),
|
|
};
|
|
|
|
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|
{
|
|
int i;
|
|
|
|
/* Allocate page table to map GPA -> RPA */
|
|
kvm->arch.pgd = kvm_pgd_alloc();
|
|
if (!kvm->arch.pgd)
|
|
return -ENOMEM;
|
|
|
|
kvm->arch.phyid_map = kvzalloc(sizeof(struct kvm_phyid_map), GFP_KERNEL_ACCOUNT);
|
|
if (!kvm->arch.phyid_map) {
|
|
free_page((unsigned long)kvm->arch.pgd);
|
|
kvm->arch.pgd = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
spin_lock_init(&kvm->arch.phyid_map_lock);
|
|
|
|
kvm_init_vmcs(kvm);
|
|
|
|
/* Enable all PV features by default */
|
|
kvm->arch.pv_features = BIT(KVM_FEATURE_IPI);
|
|
if (kvm_pvtime_supported())
|
|
kvm->arch.pv_features |= BIT(KVM_FEATURE_STEAL_TIME);
|
|
|
|
kvm->arch.gpa_size = BIT(cpu_vabits - 1);
|
|
kvm->arch.root_level = CONFIG_PGTABLE_LEVELS - 1;
|
|
kvm->arch.invalid_ptes[0] = 0;
|
|
kvm->arch.invalid_ptes[1] = (unsigned long)invalid_pte_table;
|
|
#if CONFIG_PGTABLE_LEVELS > 2
|
|
kvm->arch.invalid_ptes[2] = (unsigned long)invalid_pmd_table;
|
|
#endif
|
|
#if CONFIG_PGTABLE_LEVELS > 3
|
|
kvm->arch.invalid_ptes[3] = (unsigned long)invalid_pud_table;
|
|
#endif
|
|
for (i = 0; i <= kvm->arch.root_level; i++)
|
|
kvm->arch.pte_shifts[i] = PAGE_SHIFT + i * (PAGE_SHIFT - 3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void kvm_arch_destroy_vm(struct kvm *kvm)
|
|
{
|
|
kvm_destroy_vcpus(kvm);
|
|
free_page((unsigned long)kvm->arch.pgd);
|
|
kvm->arch.pgd = NULL;
|
|
kvfree(kvm->arch.phyid_map);
|
|
kvm->arch.phyid_map = NULL;
|
|
}
|
|
|
|
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|
{
|
|
int r;
|
|
|
|
switch (ext) {
|
|
case KVM_CAP_ONE_REG:
|
|
case KVM_CAP_ENABLE_CAP:
|
|
case KVM_CAP_READONLY_MEM:
|
|
case KVM_CAP_SYNC_MMU:
|
|
case KVM_CAP_IMMEDIATE_EXIT:
|
|
case KVM_CAP_IOEVENTFD:
|
|
case KVM_CAP_MP_STATE:
|
|
case KVM_CAP_SET_GUEST_DEBUG:
|
|
r = 1;
|
|
break;
|
|
case KVM_CAP_NR_VCPUS:
|
|
r = num_online_cpus();
|
|
break;
|
|
case KVM_CAP_MAX_VCPUS:
|
|
r = KVM_MAX_VCPUS;
|
|
break;
|
|
case KVM_CAP_MAX_VCPU_ID:
|
|
r = KVM_MAX_VCPU_IDS;
|
|
break;
|
|
case KVM_CAP_NR_MEMSLOTS:
|
|
r = KVM_USER_MEM_SLOTS;
|
|
break;
|
|
default:
|
|
r = 0;
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int kvm_vm_feature_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
|
|
{
|
|
switch (attr->attr) {
|
|
case KVM_LOONGARCH_VM_FEAT_LSX:
|
|
if (cpu_has_lsx)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_LASX:
|
|
if (cpu_has_lasx)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_X86BT:
|
|
if (cpu_has_lbt_x86)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_ARMBT:
|
|
if (cpu_has_lbt_arm)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_MIPSBT:
|
|
if (cpu_has_lbt_mips)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_PMU:
|
|
if (cpu_has_pmp)
|
|
return 0;
|
|
return -ENXIO;
|
|
case KVM_LOONGARCH_VM_FEAT_PV_IPI:
|
|
return 0;
|
|
case KVM_LOONGARCH_VM_FEAT_PV_STEALTIME:
|
|
if (kvm_pvtime_supported())
|
|
return 0;
|
|
return -ENXIO;
|
|
default:
|
|
return -ENXIO;
|
|
}
|
|
}
|
|
|
|
static int kvm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
|
|
{
|
|
switch (attr->group) {
|
|
case KVM_LOONGARCH_VM_FEAT_CTRL:
|
|
return kvm_vm_feature_has_attr(kvm, attr);
|
|
default:
|
|
return -ENXIO;
|
|
}
|
|
}
|
|
|
|
int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *)arg;
|
|
struct kvm *kvm = filp->private_data;
|
|
struct kvm_device_attr attr;
|
|
|
|
switch (ioctl) {
|
|
case KVM_HAS_DEVICE_ATTR:
|
|
if (copy_from_user(&attr, argp, sizeof(attr)))
|
|
return -EFAULT;
|
|
|
|
return kvm_vm_has_attr(kvm, &attr);
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
}
|