1 /* $OpenBSD: codepatch.c,v 1.1 2015/04/19 06:27:17 sf Exp $ */ 2 /* 3 * Copyright (c) 2014-2015 Stefan Fritsch <sf@sfritsch.de> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <machine/codepatch.h> 20 #include <machine/cpu.h> 21 #include <uvm/uvm_extern.h> 22 23 #ifdef CODEPATCH_DEBUG 24 #define DBGPRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) 25 #else 26 #define DBGPRINT(fmt, args...) do {} while (0) 27 #endif 28 29 struct codepatch { 30 uint32_t offset; 31 uint16_t len; 32 uint16_t tag; 33 }; 34 35 extern struct codepatch codepatch_begin; 36 extern struct codepatch codepatch_end; 37 38 #define NOP_LEN_MAX 9 39 40 static const unsigned char nops[][NOP_LEN_MAX] = { 41 { 0x90 }, 42 { 0x66, 0x90 }, 43 { 0x0F, 0x1F, 0x00 }, 44 { 0x0F, 0x1F, 0x40, 0x00 }, 45 { 0x0F, 0x1F, 0x44, 0x00, 0x00 }, 46 { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }, 47 { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }, 48 { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, 49 { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, 50 }; 51 52 void 53 codepatch_fill_nop(void *caddr, uint16_t len) 54 { 55 unsigned char *addr = caddr; 56 uint16_t nop_len; 57 58 if ((strcmp(cpu_vendor, "GenuineIntel") != 0) && 59 (strcmp(cpu_vendor, "AuthenticAMD") != 0)) { 60 /* 61 * Others don't support multi-byte NOPs. 62 * Except maybe some Via C3's, but I couldn't find 63 * definitive information, so better be safe. 64 */ 65 goto singlebyte; 66 } 67 /* 68 * Intel says family 0x6 or 0xf. 69 * AMD says "Athlon or newer", which happen to be the same families. 70 */ 71 switch (cpu_id & 0xf00) { 72 case 0x600: 73 case 0xf00: 74 /* Multi-byte NOP supported */ 75 break; 76 default: 77 goto singlebyte; 78 } 79 80 while (len > 0) { 81 if (len <= NOP_LEN_MAX) 82 nop_len = len; 83 else 84 nop_len = NOP_LEN_MAX; 85 memcpy(addr, nops[nop_len-1], nop_len); 86 addr += nop_len; 87 len -= nop_len; 88 } 89 return; 90 91 singlebyte: 92 /* Use single-byte NOP */ 93 memset(caddr, 0x90, len); 94 } 95 96 /* 97 * Create writeable aliases of memory we need 98 * to write to as kernel is mapped read-only 99 */ 100 void *codepatch_maprw(vaddr_t *nva, vaddr_t dest) 101 { 102 paddr_t kva = trunc_page((paddr_t)dest); 103 paddr_t po = (paddr_t)dest & PAGE_MASK; 104 paddr_t pa1, pa2; 105 106 if (*nva == 0) 107 *nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, 108 &kd_waitok); 109 110 pmap_extract(pmap_kernel(), kva, &pa1); 111 pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); 112 pmap_kenter_pa(*nva, pa1, PROT_READ | PROT_WRITE); 113 pmap_kenter_pa(*nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); 114 pmap_update(pmap_kernel()); 115 116 return (void *)(*nva + po); 117 } 118 119 void codepatch_unmaprw(vaddr_t nva) 120 { 121 if (nva == 0) 122 return; 123 pmap_kremove(nva, 2 * PAGE_SIZE); 124 km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); 125 } 126 127 /* Patch with NOPs */ 128 void 129 codepatch_nop(uint16_t tag) 130 { 131 struct codepatch *patch; 132 unsigned char *rwaddr; 133 vaddr_t addr, rwmap = 0; 134 int i = 0; 135 136 DBGPRINT("patching tag %u", tag); 137 138 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 139 if (patch->tag != tag) 140 continue; 141 addr = KERNBASE + patch->offset; 142 rwaddr = codepatch_maprw(&rwmap, addr); 143 codepatch_fill_nop(rwaddr, patch->len); 144 i++; 145 } 146 codepatch_unmaprw(rwmap); 147 DBGPRINT("patched %d places", i); 148 } 149 150 /* Patch with alternative code */ 151 void 152 codepatch_replace(uint16_t tag, void *code, size_t len) 153 { 154 struct codepatch *patch; 155 unsigned char *rwaddr; 156 vaddr_t addr, rwmap = 0; 157 int i = 0; 158 159 DBGPRINT("patching tag %u with %p", tag, code); 160 161 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 162 if (patch->tag != tag) 163 continue; 164 addr = KERNBASE + patch->offset; 165 166 if (len > patch->len) { 167 panic("%s: can't replace len %u with %zu at %#lx", 168 __func__, patch->len, len, addr); 169 } 170 rwaddr = codepatch_maprw(&rwmap, addr); 171 memcpy(rwaddr, code, len); 172 codepatch_fill_nop(rwaddr + len, patch->len - len); 173 i++; 174 } 175 codepatch_unmaprw(rwmap); 176 DBGPRINT("patched %d places", i); 177 } 178 179 /* Patch with calls to func */ 180 void 181 codepatch_call(uint16_t tag, void *func) 182 { 183 struct codepatch *patch; 184 unsigned char *rwaddr; 185 int32_t offset; 186 int i = 0; 187 vaddr_t addr, rwmap = 0; 188 189 DBGPRINT("patching tag %u with call %p", tag, func); 190 191 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 192 if (patch->tag != tag) 193 continue; 194 addr = KERNBASE + patch->offset; 195 if (patch->len < 5) 196 panic("%s: can't replace len %u with call at %#lx", 197 __func__, patch->len, addr); 198 199 offset = (vaddr_t)func - (addr + 5); 200 rwaddr = codepatch_maprw(&rwmap, addr); 201 rwaddr[0] = 0xe8; /* call near */ 202 memcpy(rwaddr + 1, &offset, sizeof(offset)); 203 codepatch_fill_nop(rwaddr + 5, patch->len - 5); 204 i++; 205 } 206 codepatch_unmaprw(rwmap); 207 DBGPRINT("patched %d places", i); 208 } 209