1 /* $OpenBSD: codepatch.c,v 1.4 2017/07/10 00:59:24 mortimer 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 vaddr_t addr; 31 uint16_t len; 32 uint16_t tag; 33 }; 34 35 extern struct codepatch codepatch_begin; 36 extern struct codepatch codepatch_end; 37 38 void 39 codepatch_fill_nop(void *caddr, uint16_t len) 40 { 41 unsigned char *addr = caddr; 42 uint16_t nop_len; 43 44 if ((strcmp(cpu_vendor, "GenuineIntel") != 0) && 45 (strcmp(cpu_vendor, "AuthenticAMD") != 0)) { 46 /* 47 * Others don't support multi-byte NOPs. 48 * Except maybe some Via C3's, but I couldn't find 49 * definitive information, so better be safe. 50 */ 51 goto singlebyte; 52 } 53 /* 54 * Intel says family 0x6 or 0xf. 55 * AMD says "Athlon or newer", which happen to be the same families. 56 */ 57 switch (cpu_id & 0xf00) { 58 case 0x600: 59 case 0xf00: 60 /* Multi-byte NOP supported */ 61 break; 62 default: 63 goto singlebyte; 64 } 65 66 while (len > 0) { 67 nop_len = len < 127 ? len : 127; 68 switch (nop_len) { 69 case 1: 70 addr[0] = 0x90; 71 break; 72 case 2: 73 addr[0] = 0x66; 74 addr[1] = 0x90; 75 break; 76 default: 77 addr[0] = 0xEB; 78 addr[1] = nop_len - 2; 79 memset(addr + 2, 0xCC, nop_len - 2); 80 break; 81 } 82 addr += nop_len; 83 len -= nop_len; 84 } 85 return; 86 87 singlebyte: 88 /* Use single-byte NOP */ 89 memset(caddr, 0x90, len); 90 } 91 92 /* 93 * Create writeable aliases of memory we need 94 * to write to as kernel is mapped read-only 95 */ 96 void * 97 codepatch_maprw(vaddr_t *nva, vaddr_t dest) 98 { 99 paddr_t kva = trunc_page((paddr_t)dest); 100 paddr_t po = (paddr_t)dest & PAGE_MASK; 101 paddr_t pa1, pa2; 102 103 if (*nva == 0) 104 *nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, 105 &kd_waitok); 106 107 pmap_extract(pmap_kernel(), kva, &pa1); 108 pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); 109 pmap_kenter_pa(*nva, pa1, PROT_READ | PROT_WRITE); 110 pmap_kenter_pa(*nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); 111 pmap_update(pmap_kernel()); 112 113 return (void *)(*nva + po); 114 } 115 116 void 117 codepatch_unmaprw(vaddr_t nva) 118 { 119 if (nva == 0) 120 return; 121 pmap_kremove(nva, 2 * PAGE_SIZE); 122 km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); 123 } 124 125 /* Patch with NOPs */ 126 void 127 codepatch_nop(uint16_t tag) 128 { 129 struct codepatch *patch; 130 unsigned char *rwaddr; 131 vaddr_t rwmap = 0; 132 int i = 0; 133 134 DBGPRINT("patching tag %u", tag); 135 136 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 137 if (patch->tag != tag) 138 continue; 139 rwaddr = codepatch_maprw(&rwmap, patch->addr); 140 codepatch_fill_nop(rwaddr, patch->len); 141 i++; 142 } 143 codepatch_unmaprw(rwmap); 144 DBGPRINT("patched %d places", i); 145 } 146 147 /* Patch with alternative code */ 148 void 149 codepatch_replace(uint16_t tag, void *code, size_t len) 150 { 151 struct codepatch *patch; 152 unsigned char *rwaddr; 153 vaddr_t rwmap = 0; 154 int i = 0; 155 156 DBGPRINT("patching tag %u with %p", tag, code); 157 158 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 159 if (patch->tag != tag) 160 continue; 161 162 if (len > patch->len) { 163 panic("%s: can't replace len %u with %zu at %#lx", 164 __func__, patch->len, len, patch->addr); 165 } 166 rwaddr = codepatch_maprw(&rwmap, patch->addr); 167 memcpy(rwaddr, code, len); 168 codepatch_fill_nop(rwaddr + len, patch->len - len); 169 i++; 170 } 171 codepatch_unmaprw(rwmap); 172 DBGPRINT("patched %d places", i); 173 } 174 175 /* Patch with calls to func */ 176 void 177 codepatch_call(uint16_t tag, void *func) 178 { 179 struct codepatch *patch; 180 unsigned char *rwaddr; 181 int32_t offset; 182 int i = 0; 183 vaddr_t rwmap = 0; 184 185 DBGPRINT("patching tag %u with call %p", tag, func); 186 187 for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { 188 if (patch->tag != tag) 189 continue; 190 if (patch->len < 5) 191 panic("%s: can't replace len %u with call at %#lx", 192 __func__, patch->len, patch->addr); 193 194 offset = (vaddr_t)func - (patch->addr + 5); 195 rwaddr = codepatch_maprw(&rwmap, patch->addr); 196 rwaddr[0] = 0xe8; /* call near */ 197 memcpy(rwaddr + 1, &offset, sizeof(offset)); 198 codepatch_fill_nop(rwaddr + 5, patch->len - 5); 199 i++; 200 } 201 codepatch_unmaprw(rwmap); 202 DBGPRINT("patched %d places", i); 203 } 204