1*8f18579dSriastradh /* $NetBSD: patch.c,v 1.53 2022/08/20 23:48:51 riastradh Exp $ */
2b07ec3fcSad
3b07ec3fcSad /*-
4d3f85b9eSad * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc.
5b07ec3fcSad * All rights reserved.
6b07ec3fcSad *
7b07ec3fcSad * This code is derived from software contributed to The NetBSD Foundation
8b07ec3fcSad * by Andrew Doran.
9b07ec3fcSad *
10b07ec3fcSad * Redistribution and use in source and binary forms, with or without
11b07ec3fcSad * modification, are permitted provided that the following conditions
12b07ec3fcSad * are met:
13b07ec3fcSad * 1. Redistributions of source code must retain the above copyright
14b07ec3fcSad * notice, this list of conditions and the following disclaimer.
15b07ec3fcSad * 2. Redistributions in binary form must reproduce the above copyright
16b07ec3fcSad * notice, this list of conditions and the following disclaimer in the
17b07ec3fcSad * documentation and/or other materials provided with the distribution.
18b07ec3fcSad *
19b07ec3fcSad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20b07ec3fcSad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21b07ec3fcSad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22b07ec3fcSad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23b07ec3fcSad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24b07ec3fcSad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25b07ec3fcSad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26b07ec3fcSad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27b07ec3fcSad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28b07ec3fcSad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b07ec3fcSad * POSSIBILITY OF SUCH DAMAGE.
30b07ec3fcSad */
31b07ec3fcSad
32b07ec3fcSad /*
33b07ec3fcSad * Patch kernel code at boot time, depending on available CPU features.
34b07ec3fcSad */
35b07ec3fcSad
36b07ec3fcSad #include <sys/cdefs.h>
37*8f18579dSriastradh __KERNEL_RCSID(0, "$NetBSD: patch.c,v 1.53 2022/08/20 23:48:51 riastradh Exp $");
38b07ec3fcSad
39b07ec3fcSad #include "opt_lockdebug.h"
40c64f34a1Sdyoung #ifdef i386
41e48f8429Sdyoung #include "opt_spldebug.h"
42c64f34a1Sdyoung #endif
43b07ec3fcSad
44b07ec3fcSad #include <sys/types.h>
45b07ec3fcSad #include <sys/systm.h>
46b07ec3fcSad
47b07ec3fcSad #include <machine/cpu.h>
48b07ec3fcSad #include <machine/cpufunc.h>
49b07ec3fcSad #include <machine/specialreg.h>
5099d8611cSmaxv #include <machine/frameasm.h>
51b07ec3fcSad
52fc3c63d9Smaxv #include <uvm/uvm.h>
53fc3c63d9Smaxv #include <machine/pmap.h>
54*8f18579dSriastradh #include <machine/pmap_private.h>
55fc3c63d9Smaxv
56448b1ee9Sriastradh #include <x86/bootspace.h>
57b07ec3fcSad #include <x86/cpuvar.h>
58b07ec3fcSad #include <x86/cputypes.h>
59b07ec3fcSad
60daaf2254Smaxv __link_set_decl(x86_hotpatch_descriptors, struct x86_hotpatch_descriptor);
61daaf2254Smaxv
62daaf2254Smaxv struct x86_hotpatch_destination {
6399d8611cSmaxv uint8_t name;
6499d8611cSmaxv uint8_t size;
6599d8611cSmaxv void *addr;
6699d8611cSmaxv } __packed;
6799d8611cSmaxv
68daaf2254Smaxv /* -------------------------------------------------------------------------- */
69daaf2254Smaxv
70daaf2254Smaxv /* CLAC instruction, part of SMAP. */
71daaf2254Smaxv extern uint8_t hp_clac, hp_clac_end;
72daaf2254Smaxv static const struct x86_hotpatch_source hp_clac_source = {
73daaf2254Smaxv .saddr = &hp_clac,
74daaf2254Smaxv .eaddr = &hp_clac_end
75daaf2254Smaxv };
76daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_clac_desc = {
77daaf2254Smaxv .name = HP_NAME_CLAC,
78daaf2254Smaxv .nsrc = 1,
79daaf2254Smaxv .srcs = { &hp_clac_source }
80daaf2254Smaxv };
81daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_clac_desc);
82daaf2254Smaxv
83daaf2254Smaxv /* STAC instruction, part of SMAP. */
84daaf2254Smaxv extern uint8_t hp_stac, hp_stac_end;
85daaf2254Smaxv static const struct x86_hotpatch_source hp_stac_source = {
86daaf2254Smaxv .saddr = &hp_stac,
87daaf2254Smaxv .eaddr = &hp_stac_end
88daaf2254Smaxv };
89daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_stac_desc = {
90daaf2254Smaxv .name = HP_NAME_STAC,
91daaf2254Smaxv .nsrc = 1,
92daaf2254Smaxv .srcs = { &hp_stac_source }
93daaf2254Smaxv };
94daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_stac_desc);
95daaf2254Smaxv
96daaf2254Smaxv /* Errata on certain AMD CPUs. */
97daaf2254Smaxv extern uint8_t hp_retfence, hp_retfence_end;
98daaf2254Smaxv static const struct x86_hotpatch_source hp_retfence_source = {
99daaf2254Smaxv .saddr = &hp_retfence,
100daaf2254Smaxv .eaddr = &hp_retfence_end
101daaf2254Smaxv };
102daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_retfence_desc = {
103daaf2254Smaxv .name = HP_NAME_RETFENCE,
104daaf2254Smaxv .nsrc = 1,
105daaf2254Smaxv .srcs = { &hp_retfence_source }
106daaf2254Smaxv };
107daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_retfence_desc);
108daaf2254Smaxv
109daaf2254Smaxv /* No lock when on a single processor. */
110daaf2254Smaxv extern uint8_t hp_nolock, hp_nolock_end;
111daaf2254Smaxv static const struct x86_hotpatch_source hp_nolock_source = {
112daaf2254Smaxv .saddr = &hp_nolock,
113daaf2254Smaxv .eaddr = &hp_nolock_end
114daaf2254Smaxv };
115daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_nolock_desc = {
116daaf2254Smaxv .name = HP_NAME_NOLOCK,
117daaf2254Smaxv .nsrc = 1,
118daaf2254Smaxv .srcs = { &hp_nolock_source }
119daaf2254Smaxv };
120daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_nolock_desc);
121daaf2254Smaxv
122daaf2254Smaxv #ifdef i386
123daaf2254Smaxv /* CAS_64. */
124daaf2254Smaxv extern uint8_t _atomic_cas_cx8, _atomic_cas_cx8_end;
125daaf2254Smaxv static const struct x86_hotpatch_source hp_cas_cx8_source = {
126daaf2254Smaxv .saddr = &_atomic_cas_cx8,
127daaf2254Smaxv .eaddr = &_atomic_cas_cx8_end
128daaf2254Smaxv };
129daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_cas_cx8_desc = {
130daaf2254Smaxv .name = HP_NAME_CAS_64,
131daaf2254Smaxv .nsrc = 1,
132daaf2254Smaxv .srcs = { &hp_cas_cx8_source }
133daaf2254Smaxv };
134daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_cas_cx8_desc);
135daaf2254Smaxv
136daaf2254Smaxv /* SPLLOWER. */
137daaf2254Smaxv extern uint8_t cx8_spllower, cx8_spllower_end;
138daaf2254Smaxv static const struct x86_hotpatch_source hp_cx8_spllower_source = {
139daaf2254Smaxv .saddr = &cx8_spllower,
140daaf2254Smaxv .eaddr = &cx8_spllower_end
141daaf2254Smaxv };
142daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_cx8_spllower_desc = {
143daaf2254Smaxv .name = HP_NAME_SPLLOWER,
144daaf2254Smaxv .nsrc = 1,
145daaf2254Smaxv .srcs = { &hp_cx8_spllower_source }
146daaf2254Smaxv };
147daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_cx8_spllower_desc);
148daaf2254Smaxv
149daaf2254Smaxv /* MUTEX_EXIT. */
150d6f215d4Smaxv #ifndef LOCKDEBUG
151daaf2254Smaxv extern uint8_t i686_mutex_spin_exit, i686_mutex_spin_exit_end;
152daaf2254Smaxv static const struct x86_hotpatch_source hp_i686_mutex_spin_exit_source = {
153daaf2254Smaxv .saddr = &i686_mutex_spin_exit,
154daaf2254Smaxv .eaddr = &i686_mutex_spin_exit_end
155daaf2254Smaxv };
156daaf2254Smaxv static const struct x86_hotpatch_descriptor hp_i686_mutex_spin_exit_desc = {
157daaf2254Smaxv .name = HP_NAME_MUTEX_EXIT,
158daaf2254Smaxv .nsrc = 1,
159daaf2254Smaxv .srcs = { &hp_i686_mutex_spin_exit_source }
160daaf2254Smaxv };
161daaf2254Smaxv __link_set_add_rodata(x86_hotpatch_descriptors, hp_i686_mutex_spin_exit_desc);
162daaf2254Smaxv #endif
163d6f215d4Smaxv #endif
164daaf2254Smaxv
165daaf2254Smaxv /* -------------------------------------------------------------------------- */
166daaf2254Smaxv
1670de5da96Sgmcgarry static inline void __unused
patchbytes(void * addr,const uint8_t * bytes,size_t size)1681cb7949fSmaxv patchbytes(void *addr, const uint8_t *bytes, size_t size)
169b07ec3fcSad {
1701cb7949fSmaxv uint8_t *ptr = (uint8_t *)addr;
1711cb7949fSmaxv size_t i;
1727135f4bdSad
1731cb7949fSmaxv for (i = 0; i < size; i++) {
1741cb7949fSmaxv ptr[i] = bytes[i];
1751cb7949fSmaxv }
176b07ec3fcSad }
177b07ec3fcSad
178daaf2254Smaxv /*
179daaf2254Smaxv * Rules: each pointer accessed in this function MUST be read-only.
180daaf2254Smaxv *
181daaf2254Smaxv * Called from ASM only, prototype not public.
182daaf2254Smaxv */
183daaf2254Smaxv int x86_hotpatch_apply(uint8_t, uint8_t);
184daaf2254Smaxv int
185daaf2254Smaxv __noubsan /* the local variables have unknown alignment to UBSan */
x86_hotpatch_apply(uint8_t name,uint8_t sel)186daaf2254Smaxv x86_hotpatch_apply(uint8_t name, uint8_t sel)
18799d8611cSmaxv {
188daaf2254Smaxv struct x86_hotpatch_descriptor * const *iter;
189daaf2254Smaxv const struct x86_hotpatch_descriptor *desc;
190daaf2254Smaxv const struct x86_hotpatch_source *src;
191daaf2254Smaxv const struct x86_hotpatch_destination *hps, *hpe, *hp;
19299d8611cSmaxv extern char __rodata_hotpatch_start;
19399d8611cSmaxv extern char __rodata_hotpatch_end;
194daaf2254Smaxv const uint8_t *bytes;
195daaf2254Smaxv bool found = false;
196daaf2254Smaxv size_t size;
19799d8611cSmaxv
198daaf2254Smaxv /*
199daaf2254Smaxv * Find the descriptor, and perform some sanity checks.
200daaf2254Smaxv */
201daaf2254Smaxv __link_set_foreach(iter, x86_hotpatch_descriptors) {
202daaf2254Smaxv desc = *iter;
203daaf2254Smaxv if (desc->name == name) {
204daaf2254Smaxv found = true;
205daaf2254Smaxv break;
206daaf2254Smaxv }
207daaf2254Smaxv }
208daaf2254Smaxv if (!found)
209daaf2254Smaxv return -1;
210daaf2254Smaxv if (desc->nsrc > 2)
211daaf2254Smaxv return -1;
212daaf2254Smaxv if (sel >= desc->nsrc)
213daaf2254Smaxv return -1;
21499d8611cSmaxv
215daaf2254Smaxv /*
216daaf2254Smaxv * Get the hotpatch source.
217daaf2254Smaxv */
218daaf2254Smaxv src = desc->srcs[sel];
219daaf2254Smaxv bytes = src->saddr;
220daaf2254Smaxv size = (size_t)src->eaddr - (size_t)src->saddr;
221daaf2254Smaxv
222daaf2254Smaxv /*
223daaf2254Smaxv * Apply the hotpatch on each registered destination.
224daaf2254Smaxv */
225daaf2254Smaxv hps = (struct x86_hotpatch_destination *)&__rodata_hotpatch_start;
226daaf2254Smaxv hpe = (struct x86_hotpatch_destination *)&__rodata_hotpatch_end;
22799d8611cSmaxv for (hp = hps; hp < hpe; hp++) {
22899d8611cSmaxv if (hp->name != name) {
22999d8611cSmaxv continue;
23099d8611cSmaxv }
23199d8611cSmaxv if (hp->size != size) {
232daaf2254Smaxv return -1;
23399d8611cSmaxv }
23499d8611cSmaxv patchbytes(hp->addr, bytes, size);
23599d8611cSmaxv }
236daaf2254Smaxv
237daaf2254Smaxv return 0;
23899d8611cSmaxv }
23999d8611cSmaxv
240fc3c63d9Smaxv #ifdef __x86_64__
241fc3c63d9Smaxv /*
242fc3c63d9Smaxv * The CPU added the D bit on the text pages while we were writing to them.
243fc3c63d9Smaxv * Remove that bit. Kinda annoying, but we can't avoid it.
244fc3c63d9Smaxv */
245fc3c63d9Smaxv static void
remove_d_bit(void)246fc3c63d9Smaxv remove_d_bit(void)
247fc3c63d9Smaxv {
248fc3c63d9Smaxv extern struct bootspace bootspace;
249fc3c63d9Smaxv pt_entry_t pte;
250fc3c63d9Smaxv vaddr_t va;
251fc3c63d9Smaxv size_t i, n;
252fc3c63d9Smaxv
253fc3c63d9Smaxv for (i = 0; i < BTSPACE_NSEGS; i++) {
254fc3c63d9Smaxv if (bootspace.segs[i].type != BTSEG_TEXT)
255fc3c63d9Smaxv continue;
256fc3c63d9Smaxv va = bootspace.segs[i].va;
257fc3c63d9Smaxv n = 0;
258fc3c63d9Smaxv while (n < bootspace.segs[i].sz) {
259fc3c63d9Smaxv if (L2_BASE[pl2_i(va)] & PTE_PS) {
260fc3c63d9Smaxv pte = L2_BASE[pl2_i(va)] & ~PTE_D;
261fc3c63d9Smaxv pmap_pte_set(&L2_BASE[pl2_i(va)], pte);
262fc3c63d9Smaxv n += NBPD_L2;
263fc3c63d9Smaxv va += NBPD_L2;
264fc3c63d9Smaxv } else {
265fc3c63d9Smaxv pte = L1_BASE[pl1_i(va)] & ~PTE_D;
266fc3c63d9Smaxv pmap_pte_set(&L1_BASE[pl1_i(va)], pte);
267fc3c63d9Smaxv n += NBPD_L1;
268fc3c63d9Smaxv va += NBPD_L1;
269fc3c63d9Smaxv }
270fc3c63d9Smaxv }
271fc3c63d9Smaxv }
272fc3c63d9Smaxv
273fc3c63d9Smaxv tlbflushg();
274fc3c63d9Smaxv }
275fc3c63d9Smaxv #else
276fc3c63d9Smaxv #define remove_d_bit() __nothing
277fc3c63d9Smaxv #endif
278fc3c63d9Smaxv
279daaf2254Smaxv /*
280daaf2254Smaxv * Interrupts disabled here. Called from ASM only, prototype not public.
281daaf2254Smaxv */
282daaf2254Smaxv void x86_hotpatch_cleanup(int);
283b07ec3fcSad void
x86_hotpatch_cleanup(int retval)284daaf2254Smaxv x86_hotpatch_cleanup(int retval)
285f2cbc9d8Smaxv {
286daaf2254Smaxv if (retval != 0) {
287daaf2254Smaxv panic("x86_hotpatch_apply failed");
288daaf2254Smaxv }
289fc3c63d9Smaxv
290fc3c63d9Smaxv remove_d_bit();
291f2cbc9d8Smaxv }
292f2cbc9d8Smaxv
293daaf2254Smaxv /* -------------------------------------------------------------------------- */
294f2cbc9d8Smaxv
295f2cbc9d8Smaxv void
x86_patch(bool early)29630fd42e8Sad x86_patch(bool early)
297b07ec3fcSad {
298d3f85b9eSad static bool first, second;
299b07ec3fcSad
300d3f85b9eSad if (early) {
301d3f85b9eSad if (first)
302b07ec3fcSad return;
303d3f85b9eSad first = true;
304d3f85b9eSad } else {
305d3f85b9eSad if (second)
306d3f85b9eSad return;
307d3f85b9eSad second = true;
308d3f85b9eSad }
309b07ec3fcSad
31030fd42e8Sad if (!early && ncpu == 1) {
311163e94a6Sad #ifndef LOCKDEBUG
3129ef803feSmaxv /*
3139ef803feSmaxv * Uniprocessor: kill LOCK prefixes.
3149ef803feSmaxv */
315daaf2254Smaxv x86_hotpatch(HP_NAME_NOLOCK, 0);
3169ef803feSmaxv #endif
31730fd42e8Sad }
3189ef803feSmaxv
31930fd42e8Sad #ifdef i386
32030fd42e8Sad /*
32130fd42e8Sad * Patch early and late. Second time around the 'lock' prefix
32230fd42e8Sad * may be gone.
32330fd42e8Sad */
324bc042041Sjym if ((cpu_feature[0] & CPUID_CX8) != 0) {
325daaf2254Smaxv x86_hotpatch(HP_NAME_CAS_64, 0);
32630fd42e8Sad }
32730fd42e8Sad
328e48f8429Sdyoung #if !defined(SPLDEBUG)
329d2f1dd15Sbouyer if (!early && (cpu_feature[0] & CPUID_CX8) != 0) {
3307135f4bdSad /* Faster splx(), mutex_spin_exit(). */
331daaf2254Smaxv x86_hotpatch(HP_NAME_SPLLOWER, 0);
332c24c993fSbouyer #if !defined(LOCKDEBUG)
333daaf2254Smaxv x86_hotpatch(HP_NAME_MUTEX_EXIT, 0);
334fb30a66aSmaxv #endif
3357135f4bdSad }
336e48f8429Sdyoung #endif /* !SPLDEBUG */
337c24c993fSbouyer #endif /* i386 */
3387135f4bdSad
339c28dc6f7Sad /*
340c28dc6f7Sad * On some Opteron revisions, locked operations erroneously
341c28dc6f7Sad * allow memory references to be `bled' outside of critical
342c28dc6f7Sad * sections. Apply workaround.
343c28dc6f7Sad */
344c28dc6f7Sad if (cpu_vendor == CPUVENDOR_AMD &&
345b1a32cacSmsaitoh (CPUID_TO_FAMILY(cpu_info_primary.ci_signature) == 0xe ||
346b1a32cacSmsaitoh (CPUID_TO_FAMILY(cpu_info_primary.ci_signature) == 0xf &&
347b1a32cacSmsaitoh CPUID_TO_EXTMODEL(cpu_info_primary.ci_signature) < 0x4))) {
348daaf2254Smaxv x86_hotpatch(HP_NAME_RETFENCE, 0);
349c28dc6f7Sad }
350c28dc6f7Sad
35141713064Smaxv /*
35241713064Smaxv * If SMAP is present then patch the prepared holes with clac/stac
35341713064Smaxv * instructions.
35441713064Smaxv */
35541713064Smaxv if (!early && cpu_feature[5] & CPUID_SEF_SMAP) {
35641713064Smaxv KASSERT(rcr4() & CR4_SMAP);
3571cb7949fSmaxv
358daaf2254Smaxv x86_hotpatch(HP_NAME_CLAC, 0);
359daaf2254Smaxv x86_hotpatch(HP_NAME_STAC, 0);
36041713064Smaxv }
361b07ec3fcSad }
362