edcbe90f12
kvm_vm_ioctl_check_extension(): Return 1 if KVM_CAP_SET_GUEST_DEBUG is been checked. kvm_arch_vcpu_ioctl_set_guest_debug(): Update the guest_debug flags from userspace accordingly. Route the breakpoint exceptions to HS mode if the VCPU is being debugged by userspace, by clearing the corresponding bit in hedeleg. Initialize the hedeleg configuration in kvm_riscv_vcpu_setup_config(). Write the actual CSR in kvm_arch_vcpu_load(). Signed-off-by: Chao Du <duchao@eswincomputing.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20240402062628.5425-2-duchao@eswincomputing.com Signed-off-by: Anup Patel <anup@brainfault.org>
216 lines
4.6 KiB
C
216 lines
4.6 KiB
C
// 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/uaccess.h>
|
|
#include <linux/kvm_host.h>
|
|
|
|
const struct _kvm_stats_desc kvm_vm_stats_desc[] = {
|
|
KVM_GENERIC_VM_STATS()
|
|
};
|
|
static_assert(ARRAY_SIZE(kvm_vm_stats_desc) ==
|
|
sizeof(struct kvm_vm_stat) / sizeof(u64));
|
|
|
|
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 r;
|
|
|
|
r = kvm_riscv_gstage_alloc_pgd(kvm);
|
|
if (r)
|
|
return r;
|
|
|
|
r = kvm_riscv_gstage_vmid_init(kvm);
|
|
if (r) {
|
|
kvm_riscv_gstage_free_pgd(kvm);
|
|
return r;
|
|
}
|
|
|
|
kvm_riscv_aia_init_vm(kvm);
|
|
|
|
kvm_riscv_guest_timer_init(kvm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void kvm_arch_destroy_vm(struct kvm *kvm)
|
|
{
|
|
kvm_destroy_vcpus(kvm);
|
|
|
|
kvm_riscv_aia_destroy_vm(kvm);
|
|
}
|
|
|
|
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irql,
|
|
bool line_status)
|
|
{
|
|
if (!irqchip_in_kernel(kvm))
|
|
return -ENXIO;
|
|
|
|
return kvm_riscv_aia_inject_irq(kvm, irql->irq, irql->level);
|
|
}
|
|
|
|
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
|
|
struct kvm *kvm, int irq_source_id,
|
|
int level, bool line_status)
|
|
{
|
|
struct kvm_msi msi;
|
|
|
|
if (!level)
|
|
return -1;
|
|
|
|
msi.address_lo = e->msi.address_lo;
|
|
msi.address_hi = e->msi.address_hi;
|
|
msi.data = e->msi.data;
|
|
msi.flags = e->msi.flags;
|
|
msi.devid = e->msi.devid;
|
|
|
|
return kvm_riscv_aia_inject_msi(kvm, &msi);
|
|
}
|
|
|
|
static int kvm_riscv_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
|
struct kvm *kvm, int irq_source_id,
|
|
int level, bool line_status)
|
|
{
|
|
return kvm_riscv_aia_inject_irq(kvm, e->irqchip.pin, level);
|
|
}
|
|
|
|
int kvm_riscv_setup_default_irq_routing(struct kvm *kvm, u32 lines)
|
|
{
|
|
struct kvm_irq_routing_entry *ents;
|
|
int i, rc;
|
|
|
|
ents = kcalloc(lines, sizeof(*ents), GFP_KERNEL);
|
|
if (!ents)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < lines; i++) {
|
|
ents[i].gsi = i;
|
|
ents[i].type = KVM_IRQ_ROUTING_IRQCHIP;
|
|
ents[i].u.irqchip.irqchip = 0;
|
|
ents[i].u.irqchip.pin = i;
|
|
}
|
|
rc = kvm_set_irq_routing(kvm, ents, lines, 0);
|
|
kfree(ents);
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool kvm_arch_can_set_irq_routing(struct kvm *kvm)
|
|
{
|
|
return irqchip_in_kernel(kvm);
|
|
}
|
|
|
|
int kvm_set_routing_entry(struct kvm *kvm,
|
|
struct kvm_kernel_irq_routing_entry *e,
|
|
const struct kvm_irq_routing_entry *ue)
|
|
{
|
|
int r = -EINVAL;
|
|
|
|
switch (ue->type) {
|
|
case KVM_IRQ_ROUTING_IRQCHIP:
|
|
e->set = kvm_riscv_set_irq;
|
|
e->irqchip.irqchip = ue->u.irqchip.irqchip;
|
|
e->irqchip.pin = ue->u.irqchip.pin;
|
|
if ((e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) ||
|
|
(e->irqchip.irqchip >= KVM_NR_IRQCHIPS))
|
|
goto out;
|
|
break;
|
|
case KVM_IRQ_ROUTING_MSI:
|
|
e->set = kvm_set_msi;
|
|
e->msi.address_lo = ue->u.msi.address_lo;
|
|
e->msi.address_hi = ue->u.msi.address_hi;
|
|
e->msi.data = ue->u.msi.data;
|
|
e->msi.flags = ue->flags;
|
|
e->msi.devid = ue->u.msi.devid;
|
|
break;
|
|
default:
|
|
goto out;
|
|
}
|
|
r = 0;
|
|
out:
|
|
return r;
|
|
}
|
|
|
|
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
|
|
struct kvm *kvm, int irq_source_id, int level,
|
|
bool line_status)
|
|
{
|
|
if (!level)
|
|
return -EWOULDBLOCK;
|
|
|
|
switch (e->type) {
|
|
case KVM_IRQ_ROUTING_MSI:
|
|
return kvm_set_msi(e, kvm, irq_source_id, level, line_status);
|
|
|
|
case KVM_IRQ_ROUTING_IRQCHIP:
|
|
return kvm_riscv_set_irq(e, kvm, irq_source_id,
|
|
level, line_status);
|
|
}
|
|
|
|
return -EWOULDBLOCK;
|
|
}
|
|
|
|
bool kvm_arch_irqchip_in_kernel(struct kvm *kvm)
|
|
{
|
|
return irqchip_in_kernel(kvm);
|
|
}
|
|
|
|
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|
{
|
|
int r;
|
|
|
|
switch (ext) {
|
|
case KVM_CAP_IRQCHIP:
|
|
r = kvm_riscv_aia_available();
|
|
break;
|
|
case KVM_CAP_IOEVENTFD:
|
|
case KVM_CAP_USER_MEMORY:
|
|
case KVM_CAP_SYNC_MMU:
|
|
case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
|
|
case KVM_CAP_ONE_REG:
|
|
case KVM_CAP_READONLY_MEM:
|
|
case KVM_CAP_MP_STATE:
|
|
case KVM_CAP_IMMEDIATE_EXIT:
|
|
case KVM_CAP_SET_GUEST_DEBUG:
|
|
r = 1;
|
|
break;
|
|
case KVM_CAP_NR_VCPUS:
|
|
r = min_t(unsigned int, num_online_cpus(), KVM_MAX_VCPUS);
|
|
break;
|
|
case KVM_CAP_MAX_VCPUS:
|
|
r = KVM_MAX_VCPUS;
|
|
break;
|
|
case KVM_CAP_NR_MEMSLOTS:
|
|
r = KVM_USER_MEM_SLOTS;
|
|
break;
|
|
case KVM_CAP_VM_GPA_BITS:
|
|
r = kvm_riscv_gstage_gpa_bits();
|
|
break;
|
|
default:
|
|
r = 0;
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|
{
|
|
return -EINVAL;
|
|
}
|