This commit ports the infrastructure to do binary code patching from amd64.
The existing code patching for SMAP is converted to the new infrastruture.
ok kettenis@
"should go in" deraadt@
-# $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
#
attach mainbus at root
file arch/i386/i386/mainbus.c mainbus
+file arch/i386/i386/codepatch.c
+
#device mca at root {...}
#
--- /dev/null
+/* $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);
+}
-/* $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 $ */
/*-
#include <uvm/uvm_extern.h>
+#include <machine/codepatch.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/cpuvar.h>
#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);
}
-/* $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 $ */
/*-
#include <compat/linux/linux_syscall.h>
#endif
+#include <machine/codepatch.h>
#include <machine/cputypes.h>
#include <machine/param.h>
#include <machine/pte.h>
#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
* 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
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
+
/*****************************************************************************/
/*
* 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
GET_CURPCB(%edx)
movl $_C_LABEL(copy_fault),PCB_ONFAULT(%edx)
-_C_LABEL(_copyout_stac):
- SMAP_NOP
+ SMAP_STAC
/* bcopy(%esi, %edi, %eax); */
cld
rep
movsb
-_C_LABEL(_copyout_clac):
- SMAP_NOP
+ SMAP_CLAC
popl PCB_ONFAULT(%edx)
popl %edi
popl %esi
* 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
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
rep
movsb
-_C_LABEL(_copyin_clac):
- SMAP_NOP
+ SMAP_CLAC
GET_CURPCB(%edx)
popl PCB_ONFAULT(%edx)
popl %edi
#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
* 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
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).
*/
* 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
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
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)
/*
* 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
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
--- /dev/null
+/* $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_ */