|
@@ -54,40 +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(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_PATCH(irq_disable)] = { start_cli, end_cli },
|
|
|
- [PARAVIRT_PATCH(irq_enable)] = { start_sti, end_sti },
|
|
|
- [PARAVIRT_PATCH(restore_fl)] = { start_popf, end_popf },
|
|
|
- [PARAVIRT_PATCH(save_fl)] = { start_pushf, end_pushf },
|
|
|
- [PARAVIRT_PATCH(iret)] = { start_iret, end_iret },
|
|
|
- [PARAVIRT_PATCH(irq_enable_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;
|
|
|
+
|
|
|
+ default:
|
|
|
+ ret = paravirt_patch_default(type, clobbers, insns, len);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
|
|
|
- /* Don't touch it if we don't have a replacement */
|
|
|
- if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start)
|
|
|
- return len;
|
|
|
+ if (len < 5)
|
|
|
+ return len; /* call too long for patch site */
|
|
|
|
|
|
- insn_len = native_insns[type].end - native_insns[type].start;
|
|
|
+ *jmp++ = 0xe9; /* jmp */
|
|
|
+ *(unsigned long *)jmp = delta;
|
|
|
+
|
|
|
+ return 5;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len)
|
|
|
+{
|
|
|
+ void *opfunc = *((void **)¶virt_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;
|
|
|
|
|
|
- /* Similarly if we can't fit replacement. */
|
|
|
- if (len < insn_len)
|
|
|
- return len;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
@@ -110,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);
|
|
|
}
|