aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/paravirt.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/kernel/paravirt.c')
-rw-r--r--arch/i386/kernel/paravirt.c154
1 files changed, 128 insertions, 26 deletions
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}