aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <jeremy@goop.org>2007-05-02 13:27:14 -0400
committerAndi Kleen <andi@basil.nowhere.org>2007-05-02 13:27:14 -0400
commit63f70270ccd981ce40a8ff58c03a8c2e97e368be (patch)
tree7ffe766f25007845c99cfc337365c3006a153b83 /arch/i386
parent294688c028e80fd467cdd22da79f62c5f311eaf5 (diff)
[PATCH] i386: PARAVIRT: add common patching machinery
Implement the actual patching machinery. paravirt_patch_default() contains the logic to automatically patch a callsite based on a few simple rules: - if the paravirt_op function is paravirt_nop, then patch nops - if the paravirt_op function is a jmp target, then jmp to it - if the paravirt_op function is callable and doesn't clobber too much for the callsite, call it directly paravirt_patch_default is suitable as a default implementation of paravirt_ops.patch, will remove most of the expensive indirect calls in favour of either a direct call or a pile of nops. Backends may implement their own patcher, however. There are several helper functions to help with this: paravirt_patch_nop nop out a callsite paravirt_patch_ignore leave the callsite as-is paravirt_patch_call patch a call if the caller and callee have compatible clobbers paravirt_patch_jmp patch in a jmp paravirt_patch_insns patch some literal instructions over the callsite, if they fit This patch also implements more direct patches for the native case, so that when running on native hardware many common operations are implemented inline. Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Signed-off-by: Andi Kleen <ak@suse.de> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Zachary Amsden <zach@vmware.com> Cc: Anthony Liguori <anthony@codemonkey.ws> Acked-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/kernel/alternative.c5
-rw-r--r--arch/i386/kernel/paravirt.c154
2 files changed, 132 insertions, 27 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c
index dae3ded9041c..c5d037c60950 100644
--- a/arch/i386/kernel/alternative.c
+++ b/arch/i386/kernel/alternative.c
@@ -336,11 +336,14 @@ void apply_paravirt(struct paravirt_patch_site *start,
336 used = paravirt_ops.patch(p->instrtype, p->clobbers, p->instr, 336 used = paravirt_ops.patch(p->instrtype, p->clobbers, p->instr,
337 p->len); 337 p->len);
338 338
339 BUG_ON(used > p->len);
340
339 /* Pad the rest with nops */ 341 /* Pad the rest with nops */
340 nop_out(p->instr + used, p->len - used); 342 nop_out(p->instr + used, p->len - used);
341 } 343 }
342 344
343 /* Sync to be conservative, in case we patched following instructions */ 345 /* Sync to be conservative, in case we patched following
346 * instructions */
344 sync_core(); 347 sync_core();
345} 348}
346extern struct paravirt_patch_site __start_parainstructions[], 349extern struct paravirt_patch_site __start_parainstructions[],
diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c
index f2982832d3b9..b0ed163e6f70 100644
--- a/arch/i386/kernel/paravirt.c
+++ b/arch/i386/kernel/paravirt.c
@@ -54,40 +54,142 @@ char *memory_setup(void)
54#define DEF_NATIVE(name, code) \ 54#define DEF_NATIVE(name, code) \
55 extern const char start_##name[], end_##name[]; \ 55 extern const char start_##name[], end_##name[]; \
56 asm("start_" #name ": " code "; end_" #name ":") 56 asm("start_" #name ": " code "; end_" #name ":")
57DEF_NATIVE(cli, "cli"); 57
58DEF_NATIVE(sti, "sti"); 58DEF_NATIVE(irq_disable, "cli");
59DEF_NATIVE(popf, "push %eax; popf"); 59DEF_NATIVE(irq_enable, "sti");
60DEF_NATIVE(pushf, "pushf; pop %eax"); 60DEF_NATIVE(restore_fl, "push %eax; popf");
61DEF_NATIVE(save_fl, "pushf; pop %eax");
61DEF_NATIVE(iret, "iret"); 62DEF_NATIVE(iret, "iret");
62DEF_NATIVE(sti_sysexit, "sti; sysexit"); 63DEF_NATIVE(irq_enable_sysexit, "sti; sysexit");
64DEF_NATIVE(read_cr2, "mov %cr2, %eax");
65DEF_NATIVE(write_cr3, "mov %eax, %cr3");
66DEF_NATIVE(read_cr3, "mov %cr3, %eax");
67DEF_NATIVE(clts, "clts");
68DEF_NATIVE(read_tsc, "rdtsc");
63 69
64static const struct native_insns 70DEF_NATIVE(ud2a, "ud2a");
65{
66 const char *start, *end;
67} native_insns[] = {
68 [PARAVIRT_PATCH(irq_disable)] = { start_cli, end_cli },
69 [PARAVIRT_PATCH(irq_enable)] = { start_sti, end_sti },
70 [PARAVIRT_PATCH(restore_fl)] = { start_popf, end_popf },
71 [PARAVIRT_PATCH(save_fl)] = { start_pushf, end_pushf },
72 [PARAVIRT_PATCH(iret)] = { start_iret, end_iret },
73 [PARAVIRT_PATCH(irq_enable_sysexit)] = { start_sti_sysexit, end_sti_sysexit },
74};
75 71
76static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len) 72static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
77{ 73{
78 unsigned int insn_len; 74 const unsigned char *start, *end;
75 unsigned ret;
76
77 switch(type) {
78#define SITE(x) case PARAVIRT_PATCH(x): start = start_##x; end = end_##x; goto patch_site
79 SITE(irq_disable);
80 SITE(irq_enable);
81 SITE(restore_fl);
82 SITE(save_fl);
83 SITE(iret);
84 SITE(irq_enable_sysexit);
85 SITE(read_cr2);
86 SITE(read_cr3);
87 SITE(write_cr3);
88 SITE(clts);
89 SITE(read_tsc);
90#undef SITE
91
92 patch_site:
93 ret = paravirt_patch_insns(insns, len, start, end);
94 break;
95
96 case PARAVIRT_PATCH(make_pgd):
97 case PARAVIRT_PATCH(make_pte):
98 case PARAVIRT_PATCH(pgd_val):
99 case PARAVIRT_PATCH(pte_val):
100#ifdef CONFIG_X86_PAE
101 case PARAVIRT_PATCH(make_pmd):
102 case PARAVIRT_PATCH(pmd_val):
103#endif
104 /* These functions end up returning exactly what
105 they're passed, in the same registers. */
106 ret = paravirt_patch_nop();
107 break;
108
109 default:
110 ret = paravirt_patch_default(type, clobbers, insns, len);
111 break;
112 }
113
114 return ret;
115}
116
117unsigned paravirt_patch_nop(void)
118{
119 return 0;
120}
121
122unsigned paravirt_patch_ignore(unsigned len)
123{
124 return len;
125}
126
127unsigned paravirt_patch_call(void *target, u16 tgt_clobbers,
128 void *site, u16 site_clobbers,
129 unsigned len)
130{
131 unsigned char *call = site;
132 unsigned long delta = (unsigned long)target - (unsigned long)(call+5);
133
134 if (tgt_clobbers & ~site_clobbers)
135 return len; /* target would clobber too much for this site */
136 if (len < 5)
137 return len; /* call too long for patch site */
138
139 *call++ = 0xe8; /* call */
140 *(unsigned long *)call = delta;
141
142 return 5;
143}
144
145unsigned paravirt_patch_jmp(void *target, void *site, unsigned len)
146{
147 unsigned char *jmp = site;
148 unsigned long delta = (unsigned long)target - (unsigned long)(jmp+5);
79 149
80 /* Don't touch it if we don't have a replacement */ 150 if (len < 5)
81 if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start) 151 return len; /* call too long for patch site */
82 return len;
83 152
84 insn_len = native_insns[type].end - native_insns[type].start; 153 *jmp++ = 0xe9; /* jmp */
154 *(unsigned long *)jmp = delta;
155
156 return 5;
157}
158
159unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len)
160{
161 void *opfunc = *((void **)&paravirt_ops + type);
162 unsigned ret;
163
164 if (opfunc == NULL)
165 /* If there's no function, patch it with a ud2a (BUG) */
166 ret = paravirt_patch_insns(site, len, start_ud2a, end_ud2a);
167 else if (opfunc == paravirt_nop)
168 /* If the operation is a nop, then nop the callsite */
169 ret = paravirt_patch_nop();
170 else if (type == PARAVIRT_PATCH(iret) ||
171 type == PARAVIRT_PATCH(irq_enable_sysexit))
172 /* If operation requires a jmp, then jmp */
173 ret = paravirt_patch_jmp(opfunc, site, len);
174 else
175 /* Otherwise call the function; assume target could
176 clobber any caller-save reg */
177 ret = paravirt_patch_call(opfunc, CLBR_ANY,
178 site, clobbers, len);
179
180 return ret;
181}
182
183unsigned paravirt_patch_insns(void *site, unsigned len,
184 const char *start, const char *end)
185{
186 unsigned insn_len = end - start;
85 187
86 /* Similarly if we can't fit replacement. */ 188 if (insn_len > len || start == NULL)
87 if (len < insn_len) 189 insn_len = len;
88 return len; 190 else
191 memcpy(site, start, insn_len);
89 192
90 memcpy(insns, native_insns[type].start, insn_len);
91 return insn_len; 193 return insn_len;
92} 194}
93 195
@@ -110,7 +212,7 @@ static void native_flush_tlb_global(void)
110 __native_flush_tlb_global(); 212 __native_flush_tlb_global();
111} 213}
112 214
113static void native_flush_tlb_single(u32 addr) 215static void native_flush_tlb_single(unsigned long addr)
114{ 216{
115 __native_flush_tlb_single(addr); 217 __native_flush_tlb_single(addr);
116} 218}