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