xref: /openbsd-src/sys/arch/i386/i386/codepatch.c (revision 758370f9b4b9ae70cbde195e176612bb0bacfcde)
1 /*      $OpenBSD: codepatch.c,v 1.6 2023/07/31 17:10:31 bluhm 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
codepatch_fill_nop(void * caddr,uint16_t len)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 *
codepatch_maprw(vaddr_t * nva,vaddr_t dest)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
codepatch_unmaprw(vaddr_t nva)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
codepatch_nop(uint16_t tag)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
codepatch_replace(uint16_t tag,const void * code,size_t len)150 codepatch_replace(uint16_t tag, const 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
codepatch_call(uint16_t tag,void * func)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