diff options
author | Isaku Yamahata <yamahata@valinux.co.jp> | 2009-03-04 07:06:51 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2009-03-26 14:02:31 -0400 |
commit | bf7ab02f620c1020c869fc71a2c855918b6a5375 (patch) | |
tree | 9b6ac860fc762869069d9605f5c52c0199e642e5 | |
parent | f8de2ec678fa09276cf7ad02838eb80e86c73097 (diff) |
ia64/pv_op/binarypatch: add helper functions to support binary patching for paravirt_ops.
add helper functions to support binary patching for paravirt_ops.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r-- | arch/ia64/include/asm/paravirt_patch.h | 143 | ||||
-rw-r--r-- | arch/ia64/kernel/paravirt_patch.c | 514 | ||||
-rw-r--r-- | arch/ia64/kernel/paravirtentry.S | 56 | ||||
-rw-r--r-- | arch/ia64/kernel/vmlinux.lds.S | 24 |
4 files changed, 737 insertions, 0 deletions
diff --git a/arch/ia64/include/asm/paravirt_patch.h b/arch/ia64/include/asm/paravirt_patch.h new file mode 100644 index 000000000000..128ff5db6e67 --- /dev/null +++ b/arch/ia64/include/asm/paravirt_patch.h | |||
@@ -0,0 +1,143 @@ | |||
1 | /****************************************************************************** | ||
2 | * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp> | ||
3 | * VA Linux Systems Japan K.K. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #ifndef __ASM_PARAVIRT_PATCH_H | ||
22 | #define __ASM_PARAVIRT_PATCH_H | ||
23 | |||
24 | #ifdef __ASSEMBLY__ | ||
25 | |||
26 | .section .paravirt_branches, "a" | ||
27 | .previous | ||
28 | #define PARAVIRT_PATCH_SITE_BR(type) \ | ||
29 | { \ | ||
30 | [1:] ; \ | ||
31 | br.cond.sptk.many 2f ; \ | ||
32 | nop.b 0 ; \ | ||
33 | nop.b 0;; ; \ | ||
34 | } ; \ | ||
35 | 2: \ | ||
36 | .xdata8 ".paravirt_branches", 1b, type | ||
37 | |||
38 | #else | ||
39 | |||
40 | #include <linux/stringify.h> | ||
41 | #include <asm/intrinsics.h> | ||
42 | |||
43 | /* for binary patch */ | ||
44 | struct paravirt_patch_site_bundle { | ||
45 | void *sbundle; | ||
46 | void *ebundle; | ||
47 | unsigned long type; | ||
48 | }; | ||
49 | |||
50 | /* label means the beginning of new bundle */ | ||
51 | #define paravirt_alt_bundle(instr, privop) \ | ||
52 | "\t998:\n" \ | ||
53 | "\t" instr "\n" \ | ||
54 | "\t999:\n" \ | ||
55 | "\t.pushsection .paravirt_bundles, \"a\"\n" \ | ||
56 | "\t.popsection\n" \ | ||
57 | "\t.xdata8 \".paravirt_bundles\", 998b, 999b, " \ | ||
58 | __stringify(privop) "\n" | ||
59 | |||
60 | |||
61 | struct paravirt_patch_bundle_elem { | ||
62 | const void *sbundle; | ||
63 | const void *ebundle; | ||
64 | unsigned long type; | ||
65 | }; | ||
66 | |||
67 | |||
68 | struct paravirt_patch_site_inst { | ||
69 | unsigned long stag; | ||
70 | unsigned long etag; | ||
71 | unsigned long type; | ||
72 | }; | ||
73 | |||
74 | #define paravirt_alt_inst(instr, privop) \ | ||
75 | "\t[998:]\n" \ | ||
76 | "\t" instr "\n" \ | ||
77 | "\t[999:]\n" \ | ||
78 | "\t.pushsection .paravirt_insts, \"a\"\n" \ | ||
79 | "\t.popsection\n" \ | ||
80 | "\t.xdata8 \".paravirt_insts\", 998b, 999b, " \ | ||
81 | __stringify(privop) "\n" | ||
82 | |||
83 | struct paravirt_patch_site_branch { | ||
84 | unsigned long tag; | ||
85 | unsigned long type; | ||
86 | }; | ||
87 | |||
88 | struct paravirt_patch_branch_target { | ||
89 | const void *entry; | ||
90 | unsigned long type; | ||
91 | }; | ||
92 | |||
93 | void | ||
94 | __paravirt_patch_apply_branch( | ||
95 | unsigned long tag, unsigned long type, | ||
96 | const struct paravirt_patch_branch_target *entries, | ||
97 | unsigned int nr_entries); | ||
98 | |||
99 | void | ||
100 | paravirt_patch_reloc_br(unsigned long tag, const void *target); | ||
101 | |||
102 | void | ||
103 | paravirt_patch_reloc_brl(unsigned long tag, const void *target); | ||
104 | |||
105 | |||
106 | #if defined(ASM_SUPPORTED) && defined(CONFIG_PARAVIRT) | ||
107 | unsigned long | ||
108 | ia64_native_patch_bundle(void *sbundle, void *ebundle, unsigned long type); | ||
109 | |||
110 | unsigned long | ||
111 | __paravirt_patch_apply_bundle(void *sbundle, void *ebundle, unsigned long type, | ||
112 | const struct paravirt_patch_bundle_elem *elems, | ||
113 | unsigned long nelems, | ||
114 | const struct paravirt_patch_bundle_elem **found); | ||
115 | |||
116 | void | ||
117 | paravirt_patch_apply_bundle(const struct paravirt_patch_site_bundle *start, | ||
118 | const struct paravirt_patch_site_bundle *end); | ||
119 | |||
120 | void | ||
121 | paravirt_patch_apply_inst(const struct paravirt_patch_site_inst *start, | ||
122 | const struct paravirt_patch_site_inst *end); | ||
123 | |||
124 | void paravirt_patch_apply(void); | ||
125 | #else | ||
126 | #define paravirt_patch_apply_bundle(start, end) do { } while (0) | ||
127 | #define paravirt_patch_apply_inst(start, end) do { } while (0) | ||
128 | #define paravirt_patch_apply() do { } while (0) | ||
129 | #endif | ||
130 | |||
131 | #endif /* !__ASSEMBLEY__ */ | ||
132 | |||
133 | #endif /* __ASM_PARAVIRT_PATCH_H */ | ||
134 | |||
135 | /* | ||
136 | * Local variables: | ||
137 | * mode: C | ||
138 | * c-set-style: "linux" | ||
139 | * c-basic-offset: 8 | ||
140 | * tab-width: 8 | ||
141 | * indent-tabs-mode: t | ||
142 | * End: | ||
143 | */ | ||
diff --git a/arch/ia64/kernel/paravirt_patch.c b/arch/ia64/kernel/paravirt_patch.c new file mode 100644 index 000000000000..bfdfef1b1ffd --- /dev/null +++ b/arch/ia64/kernel/paravirt_patch.c | |||
@@ -0,0 +1,514 @@ | |||
1 | /****************************************************************************** | ||
2 | * linux/arch/ia64/xen/paravirt_patch.c | ||
3 | * | ||
4 | * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp> | ||
5 | * VA Linux Systems Japan K.K. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/init.h> | ||
24 | #include <asm/intrinsics.h> | ||
25 | #include <asm/kprobes.h> | ||
26 | #include <asm/paravirt.h> | ||
27 | #include <asm/paravirt_patch.h> | ||
28 | |||
29 | typedef union ia64_inst { | ||
30 | struct { | ||
31 | unsigned long long qp : 6; | ||
32 | unsigned long long : 31; | ||
33 | unsigned long long opcode : 4; | ||
34 | unsigned long long reserved : 23; | ||
35 | } generic; | ||
36 | unsigned long long l; | ||
37 | } ia64_inst_t; | ||
38 | |||
39 | /* | ||
40 | * flush_icache_range() can't be used here. | ||
41 | * we are here before cpu_init() which initializes | ||
42 | * ia64_i_cache_stride_shift. flush_icache_range() uses it. | ||
43 | */ | ||
44 | void __init_or_module | ||
45 | paravirt_flush_i_cache_range(const void *instr, unsigned long size) | ||
46 | { | ||
47 | extern void paravirt_fc_i(const void *addr); | ||
48 | unsigned long i; | ||
49 | |||
50 | for (i = 0; i < size; i += sizeof(bundle_t)) | ||
51 | paravirt_fc_i(instr + i); | ||
52 | } | ||
53 | |||
54 | bundle_t* __init_or_module | ||
55 | paravirt_get_bundle(unsigned long tag) | ||
56 | { | ||
57 | return (bundle_t *)(tag & ~3UL); | ||
58 | } | ||
59 | |||
60 | unsigned long __init_or_module | ||
61 | paravirt_get_slot(unsigned long tag) | ||
62 | { | ||
63 | return tag & 3UL; | ||
64 | } | ||
65 | |||
66 | unsigned long __init_or_module | ||
67 | paravirt_get_num_inst(unsigned long stag, unsigned long etag) | ||
68 | { | ||
69 | bundle_t *sbundle = paravirt_get_bundle(stag); | ||
70 | unsigned long sslot = paravirt_get_slot(stag); | ||
71 | bundle_t *ebundle = paravirt_get_bundle(etag); | ||
72 | unsigned long eslot = paravirt_get_slot(etag); | ||
73 | |||
74 | return (ebundle - sbundle) * 3 + eslot - sslot + 1; | ||
75 | } | ||
76 | |||
77 | unsigned long __init_or_module | ||
78 | paravirt_get_next_tag(unsigned long tag) | ||
79 | { | ||
80 | unsigned long slot = paravirt_get_slot(tag); | ||
81 | |||
82 | switch (slot) { | ||
83 | case 0: | ||
84 | case 1: | ||
85 | return tag + 1; | ||
86 | case 2: { | ||
87 | bundle_t *bundle = paravirt_get_bundle(tag); | ||
88 | return (unsigned long)(bundle + 1); | ||
89 | } | ||
90 | default: | ||
91 | BUG(); | ||
92 | } | ||
93 | /* NOTREACHED */ | ||
94 | } | ||
95 | |||
96 | ia64_inst_t __init_or_module | ||
97 | paravirt_read_slot0(const bundle_t *bundle) | ||
98 | { | ||
99 | ia64_inst_t inst; | ||
100 | inst.l = bundle->quad0.slot0; | ||
101 | return inst; | ||
102 | } | ||
103 | |||
104 | ia64_inst_t __init_or_module | ||
105 | paravirt_read_slot1(const bundle_t *bundle) | ||
106 | { | ||
107 | ia64_inst_t inst; | ||
108 | inst.l = bundle->quad0.slot1_p0 | | ||
109 | ((unsigned long long)bundle->quad1.slot1_p1 << 18UL); | ||
110 | return inst; | ||
111 | } | ||
112 | |||
113 | ia64_inst_t __init_or_module | ||
114 | paravirt_read_slot2(const bundle_t *bundle) | ||
115 | { | ||
116 | ia64_inst_t inst; | ||
117 | inst.l = bundle->quad1.slot2; | ||
118 | return inst; | ||
119 | } | ||
120 | |||
121 | ia64_inst_t __init_or_module | ||
122 | paravirt_read_inst(unsigned long tag) | ||
123 | { | ||
124 | bundle_t *bundle = paravirt_get_bundle(tag); | ||
125 | unsigned long slot = paravirt_get_slot(tag); | ||
126 | |||
127 | switch (slot) { | ||
128 | case 0: | ||
129 | return paravirt_read_slot0(bundle); | ||
130 | case 1: | ||
131 | return paravirt_read_slot1(bundle); | ||
132 | case 2: | ||
133 | return paravirt_read_slot2(bundle); | ||
134 | default: | ||
135 | BUG(); | ||
136 | } | ||
137 | /* NOTREACHED */ | ||
138 | } | ||
139 | |||
140 | void __init_or_module | ||
141 | paravirt_write_slot0(bundle_t *bundle, ia64_inst_t inst) | ||
142 | { | ||
143 | bundle->quad0.slot0 = inst.l; | ||
144 | } | ||
145 | |||
146 | void __init_or_module | ||
147 | paravirt_write_slot1(bundle_t *bundle, ia64_inst_t inst) | ||
148 | { | ||
149 | bundle->quad0.slot1_p0 = inst.l; | ||
150 | bundle->quad1.slot1_p1 = inst.l >> 18UL; | ||
151 | } | ||
152 | |||
153 | void __init_or_module | ||
154 | paravirt_write_slot2(bundle_t *bundle, ia64_inst_t inst) | ||
155 | { | ||
156 | bundle->quad1.slot2 = inst.l; | ||
157 | } | ||
158 | |||
159 | void __init_or_module | ||
160 | paravirt_write_inst(unsigned long tag, ia64_inst_t inst) | ||
161 | { | ||
162 | bundle_t *bundle = paravirt_get_bundle(tag); | ||
163 | unsigned long slot = paravirt_get_slot(tag); | ||
164 | |||
165 | switch (slot) { | ||
166 | case 0: | ||
167 | paravirt_write_slot0(bundle, inst); | ||
168 | break; | ||
169 | case 1: | ||
170 | paravirt_write_slot1(bundle, inst); | ||
171 | break; | ||
172 | case 2: | ||
173 | paravirt_write_slot2(bundle, inst); | ||
174 | break; | ||
175 | default: | ||
176 | BUG(); | ||
177 | break; | ||
178 | } | ||
179 | paravirt_flush_i_cache_range(bundle, sizeof(*bundle)); | ||
180 | } | ||
181 | |||
182 | /* for debug */ | ||
183 | void | ||
184 | paravirt_print_bundle(const bundle_t *bundle) | ||
185 | { | ||
186 | const unsigned long *quad = (const unsigned long *)bundle; | ||
187 | ia64_inst_t slot0 = paravirt_read_slot0(bundle); | ||
188 | ia64_inst_t slot1 = paravirt_read_slot1(bundle); | ||
189 | ia64_inst_t slot2 = paravirt_read_slot2(bundle); | ||
190 | |||
191 | printk(KERN_DEBUG | ||
192 | "bundle 0x%p 0x%016lx 0x%016lx\n", bundle, quad[0], quad[1]); | ||
193 | printk(KERN_DEBUG | ||
194 | "bundle template 0x%x\n", | ||
195 | bundle->quad0.template); | ||
196 | printk(KERN_DEBUG | ||
197 | "slot0 0x%lx slot1_p0 0x%lx slot1_p1 0x%lx slot2 0x%lx\n", | ||
198 | (unsigned long)bundle->quad0.slot0, | ||
199 | (unsigned long)bundle->quad0.slot1_p0, | ||
200 | (unsigned long)bundle->quad1.slot1_p1, | ||
201 | (unsigned long)bundle->quad1.slot2); | ||
202 | printk(KERN_DEBUG | ||
203 | "slot0 0x%016llx slot1 0x%016llx slot2 0x%016llx\n", | ||
204 | slot0.l, slot1.l, slot2.l); | ||
205 | } | ||
206 | |||
207 | static int noreplace_paravirt __init_or_module = 0; | ||
208 | |||
209 | static int __init setup_noreplace_paravirt(char *str) | ||
210 | { | ||
211 | noreplace_paravirt = 1; | ||
212 | return 1; | ||
213 | } | ||
214 | __setup("noreplace-paravirt", setup_noreplace_paravirt); | ||
215 | |||
216 | #ifdef ASM_SUPPORTED | ||
217 | static void __init_or_module | ||
218 | fill_nop_bundle(void *sbundle, void *ebundle) | ||
219 | { | ||
220 | extern const char paravirt_nop_bundle[]; | ||
221 | extern const unsigned long paravirt_nop_bundle_size; | ||
222 | |||
223 | void *bundle = sbundle; | ||
224 | |||
225 | BUG_ON((((unsigned long)sbundle) % sizeof(bundle_t)) != 0); | ||
226 | BUG_ON((((unsigned long)ebundle) % sizeof(bundle_t)) != 0); | ||
227 | |||
228 | while (bundle < ebundle) { | ||
229 | memcpy(bundle, paravirt_nop_bundle, paravirt_nop_bundle_size); | ||
230 | |||
231 | bundle += paravirt_nop_bundle_size; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /* helper function */ | ||
236 | unsigned long __init_or_module | ||
237 | __paravirt_patch_apply_bundle(void *sbundle, void *ebundle, unsigned long type, | ||
238 | const struct paravirt_patch_bundle_elem *elems, | ||
239 | unsigned long nelems, | ||
240 | const struct paravirt_patch_bundle_elem **found) | ||
241 | { | ||
242 | unsigned long used = 0; | ||
243 | unsigned long i; | ||
244 | |||
245 | BUG_ON((((unsigned long)sbundle) % sizeof(bundle_t)) != 0); | ||
246 | BUG_ON((((unsigned long)ebundle) % sizeof(bundle_t)) != 0); | ||
247 | |||
248 | found = NULL; | ||
249 | for (i = 0; i < nelems; i++) { | ||
250 | const struct paravirt_patch_bundle_elem *p = &elems[i]; | ||
251 | if (p->type == type) { | ||
252 | unsigned long need = p->ebundle - p->sbundle; | ||
253 | unsigned long room = ebundle - sbundle; | ||
254 | |||
255 | if (found != NULL) | ||
256 | *found = p; | ||
257 | |||
258 | if (room < need) { | ||
259 | /* no room to replace. skip it */ | ||
260 | printk(KERN_DEBUG | ||
261 | "the space is too small to put " | ||
262 | "bundles. type %ld need %ld room %ld\n", | ||
263 | type, need, room); | ||
264 | break; | ||
265 | } | ||
266 | |||
267 | used = need; | ||
268 | memcpy(sbundle, p->sbundle, used); | ||
269 | break; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return used; | ||
274 | } | ||
275 | |||
276 | void __init_or_module | ||
277 | paravirt_patch_apply_bundle(const struct paravirt_patch_site_bundle *start, | ||
278 | const struct paravirt_patch_site_bundle *end) | ||
279 | { | ||
280 | const struct paravirt_patch_site_bundle *p; | ||
281 | |||
282 | if (noreplace_paravirt) | ||
283 | return; | ||
284 | if (pv_init_ops.patch_bundle == NULL) | ||
285 | return; | ||
286 | |||
287 | for (p = start; p < end; p++) { | ||
288 | unsigned long used; | ||
289 | |||
290 | used = (*pv_init_ops.patch_bundle)(p->sbundle, p->ebundle, | ||
291 | p->type); | ||
292 | if (used == 0) | ||
293 | continue; | ||
294 | |||
295 | fill_nop_bundle(p->sbundle + used, p->ebundle); | ||
296 | paravirt_flush_i_cache_range(p->sbundle, | ||
297 | p->ebundle - p->sbundle); | ||
298 | } | ||
299 | ia64_sync_i(); | ||
300 | ia64_srlz_i(); | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * nop.i, nop.m, nop.f instruction are same format. | ||
305 | * but nop.b has differennt format. | ||
306 | * This doesn't support nop.b for now. | ||
307 | */ | ||
308 | static void __init_or_module | ||
309 | fill_nop_inst(unsigned long stag, unsigned long etag) | ||
310 | { | ||
311 | extern const bundle_t paravirt_nop_mfi_inst_bundle[]; | ||
312 | unsigned long tag; | ||
313 | const ia64_inst_t nop_inst = | ||
314 | paravirt_read_slot0(paravirt_nop_mfi_inst_bundle); | ||
315 | |||
316 | for (tag = stag; tag < etag; tag = paravirt_get_next_tag(tag)) | ||
317 | paravirt_write_inst(tag, nop_inst); | ||
318 | } | ||
319 | |||
320 | void __init_or_module | ||
321 | paravirt_patch_apply_inst(const struct paravirt_patch_site_inst *start, | ||
322 | const struct paravirt_patch_site_inst *end) | ||
323 | { | ||
324 | const struct paravirt_patch_site_inst *p; | ||
325 | |||
326 | if (noreplace_paravirt) | ||
327 | return; | ||
328 | if (pv_init_ops.patch_inst == NULL) | ||
329 | return; | ||
330 | |||
331 | for (p = start; p < end; p++) { | ||
332 | unsigned long tag; | ||
333 | bundle_t *sbundle; | ||
334 | bundle_t *ebundle; | ||
335 | |||
336 | tag = (*pv_init_ops.patch_inst)(p->stag, p->etag, p->type); | ||
337 | if (tag == p->stag) | ||
338 | continue; | ||
339 | |||
340 | fill_nop_inst(tag, p->etag); | ||
341 | sbundle = paravirt_get_bundle(p->stag); | ||
342 | ebundle = paravirt_get_bundle(p->etag) + 1; | ||
343 | paravirt_flush_i_cache_range(sbundle, (ebundle - sbundle) * | ||
344 | sizeof(bundle_t)); | ||
345 | } | ||
346 | ia64_sync_i(); | ||
347 | ia64_srlz_i(); | ||
348 | } | ||
349 | #endif /* ASM_SUPPOTED */ | ||
350 | |||
351 | /* brl.cond.sptk.many <target64> X3 */ | ||
352 | typedef union inst_x3_op { | ||
353 | ia64_inst_t inst; | ||
354 | struct { | ||
355 | unsigned long qp: 6; | ||
356 | unsigned long btyp: 3; | ||
357 | unsigned long unused: 3; | ||
358 | unsigned long p: 1; | ||
359 | unsigned long imm20b: 20; | ||
360 | unsigned long wh: 2; | ||
361 | unsigned long d: 1; | ||
362 | unsigned long i: 1; | ||
363 | unsigned long opcode: 4; | ||
364 | }; | ||
365 | unsigned long l; | ||
366 | } inst_x3_op_t; | ||
367 | |||
368 | typedef union inst_x3_imm { | ||
369 | ia64_inst_t inst; | ||
370 | struct { | ||
371 | unsigned long unused: 2; | ||
372 | unsigned long imm39: 39; | ||
373 | }; | ||
374 | unsigned long l; | ||
375 | } inst_x3_imm_t; | ||
376 | |||
377 | void __init_or_module | ||
378 | paravirt_patch_reloc_brl(unsigned long tag, const void *target) | ||
379 | { | ||
380 | unsigned long tag_op = paravirt_get_next_tag(tag); | ||
381 | unsigned long tag_imm = tag; | ||
382 | bundle_t *bundle = paravirt_get_bundle(tag); | ||
383 | |||
384 | ia64_inst_t inst_op = paravirt_read_inst(tag_op); | ||
385 | ia64_inst_t inst_imm = paravirt_read_inst(tag_imm); | ||
386 | |||
387 | inst_x3_op_t inst_x3_op = { .l = inst_op.l }; | ||
388 | inst_x3_imm_t inst_x3_imm = { .l = inst_imm.l }; | ||
389 | |||
390 | unsigned long imm60 = | ||
391 | ((unsigned long)target - (unsigned long)bundle) >> 4; | ||
392 | |||
393 | BUG_ON(paravirt_get_slot(tag) != 1); /* MLX */ | ||
394 | BUG_ON(((unsigned long)target & (sizeof(bundle_t) - 1)) != 0); | ||
395 | |||
396 | /* imm60[59] 1bit */ | ||
397 | inst_x3_op.i = (imm60 >> 59) & 1; | ||
398 | /* imm60[19:0] 20bit */ | ||
399 | inst_x3_op.imm20b = imm60 & ((1UL << 20) - 1); | ||
400 | /* imm60[58:20] 39bit */ | ||
401 | inst_x3_imm.imm39 = (imm60 >> 20) & ((1UL << 39) - 1); | ||
402 | |||
403 | inst_op.l = inst_x3_op.l; | ||
404 | inst_imm.l = inst_x3_imm.l; | ||
405 | |||
406 | paravirt_write_inst(tag_op, inst_op); | ||
407 | paravirt_write_inst(tag_imm, inst_imm); | ||
408 | } | ||
409 | |||
410 | /* br.cond.sptk.many <target25> B1 */ | ||
411 | typedef union inst_b1 { | ||
412 | ia64_inst_t inst; | ||
413 | struct { | ||
414 | unsigned long qp: 6; | ||
415 | unsigned long btype: 3; | ||
416 | unsigned long unused: 3; | ||
417 | unsigned long p: 1; | ||
418 | unsigned long imm20b: 20; | ||
419 | unsigned long wh: 2; | ||
420 | unsigned long d: 1; | ||
421 | unsigned long s: 1; | ||
422 | unsigned long opcode: 4; | ||
423 | }; | ||
424 | unsigned long l; | ||
425 | } inst_b1_t; | ||
426 | |||
427 | void __init | ||
428 | paravirt_patch_reloc_br(unsigned long tag, const void *target) | ||
429 | { | ||
430 | bundle_t *bundle = paravirt_get_bundle(tag); | ||
431 | ia64_inst_t inst = paravirt_read_inst(tag); | ||
432 | unsigned long target25 = (unsigned long)target - (unsigned long)bundle; | ||
433 | inst_b1_t inst_b1; | ||
434 | |||
435 | BUG_ON(((unsigned long)target & (sizeof(bundle_t) - 1)) != 0); | ||
436 | |||
437 | inst_b1.l = inst.l; | ||
438 | if (target25 & (1UL << 63)) | ||
439 | inst_b1.s = 1; | ||
440 | else | ||
441 | inst_b1.s = 0; | ||
442 | |||
443 | inst_b1.imm20b = target25 >> 4; | ||
444 | inst.l = inst_b1.l; | ||
445 | |||
446 | paravirt_write_inst(tag, inst); | ||
447 | } | ||
448 | |||
449 | void __init | ||
450 | __paravirt_patch_apply_branch( | ||
451 | unsigned long tag, unsigned long type, | ||
452 | const struct paravirt_patch_branch_target *entries, | ||
453 | unsigned int nr_entries) | ||
454 | { | ||
455 | unsigned int i; | ||
456 | for (i = 0; i < nr_entries; i++) { | ||
457 | if (entries[i].type == type) { | ||
458 | paravirt_patch_reloc_br(tag, entries[i].entry); | ||
459 | break; | ||
460 | } | ||
461 | } | ||
462 | } | ||
463 | |||
464 | static void __init | ||
465 | paravirt_patch_apply_branch(const struct paravirt_patch_site_branch *start, | ||
466 | const struct paravirt_patch_site_branch *end) | ||
467 | { | ||
468 | const struct paravirt_patch_site_branch *p; | ||
469 | |||
470 | if (noreplace_paravirt) | ||
471 | return; | ||
472 | if (pv_init_ops.patch_branch == NULL) | ||
473 | return; | ||
474 | |||
475 | for (p = start; p < end; p++) | ||
476 | (*pv_init_ops.patch_branch)(p->tag, p->type); | ||
477 | |||
478 | ia64_sync_i(); | ||
479 | ia64_srlz_i(); | ||
480 | } | ||
481 | |||
482 | void __init | ||
483 | paravirt_patch_apply(void) | ||
484 | { | ||
485 | extern const char __start_paravirt_bundles[]; | ||
486 | extern const char __stop_paravirt_bundles[]; | ||
487 | extern const char __start_paravirt_insts[]; | ||
488 | extern const char __stop_paravirt_insts[]; | ||
489 | extern const char __start_paravirt_branches[]; | ||
490 | extern const char __stop_paravirt_branches[]; | ||
491 | |||
492 | paravirt_patch_apply_bundle((const struct paravirt_patch_site_bundle *) | ||
493 | __start_paravirt_bundles, | ||
494 | (const struct paravirt_patch_site_bundle *) | ||
495 | __stop_paravirt_bundles); | ||
496 | paravirt_patch_apply_inst((const struct paravirt_patch_site_inst *) | ||
497 | __start_paravirt_insts, | ||
498 | (const struct paravirt_patch_site_inst *) | ||
499 | __stop_paravirt_insts); | ||
500 | paravirt_patch_apply_branch((const struct paravirt_patch_site_branch *) | ||
501 | __start_paravirt_branches, | ||
502 | (const struct paravirt_patch_site_branch *) | ||
503 | __stop_paravirt_branches); | ||
504 | } | ||
505 | |||
506 | /* | ||
507 | * Local variables: | ||
508 | * mode: C | ||
509 | * c-set-style: "linux" | ||
510 | * c-basic-offset: 8 | ||
511 | * tab-width: 8 | ||
512 | * indent-tabs-mode: t | ||
513 | * End: | ||
514 | */ | ||
diff --git a/arch/ia64/kernel/paravirtentry.S b/arch/ia64/kernel/paravirtentry.S index 2f42fcb9776a..80c0d365cbc0 100644 --- a/arch/ia64/kernel/paravirtentry.S +++ b/arch/ia64/kernel/paravirtentry.S | |||
@@ -58,3 +58,59 @@ BRANCH_PROC(switch_to, r22, b7) | |||
58 | BRANCH_PROC_UNWINFO(leave_syscall, r22, b7) | 58 | BRANCH_PROC_UNWINFO(leave_syscall, r22, b7) |
59 | BRANCH_PROC(work_processed_syscall, r2, b7) | 59 | BRANCH_PROC(work_processed_syscall, r2, b7) |
60 | BRANCH_PROC_UNWINFO(leave_kernel, r22, b7) | 60 | BRANCH_PROC_UNWINFO(leave_kernel, r22, b7) |
61 | |||
62 | |||
63 | #ifdef CONFIG_MODULES | ||
64 | #define __INIT_OR_MODULE .text | ||
65 | #define __INITDATA_OR_MODULE .data | ||
66 | #else | ||
67 | #define __INIT_OR_MODULE __INIT | ||
68 | #define __INITDATA_OR_MODULE __INITDATA | ||
69 | #endif /* CONFIG_MODULES */ | ||
70 | |||
71 | __INIT_OR_MODULE | ||
72 | GLOBAL_ENTRY(paravirt_fc_i) | ||
73 | fc.i r32 | ||
74 | br.ret.sptk.many rp | ||
75 | END(paravirt_fc_i) | ||
76 | __FINIT | ||
77 | |||
78 | __INIT_OR_MODULE | ||
79 | .align 32 | ||
80 | GLOBAL_ENTRY(paravirt_nop_b_inst_bundle) | ||
81 | { | ||
82 | nop.b 0 | ||
83 | nop.b 0 | ||
84 | nop.b 0 | ||
85 | } | ||
86 | END(paravirt_nop_b_inst_bundle) | ||
87 | __FINIT | ||
88 | |||
89 | /* NOTE: nop.[mfi] has same format */ | ||
90 | __INIT_OR_MODULE | ||
91 | GLOBAL_ENTRY(paravirt_nop_mfi_inst_bundle) | ||
92 | { | ||
93 | nop.m 0 | ||
94 | nop.f 0 | ||
95 | nop.i 0 | ||
96 | } | ||
97 | END(paravirt_nop_mfi_inst_bundle) | ||
98 | __FINIT | ||
99 | |||
100 | __INIT_OR_MODULE | ||
101 | GLOBAL_ENTRY(paravirt_nop_bundle) | ||
102 | paravirt_nop_bundle_start: | ||
103 | { | ||
104 | nop 0 | ||
105 | nop 0 | ||
106 | nop 0 | ||
107 | } | ||
108 | paravirt_nop_bundle_end: | ||
109 | END(paravirt_nop_bundle) | ||
110 | __FINIT | ||
111 | |||
112 | __INITDATA_OR_MODULE | ||
113 | .align 8 | ||
114 | .global paravirt_nop_bundle_size | ||
115 | paravirt_nop_bundle_size: | ||
116 | data8 paravirt_nop_bundle_end - paravirt_nop_bundle_start | ||
diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 92ae7e8f0142..794d168bc8a4 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S | |||
@@ -169,6 +169,30 @@ SECTIONS | |||
169 | __end___mckinley_e9_bundles = .; | 169 | __end___mckinley_e9_bundles = .; |
170 | } | 170 | } |
171 | 171 | ||
172 | #if defined(CONFIG_PARAVIRT) | ||
173 | . = ALIGN(16); | ||
174 | .paravirt_bundles : AT(ADDR(.paravirt_bundles) - LOAD_OFFSET) | ||
175 | { | ||
176 | __start_paravirt_bundles = .; | ||
177 | *(.paravirt_bundles) | ||
178 | __stop_paravirt_bundles = .; | ||
179 | } | ||
180 | . = ALIGN(16); | ||
181 | .paravirt_insts : AT(ADDR(.paravirt_insts) - LOAD_OFFSET) | ||
182 | { | ||
183 | __start_paravirt_insts = .; | ||
184 | *(.paravirt_insts) | ||
185 | __stop_paravirt_insts = .; | ||
186 | } | ||
187 | . = ALIGN(16); | ||
188 | .paravirt_branches : AT(ADDR(.paravirt_branches) - LOAD_OFFSET) | ||
189 | { | ||
190 | __start_paravirt_branches = .; | ||
191 | *(.paravirt_branches) | ||
192 | __stop_paravirt_branches = .; | ||
193 | } | ||
194 | #endif | ||
195 | |||
172 | #if defined(CONFIG_IA64_GENERIC) | 196 | #if defined(CONFIG_IA64_GENERIC) |
173 | /* Machine Vector */ | 197 | /* Machine Vector */ |
174 | . = ALIGN(16); | 198 | . = ALIGN(16); |