xref: /freebsd/lib/libsys/x86/pkru.c (revision 4bc66c0f)
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