xref: /openbsd-src/sys/arch/i386/i386/codepatch.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
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