diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/i386/conf/files.i386 | 4 | ||||
-rw-r--r-- | sys/arch/i386/i386/codepatch.c | 208 | ||||
-rw-r--r-- | sys/arch/i386/i386/cpu.c | 55 | ||||
-rw-r--r-- | sys/arch/i386/i386/locore.s | 69 | ||||
-rw-r--r-- | sys/arch/i386/include/codepatch.h | 52 |
5 files changed, 308 insertions, 80 deletions
diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index b4679e6bea7..85a0f840b96 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.221 2015/04/12 18:37:53 mlarkin Exp $ +# $OpenBSD: files.i386,v 1.222 2015/04/19 06:27:17 sf Exp $ # # new style config file for i386 architecture # @@ -86,6 +86,8 @@ device mainbus: isabus, eisabus, pcibus, mainbus attach mainbus at root file arch/i386/i386/mainbus.c mainbus +file arch/i386/i386/codepatch.c + #device mca at root {...} # diff --git a/sys/arch/i386/i386/codepatch.c b/sys/arch/i386/i386/codepatch.c new file mode 100644 index 00000000000..b1648548d1b --- /dev/null +++ b/sys/arch/i386/i386/codepatch.c @@ -0,0 +1,208 @@ +/* $OpenBSD: codepatch.c,v 1.1 2015/04/19 06:27:17 sf Exp $ */ +/* + * Copyright (c) 2014-2015 Stefan Fritsch <sf@sfritsch.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <machine/codepatch.h> +#include <machine/cpu.h> +#include <uvm/uvm_extern.h> + +#ifdef CODEPATCH_DEBUG +#define DBGPRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) +#else +#define DBGPRINT(fmt, args...) do {} while (0) +#endif + +struct codepatch { + uint32_t offset; + uint16_t len; + uint16_t tag; +}; + +extern struct codepatch codepatch_begin; +extern struct codepatch codepatch_end; + +#define NOP_LEN_MAX 9 + +static const unsigned char nops[][NOP_LEN_MAX] = { + { 0x90 }, + { 0x66, 0x90 }, + { 0x0F, 0x1F, 0x00 }, + { 0x0F, 0x1F, 0x40, 0x00 }, + { 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +void +codepatch_fill_nop(void *caddr, uint16_t len) +{ + unsigned char *addr = caddr; + uint16_t nop_len; + + if ((strcmp(cpu_vendor, "GenuineIntel") != 0) && + (strcmp(cpu_vendor, "AuthenticAMD") != 0)) { + /* + * Others don't support multi-byte NOPs. + * Except maybe some Via C3's, but I couldn't find + * definitive information, so better be safe. + */ + goto singlebyte; + } + /* + * Intel says family 0x6 or 0xf. + * AMD says "Athlon or newer", which happen to be the same families. + */ + switch (cpu_id & 0xf00) { + case 0x600: + case 0xf00: + /* Multi-byte NOP supported */ + break; + default: + goto singlebyte; + } + + while (len > 0) { + if (len <= NOP_LEN_MAX) + nop_len = len; + else + nop_len = NOP_LEN_MAX; + memcpy(addr, nops[nop_len-1], nop_len); + addr += nop_len; + len -= nop_len; + } + return; + +singlebyte: + /* Use single-byte NOP */ + memset(caddr, 0x90, len); +} + +/* + * Create writeable aliases of memory we need + * to write to as kernel is mapped read-only + */ +void *codepatch_maprw(vaddr_t *nva, vaddr_t dest) +{ + paddr_t kva = trunc_page((paddr_t)dest); + paddr_t po = (paddr_t)dest & PAGE_MASK; + paddr_t pa1, pa2; + + if (*nva == 0) + *nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, + &kd_waitok); + + pmap_extract(pmap_kernel(), kva, &pa1); + pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); + pmap_kenter_pa(*nva, pa1, PROT_READ | PROT_WRITE); + pmap_kenter_pa(*nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); + pmap_update(pmap_kernel()); + + return (void *)(*nva + po); +} + +void codepatch_unmaprw(vaddr_t nva) +{ + if (nva == 0) + return; + pmap_kremove(nva, 2 * PAGE_SIZE); + km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); +} + +/* Patch with NOPs */ +void +codepatch_nop(uint16_t tag) +{ + struct codepatch *patch; + unsigned char *rwaddr; + vaddr_t addr, rwmap = 0; + int i = 0; + + DBGPRINT("patching tag %u", tag); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + rwaddr = codepatch_maprw(&rwmap, addr); + codepatch_fill_nop(rwaddr, patch->len); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} + +/* Patch with alternative code */ +void +codepatch_replace(uint16_t tag, void *code, size_t len) +{ + struct codepatch *patch; + unsigned char *rwaddr; + vaddr_t addr, rwmap = 0; + int i = 0; + + DBGPRINT("patching tag %u with %p", tag, code); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + + if (len > patch->len) { + panic("%s: can't replace len %u with %zu at %#lx", + __func__, patch->len, len, addr); + } + rwaddr = codepatch_maprw(&rwmap, addr); + memcpy(rwaddr, code, len); + codepatch_fill_nop(rwaddr + len, patch->len - len); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} + +/* Patch with calls to func */ +void +codepatch_call(uint16_t tag, void *func) +{ + struct codepatch *patch; + unsigned char *rwaddr; + int32_t offset; + int i = 0; + vaddr_t addr, rwmap = 0; + + DBGPRINT("patching tag %u with call %p", tag, func); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + if (patch->len < 5) + panic("%s: can't replace len %u with call at %#lx", + __func__, patch->len, addr); + + offset = (vaddr_t)func - (addr + 5); + rwaddr = codepatch_maprw(&rwmap, addr); + rwaddr[0] = 0xe8; /* call near */ + memcpy(rwaddr + 1, &offset, sizeof(offset)); + codepatch_fill_nop(rwaddr + 5, patch->len - 5); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} diff --git a/sys/arch/i386/i386/cpu.c b/sys/arch/i386/i386/cpu.c index 78cf7fd033f..a58e811cd31 100644 --- a/sys/arch/i386/i386/cpu.c +++ b/sys/arch/i386/i386/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.62 2015/04/18 22:16:21 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.63 2015/04/19 06:27:17 sf Exp $ */ /* $NetBSD: cpu.c,v 1.1.2.7 2000/06/26 02:04:05 sommerfeld Exp $ */ /*- @@ -77,6 +77,7 @@ #include <uvm/uvm_extern.h> +#include <machine/codepatch.h> #include <machine/cpu.h> #include <machine/cpufunc.h> #include <machine/cpuvar.h> @@ -167,69 +168,23 @@ struct cfdriver cpu_cd = { #ifndef SMALL_KERNEL void replacesmap(void); -extern int _copyout_stac; -extern int _copyout_clac; -extern int _copyin_stac; -extern int _copyin_clac; -extern int _copy_fault_clac; -extern int _copyoutstr_stac; -extern int _copyinstr_stac; -extern int _copystr_fault_clac; -extern int _ucas_32_stac; -extern int _ucas_32_clac; extern int _stac; extern int _clac; -static const struct { - void *daddr; - void *saddr; -} ireplace[] = { - { &_copyout_stac, &_stac }, - { &_copyout_clac, &_clac }, - { &_copyin_stac, &_stac }, - { &_copyin_clac, &_clac }, - { &_copy_fault_clac, &_clac }, - { &_copyoutstr_stac, &_stac }, - { &_copyinstr_stac, &_stac }, - { &_copystr_fault_clac, &_clac }, - { &_ucas_32_stac, &_stac }, - { &_ucas_32_clac, &_clac }, -}; - void replacesmap(void) { static int replacedone = 0; - int i, s; - vaddr_t nva; + int s; if (replacedone) return; replacedone = 1; s = splhigh(); - /* - * Create writeable aliases of memory we need - * to write to as kernel is mapped read-only - */ - nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, &kd_waitok); - - for (i = 0; i < nitems(ireplace); i++) { - paddr_t kva = trunc_page((paddr_t)ireplace[i].daddr); - paddr_t po = (paddr_t)ireplace[i].daddr & PAGE_MASK; - paddr_t pa1, pa2; - - pmap_extract(pmap_kernel(), kva, &pa1); - pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); - pmap_kenter_pa(nva, pa1, PROT_READ | PROT_WRITE); - pmap_kenter_pa(nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); - pmap_update(pmap_kernel()); - - /* replace 3 byte nops with stac/clac instructions */ - bcopy(ireplace[i].saddr, (void *)(nva + po), 3); - } - km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); + codepatch_replace(CPTAG_STAC, &_stac, 3); + codepatch_replace(CPTAG_CLAC, &_clac, 3); splx(s); } diff --git a/sys/arch/i386/i386/locore.s b/sys/arch/i386/i386/locore.s index 81e61c70cdb..7413d2bc29a 100644 --- a/sys/arch/i386/i386/locore.s +++ b/sys/arch/i386/i386/locore.s @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.s,v 1.153 2015/04/18 05:14:05 guenther Exp $ */ +/* $OpenBSD: locore.s,v 1.154 2015/04/19 06:27:17 sf Exp $ */ /* $NetBSD: locore.s,v 1.145 1996/05/03 19:41:19 christos Exp $ */ /*- @@ -51,6 +51,7 @@ #include <compat/linux/linux_syscall.h> #endif +#include <machine/codepatch.h> #include <machine/cputypes.h> #include <machine/param.h> #include <machine/pte.h> @@ -64,6 +65,7 @@ #include <machine/i82489reg.h> #endif +#ifndef SMALL_KERNEL /* * As stac/clac SMAP instructions are 3 bytes, we want the fastest * 3 byte nop sequence possible here. This will be replaced by @@ -73,7 +75,21 @@ * on all family 0x6 and 0xf processors (ie 686+) * So use 3 of the single byte nops for compatibility */ -#define SMAP_NOP .byte 0x90, 0x90, 0x90 +#define SMAP_NOP .byte 0x90, 0x90, 0x90 +#define SMAP_STAC CODEPATCH_START ;\ + SMAP_NOP ;\ + CODEPATCH_END(CPTAG_STAC) +#define SMAP_CLAC CODEPATCH_START ;\ + SMAP_NOP ;\ + CODEPATCH_END(CPTAG_CLAC) + +#else + +#define SMAP_STAC +#define SMAP_CLAC + +#endif + /* * override user-land alignment before including asm.h @@ -662,6 +678,18 @@ NENTRY(proc_trampoline) INTRFASTEXIT /* NOTREACHED */ + /* This must come before any use of the CODEPATCH macros */ + .section .codepatch,"a" + .align 8 + .globl _C_LABEL(codepatch_begin) +_C_LABEL(codepatch_begin): + .previous + + .section .codepatchend,"a" + .globl _C_LABEL(codepatch_end) +_C_LABEL(codepatch_end): + .previous + /*****************************************************************************/ /* @@ -793,7 +821,6 @@ ENTRY(kcopy) * copyout(caddr_t from, caddr_t to, size_t len); * Copy len bytes into the user's address space. */ -.globl _C_LABEL(_copyout_stac), _C_LABEL(_copyout_clac) ENTRY(copyout) #ifdef DDB pushl %ebp @@ -822,8 +849,7 @@ ENTRY(copyout) GET_CURPCB(%edx) movl $_C_LABEL(copy_fault),PCB_ONFAULT(%edx) -_C_LABEL(_copyout_stac): - SMAP_NOP + SMAP_STAC /* bcopy(%esi, %edi, %eax); */ cld @@ -836,8 +862,7 @@ _C_LABEL(_copyout_stac): rep movsb -_C_LABEL(_copyout_clac): - SMAP_NOP + SMAP_CLAC popl PCB_ONFAULT(%edx) popl %edi popl %esi @@ -851,7 +876,6 @@ _C_LABEL(_copyout_clac): * copyin(caddr_t from, caddr_t to, size_t len); * Copy len bytes from the user's address space. */ -.globl _C_LABEL(_copyin_stac), _C_LABEL(_copyin_clac) ENTRY(copyin) #ifdef DDB pushl %ebp @@ -862,8 +886,7 @@ ENTRY(copyin) GET_CURPCB(%eax) pushl $0 movl $_C_LABEL(copy_fault),PCB_ONFAULT(%eax) -_C_LABEL(_copyin_stac): - SMAP_NOP + SMAP_STAC movl 16+FPADD(%esp),%esi movl 20+FPADD(%esp),%edi @@ -891,8 +914,7 @@ _C_LABEL(_copyin_stac): rep movsb -_C_LABEL(_copyin_clac): - SMAP_NOP + SMAP_CLAC GET_CURPCB(%edx) popl PCB_ONFAULT(%edx) popl %edi @@ -903,10 +925,8 @@ _C_LABEL(_copyin_clac): #endif ret -.globl _C_LABEL(_copy_fault_clac) ENTRY(copy_fault) -_C_LABEL(_copy_fault_clac): - SMAP_NOP + SMAP_CLAC GET_CURPCB(%edx) popl PCB_ONFAULT(%edx) popl %edi @@ -924,7 +944,6 @@ _C_LABEL(_copy_fault_clac): * NUL) in *lencopied. If the string is too long, return ENAMETOOLONG; else * return 0 or EFAULT. */ -.globl _C_LABEL(_copyoutstr_stac) ENTRY(copyoutstr) #ifdef DDB pushl %ebp @@ -939,8 +958,7 @@ ENTRY(copyoutstr) 5: GET_CURPCB(%eax) movl $_C_LABEL(copystr_fault),PCB_ONFAULT(%eax) -_C_LABEL(_copyoutstr_stac): - SMAP_NOP + SMAP_STAC /* * Get min(%edx, VM_MAXUSER_ADDRESS-%edi). */ @@ -983,7 +1001,6 @@ _C_LABEL(_copyoutstr_stac): * NUL) in *lencopied. If the string is too long, return ENAMETOOLONG; else * return 0 or EFAULT. */ -.globl _C_LABEL(_copyinstr_stac) ENTRY(copyinstr) #ifdef DDB pushl %ebp @@ -993,8 +1010,7 @@ ENTRY(copyinstr) pushl %edi GET_CURPCB(%ecx) movl $_C_LABEL(copystr_fault),PCB_ONFAULT(%ecx) -_C_LABEL(_copyinstr_stac): - SMAP_NOP + SMAP_STAC movl 12+FPADD(%esp),%esi # %esi = from movl 16+FPADD(%esp),%edi # %edi = to @@ -1034,13 +1050,11 @@ _C_LABEL(_copyinstr_stac): movl $ENAMETOOLONG,%eax jmp copystr_return -.globl _C_LABEL(_copystr_fault_clac) ENTRY(copystr_fault) movl $EFAULT,%eax copystr_return: -_C_LABEL(_copystr_fault_clac): - SMAP_NOP + SMAP_CLAC /* Set *lencopied and return %eax. */ GET_CURPCB(%ecx) movl $0,PCB_ONFAULT(%ecx) @@ -1686,7 +1700,6 @@ ENTRY(cpu_paenable) /* * ucas_32(volatile int32_t *uptr, int32_t old, int32_t new); */ -.global _C_LABEL(_ucas_32_stac), _C_LABEL(_ucas_32_clac) ENTRY(ucas_32) #ifdef DDB pushl %ebp @@ -1705,14 +1718,12 @@ ENTRY(ucas_32) GET_CURPCB(%edx) movl $_C_LABEL(copy_fault),PCB_ONFAULT(%edx) -_C_LABEL(_ucas_32_stac): - SMAP_NOP + SMAP_STAC lock cmpxchgl %edi, (%esi) -_C_LABEL(_ucas_32_clac): - SMAP_NOP + SMAP_CLAC popl PCB_ONFAULT(%edx) popl %edi popl %esi diff --git a/sys/arch/i386/include/codepatch.h b/sys/arch/i386/include/codepatch.h new file mode 100644 index 00000000000..cf9ad7abaca --- /dev/null +++ b/sys/arch/i386/include/codepatch.h @@ -0,0 +1,52 @@ +/* $OpenBSD: codepatch.h,v 1.1 2015/04/19 06:27:17 sf Exp $ */ +/* + * Copyright (c) 2014-2015 Stefan Fritsch <sf@sfritsch.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_CODEPATCH_H_ +#define _MACHINE_CODEPATCH_H_ + +#include <machine/param.h> + +#ifndef _LOCORE + +void *codepatch_maprw(vaddr_t *nva, vaddr_t dest); +void codepatch_unmaprw(vaddr_t nva); +void codepatch_fill_nop(void *caddr, uint16_t len); +void codepatch_nop(uint16_t tag); +void codepatch_replace(uint16_t tag, void *code, size_t len); +void codepatch_call(uint16_t tag, void *func); + +#endif /* !_LOCORE */ + +/* + * Mark the start of some code snippet to be patched. + */ +#define CODEPATCH_START 998: +/* + * Mark the end of some code to be patched, and assign the given tag. + */ +#define CODEPATCH_END(tag) \ + 999: \ + .section .codepatch, "a" ;\ + .int (998b - KERNBASE) ;\ + .short (999b - 998b) ;\ + .short tag ;\ + .previous + +#define CPTAG_STAC 1 +#define CPTAG_CLAC 2 + +#endif /* _MACHINE_CODEPATCH_H_ */ |