2 * Copyright (c) 2021 The FreeBSD Foundation
4 * This software was developed by Andrew Turner under sponsorship from
5 * the FreeBSD Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
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.
34 #ifdef __ARM_FEATURE_PAC_DEFAULT
35 #error Must be built with pointer authentication disabled
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
42 #include <sys/kernel.h>
43 #include <sys/libkern.h>
45 #include <sys/reboot.h>
47 #include <machine/armreg.h>
48 #include <machine/cpu.h>
49 #include <machine/reg.h>
50 #include <machine/vmparam.h>
52 #define SCTLR_PTRAUTH (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)
54 static bool __read_mostly enable_ptrauth = false;
56 /* Functions called from assembly. */
57 void ptrauth_start(void);
58 struct thread *ptrauth_switch(struct thread *);
59 void ptrauth_exit_el0(struct thread *);
60 void ptrauth_enter_el0(struct thread *);
65 const char *family, *maker, *product;
67 family = kern_getenv("smbios.system.family");
68 maker = kern_getenv("smbios.system.maker");
69 product = kern_getenv("smbios.system.product");
70 if (family == NULL || maker == NULL || product == NULL)
74 * The Dev Kit appears to be configured to trap upon access to PAC
75 * registers, but the kernel boots at EL1 and so we have no way to
76 * inspect or change this configuration. As a workaround, simply
77 * disable PAC on this platform.
79 if (strcmp(maker, "Microsoft Corporation") == 0 &&
80 strcmp(family, "Surface") == 0 &&
81 strcmp(product, "Windows Dev Kit 2023") == 0)
94 * Allow the sysadmin to disable pointer authentication globally,
95 * e.g. on broken hardware.
98 TUNABLE_INT_FETCH("hw.pac.enable", &pac_enable);
100 if (boothowto & RB_VERBOSE)
101 printf("Pointer authentication is disabled\n");
105 if (!get_kernel_reg(ID_AA64ISAR1_EL1, &isar1))
108 if (ptrauth_disable())
112 * This assumes if there is pointer authentication on the boot CPU
113 * it will also be available on any non-boot CPUs. If this is ever
114 * not the case we will have to add a quirk.
116 if (ID_AA64ISAR1_APA_VAL(isar1) > 0 ||
117 ID_AA64ISAR1_API_VAL(isar1) > 0) {
118 enable_ptrauth = true;
119 elf64_addr_mask.code |= PAC_ADDR_MASK;
120 elf64_addr_mask.data |= PAC_ADDR_MASK;
124 /* Copy the keys when forking a new process */
126 ptrauth_fork(struct thread *new_td, struct thread *orig_td)
131 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
132 sizeof(new_td->td_md.md_ptrauth_user));
135 /* Generate new userspace keys when executing a new process */
137 ptrauth_exec(struct thread *td)
142 arc4rand(&td->td_md.md_ptrauth_user, sizeof(td->td_md.md_ptrauth_user),
147 * Copy the user keys when creating a new userspace thread until it's clear
148 * how the ABI expects the various keys to be assigned.
151 ptrauth_copy_thread(struct thread *new_td, struct thread *orig_td)
156 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
157 sizeof(new_td->td_md.md_ptrauth_user));
160 /* Generate new kernel keys when executing a new kernel thread */
162 ptrauth_thread_alloc(struct thread *td)
167 arc4rand(&td->td_md.md_ptrauth_kern, sizeof(td->td_md.md_ptrauth_kern),
172 * Load the userspace keys. We can't use WRITE_SPECIALREG as we need
173 * to set the architecture extension.
175 #define LOAD_KEY(space, name) \
177 ".arch_extension pauth \n" \
178 "msr "#name"keylo_el1, %0 \n" \
179 "msr "#name"keyhi_el1, %1 \n" \
180 ".arch_extension nopauth \n" \
181 :: "r"(td->td_md.md_ptrauth_##space.name.pa_key_lo), \
182 "r"(td->td_md.md_ptrauth_##space.name.pa_key_hi))
185 ptrauth_thread0(struct thread *td)
190 /* TODO: Generate a random number here */
191 memset(&td->td_md.md_ptrauth_kern, 0,
192 sizeof(td->td_md.md_ptrauth_kern));
193 LOAD_KEY(kern, apia);
195 * No isb as this is called before ptrauth_start so can rely on
196 * the instruction barrier there.
201 * Enable pointer authentication. After this point userspace and the kernel
202 * can sign return addresses, etc. based on their keys
204 * This assumes either all or no CPUs have pointer authentication support,
205 * and, if supported, all CPUs have the same algorithm.
215 /* Enable pointer authentication */
216 sctlr = READ_SPECIALREG(sctlr_el1);
217 sctlr |= SCTLR_PTRAUTH;
218 WRITE_SPECIALREG(sctlr_el1, sctlr);
224 ptrauth_mp_start(uint64_t cpu)
226 struct ptrauth_key start_key;
233 * We need a key until we call sched_throw, however we don't have
234 * a thread until then. Create a key just for use within
235 * init_secondary and whatever it calls. As init_secondary never
236 * returns it is safe to do so from within it.
238 * As it's only used for a short length of time just use the cpu
241 start_key.pa_key_lo = cpu;
242 start_key.pa_key_hi = ~cpu;
245 ".arch_extension pauth \n"
246 "msr apiakeylo_el1, %0 \n"
247 "msr apiakeyhi_el1, %1 \n"
248 ".arch_extension nopauth \n"
249 :: "r"(start_key.pa_key_lo), "r"(start_key.pa_key_hi));
251 /* Enable pointer authentication */
252 sctlr = READ_SPECIALREG(sctlr_el1);
253 sctlr |= SCTLR_PTRAUTH;
254 WRITE_SPECIALREG(sctlr_el1, sctlr);
260 ptrauth_switch(struct thread *td)
262 if (enable_ptrauth) {
263 LOAD_KEY(kern, apia);
270 /* Called when we are exiting uerspace and entering the kernel */
272 ptrauth_exit_el0(struct thread *td)
277 LOAD_KEY(kern, apia);
281 /* Called when we are about to exit the kernel and enter userspace */
283 ptrauth_enter_el0(struct thread *td)
288 LOAD_KEY(user, apia);
289 LOAD_KEY(user, apib);
290 LOAD_KEY(user, apda);
291 LOAD_KEY(user, apdb);
292 LOAD_KEY(user, apga);
294 * No isb as this is called from the exception handler so can rely
295 * on the eret instruction to be the needed context synchronizing event.