* Copyright (C) 2018 - os kernal
* Author: fire3 <fire3@example.com> yangzh <yangzh@gmail.com>
* linhn <linhn@example.com>
*/
#include <linux/debugfs.h>
#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 <asm/debug.h>
#include <asm/kvm_timer.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
#include <asm/barrier.h>
#include <asm/pci_impl.h>
#include "trace.h"
#include "vmem.c"
#define OFFSET_LONG_TIME 0x180UL
__read_mostly bool bind_vcpu_enabled;
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_NUMA)
static int __init bind_vcpu_init(void)
{
if (!sw64_debugfs_dir)
return -ENODEV;
debugfs_create_bool("bind_vcpu", 0644,
sw64_debugfs_dir, &bind_vcpu_enabled);
return 0;
}
static void bind_vcpu_exit(void)
{
bind_vcpu_enabled = false;
}
#else
static int __init bind_vcpu_init(void)
{
return 0;
}
static void bind_vcpu_exit(void) { }
#endif
static unsigned long longtime_offset;
static unsigned long get_vpcr(struct kvm_vcpu *vcpu, u64 vpn)
{
unsigned long base, size;
base = vcpu->kvm->arch.host_phys_addr;
size = vcpu->kvm->arch.size;
return (base >> 23) | ((size >> 23) << 16) | ((vpn & VPN_MASK) << 44);
}
void vcpu_set_numa_affinity(struct kvm_vcpu *vcpu)
{
if (vcpu->arch.vcb.vpcr == 0) {
vcpu->arch.vcb.vpcr = get_vpcr(vcpu, 0);
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, cpumask_of_node(nid));
}
vcpu->arch.vcb.upcr = 0x7;
}
}
void kvm_flush_tlb_all(void)
{
tbia();
}
void kvm_sw64_update_vpn(struct kvm_vcpu *vcpu, unsigned long vpn)
{
vcpu->arch.vcb.vpcr = ((vcpu->arch.vcb.vpcr) & (~(VPN_MASK << 44))) | (vpn << 44);
vcpu->arch.vcb.dtb_vpcr = ((vcpu->arch.vcb.dtb_vpcr) & (~(VPN_MASK << VPN_SHIFT))) | (vpn << VPN_SHIFT);
}
int kvm_sw64_init_vm(struct kvm *kvm)
{
return 0;
}
void kvm_sw64_destroy_vm(struct kvm *kvm)
{
kvm_destroy_vcpus(kvm);
}
int kvm_arch_prepare_memory_region(struct kvm *kvm,
const struct kvm_memory_slot *old,
struct kvm_memory_slot *new,
enum kvm_mr_change change)
{
unsigned long addr;
struct file *vm_file;
struct vm_area_struct *vma;
struct vmem_info *info;
struct kvm_userspace_memory_region new_mem;
struct kvm_userspace_memory_region *mem = &new_mem;
unsigned long ret;
size_t size;
if (change == KVM_MR_FLAGS_ONLY || change == KVM_MR_DELETE)
return 0;
mem->guest_phys_addr = ((new->base_gfn) << PAGE_SHIFT);
mem->memory_size = ((new->npages) << PAGE_SHIFT);
mem->userspace_addr = new->userspace_addr;
if (test_bit(IO_MARK_BIT, (unsigned long *)(&(mem->guest_phys_addr))))
return 0;
if (test_bit(IO_MARK_BIT + 1, (unsigned long *)(&(mem->guest_phys_addr))))
return 0;
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;
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);
kvm->arch.host_phys_addr = (u64)addr;
kvm->arch.size = round_up(mem->memory_size, 8<<20);
memset(__va(addr), 0, 0x2000000);
return 0;
}
* 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;
unsigned long 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,
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 tracked while the
* memory slot is write protected.
*/
if ((change == KVM_MR_FLAGS_ONLY) && (old->flags & KVM_MEM_LOG_DIRTY_PAGES)
&& (!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))) {
kvm_mark_migration(kvm, 0);
return;
}
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_sw64_vcpu_reset(struct kvm_vcpu *vcpu)
{
unsigned long addr = vcpu->kvm->arch.host_phys_addr;
hrtimer_cancel(&vcpu->arch.hrt);
vcpu->arch.vcb.soft_cid = vcpu->vcpu_id;
vcpu->arch.vcb.vcpu_irq_disabled = 1;
vcpu->arch.pcpu_id = -1;
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;
}
long kvm_sw64_get_vcb(struct file *filp, unsigned long arg)
{
void __iomem *intpu_base = misc_platform_get_intpu_base(0);
struct kvm_vcpu *vcpu = filp->private_data;
if (vcpu->arch.vcb.migration_mark) {
unsigned long result = readq(intpu_base + OFFSET_LONG_TIME)
+ vcpu->arch.vcb.guest_longtime_offset;
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;
return 0;
}
long kvm_sw64_set_vcb(struct file *filp, unsigned long arg)
{
unsigned long result;
struct kvm_vcpu *vcpu = filp->private_data;
struct vcpucb *kvm_vcb;
void __iomem *intpu_base = misc_platform_get_intpu_base(0);
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) {
vcpu->arch.vcb.vpcr = get_vpcr(vcpu, 0);
if (vcpu->arch.vcb.soft_cid == 0) {
result = readq(intpu_base + OFFSET_LONG_TIME);
vcpu->arch.vcb.guest_longtime_offset = vcpu->arch.vcb.guest_longtime - result;
longtime_offset = vcpu->arch.vcb.guest_longtime_offset;
} else
vcpu->arch.vcb.guest_longtime_offset = longtime_offset;
set_timer(vcpu, 200000000);
vcpu->arch.vcb.migration_mark = 0;
}
return 0;
}
void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu)
{
}
void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn_offset,
unsigned long mask)
{
}
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
struct kvm_memory_slot *slot)
{
}
void kvm_arch_flush_shadow_all(struct kvm *kvm)
{
}
void update_aptp(unsigned long pgd)
{
}
static int __init kvm_core3_init(void)
{
int i, ret;
bind_vcpu_init();
ret = vmem_init();
if (unlikely(ret))
goto out;
for (i = 0; i < NR_CPUS; i++)
last_vpn(i) = VPN_FIRST_VERSION;
ret = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
if (likely(!ret))
return 0;
vmem_exit();
out:
bind_vcpu_exit();
return ret;
}
static void __exit kvm_core3_exit(void)
{
kvm_exit();
vmem_exit();
bind_vcpu_exit();
}
module_init(kvm_core3_init);
module_exit(kvm_core3_exit);