1 /*-
2 * Copyright (c) 2019 The FreeBSD Foundation
3 *
4 * Portions of this software were developed by Konstantin Belousov
5 * under sponsorship from 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 #include <sys/param.h>
30 #include <machine/cpufunc.h>
31 #include <machine/specialreg.h>
32 #include <machine/sysarch.h>
33 #include <x86/ifunc.h>
34 #include <errno.h>
35 #include <string.h>
36
37 #define MAX_PKRU_IDX 0xf
38 #ifdef __i386__
39 #define X86_SET_PKRU I386_SET_PKRU
40 #define X86_CLEAR_PKRU I386_CLEAR_PKRU
41 #else
42 #define X86_SET_PKRU AMD64_SET_PKRU
43 #define X86_CLEAR_PKRU AMD64_CLEAR_PKRU
44 #endif
45
46 static int
x86_pkru_get_perm_unsup(u_int keyidx,int * access,int * modify)47 x86_pkru_get_perm_unsup(u_int keyidx, int *access, int *modify)
48 {
49
50 errno = EOPNOTSUPP;
51 return (-1);
52 }
53
54 static int
x86_pkru_get_perm_hw(u_int keyidx,int * access,int * modify)55 x86_pkru_get_perm_hw(u_int keyidx, int *access, int *modify)
56 {
57 uint32_t pkru;
58
59 if (keyidx > MAX_PKRU_IDX) {
60 errno = EINVAL;
61 return (-1);
62 }
63 keyidx *= 2;
64 pkru = rdpkru();
65 *access = (pkru & (1 << keyidx)) == 0;
66 *modify = (pkru & (2 << keyidx)) == 0;
67 return (0);
68 }
69
70 DEFINE_UIFUNC(, int, x86_pkru_get_perm, (u_int, int *, int *))
71 {
72
73 return ((cpu_stdext_feature2 & CPUID_STDEXT2_OSPKE) == 0 ?
74 x86_pkru_get_perm_unsup : x86_pkru_get_perm_hw);
75 }
76
77 static int
x86_pkru_set_perm_unsup(u_int keyidx,int access,int modify)78 x86_pkru_set_perm_unsup(u_int keyidx, int access, int modify)
79 {
80
81 errno = EOPNOTSUPP;
82 return (-1);
83 }
84
85 static int
x86_pkru_set_perm_hw(u_int keyidx,int access,int modify)86 x86_pkru_set_perm_hw(u_int keyidx, int access, int modify)
87 {
88 uint32_t pkru;
89
90 if (keyidx > MAX_PKRU_IDX) {
91 errno = EINVAL;
92 return (-1);
93 }
94 keyidx *= 2;
95 pkru = rdpkru();
96 pkru &= ~(3 << keyidx);
97 if (!access)
98 pkru |= 1 << keyidx;
99 if (!modify)
100 pkru |= 2 << keyidx;
101 wrpkru(pkru);
102 return (0);
103 }
104
105 DEFINE_UIFUNC(, int, x86_pkru_set_perm, (u_int, int, int))
106 {
107
108 return ((cpu_stdext_feature2 & CPUID_STDEXT2_OSPKE) == 0 ?
109 x86_pkru_set_perm_unsup : x86_pkru_set_perm_hw);
110 }
111
112 int
x86_pkru_protect_range(void * addr,unsigned long len,u_int keyidx,int flags)113 x86_pkru_protect_range(void *addr, unsigned long len, u_int keyidx, int flags)
114 {
115 struct amd64_set_pkru a64pkru;
116
117 memset(&a64pkru, 0, sizeof(a64pkru));
118 a64pkru.addr = addr;
119 a64pkru.len = len;
120 a64pkru.keyidx = keyidx;
121 a64pkru.flags = flags;
122 return (sysarch(X86_SET_PKRU, &a64pkru));
123 }
124
125 int
x86_pkru_unprotect_range(void * addr,unsigned long len)126 x86_pkru_unprotect_range(void *addr, unsigned long len)
127 {
128 struct amd64_set_pkru a64pkru;
129
130 memset(&a64pkru, 0, sizeof(a64pkru));
131 a64pkru.addr = addr;
132 a64pkru.len = len;
133 return (sysarch(X86_CLEAR_PKRU, &a64pkru));
134 }
135