]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/i386/kernel/paravirt.c
signal/timer/event: signalfd wire up x86 arches
[linux-2.6-omap-h63xx.git] / arch / i386 / kernel / paravirt.c
index cba7a15ce1b00016f702af326809b77dc0c0c5c4..faab09abca5e33a3bb1cff6b50848a788bc20349 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/module.h>
 #include <linux/efi.h>
 #include <linux/bcd.h>
-#include <linux/start_kernel.h>
+#include <linux/highmem.h>
 
 #include <asm/bug.h>
 #include <asm/paravirt.h>
@@ -54,42 +54,142 @@ char *memory_setup(void)
 #define DEF_NATIVE(name, code)                                 \
        extern const char start_##name[], end_##name[];         \
        asm("start_" #name ": " code "; end_" #name ":")
-DEF_NATIVE(cli, "cli");
-DEF_NATIVE(sti, "sti");
-DEF_NATIVE(popf, "push %eax; popf");
-DEF_NATIVE(pushf, "pushf; pop %eax");
-DEF_NATIVE(pushf_cli, "pushf; pop %eax; cli");
+
+DEF_NATIVE(irq_disable, "cli");
+DEF_NATIVE(irq_enable, "sti");
+DEF_NATIVE(restore_fl, "push %eax; popf");
+DEF_NATIVE(save_fl, "pushf; pop %eax");
 DEF_NATIVE(iret, "iret");
-DEF_NATIVE(sti_sysexit, "sti; sysexit");
+DEF_NATIVE(irq_enable_sysexit, "sti; sysexit");
+DEF_NATIVE(read_cr2, "mov %cr2, %eax");
+DEF_NATIVE(write_cr3, "mov %eax, %cr3");
+DEF_NATIVE(read_cr3, "mov %cr3, %eax");
+DEF_NATIVE(clts, "clts");
+DEF_NATIVE(read_tsc, "rdtsc");
 
-static const struct native_insns
-{
-       const char *start, *end;
-} native_insns[] = {
-       [PARAVIRT_IRQ_DISABLE] = { start_cli, end_cli },
-       [PARAVIRT_IRQ_ENABLE] = { start_sti, end_sti },
-       [PARAVIRT_RESTORE_FLAGS] = { start_popf, end_popf },
-       [PARAVIRT_SAVE_FLAGS] = { start_pushf, end_pushf },
-       [PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushf_cli, end_pushf_cli },
-       [PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret },
-       [PARAVIRT_STI_SYSEXIT] = { start_sti_sysexit, end_sti_sysexit },
-};
+DEF_NATIVE(ud2a, "ud2a");
 
 static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
 {
-       unsigned int insn_len;
+       const unsigned char *start, *end;
+       unsigned ret;
+
+       switch(type) {
+#define SITE(x)        case PARAVIRT_PATCH(x): start = start_##x; end = end_##x; goto patch_site
+               SITE(irq_disable);
+               SITE(irq_enable);
+               SITE(restore_fl);
+               SITE(save_fl);
+               SITE(iret);
+               SITE(irq_enable_sysexit);
+               SITE(read_cr2);
+               SITE(read_cr3);
+               SITE(write_cr3);
+               SITE(clts);
+               SITE(read_tsc);
+#undef SITE
+
+       patch_site:
+               ret = paravirt_patch_insns(insns, len, start, end);
+               break;
+
+       case PARAVIRT_PATCH(make_pgd):
+       case PARAVIRT_PATCH(make_pte):
+       case PARAVIRT_PATCH(pgd_val):
+       case PARAVIRT_PATCH(pte_val):
+#ifdef CONFIG_X86_PAE
+       case PARAVIRT_PATCH(make_pmd):
+       case PARAVIRT_PATCH(pmd_val):
+#endif
+               /* These functions end up returning exactly what
+                  they're passed, in the same registers. */
+               ret = paravirt_patch_nop();
+               break;
 
-       /* Don't touch it if we don't have a replacement */
-       if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start)
-               return len;
+       default:
+               ret = paravirt_patch_default(type, clobbers, insns, len);
+               break;
+       }
 
-       insn_len = native_insns[type].end - native_insns[type].start;
+       return ret;
+}
 
-       /* Similarly if we can't fit replacement. */
-       if (len < insn_len)
-               return len;
+unsigned paravirt_patch_nop(void)
+{
+       return 0;
+}
+
+unsigned paravirt_patch_ignore(unsigned len)
+{
+       return len;
+}
+
+unsigned paravirt_patch_call(void *target, u16 tgt_clobbers,
+                            void *site, u16 site_clobbers,
+                            unsigned len)
+{
+       unsigned char *call = site;
+       unsigned long delta = (unsigned long)target - (unsigned long)(call+5);
+
+       if (tgt_clobbers & ~site_clobbers)
+               return len;     /* target would clobber too much for this site */
+       if (len < 5)
+               return len;     /* call too long for patch site */
+
+       *call++ = 0xe8;         /* call */
+       *(unsigned long *)call = delta;
+
+       return 5;
+}
+
+unsigned paravirt_patch_jmp(void *target, void *site, unsigned len)
+{
+       unsigned char *jmp = site;
+       unsigned long delta = (unsigned long)target - (unsigned long)(jmp+5);
+
+       if (len < 5)
+               return len;     /* call too long for patch site */
+
+       *jmp++ = 0xe9;          /* jmp */
+       *(unsigned long *)jmp = delta;
+
+       return 5;
+}
+
+unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len)
+{
+       void *opfunc = *((void **)&paravirt_ops + type);
+       unsigned ret;
+
+       if (opfunc == NULL)
+               /* If there's no function, patch it with a ud2a (BUG) */
+               ret = paravirt_patch_insns(site, len, start_ud2a, end_ud2a);
+       else if (opfunc == paravirt_nop)
+               /* If the operation is a nop, then nop the callsite */
+               ret = paravirt_patch_nop();
+       else if (type == PARAVIRT_PATCH(iret) ||
+                type == PARAVIRT_PATCH(irq_enable_sysexit))
+               /* If operation requires a jmp, then jmp */
+               ret = paravirt_patch_jmp(opfunc, site, len);
+       else
+               /* Otherwise call the function; assume target could
+                  clobber any caller-save reg */
+               ret = paravirt_patch_call(opfunc, CLBR_ANY,
+                                         site, clobbers, len);
+
+       return ret;
+}
+
+unsigned paravirt_patch_insns(void *site, unsigned len,
+                             const char *start, const char *end)
+{
+       unsigned insn_len = end - start;
+
+       if (insn_len > len || start == NULL)
+               insn_len = len;
+       else
+               memcpy(site, start, insn_len);
 
-       memcpy(insns, native_insns[type].start, insn_len);
        return insn_len;
 }
 
@@ -112,7 +212,7 @@ static void native_flush_tlb_global(void)
        __native_flush_tlb_global();
 }
 
-static void native_flush_tlb_single(u32 addr)
+static void native_flush_tlb_single(unsigned long addr)
 {
        __native_flush_tlb_single(addr);
 }
@@ -132,6 +232,7 @@ struct paravirt_ops paravirt_ops = {
        .name = "bare hardware",
        .paravirt_enabled = 0,
        .kernel_rpl = 0,
+       .shared_kernel_pmd = 1, /* Only used when CONFIG_X86_PAE is set */
 
        .patch = native_patch,
        .banner = default_banner,
@@ -190,14 +291,17 @@ struct paravirt_ops paravirt_ops = {
        .apic_read = native_apic_read,
        .setup_boot_clock = setup_boot_APIC_clock,
        .setup_secondary_clock = setup_secondary_APIC_clock,
+       .startup_ipi_hook = paravirt_nop,
 #endif
        .set_lazy_mode = paravirt_nop,
 
+       .pagetable_setup_start = native_pagetable_setup_start,
+       .pagetable_setup_done = native_pagetable_setup_done,
+
        .flush_tlb_user = native_flush_tlb,
        .flush_tlb_kernel = native_flush_tlb_global,
        .flush_tlb_single = native_flush_tlb_single,
-
-       .map_pt_hook = paravirt_nop,
+       .flush_tlb_others = native_flush_tlb_others,
 
        .alloc_pt = paravirt_nop,
        .alloc_pd = paravirt_nop,
@@ -211,7 +315,9 @@ struct paravirt_ops paravirt_ops = {
        .pte_update = paravirt_nop,
        .pte_update_defer = paravirt_nop,
 
-       .ptep_get_and_clear = native_ptep_get_and_clear,
+#ifdef CONFIG_HIGHPTE
+       .kmap_atomic_pte = kmap_atomic,
+#endif
 
 #ifdef CONFIG_X86_PAE
        .set_pte_atomic = native_set_pte_atomic,
@@ -233,13 +339,9 @@ struct paravirt_ops paravirt_ops = {
        .irq_enable_sysexit = native_irq_enable_sysexit,
        .iret = native_iret,
 
-       .startup_ipi_hook = paravirt_nop,
+       .dup_mmap = paravirt_nop,
+       .exit_mmap = paravirt_nop,
+       .activate_mm = paravirt_nop,
 };
 
-/*
- * NOTE: CONFIG_PARAVIRT is experimental and the paravirt_ops
- * semantics are subject to change. Hence we only do this
- * internal-only export of this, until it gets sorted out and
- * all lowlevel CPU ops used by modules are separately exported.
- */
-EXPORT_SYMBOL_GPL(paravirt_ops);
+EXPORT_SYMBOL(paravirt_ops);