1/*- 2 * Copyright (c) 2021 The FreeBSD Foundation 3 * 4 * This software was developed by Andrew Turner under sponsorship from 5 * the FreeBSD Foundation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29/* 30 * This manages pointer authentication. As it needs to enable the use of 31 * pointer authentication and change the keys we must built this with 32 * pointer authentication disabled. 33 */ 34#ifdef __ARM_FEATURE_PAC_DEFAULT 35#error Must be built with pointer authentication disabled 36#endif 37 38#include <sys/param.h> 39#include <sys/kernel.h> 40#include <sys/libkern.h> 41#include <sys/proc.h> 42#include <sys/reboot.h> 43 44#include <machine/armreg.h> 45#include <machine/cpu.h> 46#include <machine/reg.h> 47#include <machine/vmparam.h> 48 49#define SCTLR_PTRAUTH (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB) 50 51static bool __read_mostly enable_ptrauth = false; 52 53/* Functions called from assembly. */ 54void ptrauth_start(void); 55struct thread *ptrauth_switch(struct thread *); 56void ptrauth_exit_el0(struct thread *); 57void ptrauth_enter_el0(struct thread *); 58 59static bool 60ptrauth_disable(void) 61{ 62 const char *family, *maker, *product; 63 64 family = kern_getenv("smbios.system.family"); 65 maker = kern_getenv("smbios.system.maker"); 66 product = kern_getenv("smbios.system.product"); 67 if (family == NULL || maker == NULL || product == NULL) 68 return (false); 69 70 /* 71 * The Dev Kit appears to be configured to trap upon access to PAC 72 * registers, but the kernel boots at EL1 and so we have no way to 73 * inspect or change this configuration. As a workaround, simply 74 * disable PAC on this platform. 75 */ 76 if (strcmp(maker, "Microsoft Corporation") == 0 && 77 strcmp(family, "Surface") == 0 && 78 strcmp(product, "Windows Dev Kit 2023") == 0) 79 return (true); 80 81 return (false); 82} 83 84void 85ptrauth_init(void) 86{ 87 uint64_t isar1; 88 int pac_enable; 89 90 /* 91 * Allow the sysadmin to disable pointer authentication globally, 92 * e.g. on broken hardware. 93 */ 94 pac_enable = 1; 95 TUNABLE_INT_FETCH("hw.pac.enable", &pac_enable); 96 if (!pac_enable) { 97 if (boothowto & RB_VERBOSE) 98 printf("Pointer authentication is disabled\n"); 99 return; 100 } 101 102 if (!get_kernel_reg(ID_AA64ISAR1_EL1, &isar1)) 103 return; 104 105 if (ptrauth_disable()) 106 return; 107 108 /* 109 * This assumes if there is pointer authentication on the boot CPU 110 * it will also be available on any non-boot CPUs. If this is ever 111 * not the case we will have to add a quirk. 112 */ 113 if (ID_AA64ISAR1_APA_VAL(isar1) > 0 || 114 ID_AA64ISAR1_API_VAL(isar1) > 0) { 115 enable_ptrauth = true; 116 elf64_addr_mask.code |= PAC_ADDR_MASK; 117 elf64_addr_mask.data |= PAC_ADDR_MASK; 118 } 119} 120 121/* Copy the keys when forking a new process */ 122void 123ptrauth_fork(struct thread *new_td, struct thread *orig_td) 124{ 125 if (!enable_ptrauth) 126 return; 127 128 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user, 129 sizeof(new_td->td_md.md_ptrauth_user)); 130} 131 132/* Generate new userspace keys when executing a new process */ 133void 134ptrauth_exec(struct thread *td) 135{ 136 if (!enable_ptrauth) 137 return; 138 139 arc4rand(&td->td_md.md_ptrauth_user, sizeof(td->td_md.md_ptrauth_user), 140 0); 141} 142 143/* 144 * Copy the user keys when creating a new userspace thread until it's clear 145 * how the ABI expects the various keys to be assigned. 146 */ 147void 148ptrauth_copy_thread(struct thread *new_td, struct thread *orig_td) 149{ 150 if (!enable_ptrauth) 151 return; 152 153 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user, 154 sizeof(new_td->td_md.md_ptrauth_user)); 155} 156 157/* Generate new kernel keys when executing a new kernel thread */ 158void 159ptrauth_thread_alloc(struct thread *td) 160{ 161 if (!enable_ptrauth) 162 return; 163 164 arc4rand(&td->td_md.md_ptrauth_kern, sizeof(td->td_md.md_ptrauth_kern), 165 0); 166} 167 168/* 169 * Load the userspace keys. We can't use WRITE_SPECIALREG as we need 170 * to set the architecture extension. 171 */ 172#define LOAD_KEY(space, name, reg) \ 173__asm __volatile( \ 174 "msr "__XSTRING(MRS_REG_ALT_NAME(reg ## KeyLo_EL1))", %0 \n" \ 175 "msr "__XSTRING(MRS_REG_ALT_NAME(reg ## KeyHi_EL1))", %1 \n" \ 176 :: "r"(td->td_md.md_ptrauth_##space.name.pa_key_lo), \ 177 "r"(td->td_md.md_ptrauth_##space.name.pa_key_hi)) 178 179void 180ptrauth_thread0(struct thread *td) 181{ 182 if (!enable_ptrauth) 183 return; 184 185 /* TODO: Generate a random number here */ 186 memset(&td->td_md.md_ptrauth_kern, 0, 187 sizeof(td->td_md.md_ptrauth_kern)); 188 LOAD_KEY(kern, apia, APIA); 189 /* 190 * No isb as this is called before ptrauth_start so can rely on 191 * the instruction barrier there. 192 */ 193} 194 195/* 196 * Enable pointer authentication. After this point userspace and the kernel 197 * can sign return addresses, etc. based on their keys 198 * 199 * This assumes either all or no CPUs have pointer authentication support, 200 * and, if supported, all CPUs have the same algorithm. 201 */ 202void 203ptrauth_start(void) 204{ 205 uint64_t sctlr; 206 207 if (!enable_ptrauth) 208 return; 209 210 /* Enable pointer authentication */ 211 sctlr = READ_SPECIALREG(sctlr_el1); 212 sctlr |= SCTLR_PTRAUTH; 213 WRITE_SPECIALREG(sctlr_el1, sctlr); 214 isb(); 215} 216 217#ifdef SMP 218void 219ptrauth_mp_start(uint64_t cpu) 220{ 221 struct ptrauth_key start_key; 222 uint64_t sctlr; 223 224 if (!enable_ptrauth) 225 return; 226 227 /* 228 * We need a key until we call sched_throw, however we don't have 229 * a thread until then. Create a key just for use within 230 * init_secondary and whatever it calls. As init_secondary never 231 * returns it is safe to do so from within it. 232 * 233 * As it's only used for a short length of time just use the cpu 234 * as the key. 235 */ 236 start_key.pa_key_lo = cpu; 237 start_key.pa_key_hi = ~cpu; 238 239 __asm __volatile( 240 ".arch_extension pauth \n" 241 "msr "__XSTRING(APIAKeyLo_EL1_REG)", %0 \n" 242 "msr "__XSTRING(APIAKeyHi_EL1_REG)", %1 \n" 243 ".arch_extension nopauth \n" 244 :: "r"(start_key.pa_key_lo), "r"(start_key.pa_key_hi)); 245 246 /* Enable pointer authentication */ 247 sctlr = READ_SPECIALREG(sctlr_el1); 248 sctlr |= SCTLR_PTRAUTH; 249 WRITE_SPECIALREG(sctlr_el1, sctlr); 250 isb(); 251} 252#endif 253 254struct thread * 255ptrauth_switch(struct thread *td) 256{ 257 if (enable_ptrauth) { 258 LOAD_KEY(kern, apia, APIA); 259 isb(); 260 } 261 262 return (td); 263} 264 265/* Called when we are exiting uerspace and entering the kernel */ 266void 267ptrauth_exit_el0(struct thread *td) 268{ 269 if (!enable_ptrauth) 270 return; 271 272 LOAD_KEY(kern, apia, APIA); 273 isb(); 274} 275 276/* Called when we are about to exit the kernel and enter userspace */ 277void 278ptrauth_enter_el0(struct thread *td) 279{ 280 if (!enable_ptrauth) 281 return; 282 283 LOAD_KEY(user, apia, APIA); 284 LOAD_KEY(user, apib, APIB); 285 LOAD_KEY(user, apda, APDA); 286 LOAD_KEY(user, apdb, APDB); 287 LOAD_KEY(user, apga, APGA); 288 /* 289 * No isb as this is called from the exception handler so can rely 290 * on the eret instruction to be the needed context synchronizing event. 291 */ 292} 293