Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 62475
b: refs/heads/master
c: 19d36cc
h: refs/heads/master
i:
  62473: 30fd237
  62471: 5b94a3f
v: v3
  • Loading branch information
Andi Kleen authored and Linus Torvalds committed Jul 22, 2007
1 parent 10d65a3 commit 240bf17
Show file tree
Hide file tree
Showing 11 changed files with 64 additions and 46 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f51c94528a9bc73504928926ca4d791a2b7ddd7c
refs/heads/master: 19d36ccdc34f5ed444f8a6af0cbfdb6790eb1177
40 changes: 34 additions & 6 deletions trunk/arch/i386/kernel/alternative.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/kprobes.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <asm/alternative.h>
#include <asm/sections.h>
#include <asm/pgtable.h>

#ifdef CONFIG_HOTPLUG_CPU
static int smp_alt_once;
Expand Down Expand Up @@ -150,7 +154,7 @@ static void nop_out(void *insns, unsigned int len)
unsigned int noplen = len;
if (noplen > ASM_NOP_MAX)
noplen = ASM_NOP_MAX;
memcpy(insns, noptable[noplen], noplen);
text_poke(insns, noptable[noplen], noplen);
insns += noplen;
len -= noplen;
}
Expand Down Expand Up @@ -202,7 +206,7 @@ static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end)
continue;
if (*ptr > text_end)
continue;
**ptr = 0xf0; /* lock prefix */
text_poke(*ptr, ((unsigned char []){0xf0}), 1); /* add lock prefix */
};
}

Expand Down Expand Up @@ -360,10 +364,6 @@ void apply_paravirt(struct paravirt_patch_site *start,
/* Pad the rest with nops */
nop_out(p->instr + used, p->len - used);
}

/* Sync to be conservative, in case we patched following
* instructions */
sync_core();
}
extern struct paravirt_patch_site __start_parainstructions[],
__stop_parainstructions[];
Expand Down Expand Up @@ -406,3 +406,31 @@ void __init alternative_instructions(void)
apply_paravirt(__parainstructions, __parainstructions_end);
local_irq_restore(flags);
}

/*
* Warning:
* When you use this code to patch more than one byte of an instruction
* you need to make sure that other CPUs cannot execute this code in parallel.
* Also no thread must be currently preempted in the middle of these instructions.
* And on the local CPU you need to be protected again NMI or MCE handlers
* seeing an inconsistent instruction while you patch.
*/
void __kprobes text_poke(void *oaddr, unsigned char *opcode, int len)
{
u8 *addr = oaddr;
if (!pte_write(*lookup_address((unsigned long)addr))) {
struct page *p[2] = { virt_to_page(addr), virt_to_page(addr+PAGE_SIZE) };
addr = vmap(p, 2, VM_MAP, PAGE_KERNEL);
if (!addr)
return;
addr += ((unsigned long)oaddr) % PAGE_SIZE;
}
memcpy(addr, opcode, len);
sync_core();
/* Not strictly needed, but can speed CPU recovery up. Ignore cross cacheline
case. */
if (cpu_has_clflush)
asm("clflush (%0) " :: "r" (oaddr) : "memory");
if (addr != oaddr)
vunmap(addr);
}
9 changes: 3 additions & 6 deletions trunk/arch/i386/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <asm/cacheflush.h>
#include <asm/desc.h>
#include <asm/uaccess.h>
#include <asm/alternative.h>

void jprobe_return_end(void);

Expand Down Expand Up @@ -169,16 +170,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
*p->addr = BREAKPOINT_INSTRUCTION;
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1);
}

void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
*p->addr = p->opcode;
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
text_poke(p->addr, &p->opcode, 1);
}

void __kprobes arch_remove_kprobe(struct kprobe *p)
Expand Down
17 changes: 13 additions & 4 deletions trunk/arch/i386/kernel/paravirt.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,28 @@ unsigned paravirt_patch_ignore(unsigned len)
return len;
}

struct branch {
unsigned char opcode;
u32 delta;
} __attribute__((packed));

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);
struct branch b;

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;
b.opcode = 0xe8; /* call */
b.delta = delta;
BUILD_BUG_ON(sizeof(b) != 5);
text_poke(call, (unsigned char *)&b, 5);

return 5;
}
Expand All @@ -150,8 +158,9 @@ unsigned paravirt_patch_jmp(void *target, void *site, unsigned len)
if (len < 5)
return len; /* call too long for patch site */

*jmp++ = 0xe9; /* jmp */
*(unsigned long *)jmp = delta;
b.opcode = 0xe9; /* jmp */
b.delta = delta;
text_poke(call, (unsigned char *)&b, 5);

return 5;
}
Expand Down
14 changes: 3 additions & 11 deletions trunk/arch/i386/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -800,17 +800,9 @@ void mark_rodata_ro(void)
unsigned long start = PFN_ALIGN(_text);
unsigned long size = PFN_ALIGN(_etext) - start;

#ifndef CONFIG_KPROBES
#ifdef CONFIG_HOTPLUG_CPU
/* It must still be possible to apply SMP alternatives. */
if (num_possible_cpus() <= 1)
#endif
{
change_page_attr(virt_to_page(start),
size >> PAGE_SHIFT, PAGE_KERNEL_RX);
printk("Write protecting the kernel text: %luk\n", size >> 10);
}
#endif
change_page_attr(virt_to_page(start),
size >> PAGE_SHIFT, PAGE_KERNEL_RX);
printk("Write protecting the kernel text: %luk\n", size >> 10);
start += size;
size = (unsigned long)__end_rodata - start;
change_page_attr(virt_to_page(start),
Expand Down
10 changes: 3 additions & 7 deletions trunk/arch/x86_64/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
#include <linux/module.h>
#include <linux/kdebug.h>

#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/alternative.h>

void jprobe_return_end(void);
static void __kprobes arch_copy_kprobe(struct kprobe *p);
Expand Down Expand Up @@ -209,16 +209,12 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p)

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
*p->addr = BREAKPOINT_INSTRUCTION;
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1);
}

void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
*p->addr = p->opcode;
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
text_poke(p->addr, &p->opcode, 1);
}

void __kprobes arch_remove_kprobe(struct kprobe *p)
Expand Down
10 changes: 0 additions & 10 deletions trunk/arch/x86_64/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,16 +600,6 @@ void mark_rodata_ro(void)
{
unsigned long start = (unsigned long)_stext, end;

#ifdef CONFIG_HOTPLUG_CPU
/* It must still be possible to apply SMP alternatives. */
if (num_possible_cpus() > 1)
start = (unsigned long)_etext;
#endif

#ifdef CONFIG_KPROBES
start = (unsigned long)__start_rodata;
#endif

end = (unsigned long)__end_rodata;
start = (start + PAGE_SIZE - 1) & PAGE_MASK;
end &= PAGE_MASK;
Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/x86_64/mm/pageattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <asm/tlbflush.h>
#include <asm/io.h>

static inline pte_t *lookup_address(unsigned long address)
pte_t *lookup_address(unsigned long address)
{
pgd_t *pgd = pgd_offset_k(address);
pud_t *pud;
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/asm-i386/alternative.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,6 @@ apply_paravirt(struct paravirt_patch_site *start,
#define __parainstructions_end NULL
#endif

extern void text_poke(void *addr, unsigned char *opcode, int len);

#endif /* _I386_ALTERNATIVE_H */
2 changes: 2 additions & 0 deletions trunk/include/asm-x86_64/alternative.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,6 @@ apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end)
#define __parainstructions_end NULL
#endif

extern void text_poke(void *addr, unsigned char *opcode, int len);

#endif /* _X86_64_ALTERNATIVE_H */
2 changes: 2 additions & 0 deletions trunk/include/asm-x86_64/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ extern struct list_head pgd_list;

extern int kern_addr_valid(unsigned long addr);

pte_t *lookup_address(unsigned long addr);

#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
remap_pfn_range(vma, vaddr, pfn, size, prot)

Expand Down

0 comments on commit 240bf17

Please sign in to comment.