xref: /xnu-11417.121.6/tests/cpu_counters/cpc_security_tests.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1*a1e26a70SApple OSS Distributions // Copyright 2023 (c) Apple Inc.  All rights reserved.
2*a1e26a70SApple OSS Distributions 
3*a1e26a70SApple OSS Distributions #include <darwintest.h>
4*a1e26a70SApple OSS Distributions #include <darwintest_utils.h>
5*a1e26a70SApple OSS Distributions #include <dirent.h>
6*a1e26a70SApple OSS Distributions #include <kperf/kpc.h>
7*a1e26a70SApple OSS Distributions #include <kperfdata/kpep.h>
8*a1e26a70SApple OSS Distributions #include <stdarg.h>
9*a1e26a70SApple OSS Distributions #include <stdbool.h>
10*a1e26a70SApple OSS Distributions #include <string.h>
11*a1e26a70SApple OSS Distributions #include <sys/guarded.h>
12*a1e26a70SApple OSS Distributions #include <sys/ioctl.h>
13*a1e26a70SApple OSS Distributions #include <sys/monotonic.h>
14*a1e26a70SApple OSS Distributions 
15*a1e26a70SApple OSS Distributions #include "test_utils.h"
16*a1e26a70SApple OSS Distributions 
17*a1e26a70SApple OSS Distributions #if __arm64__
18*a1e26a70SApple OSS Distributions #define HAS_CPC_SECURITY true
19*a1e26a70SApple OSS Distributions #else // __arm64__
20*a1e26a70SApple OSS Distributions #define HAS_CPC_SECURITY false
21*a1e26a70SApple OSS Distributions #endif // !__arm64__
22*a1e26a70SApple OSS Distributions 
23*a1e26a70SApple OSS Distributions #define _T_META_REQUIRES_CPC_SUPPORT \
24*a1e26a70SApple OSS Distributions 	T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1)
25*a1e26a70SApple OSS Distributions 
26*a1e26a70SApple OSS Distributions T_GLOBAL_META(
27*a1e26a70SApple OSS Distributions 	T_META_NAMESPACE("xnu.cpc"),
28*a1e26a70SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
29*a1e26a70SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("cpu counters"),
30*a1e26a70SApple OSS Distributions 	T_META_OWNER("mwidmann"),
31*a1e26a70SApple OSS Distributions 	T_META_CHECK_LEAKS(false),
32*a1e26a70SApple OSS Distributions 	T_META_ASROOT(true),
33*a1e26a70SApple OSS Distributions 	XNU_T_META_SOC_SPECIFIC,
34*a1e26a70SApple OSS Distributions 	T_META_ENABLED(HAS_CPC_SECURITY),
35*a1e26a70SApple OSS Distributions 	_T_META_REQUIRES_CPC_SUPPORT);
36*a1e26a70SApple OSS Distributions 
37*a1e26a70SApple OSS Distributions // Several of these tests have two variants to support running on development and release kernels.
38*a1e26a70SApple OSS Distributions // Tests prefixed with `secure_` put the development kernel into a secure CPC mode while tests prefixed with `release_` can run on the RELEASE build variant.
39*a1e26a70SApple OSS Distributions 
40*a1e26a70SApple OSS Distributions // Metadata for running on a development kernel in CPC secure mode.
41*a1e26a70SApple OSS Distributions #define _T_META_CPC_SECURE_ON_DEV T_META_SYSCTL_INT("kern.cpc.secure=1"), XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL
42*a1e26a70SApple OSS Distributions 
43*a1e26a70SApple OSS Distributions static void
_assert_kpep_ok(int kpep_err,const char * fmt,...)44*a1e26a70SApple OSS Distributions _assert_kpep_ok(int kpep_err, const char *fmt, ...)
45*a1e26a70SApple OSS Distributions {
46*a1e26a70SApple OSS Distributions 	char msg[1024] = "";
47*a1e26a70SApple OSS Distributions 	va_list args;
48*a1e26a70SApple OSS Distributions 	va_start(args, fmt);
49*a1e26a70SApple OSS Distributions 	vsnprintf(msg, sizeof(msg), fmt, args);
50*a1e26a70SApple OSS Distributions 	va_end(args);
51*a1e26a70SApple OSS Distributions 	T_QUIET;
52*a1e26a70SApple OSS Distributions 	T_ASSERT_EQ(kpep_err, KPEP_ERR_NONE, "%s: %s", msg, kpep_strerror(kpep_err));
53*a1e26a70SApple OSS Distributions }
54*a1e26a70SApple OSS Distributions 
55*a1e26a70SApple OSS Distributions static void
_skip_for_db(const char * kind,int kpep_err)56*a1e26a70SApple OSS Distributions _skip_for_db(const char *kind, int kpep_err)
57*a1e26a70SApple OSS Distributions {
58*a1e26a70SApple OSS Distributions 	const char * const public_kpep_path = "/usr/share/kpep";
59*a1e26a70SApple OSS Distributions 	const char * const internal_kpep_path = "/usr/local/share/kpep";
60*a1e26a70SApple OSS Distributions 	const char * const paths[2] = { public_kpep_path, internal_kpep_path, };
61*a1e26a70SApple OSS Distributions 	for (int i = 0; i < 2; i++) {
62*a1e26a70SApple OSS Distributions 		const char * const path = paths[i];
63*a1e26a70SApple OSS Distributions 		T_LOG("contents of %s:", path);
64*a1e26a70SApple OSS Distributions 		DIR *dir = opendir(path);
65*a1e26a70SApple OSS Distributions 		if (dir) {
66*a1e26a70SApple OSS Distributions 			struct dirent *entry = NULL;
67*a1e26a70SApple OSS Distributions 			while ((entry = readdir(dir)) != NULL) {
68*a1e26a70SApple OSS Distributions 				T_LOG("    %s", entry->d_name);
69*a1e26a70SApple OSS Distributions 			}
70*a1e26a70SApple OSS Distributions 			(void)closedir(dir);
71*a1e26a70SApple OSS Distributions 		} else {
72*a1e26a70SApple OSS Distributions 			T_LOG("failed to open directory: %s", strerror(errno));
73*a1e26a70SApple OSS Distributions 		}
74*a1e26a70SApple OSS Distributions 	}
75*a1e26a70SApple OSS Distributions 	int cpu_family = 0;
76*a1e26a70SApple OSS Distributions 	size_t family_size = sizeof(cpu_family);
77*a1e26a70SApple OSS Distributions 	int ret = sysctlbyname("hw.cpufamily", &cpu_family, &family_size, NULL, 0);
78*a1e26a70SApple OSS Distributions 	if (ret != 0) {
79*a1e26a70SApple OSS Distributions 		T_LOG("HW CPU family: 0x%8x", cpu_family);
80*a1e26a70SApple OSS Distributions 	} else {
81*a1e26a70SApple OSS Distributions 		T_LOG("failed to get hw.cpufamily: %s", strerror(errno));
82*a1e26a70SApple OSS Distributions 	}
83*a1e26a70SApple OSS Distributions 	T_SKIP("cannot open %s event database: %s", kind, kpep_strerror(kpep_err));
84*a1e26a70SApple OSS Distributions }
85*a1e26a70SApple OSS Distributions 
86*a1e26a70SApple OSS Distributions // Check that a secure kernel disallows restricted events.
87*a1e26a70SApple OSS Distributions 
88*a1e26a70SApple OSS Distributions static void
check_secure_cpmu(void)89*a1e26a70SApple OSS Distributions check_secure_cpmu(void)
90*a1e26a70SApple OSS Distributions {
91*a1e26a70SApple OSS Distributions 	kpep_db_t public_db = NULL;
92*a1e26a70SApple OSS Distributions 	int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &public_db);
93*a1e26a70SApple OSS Distributions 	if (ret != KPEP_ERR_NONE) {
94*a1e26a70SApple OSS Distributions 		_skip_for_db("public", ret);
95*a1e26a70SApple OSS Distributions 	}
96*a1e26a70SApple OSS Distributions 	kpep_db_t internal_db = NULL;
97*a1e26a70SApple OSS Distributions 	ret = kpep_db_createx(NULL, KPEP_DB_FLAG_INTERNAL_ONLY, &internal_db);
98*a1e26a70SApple OSS Distributions 	if (ret != KPEP_ERR_NONE) {
99*a1e26a70SApple OSS Distributions 		_skip_for_db("internal", ret);
100*a1e26a70SApple OSS Distributions 	}
101*a1e26a70SApple OSS Distributions 	const char *na = NULL;
102*a1e26a70SApple OSS Distributions 	kpep_db_name(public_db, &na);
103*a1e26a70SApple OSS Distributions 
104*a1e26a70SApple OSS Distributions 	size_t internal_event_count = 0;
105*a1e26a70SApple OSS Distributions 	ret = kpep_db_events_count(internal_db, &internal_event_count);
106*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting internal event count");
107*a1e26a70SApple OSS Distributions 
108*a1e26a70SApple OSS Distributions 	kpep_event_t *internal_events = calloc(internal_event_count,
109*a1e26a70SApple OSS Distributions 	    sizeof(internal_events[0]));
110*a1e26a70SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO;
111*a1e26a70SApple OSS Distributions 	T_ASSERT_NOTNULL(internal_events, "allocate space for internal events");
112*a1e26a70SApple OSS Distributions 
113*a1e26a70SApple OSS Distributions 	ret = kpep_db_events(internal_db, internal_events,
114*a1e26a70SApple OSS Distributions 	    internal_event_count * sizeof(internal_events[0]));
115*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting internal events");
116*a1e26a70SApple OSS Distributions 
117*a1e26a70SApple OSS Distributions 	kpep_config_t config = NULL;
118*a1e26a70SApple OSS Distributions 	ret = kpep_config_create(internal_db, &config);
119*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "creating event configuration");
120*a1e26a70SApple OSS Distributions 	ret = kpep_config_force_counters(config);
121*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "forcing counters with configuration");
122*a1e26a70SApple OSS Distributions 
123*a1e26a70SApple OSS Distributions 	size_t kpc_count_allocated = 0;
124*a1e26a70SApple OSS Distributions 	uint64_t *kpc_configs = NULL;
125*a1e26a70SApple OSS Distributions 	size_t *kpc_map = NULL;
126*a1e26a70SApple OSS Distributions 
127*a1e26a70SApple OSS Distributions 	unsigned int tested = 0;
128*a1e26a70SApple OSS Distributions 	unsigned int filtered = 0;
129*a1e26a70SApple OSS Distributions 	unsigned int public_tested = 0;
130*a1e26a70SApple OSS Distributions 	for (size_t i = 0; i < internal_event_count; i++) {
131*a1e26a70SApple OSS Distributions 		kpep_event_t event = internal_events[i];
132*a1e26a70SApple OSS Distributions 		const char *name = NULL;
133*a1e26a70SApple OSS Distributions 		ret = kpep_event_alias(event, &name);
134*a1e26a70SApple OSS Distributions 		if (!name) {
135*a1e26a70SApple OSS Distributions 			ret = kpep_event_name(event, &name);
136*a1e26a70SApple OSS Distributions 		}
137*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting event name");
138*a1e26a70SApple OSS Distributions 		if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
139*a1e26a70SApple OSS Distributions 			T_LOG("skipping non-configurable %s event", name);
140*a1e26a70SApple OSS Distributions 			continue;
141*a1e26a70SApple OSS Distributions 		}
142*a1e26a70SApple OSS Distributions 		bool empty_event = strcmp(name, "NO_EVNT") == 0;
143*a1e26a70SApple OSS Distributions 		if (empty_event) {
144*a1e26a70SApple OSS Distributions 			continue;
145*a1e26a70SApple OSS Distributions 		}
146*a1e26a70SApple OSS Distributions 
147*a1e26a70SApple OSS Distributions 		kpep_event_t public_event = NULL;
148*a1e26a70SApple OSS Distributions 		ret = kpep_db_event(public_db, name, &public_event);
149*a1e26a70SApple OSS Distributions 		bool internal_only = ret == KPEP_ERR_EVENT_NOT_FOUND;
150*a1e26a70SApple OSS Distributions 		ret = kpep_config_add_event(config, &event, 0, NULL);
151*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "adding event %s to configuration", name);
152*a1e26a70SApple OSS Distributions 
153*a1e26a70SApple OSS Distributions 		size_t kpc_count = 0;
154*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc_count(config, &kpc_count);
155*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC count");
156*a1e26a70SApple OSS Distributions 		if (kpc_count == 0) {
157*a1e26a70SApple OSS Distributions 			T_LOG("skipping event %s with no configuration", name);
158*a1e26a70SApple OSS Distributions 			ret = kpep_config_remove_event(config, 0);
159*a1e26a70SApple OSS Distributions 			_assert_kpep_ok(ret, "removing event %s from configuration", name);
160*a1e26a70SApple OSS Distributions 			continue;
161*a1e26a70SApple OSS Distributions 		}
162*a1e26a70SApple OSS Distributions 		if (kpc_count > kpc_count_allocated) {
163*a1e26a70SApple OSS Distributions 			free(kpc_configs);
164*a1e26a70SApple OSS Distributions 			kpc_configs = calloc(kpc_count, sizeof(kpc_config_t));
165*a1e26a70SApple OSS Distributions 			T_QUIET; T_WITH_ERRNO;
166*a1e26a70SApple OSS Distributions 			T_ASSERT_NOTNULL(kpc_configs, "allocate space for KPC configs");
167*a1e26a70SApple OSS Distributions 
168*a1e26a70SApple OSS Distributions 			free(kpc_map);
169*a1e26a70SApple OSS Distributions 			kpc_map = calloc(kpc_count, sizeof(size_t));
170*a1e26a70SApple OSS Distributions 			T_QUIET; T_WITH_ERRNO;
171*a1e26a70SApple OSS Distributions 			T_ASSERT_NOTNULL(kpc_map, "allocate space for KPC map");
172*a1e26a70SApple OSS Distributions 
173*a1e26a70SApple OSS Distributions 			kpc_count_allocated = kpc_count;
174*a1e26a70SApple OSS Distributions 		}
175*a1e26a70SApple OSS Distributions 
176*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc_map(config, kpc_map, kpc_count * sizeof(size_t));
177*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC map for event %s", name);
178*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc(config, (uint8_t *)kpc_configs, kpc_count * sizeof(kpc_config_t));
179*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC configs for event %s", name);
180*a1e26a70SApple OSS Distributions 		kpc_config_t kpc_config = kpc_configs[kpc_map[0]];
181*a1e26a70SApple OSS Distributions 
182*a1e26a70SApple OSS Distributions 		ret = kpep_config_apply(config);
183*a1e26a70SApple OSS Distributions 		bool not_permitted = ret == KPEP_ERR_ERRNO && errno == EPERM;
184*a1e26a70SApple OSS Distributions 		if (not_permitted) {
185*a1e26a70SApple OSS Distributions 			if (!internal_only) {
186*a1e26a70SApple OSS Distributions 				T_LOG("failed to configure public event %s (0x%04llx)", name, kpc_config);
187*a1e26a70SApple OSS Distributions 			}
188*a1e26a70SApple OSS Distributions 			filtered++;
189*a1e26a70SApple OSS Distributions 		} else if (internal_only) {
190*a1e26a70SApple OSS Distributions 			T_FAIL("configured internal-only event %s (0x%04llx) with secure CPC", name, kpc_config);
191*a1e26a70SApple OSS Distributions 		} else {
192*a1e26a70SApple OSS Distributions 			public_tested++;
193*a1e26a70SApple OSS Distributions 		}
194*a1e26a70SApple OSS Distributions 		ret = kpep_config_remove_event(config, 0);
195*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "removing event %s from configuration", name);
196*a1e26a70SApple OSS Distributions 		tested++;
197*a1e26a70SApple OSS Distributions 	}
198*a1e26a70SApple OSS Distributions 
199*a1e26a70SApple OSS Distributions 	T_LOG("tested %u internal/public events", tested);
200*a1e26a70SApple OSS Distributions 	T_LOG("correctly permitted to configure %u public events", public_tested);
201*a1e26a70SApple OSS Distributions 	T_LOG("correctly not permitted to configure %u internal-only events",
202*a1e26a70SApple OSS Distributions 	    filtered);
203*a1e26a70SApple OSS Distributions 	kpep_config_free(config);
204*a1e26a70SApple OSS Distributions 	kpep_db_free(public_db);
205*a1e26a70SApple OSS Distributions 	kpep_db_free(internal_db);
206*a1e26a70SApple OSS Distributions 	free(kpc_configs);
207*a1e26a70SApple OSS Distributions 	free(kpc_map);
208*a1e26a70SApple OSS Distributions }
209*a1e26a70SApple OSS Distributions 
210*a1e26a70SApple OSS Distributions T_DECL(secure_cpmu_event_restrictions, "secured CPMU should be restricted to known events",
211*a1e26a70SApple OSS Distributions     _T_META_CPC_SECURE_ON_DEV, T_META_TAG_VM_NOT_ELIGIBLE)
212*a1e26a70SApple OSS Distributions {
213*a1e26a70SApple OSS Distributions 	check_secure_cpmu();
214*a1e26a70SApple OSS Distributions }
215*a1e26a70SApple OSS Distributions 
216*a1e26a70SApple OSS Distributions T_DECL(release_cpmu_event_restrictions, "release CPMU should be restricted to known events",
217*a1e26a70SApple OSS Distributions     XNU_T_META_REQUIRES_RELEASE_KERNEL, T_META_TAG_VM_NOT_ELIGIBLE)
218*a1e26a70SApple OSS Distributions {
219*a1e26a70SApple OSS Distributions 	check_secure_cpmu();
220*a1e26a70SApple OSS Distributions }
221*a1e26a70SApple OSS Distributions 
222*a1e26a70SApple OSS Distributions #define UNCORE_DEV_PATH "/dev/monotonic/uncore"
223*a1e26a70SApple OSS Distributions #define UPMU_REF_CYCLES 0x02
224*a1e26a70SApple OSS Distributions 
225*a1e26a70SApple OSS Distributions static void
check_secure_upmu(void)226*a1e26a70SApple OSS Distributions check_secure_upmu(void)
227*a1e26a70SApple OSS Distributions {
228*a1e26a70SApple OSS Distributions 	guardid_t guard;
229*a1e26a70SApple OSS Distributions 	int fd;
230*a1e26a70SApple OSS Distributions 
231*a1e26a70SApple OSS Distributions 	guard = 0xa5adcafe;
232*a1e26a70SApple OSS Distributions 
233*a1e26a70SApple OSS Distributions 	T_SETUPBEGIN;
234*a1e26a70SApple OSS Distributions 
235*a1e26a70SApple OSS Distributions 	fd = guarded_open_np(UNCORE_DEV_PATH, &guard,
236*a1e26a70SApple OSS Distributions 	    GUARD_CLOSE | GUARD_DUP | GUARD_WRITE, O_CLOEXEC | O_EXCL);
237*a1e26a70SApple OSS Distributions 	if (fd < 0 && errno == ENOENT) {
238*a1e26a70SApple OSS Distributions 		T_SKIP("uncore counters are unsupported");
239*a1e26a70SApple OSS Distributions 	}
240*a1e26a70SApple OSS Distributions 
241*a1e26a70SApple OSS Distributions 	union monotonic_ctl_add add_ctl = {
242*a1e26a70SApple OSS Distributions 		.in.config.event = UPMU_REF_CYCLES,
243*a1e26a70SApple OSS Distributions 		.in.config.allowed_ctr_mask = 0xffff,
244*a1e26a70SApple OSS Distributions 	};
245*a1e26a70SApple OSS Distributions 
246*a1e26a70SApple OSS Distributions 	T_SETUPEND;
247*a1e26a70SApple OSS Distributions 
248*a1e26a70SApple OSS Distributions 	int ret = ioctl(fd, MT_IOC_ADD, &add_ctl);
249*a1e26a70SApple OSS Distributions 	T_EXPECT_POSIX_FAILURE(ret, EPERM,
250*a1e26a70SApple OSS Distributions 	    "should not be allowed to count any events on UPMU");
251*a1e26a70SApple OSS Distributions }
252*a1e26a70SApple OSS Distributions 
253*a1e26a70SApple OSS Distributions T_DECL(secure_upmu_event_restrictions, "secured UPMU should be restricted to no events",
254*a1e26a70SApple OSS Distributions     _T_META_CPC_SECURE_ON_DEV, T_META_TAG_VM_NOT_ELIGIBLE)
255*a1e26a70SApple OSS Distributions {
256*a1e26a70SApple OSS Distributions 	check_secure_upmu();
257*a1e26a70SApple OSS Distributions }
258*a1e26a70SApple OSS Distributions 
259*a1e26a70SApple OSS Distributions T_DECL(release_upmu_event_restrictions, "release UPMU should be restricted to no events",
260*a1e26a70SApple OSS Distributions     XNU_T_META_REQUIRES_RELEASE_KERNEL, T_META_TAG_VM_NOT_ELIGIBLE)
261*a1e26a70SApple OSS Distributions {
262*a1e26a70SApple OSS Distributions 	check_secure_upmu();
263*a1e26a70SApple OSS Distributions }
264*a1e26a70SApple OSS Distributions 
265*a1e26a70SApple OSS Distributions // Check that events which are exposed publicly are allowed to be configured.
266*a1e26a70SApple OSS Distributions 
267*a1e26a70SApple OSS Distributions static void
check_event_coverage(kpep_db_flags_t flag,const char * kind)268*a1e26a70SApple OSS Distributions check_event_coverage(kpep_db_flags_t flag, const char *kind)
269*a1e26a70SApple OSS Distributions {
270*a1e26a70SApple OSS Distributions 	kpep_db_t db = NULL;
271*a1e26a70SApple OSS Distributions 	int ret = kpep_db_createx(NULL, flag, &db);
272*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "creating %s event database", kind);
273*a1e26a70SApple OSS Distributions 
274*a1e26a70SApple OSS Distributions 	size_t event_count = 0;
275*a1e26a70SApple OSS Distributions 	ret = kpep_db_events_count(db, &event_count);
276*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting %s event count", kind);
277*a1e26a70SApple OSS Distributions 
278*a1e26a70SApple OSS Distributions 	kpep_event_t *events = calloc(event_count, sizeof(events[0]));
279*a1e26a70SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO;
280*a1e26a70SApple OSS Distributions 	T_ASSERT_NOTNULL(events, "allocate space for events");
281*a1e26a70SApple OSS Distributions 
282*a1e26a70SApple OSS Distributions 	ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
283*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting public events");
284*a1e26a70SApple OSS Distributions 
285*a1e26a70SApple OSS Distributions 	kpep_config_t config = NULL;
286*a1e26a70SApple OSS Distributions 	ret = kpep_config_create(db, &config);
287*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "creating event configuration");
288*a1e26a70SApple OSS Distributions 	ret = kpep_config_force_counters(config);
289*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "forcing counters with configuration");
290*a1e26a70SApple OSS Distributions 
291*a1e26a70SApple OSS Distributions 	size_t kpc_count_allocated = 0;
292*a1e26a70SApple OSS Distributions 	uint64_t *kpc_configs = NULL;
293*a1e26a70SApple OSS Distributions 	size_t *kpc_map = NULL;
294*a1e26a70SApple OSS Distributions 
295*a1e26a70SApple OSS Distributions 	unsigned int tested = 0;
296*a1e26a70SApple OSS Distributions 	for (size_t i = 0; i < event_count; i++) {
297*a1e26a70SApple OSS Distributions 		kpep_event_t event = events[i];
298*a1e26a70SApple OSS Distributions 		const char *name = NULL;
299*a1e26a70SApple OSS Distributions 		ret = kpep_event_name(event, &name);
300*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting event name");
301*a1e26a70SApple OSS Distributions 		if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
302*a1e26a70SApple OSS Distributions 			T_LOG("skipping non-configurable %s event", name);
303*a1e26a70SApple OSS Distributions 			continue;
304*a1e26a70SApple OSS Distributions 		}
305*a1e26a70SApple OSS Distributions 
306*a1e26a70SApple OSS Distributions 		ret = kpep_config_add_event(config, &event, 0, NULL);
307*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "adding event %s to configuration", name);
308*a1e26a70SApple OSS Distributions 
309*a1e26a70SApple OSS Distributions 		size_t kpc_count = 0;
310*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc_count(config, &kpc_count);
311*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC count");
312*a1e26a70SApple OSS Distributions 		if (kpc_count > kpc_count_allocated) {
313*a1e26a70SApple OSS Distributions 			free(kpc_configs);
314*a1e26a70SApple OSS Distributions 			kpc_configs = calloc(kpc_count, sizeof(kpc_config_t));
315*a1e26a70SApple OSS Distributions 			T_QUIET; T_WITH_ERRNO;
316*a1e26a70SApple OSS Distributions 			T_ASSERT_NOTNULL(kpc_configs, "allocate space for KPC configs");
317*a1e26a70SApple OSS Distributions 			kpc_count_allocated = kpc_count;
318*a1e26a70SApple OSS Distributions 
319*a1e26a70SApple OSS Distributions 			free(kpc_map);
320*a1e26a70SApple OSS Distributions 			kpc_map = calloc(kpc_count, sizeof(size_t));
321*a1e26a70SApple OSS Distributions 			T_QUIET; T_WITH_ERRNO;
322*a1e26a70SApple OSS Distributions 			T_ASSERT_NOTNULL(kpc_map, "allocate space for KPC map");
323*a1e26a70SApple OSS Distributions 		}
324*a1e26a70SApple OSS Distributions 
325*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc_map(config, kpc_map, kpc_count * sizeof(size_t));
326*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC map for event %s", name);
327*a1e26a70SApple OSS Distributions 		ret = kpep_config_kpc(config, (uint8_t *)kpc_configs, kpc_count * sizeof(kpc_config_t));
328*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting KPC configs for event %s", name);
329*a1e26a70SApple OSS Distributions 		kpc_config_t kpc_config = kpc_configs[kpc_map[0]];
330*a1e26a70SApple OSS Distributions 
331*a1e26a70SApple OSS Distributions 		ret = kpep_config_apply(config);
332*a1e26a70SApple OSS Distributions 		if (ret == KPEP_ERR_ERRNO && errno == EPERM) {
333*a1e26a70SApple OSS Distributions 			T_FAIL("failed to configure %s event %s (0x%04llx) with secure CPC", kind, name, kpc_config);
334*a1e26a70SApple OSS Distributions 		} else {
335*a1e26a70SApple OSS Distributions 			_assert_kpep_ok(ret, "applying configuration with event %s (0x%04llx)", name, kpc_config);
336*a1e26a70SApple OSS Distributions 		}
337*a1e26a70SApple OSS Distributions 		ret = kpep_config_remove_event(config, 0);
338*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "removing event %s from configuration", name);
339*a1e26a70SApple OSS Distributions 		tested++;
340*a1e26a70SApple OSS Distributions 	}
341*a1e26a70SApple OSS Distributions 
342*a1e26a70SApple OSS Distributions 	T_LOG("attempted to configure %u %s events", tested, kind);
343*a1e26a70SApple OSS Distributions 	kpep_config_free(config);
344*a1e26a70SApple OSS Distributions 	kpep_db_free(db);
345*a1e26a70SApple OSS Distributions 	free(kpc_configs);
346*a1e26a70SApple OSS Distributions 	free(kpc_map);
347*a1e26a70SApple OSS Distributions }
348*a1e26a70SApple OSS Distributions 
349*a1e26a70SApple OSS Distributions T_DECL(secure_public_event_coverage, "all public events in kpep should be allowed",
350*a1e26a70SApple OSS Distributions     _T_META_CPC_SECURE_ON_DEV, T_META_TAG_VM_NOT_ELIGIBLE)
351*a1e26a70SApple OSS Distributions {
352*a1e26a70SApple OSS Distributions 	check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
353*a1e26a70SApple OSS Distributions }
354*a1e26a70SApple OSS Distributions 
355*a1e26a70SApple OSS Distributions T_DECL(release_public_event_coverage, "all public events in kpep should be allowed",
356*a1e26a70SApple OSS Distributions     XNU_T_META_REQUIRES_RELEASE_KERNEL, T_META_TAG_VM_NOT_ELIGIBLE)
357*a1e26a70SApple OSS Distributions {
358*a1e26a70SApple OSS Distributions 	check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
359*a1e26a70SApple OSS Distributions }
360*a1e26a70SApple OSS Distributions 
361*a1e26a70SApple OSS Distributions // Check for internal development behaviors.
362*a1e26a70SApple OSS Distributions T_DECL(insecure_cpmu_unrestricted, "insecure CPMU should be unrestricted",
363*a1e26a70SApple OSS Distributions     XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL, T_META_SYSCTL_INT("kern.cpc.secure=0"), T_META_TAG_VM_NOT_ELIGIBLE)
364*a1e26a70SApple OSS Distributions {
365*a1e26a70SApple OSS Distributions 	check_event_coverage(KPEP_DB_FLAG_INTERNAL_ONLY, "internal");
366*a1e26a70SApple OSS Distributions }
367*a1e26a70SApple OSS Distributions 
368*a1e26a70SApple OSS Distributions T_DECL(secure_kpc_counting_system, "kpc should not allow counting the kernel when secure",
369*a1e26a70SApple OSS Distributions     _T_META_CPC_SECURE_ON_DEV)
370*a1e26a70SApple OSS Distributions {
371*a1e26a70SApple OSS Distributions 	kpep_db_t db = NULL;
372*a1e26a70SApple OSS Distributions 	int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &db);
373*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "creating public event database");
374*a1e26a70SApple OSS Distributions 
375*a1e26a70SApple OSS Distributions 	size_t event_count = 0;
376*a1e26a70SApple OSS Distributions 	ret = kpep_db_events_count(db, &event_count);
377*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting public event count");
378*a1e26a70SApple OSS Distributions 
379*a1e26a70SApple OSS Distributions 	kpep_event_t *events = calloc(event_count, sizeof(events[0]));
380*a1e26a70SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO;
381*a1e26a70SApple OSS Distributions 	T_ASSERT_NOTNULL(events, "allocate space for events");
382*a1e26a70SApple OSS Distributions 
383*a1e26a70SApple OSS Distributions 	ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
384*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "getting public events");
385*a1e26a70SApple OSS Distributions 
386*a1e26a70SApple OSS Distributions 	kpep_config_t config = NULL;
387*a1e26a70SApple OSS Distributions 	ret = kpep_config_create(db, &config);
388*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "creating event configuration");
389*a1e26a70SApple OSS Distributions 	ret = kpep_config_force_counters(config);
390*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "forcing counters with configuration");
391*a1e26a70SApple OSS Distributions 
392*a1e26a70SApple OSS Distributions 
393*a1e26a70SApple OSS Distributions 	kpep_event_t event = NULL;
394*a1e26a70SApple OSS Distributions 	const char *name = NULL;
395*a1e26a70SApple OSS Distributions 	for (size_t i = 0; i < event_count; i++) {
396*a1e26a70SApple OSS Distributions 		event = events[i];
397*a1e26a70SApple OSS Distributions 		ret = kpep_event_name(event, &name);
398*a1e26a70SApple OSS Distributions 		_assert_kpep_ok(ret, "getting event name");
399*a1e26a70SApple OSS Distributions 		if (strncmp(name, "FIXED", strlen("FIXED")) != 0) {
400*a1e26a70SApple OSS Distributions 			break;
401*a1e26a70SApple OSS Distributions 		}
402*a1e26a70SApple OSS Distributions 		T_LOG("skipping non-configurable %s event", name);
403*a1e26a70SApple OSS Distributions 	}
404*a1e26a70SApple OSS Distributions 
405*a1e26a70SApple OSS Distributions 	ret = kpep_config_add_event(config, &event, KPEP_EVENT_FLAG_KERNEL, NULL);
406*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "adding event %s to configuration", name);
407*a1e26a70SApple OSS Distributions 
408*a1e26a70SApple OSS Distributions 	ret = kpep_config_apply(config);
409*a1e26a70SApple OSS Distributions 	_assert_kpep_ok(ret, "applying configuration with event %s", name);
410*a1e26a70SApple OSS Distributions 
411*a1e26a70SApple OSS Distributions 	uint32_t config_count = kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK);
412*a1e26a70SApple OSS Distributions 	uint64_t *configs = calloc(config_count, sizeof(configs[0]));
413*a1e26a70SApple OSS Distributions 	T_QUIET;
414*a1e26a70SApple OSS Distributions 	T_ASSERT_NOTNULL(configs, "allocated %u * %zu", config_count, sizeof(configs[0]));
415*a1e26a70SApple OSS Distributions 	ret = kpc_get_config(KPC_CLASS_CONFIGURABLE_MASK, configs);
416*a1e26a70SApple OSS Distributions 	for (uint32_t i = 0; i < config_count; i++) {
417*a1e26a70SApple OSS Distributions 		if ((configs[i] & 0x40000)) {
418*a1e26a70SApple OSS Distributions 			T_FAIL("found configurable counter %u with configuration 0x%llx", i, configs[i]);
419*a1e26a70SApple OSS Distributions 		}
420*a1e26a70SApple OSS Distributions 	}
421*a1e26a70SApple OSS Distributions 	T_LOG("checked %d events for EL2 counting", config_count);
422*a1e26a70SApple OSS Distributions 
423*a1e26a70SApple OSS Distributions 	kpep_config_free(config);
424*a1e26a70SApple OSS Distributions 	kpep_db_free(db);
425*a1e26a70SApple OSS Distributions }
426