#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: patch.c,v 1.53 2022/08/20 23:48:51 riastradh Exp $");
#include "opt_lockdebug.h"
#ifdef i386
#include "opt_spldebug.h"
#endif
#include <sys/types.h>
#include <sys/systm.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/frameasm.h>
#include <uvm/uvm.h>
#include <machine/pmap.h>
#include <machine/pmap_private.h>
#include <x86/bootspace.h>
#include <x86/cpuvar.h>
#include <x86/cputypes.h>
__link_set_decl(x86_hotpatch_descriptors, struct x86_hotpatch_descriptor);
struct x86_hotpatch_destination {
uint8_t name;
uint8_t size;
void *addr;
} __packed;
extern uint8_t hp_clac, hp_clac_end;
static const struct x86_hotpatch_source hp_clac_source = {
.saddr = &hp_clac,
.eaddr = &hp_clac_end
};
static const struct x86_hotpatch_descriptor hp_clac_desc = {
.name = HP_NAME_CLAC,
.nsrc = 1,
.srcs = { &hp_clac_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_clac_desc);
extern uint8_t hp_stac, hp_stac_end;
static const struct x86_hotpatch_source hp_stac_source = {
.saddr = &hp_stac,
.eaddr = &hp_stac_end
};
static const struct x86_hotpatch_descriptor hp_stac_desc = {
.name = HP_NAME_STAC,
.nsrc = 1,
.srcs = { &hp_stac_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_stac_desc);
extern uint8_t hp_retfence, hp_retfence_end;
static const struct x86_hotpatch_source hp_retfence_source = {
.saddr = &hp_retfence,
.eaddr = &hp_retfence_end
};
static const struct x86_hotpatch_descriptor hp_retfence_desc = {
.name = HP_NAME_RETFENCE,
.nsrc = 1,
.srcs = { &hp_retfence_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_retfence_desc);
extern uint8_t hp_nolock, hp_nolock_end;
static const struct x86_hotpatch_source hp_nolock_source = {
.saddr = &hp_nolock,
.eaddr = &hp_nolock_end
};
static const struct x86_hotpatch_descriptor hp_nolock_desc = {
.name = HP_NAME_NOLOCK,
.nsrc = 1,
.srcs = { &hp_nolock_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_nolock_desc);
#ifdef i386
extern uint8_t _atomic_cas_cx8, _atomic_cas_cx8_end;
static const struct x86_hotpatch_source hp_cas_cx8_source = {
.saddr = &_atomic_cas_cx8,
.eaddr = &_atomic_cas_cx8_end
};
static const struct x86_hotpatch_descriptor hp_cas_cx8_desc = {
.name = HP_NAME_CAS_64,
.nsrc = 1,
.srcs = { &hp_cas_cx8_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_cas_cx8_desc);
extern uint8_t cx8_spllower, cx8_spllower_end;
static const struct x86_hotpatch_source hp_cx8_spllower_source = {
.saddr = &cx8_spllower,
.eaddr = &cx8_spllower_end
};
static const struct x86_hotpatch_descriptor hp_cx8_spllower_desc = {
.name = HP_NAME_SPLLOWER,
.nsrc = 1,
.srcs = { &hp_cx8_spllower_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_cx8_spllower_desc);
#ifndef LOCKDEBUG
extern uint8_t i686_mutex_spin_exit, i686_mutex_spin_exit_end;
static const struct x86_hotpatch_source hp_i686_mutex_spin_exit_source = {
.saddr = &i686_mutex_spin_exit,
.eaddr = &i686_mutex_spin_exit_end
};
static const struct x86_hotpatch_descriptor hp_i686_mutex_spin_exit_desc = {
.name = HP_NAME_MUTEX_EXIT,
.nsrc = 1,
.srcs = { &hp_i686_mutex_spin_exit_source }
};
__link_set_add_rodata(x86_hotpatch_descriptors, hp_i686_mutex_spin_exit_desc);
#endif
#endif
static inline void __unused
patchbytes(void *addr, const uint8_t *bytes, size_t size)
{
uint8_t *ptr = (uint8_t *)addr;
size_t i;
for (i = 0; i < size; i++) {
ptr[i] = bytes[i];
}
}
int x86_hotpatch_apply(uint8_t, uint8_t);
int
__noubsan
x86_hotpatch_apply(uint8_t name, uint8_t sel)
{
struct x86_hotpatch_descriptor * const *iter;
const struct x86_hotpatch_descriptor *desc;
const struct x86_hotpatch_source *src;
const struct x86_hotpatch_destination *hps, *hpe, *hp;
extern char __rodata_hotpatch_start;
extern char __rodata_hotpatch_end;
const uint8_t *bytes;
bool found = false;
size_t size;
__link_set_foreach(iter, x86_hotpatch_descriptors) {
desc = *iter;
if (desc->name == name) {
found = true;
break;
}
}
if (!found)
return -1;
if (desc->nsrc > 2)
return -1;
if (sel >= desc->nsrc)
return -1;
src = desc->srcs[sel];
bytes = src->saddr;
size = (size_t)src->eaddr - (size_t)src->saddr;
hps = (struct x86_hotpatch_destination *)&__rodata_hotpatch_start;
hpe = (struct x86_hotpatch_destination *)&__rodata_hotpatch_end;
for (hp = hps; hp < hpe; hp++) {
if (hp->name != name) {
continue;
}
if (hp->size != size) {
return -1;
}
patchbytes(hp->addr, bytes, size);
}
return 0;
}
#ifdef __x86_64__
static void
remove_d_bit(void)
{
extern struct bootspace bootspace;
pt_entry_t pte;
vaddr_t va;
size_t i, n;
for (i = 0; i < BTSPACE_NSEGS; i++) {
if (bootspace.segs[i].type != BTSEG_TEXT)
continue;
va = bootspace.segs[i].va;
n = 0;
while (n < bootspace.segs[i].sz) {
if (L2_BASE[pl2_i(va)] & PTE_PS) {
pte = L2_BASE[pl2_i(va)] & ~PTE_D;
pmap_pte_set(&L2_BASE[pl2_i(va)], pte);
n += NBPD_L2;
va += NBPD_L2;
} else {
pte = L1_BASE[pl1_i(va)] & ~PTE_D;
pmap_pte_set(&L1_BASE[pl1_i(va)], pte);
n += NBPD_L1;
va += NBPD_L1;
}
}
}
tlbflushg();
}
#else
#define remove_d_bit() __nothing
#endif
void x86_hotpatch_cleanup(int);
void
x86_hotpatch_cleanup(int retval)
{
if (retval != 0) {
panic("x86_hotpatch_apply failed");
}
remove_d_bit();
}
void
x86_patch(bool early)
{
static bool first, second;
if (early) {
if (first)
return;
first = true;
} else {
if (second)
return;
second = true;
}
if (!early && ncpu == 1) {
#ifndef LOCKDEBUG
x86_hotpatch(HP_NAME_NOLOCK, 0);
#endif
}
#ifdef i386
if ((cpu_feature[0] & CPUID_CX8) != 0) {
x86_hotpatch(HP_NAME_CAS_64, 0);
}
#if !defined(SPLDEBUG)
if (!early && (cpu_feature[0] & CPUID_CX8) != 0) {
x86_hotpatch(HP_NAME_SPLLOWER, 0);
#if !defined(LOCKDEBUG)
x86_hotpatch(HP_NAME_MUTEX_EXIT, 0);
#endif
}
#endif
#endif
if (cpu_vendor == CPUVENDOR_AMD &&
(CPUID_TO_FAMILY(cpu_info_primary.ci_signature) == 0xe ||
(CPUID_TO_FAMILY(cpu_info_primary.ci_signature) == 0xf &&
CPUID_TO_EXTMODEL(cpu_info_primary.ci_signature) < 0x4))) {
x86_hotpatch(HP_NAME_RETFENCE, 0);
}
if (!early && cpu_feature[5] & CPUID_SEF_SMAP) {
KASSERT(rcr4() & CR4_SMAP);
x86_hotpatch(HP_NAME_CLAC, 0);
x86_hotpatch(HP_NAME_STAC, 0);
}
}