]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
x86: Switchable guest code access for mmio_parse()
authorValentine Sinitsyn <valentine.sinitsyn@gmail.com>
Sat, 9 Aug 2014 18:21:55 +0000 (00:21 +0600)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 10 Oct 2014 11:47:37 +0000 (13:47 +0200)
mmio_parse() and related infrastructure use link-time resolved (rather
than hardcoded) static function to access guest memory. This way,
vendor-specific code can provide an accelerated implementation if available.

map_code_page() helper routine is now superseded by vcpu_get_inst_bytes() with
different call semantics. The function returns a pointer to the first byte
available and accepts the number of bytes to map (or otherwise make available)
to the hypervisor. It can adjust this value (it is now done to save an
unnecessary page table walk) as described inside the commit.

Signed-off-by: Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/x86/Makefile
hypervisor/arch/x86/include/asm/vcpu.h
hypervisor/arch/x86/mmio.c
hypervisor/arch/x86/vcpu.c [new file with mode: 0644]

index b2e93743b9342440a359b1fe9dbeb3c9c94b54f6..1749fd6951cc6c4403acd35ebfa0e00546bcfac8 100644 (file)
@@ -13,4 +13,4 @@
 always := built-in.o
 
 obj-y := apic.o dbg-write.o entry.o setup.o vmx.o control.o mmio.o \
-        vtd.o paging.o ../../pci.o pci.o ioapic.o i8042.o vmx-vmexit.o
+        vtd.o paging.o ../../pci.o pci.o ioapic.o i8042.o vmx-vmexit.o vcpu.o
index b59195461fcc841a6bb8cfb3180c61c4d1a50288..d816f05808dbac7f1ce3ef9c248f6f55177101f8 100644 (file)
@@ -42,4 +42,19 @@ void vcpu_tlb_flush(void);
 
 void vcpu_entry_failure(struct per_cpu *cpu_data);
 
+/*
+ * vcpu_map_inst() and vcpu_get_inst_bytes() contract:
+ *
+ * On input, *size gives the number of bytes to get.
+ * On output, *size is the number of bytes available.
+ *
+ * If the function fails (returns NULL), *size is undefined.
+ */
+
+const u8 *vcpu_map_inst(const struct guest_paging_structures *pg_structs,
+                       unsigned long pc, unsigned int *size);
+
+const u8 *vcpu_get_inst_bytes(const struct guest_paging_structures *pg_structs,
+                             unsigned long pc, unsigned int *size);
+
 #endif
index 41d55d2830926669180b7d1a4f25b692d88ef3fa..8ffdfbec492947620674670949d408e3cf52673d 100644 (file)
@@ -2,9 +2,11 @@
  * Jailhouse, a Linux-based partitioning hypervisor
  *
  * Copyright (c) Siemens AG, 2013
+ * Copyright (c) Valentine Sinitsyn, 2014
  *
  * Authors:
  *  Jan Kiszka <jan.kiszka@siemens.com>
+ *  Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -13,6 +15,9 @@
 #include <jailhouse/mmio.h>
 #include <jailhouse/paging.h>
 #include <jailhouse/printk.h>
+#include <asm/vcpu.h>
+
+#define X86_MAX_INST_LEN       15
 
 union opcode {
        u8 raw;
@@ -32,33 +37,56 @@ union opcode {
        } __attribute__((packed)) sib;
 };
 
-/* If current_page is non-NULL, pc must have been increased exactly by 1. */
-static u8 *map_code_page(const struct guest_paging_structures *pg_structs,
-                        unsigned long pc, u8 *current_page)
+struct parse_context {
+       unsigned int remaining;
+       unsigned int size;
+       const u8 *inst;
+};
+
+static void ctx_move_next_byte(struct parse_context *ctx)
+{
+       ctx->inst++;
+       ctx->size--;
+}
+
+static bool ctx_maybe_get_bytes(struct parse_context *ctx,
+                               unsigned long *pc,
+                               const struct guest_paging_structures *pg)
+{
+       if (!ctx->size) {
+               ctx->size = ctx->remaining;
+               ctx->inst = vcpu_get_inst_bytes(pg, *pc, &ctx->size);
+               if (!ctx->inst)
+                       return false;
+               ctx->remaining -= ctx->size;
+               *pc += ctx->size;
+       }
+       return true;
+}
+
+static bool ctx_advance(struct parse_context *ctx,
+                       unsigned long *pc,
+                       const struct guest_paging_structures *pg)
 {
-       /* If page offset is 0, previous pc was pointing to a different page,
-        * and we have to map a new one now. */
-       if (current_page && ((pc & ~PAGE_MASK) != 0))
-               return current_page;
-       return paging_get_guest_pages(pg_structs, pc, 1, PAGE_READONLY_FLAGS);
+       ctx_move_next_byte(ctx);
+       return ctx_maybe_get_bytes(ctx, pc, pg);
 }
 
 struct mmio_access mmio_parse(unsigned long pc,
                              const struct guest_paging_structures *pg_structs,
                              bool is_write)
 {
+       struct parse_context ctx = { .remaining = X86_MAX_INST_LEN };
        struct mmio_access access = { .inst_len = 0 };
        union opcode op[3] = { };
        bool has_rex_r = false;
        bool does_write;
-       u8 *page = NULL;
 
 restart:
-       page = map_code_page(pg_structs, pc, page);
-       if (!page)
-               goto error_nopage;
+       if (!ctx_maybe_get_bytes(&ctx, &pc, pg_structs))
+               goto error_noinst;
 
-       op[0].raw = page[pc & PAGE_OFFS_MASK];
+       op[0].raw = *(ctx.inst);
        if (op[0].rex.code == X86_REX_CODE) {
                /* REX.W is simply over-read since it is only affects the
                 * memory address in our supported modes which we get from the
@@ -68,7 +96,7 @@ restart:
                if (op[0].rex.x)
                        goto error_unsupported;
 
-               pc++;
+               ctx_move_next_byte(&ctx);
                access.inst_len++;
                goto restart;
        }
@@ -87,12 +115,10 @@ restart:
                goto error_unsupported;
        }
 
-       pc++;
-       page = map_code_page(pg_structs, pc, page);
-       if (!page)
-               goto error_nopage;
+       if (!ctx_advance(&ctx, &pc, pg_structs))
+               goto error_noinst;
 
-       op[1].raw = page[pc & PAGE_OFFS_MASK];
+       op[1].raw = *(ctx.inst);
        switch (op[1].modrm.mod) {
        case 0:
                if (op[1].modrm.rm == 5) /* 32-bit displacement */
@@ -101,12 +127,10 @@ restart:
                        break;
                access.inst_len++;
 
-               pc++;
-               page = map_code_page(pg_structs, pc, page);
-               if (!page)
-                       goto error_nopage;
+               if (!ctx_advance(&ctx, &pc, pg_structs))
+                       goto error_noinst;
 
-               op[2].raw = page[pc & PAGE_OFFS_MASK];
+               op[2].raw = *(ctx.inst);
                if (op[2].sib.base == 5)
                        access.inst_len += 4;
                break;
@@ -131,8 +155,8 @@ restart:
 
        return access;
 
-error_nopage:
-       panic_printk("FATAL: unable to map MMIO instruction page\n");
+error_noinst:
+       panic_printk("FATAL: unable to get MMIO instruction\n");
        goto error;
 
 error_unsupported:
diff --git a/hypervisor/arch/x86/vcpu.c b/hypervisor/arch/x86/vcpu.c
new file mode 100644 (file)
index 0000000..1ae6a2a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Valentine Sinitsyn, 2014
+ *
+ * Authors:
+ *  Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/paging.h>
+#include <jailhouse/types.h>
+#include <asm/percpu.h>
+#include <asm/vcpu.h>
+
+/* Can be overriden in vendor-specific code if needed */
+const u8 *vcpu_get_inst_bytes(const struct guest_paging_structures *pg_structs,
+                             unsigned long pc, unsigned int *size)
+       __attribute__((weak, alias("vcpu_map_inst")));
+
+const u8 *vcpu_map_inst(const struct guest_paging_structures *pg_structs,
+                       unsigned long pc, unsigned int *size)
+{
+       unsigned short bytes_avail;
+       u8 *page = NULL;
+
+       if (!*size)
+               goto out_err;
+       page = paging_get_guest_pages(pg_structs, pc,
+                       1, PAGE_READONLY_FLAGS);
+       if (!page)
+               goto out_err;
+
+       /* Number of bytes available before page boundary */
+       bytes_avail = PAGE_SIZE - (pc & PAGE_OFFS_MASK);
+       if (*size > bytes_avail)
+               *size = bytes_avail;
+
+       return &page[pc & PAGE_OFFS_MASK];
+
+out_err:
+       return NULL;
+}