diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2006-12-06 20:14:08 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:08 -0500 |
commit | 139ec7c416248b9ea227d21839235344edfee1e0 (patch) | |
tree | 54c396848b08367c0352c77f4633be6380a8eb16 /arch/i386/kernel | |
parent | d3561b7fa0fb0fc583bab0eeda32bec9e4c4056d (diff) |
[PATCH] paravirt: Patch inline replacements for paravirt intercepts
It turns out that the most called ops, by several orders of magnitude,
are the interrupt manipulation ops. These are obvious candidates for
patching, so mark them up and create infrastructure for it.
The method used is that the ops structure has a patch function, which
is called for each place which needs to be patched: this returns a
number of instructions (the rest are NOP-padded).
Usually we can spare a register (%eax) for the binary patched code to
use, but in a couple of critical places in entry.S we can't: we make
the clobbers explicit at the call site, and manually clobber the
allowed registers in debug mode as an extra check.
And:
Don't abuse CONFIG_DEBUG_KERNEL, add CONFIG_DEBUG_PARAVIRT.
And:
AK: Fix warnings in x86-64 alternative.c build
And:
AK: Fix compilation with defconfig
And:
^From: Andrew Morton <akpm@osdl.org>
Some binutlises still like to emit references to __stop_parainstructions and
__start_parainstructions.
And:
AK: Fix warnings about unused variables when PARAVIRT is disabled.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Zachary Amsden <zach@vmware.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/alternative.c | 63 | ||||
-rw-r--r-- | arch/i386/kernel/entry.S | 39 | ||||
-rw-r--r-- | arch/i386/kernel/module.c | 11 | ||||
-rw-r--r-- | arch/i386/kernel/paravirt.c | 44 | ||||
-rw-r--r-- | arch/i386/kernel/vmlinux.lds.S | 6 |
5 files changed, 138 insertions, 25 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 535f9794fba1..9eca21b49f6b 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c | |||
@@ -124,6 +124,20 @@ static unsigned char** find_nop_table(void) | |||
124 | 124 | ||
125 | #endif /* CONFIG_X86_64 */ | 125 | #endif /* CONFIG_X86_64 */ |
126 | 126 | ||
127 | static void nop_out(void *insns, unsigned int len) | ||
128 | { | ||
129 | unsigned char **noptable = find_nop_table(); | ||
130 | |||
131 | while (len > 0) { | ||
132 | unsigned int noplen = len; | ||
133 | if (noplen > ASM_NOP_MAX) | ||
134 | noplen = ASM_NOP_MAX; | ||
135 | memcpy(insns, noptable[noplen], noplen); | ||
136 | insns += noplen; | ||
137 | len -= noplen; | ||
138 | } | ||
139 | } | ||
140 | |||
127 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | 141 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; |
128 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; | 142 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; |
129 | extern u8 *__smp_locks[], *__smp_locks_end[]; | 143 | extern u8 *__smp_locks[], *__smp_locks_end[]; |
@@ -138,10 +152,9 @@ extern u8 __smp_alt_begin[], __smp_alt_end[]; | |||
138 | 152 | ||
139 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | 153 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) |
140 | { | 154 | { |
141 | unsigned char **noptable = find_nop_table(); | ||
142 | struct alt_instr *a; | 155 | struct alt_instr *a; |
143 | u8 *instr; | 156 | u8 *instr; |
144 | int diff, i, k; | 157 | int diff; |
145 | 158 | ||
146 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); | 159 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); |
147 | for (a = start; a < end; a++) { | 160 | for (a = start; a < end; a++) { |
@@ -159,13 +172,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | |||
159 | #endif | 172 | #endif |
160 | memcpy(instr, a->replacement, a->replacementlen); | 173 | memcpy(instr, a->replacement, a->replacementlen); |
161 | diff = a->instrlen - a->replacementlen; | 174 | diff = a->instrlen - a->replacementlen; |
162 | /* Pad the rest with nops */ | 175 | nop_out(instr + a->replacementlen, diff); |
163 | for (i = a->replacementlen; diff > 0; diff -= k, i += k) { | ||
164 | k = diff; | ||
165 | if (k > ASM_NOP_MAX) | ||
166 | k = ASM_NOP_MAX; | ||
167 | memcpy(a->instr + i, noptable[k], k); | ||
168 | } | ||
169 | } | 176 | } |
170 | } | 177 | } |
171 | 178 | ||
@@ -209,7 +216,6 @@ static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) | |||
209 | 216 | ||
210 | static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) | 217 | static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) |
211 | { | 218 | { |
212 | unsigned char **noptable = find_nop_table(); | ||
213 | u8 **ptr; | 219 | u8 **ptr; |
214 | 220 | ||
215 | for (ptr = start; ptr < end; ptr++) { | 221 | for (ptr = start; ptr < end; ptr++) { |
@@ -217,7 +223,7 @@ static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end | |||
217 | continue; | 223 | continue; |
218 | if (*ptr > text_end) | 224 | if (*ptr > text_end) |
219 | continue; | 225 | continue; |
220 | **ptr = noptable[1][0]; | 226 | nop_out(*ptr, 1); |
221 | }; | 227 | }; |
222 | } | 228 | } |
223 | 229 | ||
@@ -343,6 +349,40 @@ void alternatives_smp_switch(int smp) | |||
343 | 349 | ||
344 | #endif | 350 | #endif |
345 | 351 | ||
352 | #ifdef CONFIG_PARAVIRT | ||
353 | void apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end) | ||
354 | { | ||
355 | struct paravirt_patch *p; | ||
356 | |||
357 | for (p = start; p < end; p++) { | ||
358 | unsigned int used; | ||
359 | |||
360 | used = paravirt_ops.patch(p->instrtype, p->clobbers, p->instr, | ||
361 | p->len); | ||
362 | #ifdef CONFIG_DEBUG_PARAVIRT | ||
363 | { | ||
364 | int i; | ||
365 | /* Deliberately clobber regs using "not %reg" to find bugs. */ | ||
366 | for (i = 0; i < 3; i++) { | ||
367 | if (p->len - used >= 2 && (p->clobbers & (1 << i))) { | ||
368 | memcpy(p->instr + used, "\xf7\xd0", 2); | ||
369 | p->instr[used+1] |= i; | ||
370 | used += 2; | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | #endif | ||
375 | /* Pad the rest with nops */ | ||
376 | nop_out(p->instr + used, p->len - used); | ||
377 | } | ||
378 | |||
379 | /* Sync to be conservative, in case we patched following instructions */ | ||
380 | sync_core(); | ||
381 | } | ||
382 | extern struct paravirt_patch __start_parainstructions[], | ||
383 | __stop_parainstructions[]; | ||
384 | #endif /* CONFIG_PARAVIRT */ | ||
385 | |||
346 | void __init alternative_instructions(void) | 386 | void __init alternative_instructions(void) |
347 | { | 387 | { |
348 | unsigned long flags; | 388 | unsigned long flags; |
@@ -390,5 +430,6 @@ void __init alternative_instructions(void) | |||
390 | alternatives_smp_switch(0); | 430 | alternatives_smp_switch(0); |
391 | } | 431 | } |
392 | #endif | 432 | #endif |
433 | apply_paravirt(__start_parainstructions, __stop_parainstructions); | ||
393 | local_irq_restore(flags); | 434 | local_irq_restore(flags); |
394 | } | 435 | } |
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index d274612e05cd..de34b7fed3c1 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S | |||
@@ -53,6 +53,19 @@ | |||
53 | #include <asm/dwarf2.h> | 53 | #include <asm/dwarf2.h> |
54 | #include "irq_vectors.h" | 54 | #include "irq_vectors.h" |
55 | 55 | ||
56 | /* | ||
57 | * We use macros for low-level operations which need to be overridden | ||
58 | * for paravirtualization. The following will never clobber any registers: | ||
59 | * INTERRUPT_RETURN (aka. "iret") | ||
60 | * GET_CR0_INTO_EAX (aka. "movl %cr0, %eax") | ||
61 | * ENABLE_INTERRUPTS_SYSEXIT (aka "sti; sysexit"). | ||
62 | * | ||
63 | * For DISABLE_INTERRUPTS/ENABLE_INTERRUPTS (aka "cli"/"sti"), you must | ||
64 | * specify what registers can be overwritten (CLBR_NONE, CLBR_EAX/EDX/ECX/ANY). | ||
65 | * Allowing a register to be clobbered can shrink the paravirt replacement | ||
66 | * enough to patch inline, increasing performance. | ||
67 | */ | ||
68 | |||
56 | #define nr_syscalls ((syscall_table_size)/4) | 69 | #define nr_syscalls ((syscall_table_size)/4) |
57 | 70 | ||
58 | CF_MASK = 0x00000001 | 71 | CF_MASK = 0x00000001 |
@@ -63,9 +76,9 @@ NT_MASK = 0x00004000 | |||
63 | VM_MASK = 0x00020000 | 76 | VM_MASK = 0x00020000 |
64 | 77 | ||
65 | #ifdef CONFIG_PREEMPT | 78 | #ifdef CONFIG_PREEMPT |
66 | #define preempt_stop DISABLE_INTERRUPTS; TRACE_IRQS_OFF | 79 | #define preempt_stop(clobbers) DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF |
67 | #else | 80 | #else |
68 | #define preempt_stop | 81 | #define preempt_stop(clobbers) |
69 | #define resume_kernel restore_nocheck | 82 | #define resume_kernel restore_nocheck |
70 | #endif | 83 | #endif |
71 | 84 | ||
@@ -226,7 +239,7 @@ ENTRY(ret_from_fork) | |||
226 | ALIGN | 239 | ALIGN |
227 | RING0_PTREGS_FRAME | 240 | RING0_PTREGS_FRAME |
228 | ret_from_exception: | 241 | ret_from_exception: |
229 | preempt_stop | 242 | preempt_stop(CLBR_ANY) |
230 | ret_from_intr: | 243 | ret_from_intr: |
231 | GET_THREAD_INFO(%ebp) | 244 | GET_THREAD_INFO(%ebp) |
232 | check_userspace: | 245 | check_userspace: |
@@ -237,7 +250,7 @@ check_userspace: | |||
237 | jb resume_kernel # not returning to v8086 or userspace | 250 | jb resume_kernel # not returning to v8086 or userspace |
238 | 251 | ||
239 | ENTRY(resume_userspace) | 252 | ENTRY(resume_userspace) |
240 | DISABLE_INTERRUPTS # make sure we don't miss an interrupt | 253 | DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt |
241 | # setting need_resched or sigpending | 254 | # setting need_resched or sigpending |
242 | # between sampling and the iret | 255 | # between sampling and the iret |
243 | movl TI_flags(%ebp), %ecx | 256 | movl TI_flags(%ebp), %ecx |
@@ -248,7 +261,7 @@ ENTRY(resume_userspace) | |||
248 | 261 | ||
249 | #ifdef CONFIG_PREEMPT | 262 | #ifdef CONFIG_PREEMPT |
250 | ENTRY(resume_kernel) | 263 | ENTRY(resume_kernel) |
251 | DISABLE_INTERRUPTS | 264 | DISABLE_INTERRUPTS(CLBR_ANY) |
252 | cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? | 265 | cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? |
253 | jnz restore_nocheck | 266 | jnz restore_nocheck |
254 | need_resched: | 267 | need_resched: |
@@ -277,7 +290,7 @@ sysenter_past_esp: | |||
277 | * No need to follow this irqs on/off section: the syscall | 290 | * No need to follow this irqs on/off section: the syscall |
278 | * disabled irqs and here we enable it straight after entry: | 291 | * disabled irqs and here we enable it straight after entry: |
279 | */ | 292 | */ |
280 | ENABLE_INTERRUPTS | 293 | ENABLE_INTERRUPTS(CLBR_NONE) |
281 | pushl $(__USER_DS) | 294 | pushl $(__USER_DS) |
282 | CFI_ADJUST_CFA_OFFSET 4 | 295 | CFI_ADJUST_CFA_OFFSET 4 |
283 | /*CFI_REL_OFFSET ss, 0*/ | 296 | /*CFI_REL_OFFSET ss, 0*/ |
@@ -322,7 +335,7 @@ sysenter_past_esp: | |||
322 | jae syscall_badsys | 335 | jae syscall_badsys |
323 | call *sys_call_table(,%eax,4) | 336 | call *sys_call_table(,%eax,4) |
324 | movl %eax,PT_EAX(%esp) | 337 | movl %eax,PT_EAX(%esp) |
325 | DISABLE_INTERRUPTS | 338 | DISABLE_INTERRUPTS(CLBR_ECX|CLBR_EDX) |
326 | TRACE_IRQS_OFF | 339 | TRACE_IRQS_OFF |
327 | movl TI_flags(%ebp), %ecx | 340 | movl TI_flags(%ebp), %ecx |
328 | testw $_TIF_ALLWORK_MASK, %cx | 341 | testw $_TIF_ALLWORK_MASK, %cx |
@@ -364,7 +377,7 @@ syscall_call: | |||
364 | call *sys_call_table(,%eax,4) | 377 | call *sys_call_table(,%eax,4) |
365 | movl %eax,PT_EAX(%esp) # store the return value | 378 | movl %eax,PT_EAX(%esp) # store the return value |
366 | syscall_exit: | 379 | syscall_exit: |
367 | DISABLE_INTERRUPTS # make sure we don't miss an interrupt | 380 | DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt |
368 | # setting need_resched or sigpending | 381 | # setting need_resched or sigpending |
369 | # between sampling and the iret | 382 | # between sampling and the iret |
370 | TRACE_IRQS_OFF | 383 | TRACE_IRQS_OFF |
@@ -393,7 +406,7 @@ restore_nocheck_notrace: | |||
393 | .section .fixup,"ax" | 406 | .section .fixup,"ax" |
394 | iret_exc: | 407 | iret_exc: |
395 | TRACE_IRQS_ON | 408 | TRACE_IRQS_ON |
396 | ENABLE_INTERRUPTS | 409 | ENABLE_INTERRUPTS(CLBR_NONE) |
397 | pushl $0 # no error code | 410 | pushl $0 # no error code |
398 | pushl $do_iret_error | 411 | pushl $do_iret_error |
399 | jmp error_code | 412 | jmp error_code |
@@ -436,7 +449,7 @@ ldt_ss: | |||
436 | CFI_ADJUST_CFA_OFFSET 4 | 449 | CFI_ADJUST_CFA_OFFSET 4 |
437 | pushl %eax | 450 | pushl %eax |
438 | CFI_ADJUST_CFA_OFFSET 4 | 451 | CFI_ADJUST_CFA_OFFSET 4 |
439 | DISABLE_INTERRUPTS | 452 | DISABLE_INTERRUPTS(CLBR_EAX) |
440 | TRACE_IRQS_OFF | 453 | TRACE_IRQS_OFF |
441 | lss (%esp), %esp | 454 | lss (%esp), %esp |
442 | CFI_ADJUST_CFA_OFFSET -8 | 455 | CFI_ADJUST_CFA_OFFSET -8 |
@@ -451,7 +464,7 @@ work_pending: | |||
451 | jz work_notifysig | 464 | jz work_notifysig |
452 | work_resched: | 465 | work_resched: |
453 | call schedule | 466 | call schedule |
454 | DISABLE_INTERRUPTS # make sure we don't miss an interrupt | 467 | DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt |
455 | # setting need_resched or sigpending | 468 | # setting need_resched or sigpending |
456 | # between sampling and the iret | 469 | # between sampling and the iret |
457 | TRACE_IRQS_OFF | 470 | TRACE_IRQS_OFF |
@@ -509,7 +522,7 @@ syscall_exit_work: | |||
509 | testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl | 522 | testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl |
510 | jz work_pending | 523 | jz work_pending |
511 | TRACE_IRQS_ON | 524 | TRACE_IRQS_ON |
512 | ENABLE_INTERRUPTS # could let do_syscall_trace() call | 525 | ENABLE_INTERRUPTS(CLBR_ANY) # could let do_syscall_trace() call |
513 | # schedule() instead | 526 | # schedule() instead |
514 | movl %esp, %eax | 527 | movl %esp, %eax |
515 | movl $1, %edx | 528 | movl $1, %edx |
@@ -693,7 +706,7 @@ ENTRY(device_not_available) | |||
693 | GET_CR0_INTO_EAX | 706 | GET_CR0_INTO_EAX |
694 | testl $0x4, %eax # EM (math emulation bit) | 707 | testl $0x4, %eax # EM (math emulation bit) |
695 | jne device_not_available_emulate | 708 | jne device_not_available_emulate |
696 | preempt_stop | 709 | preempt_stop(CLBR_ANY) |
697 | call math_state_restore | 710 | call math_state_restore |
698 | jmp ret_from_exception | 711 | jmp ret_from_exception |
699 | device_not_available_emulate: | 712 | device_not_available_emulate: |
diff --git a/arch/i386/kernel/module.c b/arch/i386/kernel/module.c index 470cf97e7cd3..d7d9c8b23f72 100644 --- a/arch/i386/kernel/module.c +++ b/arch/i386/kernel/module.c | |||
@@ -108,7 +108,8 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
108 | const Elf_Shdr *sechdrs, | 108 | const Elf_Shdr *sechdrs, |
109 | struct module *me) | 109 | struct module *me) |
110 | { | 110 | { |
111 | const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL; | 111 | const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL, |
112 | *para = NULL; | ||
112 | char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | 113 | char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; |
113 | 114 | ||
114 | for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { | 115 | for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { |
@@ -118,6 +119,8 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
118 | alt = s; | 119 | alt = s; |
119 | if (!strcmp(".smp_locks", secstrings + s->sh_name)) | 120 | if (!strcmp(".smp_locks", secstrings + s->sh_name)) |
120 | locks= s; | 121 | locks= s; |
122 | if (!strcmp(".parainstructions", secstrings + s->sh_name)) | ||
123 | para = s; | ||
121 | } | 124 | } |
122 | 125 | ||
123 | if (alt) { | 126 | if (alt) { |
@@ -132,6 +135,12 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
132 | lseg, lseg + locks->sh_size, | 135 | lseg, lseg + locks->sh_size, |
133 | tseg, tseg + text->sh_size); | 136 | tseg, tseg + text->sh_size); |
134 | } | 137 | } |
138 | |||
139 | if (para) { | ||
140 | void *pseg = (void *)para->sh_addr; | ||
141 | apply_paravirt(pseg, pseg + para->sh_size); | ||
142 | } | ||
143 | |||
135 | return 0; | 144 | return 0; |
136 | } | 145 | } |
137 | 146 | ||
diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c index 478192cd4b90..d46460426446 100644 --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c | |||
@@ -45,6 +45,49 @@ char *memory_setup(void) | |||
45 | return paravirt_ops.memory_setup(); | 45 | return paravirt_ops.memory_setup(); |
46 | } | 46 | } |
47 | 47 | ||
48 | /* Simple instruction patching code. */ | ||
49 | #define DEF_NATIVE(name, code) \ | ||
50 | extern const char start_##name[], end_##name[]; \ | ||
51 | asm("start_" #name ": " code "; end_" #name ":") | ||
52 | DEF_NATIVE(cli, "cli"); | ||
53 | DEF_NATIVE(sti, "sti"); | ||
54 | DEF_NATIVE(popf, "push %eax; popf"); | ||
55 | DEF_NATIVE(pushf, "pushf; pop %eax"); | ||
56 | DEF_NATIVE(pushf_cli, "pushf; pop %eax; cli"); | ||
57 | DEF_NATIVE(iret, "iret"); | ||
58 | DEF_NATIVE(sti_sysexit, "sti; sysexit"); | ||
59 | |||
60 | static const struct native_insns | ||
61 | { | ||
62 | const char *start, *end; | ||
63 | } native_insns[] = { | ||
64 | [PARAVIRT_IRQ_DISABLE] = { start_cli, end_cli }, | ||
65 | [PARAVIRT_IRQ_ENABLE] = { start_sti, end_sti }, | ||
66 | [PARAVIRT_RESTORE_FLAGS] = { start_popf, end_popf }, | ||
67 | [PARAVIRT_SAVE_FLAGS] = { start_pushf, end_pushf }, | ||
68 | [PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushf_cli, end_pushf_cli }, | ||
69 | [PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret }, | ||
70 | [PARAVIRT_STI_SYSEXIT] = { start_sti_sysexit, end_sti_sysexit }, | ||
71 | }; | ||
72 | |||
73 | static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len) | ||
74 | { | ||
75 | unsigned int insn_len; | ||
76 | |||
77 | /* Don't touch it if we don't have a replacement */ | ||
78 | if (type >= ARRAY_SIZE(native_insns) || !native_insns[type].start) | ||
79 | return len; | ||
80 | |||
81 | insn_len = native_insns[type].end - native_insns[type].start; | ||
82 | |||
83 | /* Similarly if we can't fit replacement. */ | ||
84 | if (len < insn_len) | ||
85 | return len; | ||
86 | |||
87 | memcpy(insns, native_insns[type].start, insn_len); | ||
88 | return insn_len; | ||
89 | } | ||
90 | |||
48 | static fastcall unsigned long native_get_debugreg(int regno) | 91 | static fastcall unsigned long native_get_debugreg(int regno) |
49 | { | 92 | { |
50 | unsigned long val = 0; /* Damn you, gcc! */ | 93 | unsigned long val = 0; /* Damn you, gcc! */ |
@@ -349,6 +392,7 @@ struct paravirt_ops paravirt_ops = { | |||
349 | .paravirt_enabled = 0, | 392 | .paravirt_enabled = 0, |
350 | .kernel_rpl = 0, | 393 | .kernel_rpl = 0, |
351 | 394 | ||
395 | .patch = native_patch, | ||
352 | .banner = default_banner, | 396 | .banner = default_banner, |
353 | .arch_setup = native_nop, | 397 | .arch_setup = native_nop, |
354 | .memory_setup = machine_specific_memory_setup, | 398 | .memory_setup = machine_specific_memory_setup, |
diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S index 6860f20aa579..5c69cf0e5944 100644 --- a/arch/i386/kernel/vmlinux.lds.S +++ b/arch/i386/kernel/vmlinux.lds.S | |||
@@ -165,6 +165,12 @@ SECTIONS | |||
165 | .altinstr_replacement : AT(ADDR(.altinstr_replacement) - LOAD_OFFSET) { | 165 | .altinstr_replacement : AT(ADDR(.altinstr_replacement) - LOAD_OFFSET) { |
166 | *(.altinstr_replacement) | 166 | *(.altinstr_replacement) |
167 | } | 167 | } |
168 | . = ALIGN(4); | ||
169 | __start_parainstructions = .; | ||
170 | .parainstructions : AT(ADDR(.parainstructions) - LOAD_OFFSET) { | ||
171 | *(.parainstructions) | ||
172 | } | ||
173 | __stop_parainstructions = .; | ||
168 | /* .exit.text is discard at runtime, not link time, to deal with references | 174 | /* .exit.text is discard at runtime, not link time, to deal with references |
169 | from .altinstructions and .eh_frame */ | 175 | from .altinstructions and .eh_frame */ |
170 | .exit.text : AT(ADDR(.exit.text) - LOAD_OFFSET) { *(.exit.text) } | 176 | .exit.text : AT(ADDR(.exit.text) - LOAD_OFFSET) { *(.exit.text) } |