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