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
46static int
47x86_pkru_get_perm_unsup(u_int keyidx, int *access, int *modify)
48{
49
50	errno = EOPNOTSUPP;
51	return (-1);
52}
53
54static int
55x86_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
70DEFINE_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
77static int
78x86_pkru_set_perm_unsup(u_int keyidx, int access, int modify)
79{
80
81	errno = EOPNOTSUPP;
82	return (-1);
83}
84
85static int
86x86_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
105DEFINE_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
112int
113x86_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
125int
126x86_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