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