blob: ffcfdee58a4877f527b9ac2f41fa8fb348f71f6d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 - os kernal
* Author: fire3 <fire3@example.com> yangzh <yangzh@gmail.com>
* linhn <linhn@example.com>
*/
#include <linux/errno.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
#include <linux/mman.h>
#include <linux/sched/signal.h>
#include <linux/kvm.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <asm/kvm_timer.h>
#include <asm/kvm_emulate.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
#include "../kernel/pci_impl.h"
#include "vmem.c"
bool set_msi_flag;
unsigned long sw64_kvm_last_vpn[NR_CPUS];
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_NUMA)
extern bool bind_vcpu_enabled;
#endif
#define cpu_last_vpn(cpuid) sw64_kvm_last_vpn[cpuid]
#ifdef CONFIG_SUBARCH_C3B
#define WIDTH_HARDWARE_VPN 8
#endif
#define VPN_FIRST_VERSION (1UL << WIDTH_HARDWARE_VPN)
#define HARDWARE_VPN_MASK ((1UL << WIDTH_HARDWARE_VPN) - 1)
#define VPN_SHIFT (64 - WIDTH_HARDWARE_VPN)
static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_running_vcpu);
static void kvm_set_running_vcpu(struct kvm_vcpu *vcpu)
{
__this_cpu_write(kvm_running_vcpu, vcpu);
}
int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level)
{
set_bit(number, (vcpu->arch.irqs_pending));
kvm_vcpu_kick(vcpu);
return 0;
}
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, int irq_source_id,
int level, bool line_status)
{
int irq = e->msi.data & 0xff;
unsigned int vcpu_idx;
struct kvm_vcpu *vcpu = NULL;
vcpu_idx = irq % atomic_read(&kvm->online_vcpus);
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
if (!vcpu)
return -EINVAL;
return vcpu_interrupt_line(vcpu, irq, true);
}
extern int __sw64_vcpu_run(struct vcpucb *vcb, struct kvm_regs *regs, struct hcall_args *args);
#ifdef CONFIG_KVM_MEMHOTPLUG
static u64 get_vpcr_memhp(u64 seg_base, u64 vpn)
{
return seg_base | ((vpn & HARDWARE_VPN_MASK) << 44);
}
#else
static u64 get_vpcr(u64 hpa_base, u64 mem_size, u64 vpn)
{
return (hpa_base >> 23) | ((mem_size >> 23) << 16)
| ((vpn & HARDWARE_VPN_MASK) << 44);
}
#endif
static unsigned long __get_new_vpn_context(struct kvm_vcpu *vcpu, long cpu)
{
unsigned long vpn = cpu_last_vpn(cpu);
unsigned long next = vpn + 1;
if ((vpn & HARDWARE_VPN_MASK) >= HARDWARE_VPN_MASK) {
tbia();
next = (vpn & ~HARDWARE_VPN_MASK) + VPN_FIRST_VERSION + 1; /* bypass 0 */
}
cpu_last_vpn(cpu) = next;
return next;
}
static void sw64_kvm_switch_vpn(struct kvm_vcpu *vcpu)
{
unsigned long vpn;
unsigned long vpnc;
long cpu = smp_processor_id();
vpn = cpu_last_vpn(cpu);
vpnc = vcpu->arch.vpnc[cpu];
if ((vpnc ^ vpn) & ~HARDWARE_VPN_MASK) {
/* vpnc and cpu vpn not in the same version, get new vpnc and vpn */
vpnc = __get_new_vpn_context(vcpu, cpu);
vcpu->arch.vpnc[cpu] = vpnc;
}
vpn = vpnc & HARDWARE_VPN_MASK;
/* Always update vpn */
/* Just setup vcb, hardware CSR will be changed later in HMcode */
vcpu->arch.vcb.vpcr = ((vcpu->arch.vcb.vpcr) & (~(HARDWARE_VPN_MASK << 44))) | (vpn << 44);
vcpu->arch.vcb.dtb_pcr = ((vcpu->arch.vcb.dtb_pcr) & (~(HARDWARE_VPN_MASK << VPN_SHIFT))) | (vpn << VPN_SHIFT);
/*
* If vcpu migrate to a new physical cpu, the new physical cpu may keep
* old tlb entries for this vcpu's vpn, upn in the old tlb entries and
* current vcpu's upn may not in the same version.
* For now, we don't know the vcpu's upn version and the current version.
* If we keep track of the vcpu's upn version, the TLB-flush could be less.
* To be safe and correct, flush all tlb entries of current vpn for now.
*/
if (vcpu->arch.pcpu_id != cpu) {
tbivpn(0, 0, vpn);
vcpu->arch.pcpu_id = cpu;
vcpu->cpu = cpu;
}
}
static void check_vcpu_requests(struct kvm_vcpu *vcpu)
{
unsigned long vpn;
long cpu = smp_processor_id();
if (kvm_request_pending(vcpu)) {
if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) {
vpn = vcpu->arch.vpnc[cpu] & HARDWARE_VPN_MASK;
tbivpn(0, 0, vpn);
}
}
}
struct kvm_stats_debugfs_item debugfs_entries[] = {
{ NULL }
};
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
return ((!bitmap_empty(vcpu->arch.irqs_pending, SWVM_IRQS) || !vcpu->arch.halted)
&& !vcpu->arch.power_off);
}
int kvm_arch_check_processor_compat(void *opaque)
{
return 0;
}
int kvm_arch_hardware_enable(void)
{
return 0;
}
void kvm_arch_hardware_unsetup(void)
{
}
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
{
return false;
}
bool kvm_arch_has_vcpu_debugfs(void)
{
return false;
}
int kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu)
{
return 0;
}
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
{
return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
}
/*
* kvm_mark_migration write the mark on every vcpucbs of the kvm, which tells
* the system to do migration while the mark is on, and flush all vcpu's tlbs
* at the beginning of the migration.
*/
void kvm_mark_migration(struct kvm *kvm, int mark)
{
struct kvm_vcpu *vcpu;
int cpu;
kvm_for_each_vcpu(cpu, vcpu, kvm)
vcpu->arch.vcb.migration_mark = mark << 2;
kvm_flush_remote_tlbs(kvm);
}
void kvm_arch_commit_memory_region(struct kvm *kvm,
const struct kvm_userspace_memory_region *mem,
struct kvm_memory_slot *old,
const struct kvm_memory_slot *new,
enum kvm_mr_change change)
{
/*
* At this point memslot has been committed and there is an
* allocated dirty_bitmap[], dirty pages will be be tracked while the
* memory slot is write protected.
*/
/* If dirty logging has been stopped, do nothing for now. */
if ((change != KVM_MR_DELETE)
&& (old->flags & KVM_MEM_LOG_DIRTY_PAGES)
&& (!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))) {
kvm_mark_migration(kvm, 0);
return;
}
/* If it's the first time dirty logging, flush all vcpu tlbs. */
if ((change == KVM_MR_FLAGS_ONLY)
&& (!(old->flags & KVM_MEM_LOG_DIRTY_PAGES))
&& (new->flags & KVM_MEM_LOG_DIRTY_PAGES))
kvm_mark_migration(kvm, 1);
}
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
{
int r = 0;
switch (ext) {
case KVM_CAP_IRQCHIP:
case KVM_CAP_IOEVENTFD:
case KVM_CAP_SYNC_MMU:
case KVM_CAP_IMMEDIATE_EXIT:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
case KVM_CAP_MAX_VCPUS:
r = KVM_MAX_VCPUS;
break;
default:
r = 0;
}
return r;
}
void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn_offset,
unsigned long mask)
{
}
int kvm_sw64_pending_timer(struct kvm_vcpu *vcpu)
{
return test_bit(SW64_KVM_IRQ_TIMER, &vcpu->arch.irqs_pending);
}
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
return kvm_sw64_pending_timer(vcpu);
}
int kvm_arch_hardware_setup(void *opaque)
{
return 0;
}
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
{
hrtimer_cancel(&vcpu->arch.hrt);
}
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
#ifdef CONFIG_KVM_MEMHOTPLUG
unsigned long *seg_pgd;
if (kvm->arch.seg_pgd != NULL) {
kvm_err("kvm_arch already initialized?\n");
return -EINVAL;
}
seg_pgd = alloc_pages_exact(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO);
if (!seg_pgd)
return -ENOMEM;
kvm->arch.seg_pgd = seg_pgd;
#endif
return 0;
}
void kvm_arch_destroy_vm(struct kvm *kvm)
{
int i;
#ifdef CONFIG_KVM_MEMHOTPLUG
void *seg_pgd = NULL;
if (kvm->arch.seg_pgd) {
seg_pgd = READ_ONCE(kvm->arch.seg_pgd);
kvm->arch.seg_pgd = NULL;
}
if (seg_pgd)
free_pages_exact(seg_pgd, PAGE_SIZE);
#endif
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (kvm->vcpus[i]) {
kvm_vcpu_destroy(kvm->vcpus[i]);
kvm->vcpus[i] = NULL;
}
}
atomic_set(&kvm->online_vcpus, 0);
}
long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
return -EINVAL;
}
int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
unsigned long npages)
{
return 0;
}
#ifdef CONFIG_KVM_MEMHOTPLUG
static void setup_segment_table(struct kvm *kvm,
struct kvm_memory_slot *memslot, unsigned long addr, size_t size)
{
unsigned long *seg_pgd = kvm->arch.seg_pgd;
unsigned int num_of_entry = size >> 30;
unsigned long base_hpa = addr >> 30;
int i;
for (i = 0; i < num_of_entry; i++) {
*seg_pgd = base_hpa + i;
seg_pgd++;
}
}
#endif
int kvm_arch_prepare_memory_region(struct kvm *kvm,
struct kvm_memory_slot *memslot,
const struct kvm_userspace_memory_region *mem,
enum kvm_mr_change change)
{
unsigned long addr;
struct file *vm_file;
struct vm_area_struct *vma;
struct vmem_info *info;
unsigned long ret;
size_t size;
if (change == KVM_MR_FLAGS_ONLY || change == KVM_MR_DELETE)
return 0;
if (test_bit(IO_MARK_BIT, &(mem->guest_phys_addr)))
return 0;
if (test_bit(IO_MARK_BIT + 1, &(mem->guest_phys_addr)))
return 0;
#ifndef CONFIG_KVM_MEMHOTPLUG
if (mem->guest_phys_addr) {
pr_info("%s, No KVM MEMHOTPLUG support!\n", __func__);
return 0;
}
#endif
if (!sw64_kvm_pool)
return -ENOMEM;
pr_info("%s: %#llx %#llx, user addr: %#llx\n", __func__,
mem->guest_phys_addr, mem->memory_size, mem->userspace_addr);
vma = find_vma(current->mm, mem->userspace_addr);
if (!vma)
return -ENOMEM;
vm_file = vma->vm_file;
if (!vm_file) {
info = kzalloc(sizeof(struct vmem_info), GFP_KERNEL);
size = round_up(mem->memory_size, 8 << 20);
addr = gen_pool_alloc(sw64_kvm_pool, size);
if (!addr)
return -ENOMEM;
vm_munmap(mem->userspace_addr, mem->memory_size);
ret = vm_mmap(vm_file, mem->userspace_addr, mem->memory_size,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, 0);
if ((long)ret < 0)
return ret;
vma = find_vma(current->mm, mem->userspace_addr);
if (!vma)
return -ENOMEM;
#ifdef CONFIG_KVM_MEMHOTPLUG
if (memslot->base_gfn == 0x0UL) {
setup_segment_table(kvm, memslot, addr, size);
kvm->arch.host_phys_addr = (u64)addr;
memslot->arch.host_phys_addr = addr;
} else {
/* used for memory hotplug */
memslot->arch.host_phys_addr = addr;
memslot->arch.valid = false;
}
#endif
info->start = addr;
info->size = size;
vma->vm_private_data = (void *) info;
vma->vm_ops = &vmem_vm_ops;
vma->vm_ops->open(vma);
ret = vmem_vm_insert_page(vma);
if ((int)ret < 0)
return ret;
} else {
info = vm_file->private_data;
addr = info->start;
}
pr_info("guest phys addr = %#lx, size = %#lx\n",
addr, vma->vm_end - vma->vm_start);
#ifndef CONFIG_KVM_MEMHOTPLUG
kvm->arch.host_phys_addr = (u64)addr;
kvm->arch.size = round_up(mem->memory_size, 8 << 20);
#endif
memset(__va(addr), 0, 0x2000000);
return 0;
}
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
{
/* Set up the timer for Guest */
pr_info("vcpu: [%d], regs addr = %#lx, vcpucb = %#lx\n", vcpu->vcpu_id,
(unsigned long)&vcpu->arch.regs, (unsigned long)&vcpu->arch.vcb);
hrtimer_init(&vcpu->arch.hrt, CLOCK_REALTIME, HRTIMER_MODE_ABS);
vcpu->arch.hrt.function = clockdev_fn;
vcpu->arch.tsk = current;
/* For guest kernel "sys_call HMC_whami", indicate virtual cpu id */
vcpu->arch.vcb.whami = vcpu->vcpu_id;
vcpu->arch.vcb.vcpu_irq_disabled = 1;
vcpu->arch.pcpu_id = -1; /* force flush tlb for the first time */
return 0;
}
int kvm_arch_vcpu_reset(struct kvm_vcpu *vcpu)
{
unsigned long addr = vcpu->kvm->arch.host_phys_addr;
hrtimer_cancel(&vcpu->arch.hrt);
vcpu->arch.vcb.whami = vcpu->vcpu_id;
vcpu->arch.vcb.vcpu_irq_disabled = 1;
vcpu->arch.pcpu_id = -1; /* force flush tlb for the first time */
vcpu->arch.power_off = 0;
memset(&vcpu->arch.irqs_pending, 0, sizeof(vcpu->arch.irqs_pending));
if (vcpu->vcpu_id == 0)
memset(__va(addr), 0, 0x2000000);
return 0;
}
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_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;
set_msi_flag = true;
break;
default:
goto out;
}
r = 0;
out:
return r;
}
int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
struct kvm_translation *tr)
{
return -EINVAL; /* not implemented yet */
}
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
return 0;
}
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
vcpu->cpu = cpu;
kvm_set_running_vcpu(vcpu);
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
/*
* The arch-generic KVM code expects the cpu field of a vcpu to be -1
* if the vcpu is no longer assigned to a cpu. This is used for the
* optimized make_all_cpus_request path.
*/
vcpu->cpu = -1;
kvm_set_running_vcpu(NULL);
}
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
return -ENOIOCTLCMD;
}
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
return -ENOIOCTLCMD;
}
int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
memcpy(&(vcpu->arch.regs), regs, sizeof(struct kvm_regs));
return 0;
}
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
memcpy(regs, &(vcpu->arch.regs), sizeof(struct kvm_regs));
return 0;
}
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg)
{
return -ENOIOCTLCMD;
}
void _debug_printk_vcpu(struct kvm_vcpu *vcpu)
{
unsigned long pc = vcpu->arch.regs.pc;
unsigned long offset = vcpu->kvm->arch.host_phys_addr;
unsigned int *pc_phys = __va((pc & 0x7fffffffUL) + offset);
unsigned int insn;
int opc, ra, disp16;
insn = *pc_phys;
opc = (insn >> 26) & 0x3f;
ra = (insn >> 21) & 0x1f;
disp16 = insn & 0xffff;
if (opc == 0x06 && disp16 == 0x1000) /* RD_F */
pr_info("vcpu exit: pc = %#lx (%px), insn[%x] : rd_f r%d [%#lx]\n",
pc, pc_phys, insn, ra, vcpu_get_reg(vcpu, ra));
}
/*
* Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
* proper exit to userspace.
*/
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
{
int ret;
struct kvm_run *run = vcpu->run;
struct vcpucb *vcb = &(vcpu->arch.vcb);
struct hcall_args hargs;
int irq;
bool more;
sigset_t sigsaved;
if (run->immediate_exit)
return -EINTR;
/* Set guest vcb */
/* vpn will update later when vcpu is running */
if (vcpu->arch.vcb.vpcr == 0) {
#ifndef CONFIG_KVM_MEMHOTPLUG
vcpu->arch.vcb.vpcr
= get_vpcr(vcpu->kvm->arch.host_phys_addr, vcpu->kvm->arch.size, 0);
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_NUMA)
if (unlikely(bind_vcpu_enabled)) {
int nid;
unsigned long end;
end = vcpu->kvm->arch.host_phys_addr + vcpu->kvm->arch.size;
nid = pfn_to_nid(PHYS_PFN(vcpu->kvm->arch.host_phys_addr));
if (pfn_to_nid(PHYS_PFN(end)) == nid)
set_cpus_allowed_ptr(vcpu->arch.tsk, node_to_cpumask_map[nid]);
}
#endif
#else /* !CONFIG_KVM_MEMHOTPLUG */
unsigned long seg_base = virt_to_phys(vcpu->kvm->arch.seg_pgd);
vcpu->arch.vcb.vpcr = get_vpcr_memhp(seg_base, 0);
#endif /* CONFIG_KVM_MEMHOTPLUG */
vcpu->arch.vcb.upcr = 0x7;
}
#ifdef CONFIG_PERF_EVENTS
vcpu_load(vcpu);
#endif
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
if (run->exit_reason == KVM_EXIT_MMIO)
kvm_handle_mmio_return(vcpu, run);
run->exit_reason = KVM_EXIT_UNKNOWN;
ret = 1;
while (ret > 0) {
/* Check conditions before entering the guest */
cond_resched();
preempt_disable();
local_irq_disable();
if (signal_pending(current)) {
ret = -EINTR;
run->exit_reason = KVM_EXIT_INTR;
}
if (ret <= 0) {
local_irq_enable();
preempt_enable();
continue;
}
memset(&hargs, 0, sizeof(hargs));
clear_vcpu_irq(vcpu);
irq = interrupt_pending(vcpu, &more);
if (irq < SWVM_IRQS)
try_deliver_interrupt(vcpu, irq, more);
vcpu->arch.halted = 0;
sw64_kvm_switch_vpn(vcpu);
check_vcpu_requests(vcpu);
guest_enter_irqoff();
/* Enter the guest */
trace_kvm_sw64_entry(vcpu->vcpu_id, vcpu->arch.regs.pc);
vcpu->mode = IN_GUEST_MODE;
ret = __sw64_vcpu_run((struct vcpucb *)__phys_addr((unsigned long)vcb), &(vcpu->arch.regs), &hargs);
/* Back from guest */
vcpu->mode = OUTSIDE_GUEST_MODE;
local_irq_enable();
guest_exit_irqoff();
trace_kvm_sw64_exit(ret, vcpu->arch.regs.pc);
preempt_enable();
/* ret = 0 indicate interrupt in guest mode, ret > 0 indicate hcall */
ret = handle_exit(vcpu, run, ret, &hargs);
}
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
#ifdef CONFIG_PERF_EVENTS
vcpu_put(vcpu);
#endif
return ret;
}
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
unsigned long result;
struct kvm_vcpu *vcpu = filp->private_data;
struct vcpucb *kvm_vcb;
switch (ioctl) {
case KVM_SW64_VCPU_INIT:
return kvm_arch_vcpu_reset(vcpu);
case KVM_SW64_GET_VCB:
if (vcpu->arch.vcb.migration_mark) {
result = sw64_io_read(0, LONG_TIME);
vcpu->arch.vcb.guest_longtime = result;
vcpu->arch.vcb.guest_irqs_pending = vcpu->arch.irqs_pending[0];
}
if (copy_to_user((void __user *)arg, &(vcpu->arch.vcb), sizeof(struct vcpucb)))
return -EINVAL;
break;
case KVM_SW64_SET_VCB:
kvm_vcb = memdup_user((void __user *)arg, sizeof(*kvm_vcb));
memcpy(&(vcpu->arch.vcb), kvm_vcb, sizeof(struct vcpucb));
if (vcpu->arch.vcb.migration_mark) {
/* updated vpcr needed by destination vm */
vcpu->arch.vcb.vpcr
= get_vpcr(vcpu->kvm->arch.host_phys_addr, vcpu->kvm->arch.size, 0);
result = sw64_io_read(0, LONG_TIME);
/* synchronize the longtime of source and destination */
vcpu->arch.vcb.guest_longtime_offset = vcpu->arch.vcb.guest_longtime - result;
set_timer(vcpu, 200000000);
vcpu->arch.vcb.migration_mark = 0;
}
break;
default:
return -EINVAL;
}
return 0;
}
long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
struct kvm *kvm __maybe_unused = filp->private_data;
long r;
switch (ioctl) {
case KVM_CREATE_IRQCHIP: {
struct kvm_irq_routing_entry routing;
r = -EINVAL;
memset(&routing, 0, sizeof(routing));
r = kvm_set_irq_routing(kvm, &routing, 0, 0);
break;
}
default:
r = -ENOIOCTLCMD;
}
return r;
}
int kvm_arch_init(void *opaque)
{
kvm_sw64_perf_init();
return 0;
}
void kvm_arch_exit(void)
{
kvm_sw64_perf_teardown();
}
void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
{
}
void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm,
struct kvm_memory_slot *memslot)
{
kvm_flush_remote_tlbs(kvm);
}
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
{
return 0;
}
int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
struct kvm_sregs *sregs)
{
return -ENOIOCTLCMD;
}
int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
struct kvm_sregs *sregs)
{
return -ENOIOCTLCMD;
}
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
{
}
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{
return -ENOIOCTLCMD;
}
int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{
return -ENOIOCTLCMD;
}
vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
{
return VM_FAULT_SIGBUS;
}
int kvm_dev_ioctl_check_extension(long ext)
{
int r;
switch (ext) {
case KVM_CAP_IOEVENTFD:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
case KVM_CAP_MAX_VCPUS:
r = KVM_MAX_VCPUS;
break;
default:
r = 0;
}
return r;
}
#ifdef CONFIG_KVM_MEMHOTPLUG
void vcpu_mem_hotplug(struct kvm_vcpu *vcpu, unsigned long start_addr)
{
struct kvm *kvm = vcpu->kvm;
struct kvm_memory_slot *slot;
unsigned long start_pfn = start_addr >> PAGE_SHIFT;
kvm_for_each_memslot(slot, kvm_memslots(kvm)) {
if (start_pfn == slot->base_gfn) {
unsigned long *seg_pgd;
unsigned long num_of_entry = slot->npages >> 17;
unsigned long base_hpa = slot->arch.host_phys_addr;
int i;
seg_pgd = kvm->arch.seg_pgd + (start_pfn >> 17);
for (i = 0; i < num_of_entry; i++) {
*seg_pgd = (base_hpa >> 30) + i;
seg_pgd++;
}
}
}
}
#endif
void vcpu_send_ipi(struct kvm_vcpu *vcpu, int target_vcpuid)
{
struct kvm_vcpu *target_vcpu = kvm_get_vcpu(vcpu->kvm, target_vcpuid);
if (target_vcpu != NULL)
vcpu_interrupt_line(target_vcpu, 1, 1);
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
bool line_status)
{
u32 irq = irq_level->irq;
unsigned int vcpu_idx, irq_num;
struct kvm_vcpu *vcpu = NULL;
bool level = irq_level->level;
vcpu_idx = irq % atomic_read(&kvm->online_vcpus);
irq_num = irq;
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
if (!vcpu)
return -EINVAL;
return vcpu_interrupt_line(vcpu, irq_num, level);
}
static int __init kvm_sw64_init(void)
{
int i, ret;
ret = vmem_init();
if (ret)
return ret;
for (i = 0; i < NR_CPUS; i++)
sw64_kvm_last_vpn[i] = VPN_FIRST_VERSION;
ret = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
if (ret) {
vmem_exit();
return ret;
}
return 0;
}
static void __exit kvm_sw64_exit(void)
{
kvm_exit();
vmem_exit();
}
module_init(kvm_sw64_init);
module_exit(kvm_sw64_exit);