Re: [PATCH 04/20] KVM/MIPS32: MIPS arch specific APIs for KVM

2012-11-14 Thread Sanjay Lal

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

2012-11-01 Thread Avi Kivity
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

2012-10-31 Thread Sanjay Lal
- 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