#ifdef __ARM_FEATURE_PAC_DEFAULT
#error Must be built with pointer authentication disabled
#endif
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <machine/armreg.h>
#include <machine/cpu.h>
#include <machine/cpu_feat.h>
#include <machine/reg.h>
#include <machine/vmparam.h>
#define SCTLR_PTRAUTH (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)
static bool __read_mostly enable_ptrauth = false;
void ptrauth_start(void);
struct thread *ptrauth_switch(struct thread *);
void ptrauth_exit_el0(struct thread *);
void ptrauth_enter_el0(struct thread *);
static bool
ptrauth_disable(void)
{
const char *family, *maker, *product;
family = kern_getenv("smbios.system.family");
maker = kern_getenv("smbios.system.maker");
product = kern_getenv("smbios.system.product");
if (family == NULL || maker == NULL || product == NULL)
return (false);
if (strcmp(maker, "Microsoft Corporation") == 0 &&
strcmp(family, "Surface") == 0 &&
strcmp(product, "Windows Dev Kit 2023") == 0)
return (true);
return (false);
}
static cpu_feat_en
ptrauth_check(const struct cpu_feat *feat __unused, u_int midr __unused)
{
uint64_t isar;
int pac_enable;
pac_enable = 1;
TUNABLE_INT_FETCH("hw.pac.enable", &pac_enable);
if (!pac_enable) {
if (boothowto & RB_VERBOSE)
printf("Pointer authentication is disabled\n");
return (FEAT_ALWAYS_DISABLE);
}
if (ptrauth_disable())
return (FEAT_ALWAYS_DISABLE);
get_kernel_reg(ID_AA64ISAR1_EL1, &isar);
if (ID_AA64ISAR1_APA_VAL(isar) > 0 || ID_AA64ISAR1_API_VAL(isar) > 0) {
return (FEAT_DEFAULT_ENABLE);
}
get_kernel_reg(ID_AA64ISAR2_EL1, &isar);
if (ID_AA64ISAR2_APA3_VAL(isar) > 0) {
return (FEAT_DEFAULT_ENABLE);
}
return (FEAT_ALWAYS_DISABLE);
}
static bool
ptrauth_enable(const struct cpu_feat *feat __unused,
cpu_feat_errata errata_status __unused, u_int *errata_list __unused,
u_int errata_count __unused)
{
enable_ptrauth = true;
elf64_addr_mask.code |= PAC_ADDR_MASK;
elf64_addr_mask.data |= PAC_ADDR_MASK;
#ifdef COMPAT_FREEBSD14
elf64_addr_mask_14.code |= PAC_ADDR_MASK_14;
elf64_addr_mask_14.data |= PAC_ADDR_MASK_14;
#endif
return (true);
}
static void
ptrauth_disabled(const struct cpu_feat *feat __unused)
{
if (PCPU_GET(cpuid) == 0) {
update_special_reg(ID_AA64ISAR1_EL1, ID_AA64ISAR1_API_MASK |
ID_AA64ISAR1_APA_MASK | ID_AA64ISAR1_GPA_MASK |
ID_AA64ISAR1_GPI_MASK, 0);
update_special_reg(ID_AA64ISAR2_EL1, ID_AA64ISAR2_APA3_MASK, 0);
}
}
CPU_FEAT(feat_pauth, "Pointer Authentication",
ptrauth_check, NULL, ptrauth_enable, ptrauth_disabled,
CPU_FEAT_EARLY_BOOT | CPU_FEAT_SYSTEM);
void
ptrauth_fork(struct thread *new_td, struct thread *orig_td)
{
if (!enable_ptrauth)
return;
memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
sizeof(new_td->td_md.md_ptrauth_user));
}
void
ptrauth_exec(struct thread *td)
{
if (!enable_ptrauth)
return;
arc4rand(&td->td_md.md_ptrauth_user, sizeof(td->td_md.md_ptrauth_user),
0);
}
void
ptrauth_copy_thread(struct thread *new_td, struct thread *orig_td)
{
if (!enable_ptrauth)
return;
memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
sizeof(new_td->td_md.md_ptrauth_user));
}
void
ptrauth_thread_alloc(struct thread *td)
{
if (!enable_ptrauth)
return;
arc4rand(&td->td_md.md_ptrauth_kern, sizeof(td->td_md.md_ptrauth_kern),
0);
}
#define LOAD_KEY(space, name, reg) \
__asm __volatile( \
"msr "__XSTRING(MRS_REG_ALT_NAME(reg ## KeyLo_EL1))", %0 \n" \
"msr "__XSTRING(MRS_REG_ALT_NAME(reg ## KeyHi_EL1))", %1 \n" \
:: "r"(td->td_md.md_ptrauth_##space.name.pa_key_lo), \
"r"(td->td_md.md_ptrauth_##space.name.pa_key_hi))
void
ptrauth_thread0(struct thread *td)
{
if (!enable_ptrauth)
return;
memset(&td->td_md.md_ptrauth_kern, 0,
sizeof(td->td_md.md_ptrauth_kern));
LOAD_KEY(kern, apia, APIA);
}
void
ptrauth_start(void)
{
uint64_t sctlr;
if (!enable_ptrauth)
return;
sctlr = READ_SPECIALREG(sctlr_el1);
sctlr |= SCTLR_PTRAUTH;
WRITE_SPECIALREG(sctlr_el1, sctlr);
isb();
}
#ifdef SMP
void
ptrauth_mp_start(uint64_t cpu)
{
struct ptrauth_key start_key;
uint64_t sctlr;
if (!enable_ptrauth)
return;
start_key.pa_key_lo = cpu;
start_key.pa_key_hi = ~cpu;
__asm __volatile(
".arch_extension pauth \n"
"msr "__XSTRING(APIAKeyLo_EL1_REG)", %0 \n"
"msr "__XSTRING(APIAKeyHi_EL1_REG)", %1 \n"
".arch_extension nopauth \n"
:: "r"(start_key.pa_key_lo), "r"(start_key.pa_key_hi));
sctlr = READ_SPECIALREG(sctlr_el1);
sctlr |= SCTLR_PTRAUTH;
WRITE_SPECIALREG(sctlr_el1, sctlr);
isb();
}
#endif
struct thread *
ptrauth_switch(struct thread *td)
{
if (enable_ptrauth) {
LOAD_KEY(kern, apia, APIA);
isb();
}
return (td);
}
void
ptrauth_exit_el0(struct thread *td)
{
if (!enable_ptrauth)
return;
LOAD_KEY(kern, apia, APIA);
isb();
}
void
ptrauth_enter_el0(struct thread *td)
{
if (!enable_ptrauth)
return;
LOAD_KEY(user, apia, APIA);
LOAD_KEY(user, apib, APIB);
LOAD_KEY(user, apda, APDA);
LOAD_KEY(user, apdb, APDB);
LOAD_KEY(user, apga, APGA);
}