Re: [PATCH 04/20] KVM/MIPS32: MIPS arch specific APIs for KVM
On Nov 1, 2012, at 11:18 AM, Avi Kivity wrote: + +/* Set the appropriate status bits based on host CPU features, before we hit the scheduler */ +kvm_mips_set_c0_status(); + +local_irq_enable(); Ah, so you handle exits with interrupts enabled. But that's not how we usually do it; the standard pattern is while (can continue) disable interrupts enter guest enable interrupts process exit A bit more detail here. KVM/MIPS has its own set of exception handlers which are separate from the host kernel's handlers. We switch between the 2 sets of handlers by setting the Exception Base Register (EBASE). We enable host interrupts just before we switch to guest context so that we trap when the host gets a timer or I/O interrupt. When an exception does occur in guest context, the KVM/MIPS handlers will save the guest context, and switch back to the default host kernel exception handlers. We enter the C handler (kvm_mips_handle_exit()) with interrupts disabled, and explicitly enable them there. This allows the host kernel to handle any pending interrupts. The sequence is as follows while (can continue) disable interrupts trampoline code to save host kernel context, load guest context enable host interrupts enter guest context KVM/MIPS trap handler (called with interrupts disabled, per MIPS architecture) Restore host Linux context, setup stack to handle exception Jump to C handler Enable interrupts before handling VM exit. Regards Sanjay -- To unsubscribe from this list: send the line unsubscribe kvm in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/20] KVM/MIPS32: MIPS arch specific APIs for KVM
On 10/31/2012 05:18 PM, Sanjay Lal wrote: - Implements the arch specific APIs for KVM, some are stubs for MIPS - kvm_mips_handle_exit(): Main 'C' distpatch routine for handling exceptions while in Guest mode. - Also implements in-kernel timer interrupt support for the guest. + +static void +kvm_mips_init_tlbs (void *arg) +{ +ulong flags, wired; +struct kvm *kvm = (struct kvm *) arg; + +ENTER_CRITICAL(flags); +/* Add a wired entry to the TLB, it is used to map the commpage to the Guest kernel */ +wired = read_c0_wired(); +write_c0_wired(wired + 1); +mtc0_tlbw_hazard(); +kvm-arch.commpage_tlb = wired; +EXIT_CRITICAL(flags); + Since this is called from smp_call_function(), it is called with interrupts disabled. +kvm_debug([%d] commpage TLB: %d\n, smp_processor_id(), kvm-arch.commpage_tlb); +} + +int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) +{ + int cpu; + +if (atomic_inc_return(kvm_mips_instance) == 1) { +kvm_info(%s: 1st KVM instance, setup host TLB parameters\n, __func__); + for_each_online_cpu(cpu) { + smp_call_function_single(cpu, kvm_mips_init_tlbs, kvm, 1); +} Use on_each_cpu() instead. +} + +kvm-arch.gfn_to_pfn = gfn_to_pfn; +kvm-arch.release_pfn_clean = kvm_release_pfn_clean; +kvm-arch.is_error_pfn = is_error_pfn; + + return 0; +} + + + +void kvm_arch_flush_shadow_all(struct kvm *kvm) +{ +} + +void kvm_arch_flush_shadow_memslot(struct kvm *kvm, +struct kvm_memory_slot *slot) +{ +} + + Don't you need to flush the shadow tlbs here? + +ulong kvm_mips_get_ramsize (struct kvm *kvm) +{ +return (kvm-memslots-memslots[0].npages PAGE_SHIFT); +} What is this? Slot 0 is not special on other archs. + +void +kvm_arch_flush_shadow(struct kvm *kvm) +{ +} Flush here too? + +int +kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ +int r = 0; +sigset_t sigsaved; + +if (vcpu-sigset_active) +sigprocmask(SIG_SETMASK, vcpu-sigset, sigsaved); + +if (vcpu-mmio_needed) { +if (!vcpu-mmio_is_write) +kvm_mips_complete_mmio_load(vcpu, run); +vcpu-mmio_needed = 0; +} + +/* Check if we have any exceptions/interrupts pending */ +kvm_mips_deliver_interrupts(vcpu, kvm_read_c0_guest_cause(vcpu-arch.cop0)); + +local_irq_disable(); +kvm_guest_enter(); + +r = __kvm_mips_vcpu_run(run, vcpu); So you handle all exits with interrupts disabled? What about TLB misses that need to swap in a page? What about host interrupts? + +kvm_guest_exit(); +local_irq_enable(); + +if (vcpu-sigset_active) +sigprocmask(SIG_SETMASK, sigsaved, NULL); + +return r; +} + +int + +/* + * Return value is in the form (errcode2 | RESUME_FLAG_HOST | RESUME_FLAG_NV) + */ +int +kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) +{ +uint32_t cause = vcpu-arch.host_cp0_cause; +uint32_t exccode = (cause CAUSEB_EXCCODE) 0x1f; +uint32_t __user *opc = (uint32_t __user *) vcpu-arch.pc; +ulong badvaddr = vcpu-arch.host_cp0_badvaddr; +enum emulation_result er = EMULATE_DONE; +int ret = RESUME_GUEST; + +/* Set a default exit reason */ +run-exit_reason = KVM_EXIT_UNKNOWN; +run-ready_for_interrupt_injection = 1; ready_for_interrupt_injection is an x86ism, you probably don't need it. + +/* Set the appropriate status bits based on host CPU features, before we hit the scheduler */ +kvm_mips_set_c0_status(); + +local_irq_enable(); Ah, so you handle exits with interrupts enabled. But that's not how we usually do it; the standard pattern is while (can continue) disable interrupts enter guest enable interrupts process exit + +kvm_debug(kvm_mips_handle_exit: cause: %#x, PC: %p, kvm_run: %p, kvm_vcpu: %p\n, +cause, opc, run, vcpu); + +/* Do a privilege check, if in UM most of these exit conditions end up + * causing an exception to be delivered to the Guest Kernel + */ +er = kvm_mips_check_privilege (cause, opc, run, vcpu); +if (er == EMULATE_PRIV_FAIL) { +goto skip_emul; +} +else if (er == EMULATE_FAIL) { +run-exit_reason = KVM_EXIT_INTERNAL_ERROR; +ret = RESUME_HOST; +goto skip_emul; +} + +switch (exccode) { +case T_INT: +kvm_debug([%d]T_INT @ %p\n, vcpu-vcpu_id, opc); + +kvm_mips_account_exit(vcpu, INT_EXITS); + +if (need_resched()) { +cond_resched(); +} + +ret = RESUME_GUEST; +break; + +case T_COP_UNUSABLE: +kvm_debug(T_COP_UNUSABLE: @ PC: %p\n, opc); + +kvm_mips_account_exit(vcpu, COP_UNUSABLE_EXITS); +ret = kvm_mips_callbacks-handle_cop_unusable(vcpu); +/*
[PATCH 04/20] KVM/MIPS32: MIPS arch specific APIs for KVM
- Implements the arch specific APIs for KVM, some are stubs for MIPS - kvm_mips_handle_exit(): Main 'C' distpatch routine for handling exceptions while in Guest mode. - Also implements in-kernel timer interrupt support for the guest. Signed-off-by: Sanjay Lal sanj...@kymasys.com --- arch/mips/kvm/kvm_mips.c | 1016 ++ 1 file changed, 1016 insertions(+) create mode 100644 arch/mips/kvm/kvm_mips.c diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c new file mode 100644 index 000..eb84568 --- /dev/null +++ b/arch/mips/kvm/kvm_mips.c @@ -0,0 +1,1016 @@ +/* +* This file is subject to the terms and conditions of the GNU General Public +* License. See the file COPYING in the main directory of this archive +* for more details. +* +* KVM/MIPS: MIPS specific KVM APIs +* +* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. +* Authors: Sanjay Lal sanj...@kymasys.com +*/ + +#include linux/errno.h +#include linux/err.h +#include linux/module.h +#include linux/vmalloc.h +#include linux/fs.h +#include linux/bootmem.h +#include asm/page.h +#include asm/cacheflush.h +#include asm/mmu_context.h + +#include linux/kvm_host.h + +#include kvm_mips_int.h +#include kvm_mips_stats.h +#include kvm_mips_comm.h + +#ifndef VECTORSPACING +#define VECTORSPACING 0x100 /* for EI/VI mode */ +#endif + +struct kvm_stats_debugfs_item debugfs_entries[] = { +{NULL} +}; + +static int +kvm_mips_reset_vcpu (struct kvm_vcpu *vcpu) +{ +int i; +for (i = 0; i NR_CPUS; i++) { +vcpu-arch.guest_kernel_asid[i] = 0; +vcpu-arch.guest_user_asid[i] = 0; +} +return 0; +} + +gfn_t +unalias_gfn(struct kvm *kvm, gfn_t gfn) +{ +return gfn; +} + +/* XXXKYMA: We are simulatoring a processor that has the WII bit set in Config7, so we + * are runnable if interrupts are pending + */ +int +kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) +{ +return !!(vcpu-arch.pending_exceptions); +} + +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return 1; +} + +int +kvm_arch_hardware_enable(void *garbage) +{ +return 0; +} + +void +kvm_arch_hardware_disable(void *garbage) +{ +} + +int +kvm_arch_hardware_setup(void) +{ +return 0; +} + +void +kvm_arch_hardware_unsetup(void) +{ +} + +void +kvm_arch_check_processor_compat(void *rtn) +{ +int *r = (int *) rtn; +*r = 0; +return; +} + +static void +kvm_mips_init_tlbs (void *arg) +{ +ulong flags, wired; +struct kvm *kvm = (struct kvm *) arg; + +ENTER_CRITICAL(flags); +/* Add a wired entry to the TLB, it is used to map the commpage to the Guest kernel */ +wired = read_c0_wired(); +write_c0_wired(wired + 1); +mtc0_tlbw_hazard(); +kvm-arch.commpage_tlb = wired; +EXIT_CRITICAL(flags); + +kvm_debug([%d] commpage TLB: %d\n, smp_processor_id(), kvm-arch.commpage_tlb); +} + +int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) +{ + int cpu; + +if (atomic_inc_return(kvm_mips_instance) == 1) { +kvm_info(%s: 1st KVM instance, setup host TLB parameters\n, __func__); + for_each_online_cpu(cpu) { + smp_call_function_single(cpu, kvm_mips_init_tlbs, kvm, 1); +} +} + +kvm-arch.gfn_to_pfn = gfn_to_pfn; +kvm-arch.release_pfn_clean = kvm_release_pfn_clean; +kvm-arch.is_error_pfn = is_error_pfn; + + return 0; +} + + +void +kvm_mips_free_vcpus(struct kvm *kvm) +{ +unsigned int i; +struct kvm_vcpu *vcpu; + +/* Put the pages we reserved for the guest pmap */ +for (i = 0; i kvm-arch.guest_pmap_npages; i++) { +if (kvm-arch.guest_pmap[i] != KVM_INVALID_PAGE) +kvm-arch.release_pfn_clean(kvm-arch.guest_pmap[i]); +} + +if (kvm-arch.guest_pmap) +kfree(kvm-arch.guest_pmap); + +kvm_for_each_vcpu(i, vcpu, kvm) { +kvm_arch_vcpu_free(vcpu); +} + +mutex_lock(kvm-lock); + +for (i = 0; i atomic_read(kvm-online_vcpus); i++) +kvm-vcpus[i] = NULL; + +atomic_set(kvm-online_vcpus, 0); + +mutex_unlock(kvm-lock); +} + +void +kvm_arch_sync_events(struct kvm *kvm) +{ +} + +static void +kvm_mips_uninit_tlbs (void *arg) +{ +/* Restore wired count */ +write_c0_wired(0); +mtc0_tlbw_hazard(); +/* Clear out all the TLBs */ +kvm_local_flush_tlb_all(); +} + + +void +kvm_arch_destroy_vm(struct kvm *kvm) +{ +int cpu; +kvm_mips_free_vcpus(kvm); + +/* If this is the last instance, restore wired count */ +if (atomic_dec_return(kvm_mips_instance) == 0) { +kvm_info(%s: last KVM instance, restoring TLB parameters\n, __func__); + for_each_online_cpu(cpu) { +smp_call_function_single(cpu, kvm_mips_uninit_tlbs, NULL, 1); +} +} +} + +long +kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) +{ +return -EINVAL; +} + +int +kvm_arch_set_memory_region(struct kvm *kvm, + struct