Merge branch 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm
[sfrench/cifs-2.6.git] / arch / i386 / kernel / paravirt.c
index 3fdbd1f62379e422c2c4c30494c0396eb3e151bd..5c10f376bce1e36b437c6a76ba28e5a6c4eb0026 100644 (file)
@@ -20,6 +20,7 @@
 #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,140 +55,168 @@ 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;
-
-       /* Don't touch it if we don't have a replacement */
-       if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start)
-               return len;
-
-       insn_len = native_insns[type].end - native_insns[type].start;
+       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;
 
-       /* Similarly if we can't fit replacement. */
-       if (len < insn_len)
-               return len;
+       default:
+               ret = paravirt_patch_default(type, clobbers, insns, len);
+               break;
+       }
 
-       memcpy(insns, native_insns[type].start, insn_len);
-       return insn_len;
+       return ret;
 }
 
-void init_IRQ(void)
+unsigned paravirt_patch_nop(void)
 {
-       paravirt_ops.init_IRQ();
+       return 0;
 }
 
-static void native_flush_tlb(void)
+unsigned paravirt_patch_ignore(unsigned len)
 {
-       __native_flush_tlb();
+       return len;
 }
 
-/*
- * Global pages have to be flushed a bit differently. Not a real
- * performance problem because this does not happen often.
- */
-static void native_flush_tlb_global(void)
+unsigned paravirt_patch_call(void *target, u16 tgt_clobbers,
+                            void *site, u16 site_clobbers,
+                            unsigned len)
 {
-       __native_flush_tlb_global();
-}
+       unsigned char *call = site;
+       unsigned long delta = (unsigned long)target - (unsigned long)(call+5);
 
-static void native_flush_tlb_single(u32 addr)
-{
-       __native_flush_tlb_single(addr);
-}
+       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 */
 
-#ifndef CONFIG_X86_PAE
-static void native_set_pte(pte_t *ptep, pte_t pteval)
-{
-       *ptep = pteval;
-}
+       *call++ = 0xe8;         /* call */
+       *(unsigned long *)call = delta;
 
-static void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pteval)
-{
-       *ptep = pteval;
+       return 5;
 }
 
-static void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
+unsigned paravirt_patch_jmp(void *target, void *site, unsigned len)
 {
-       *pmdp = pmdval;
-}
+       unsigned char *jmp = site;
+       unsigned long delta = (unsigned long)target - (unsigned long)(jmp+5);
 
-#else /* CONFIG_X86_PAE */
+       if (len < 5)
+               return len;     /* call too long for patch site */
 
-static void native_set_pte(pte_t *ptep, pte_t pte)
-{
-       ptep->pte_high = pte.pte_high;
-       smp_wmb();
-       ptep->pte_low = pte.pte_low;
-}
+       *jmp++ = 0xe9;          /* jmp */
+       *(unsigned long *)jmp = delta;
 
-static void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pte)
-{
-       ptep->pte_high = pte.pte_high;
-       smp_wmb();
-       ptep->pte_low = pte.pte_low;
+       return 5;
 }
 
-static void native_set_pte_present(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
+unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len)
 {
-       ptep->pte_low = 0;
-       smp_wmb();
-       ptep->pte_high = pte.pte_high;
-       smp_wmb();
-       ptep->pte_low = pte.pte_low;
+       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;
 }
 
-static void native_set_pte_atomic(pte_t *ptep, pte_t pteval)
+unsigned paravirt_patch_insns(void *site, unsigned len,
+                             const char *start, const char *end)
 {
-       set_64bit((unsigned long long *)ptep,pte_val(pteval));
+       unsigned insn_len = end - start;
+
+       if (insn_len > len || start == NULL)
+               insn_len = len;
+       else
+               memcpy(site, start, insn_len);
+
+       return insn_len;
 }
 
-static void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
+void init_IRQ(void)
 {
-       set_64bit((unsigned long long *)pmdp,pmd_val(pmdval));
+       paravirt_ops.init_IRQ();
 }
 
-static void native_set_pud(pud_t *pudp, pud_t pudval)
+static void native_flush_tlb(void)
 {
-       *pudp = pudval;
+       __native_flush_tlb();
 }
 
-static void native_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
+/*
+ * Global pages have to be flushed a bit differently. Not a real
+ * performance problem because this does not happen often.
+ */
+static void native_flush_tlb_global(void)
 {
-       ptep->pte_low = 0;
-       smp_wmb();
-       ptep->pte_high = 0;
+       __native_flush_tlb_global();
 }
 
-static void native_pmd_clear(pmd_t *pmd)
+static void native_flush_tlb_single(unsigned long addr)
 {
-       u32 *tmp = (u32 *)pmd;
-       *tmp = 0;
-       smp_wmb();
-       *(tmp + 1) = 0;
+       __native_flush_tlb_single(addr);
 }
-#endif /* CONFIG_X86_PAE */
 
 /* These are in entry.S */
 extern void native_iret(void);
@@ -204,6 +233,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,
@@ -262,14 +292,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,
@@ -282,24 +315,34 @@ struct paravirt_ops paravirt_ops = {
        .set_pmd = native_set_pmd,
        .pte_update = paravirt_nop,
        .pte_update_defer = paravirt_nop,
+
+#ifdef CONFIG_HIGHPTE
+       .kmap_atomic_pte = kmap_atomic,
+#endif
+
 #ifdef CONFIG_X86_PAE
        .set_pte_atomic = native_set_pte_atomic,
        .set_pte_present = native_set_pte_present,
        .set_pud = native_set_pud,
        .pte_clear = native_pte_clear,
        .pmd_clear = native_pmd_clear,
+
+       .pmd_val = native_pmd_val,
+       .make_pmd = native_make_pmd,
 #endif
 
+       .pte_val = native_pte_val,
+       .pgd_val = native_pgd_val,
+
+       .make_pte = native_make_pte,
+       .make_pgd = native_make_pgd,
+
        .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);