1*1031c584SApple OSS Distributions // Copyright 2023 (c) Apple Inc. All rights reserved.
2*1031c584SApple OSS Distributions
3*1031c584SApple OSS Distributions #include <darwintest.h>
4*1031c584SApple OSS Distributions #include <darwintest_utils.h>
5*1031c584SApple OSS Distributions #include <dirent.h>
6*1031c584SApple OSS Distributions #include <kperf/kpc.h>
7*1031c584SApple OSS Distributions #include <kperfdata/kpep.h>
8*1031c584SApple OSS Distributions #include <stdarg.h>
9*1031c584SApple OSS Distributions #include <stdbool.h>
10*1031c584SApple OSS Distributions #include <string.h>
11*1031c584SApple OSS Distributions #include <sys/guarded.h>
12*1031c584SApple OSS Distributions #include <sys/ioctl.h>
13*1031c584SApple OSS Distributions #include <sys/monotonic.h>
14*1031c584SApple OSS Distributions
15*1031c584SApple OSS Distributions #include "test_utils.h"
16*1031c584SApple OSS Distributions
17*1031c584SApple OSS Distributions #if __arm64__
18*1031c584SApple OSS Distributions #define HAS_CPC_SECURITY true
19*1031c584SApple OSS Distributions #else // __arm64__
20*1031c584SApple OSS Distributions #define HAS_CPC_SECURITY false
21*1031c584SApple OSS Distributions #endif // !__arm64__
22*1031c584SApple OSS Distributions
23*1031c584SApple OSS Distributions #define _T_META_REQUIRES_CPC_SUPPORT \
24*1031c584SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", "1")
25*1031c584SApple OSS Distributions
26*1031c584SApple OSS Distributions T_GLOBAL_META(
27*1031c584SApple OSS Distributions T_META_NAMESPACE("xnu.cpc"),
28*1031c584SApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
29*1031c584SApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("cpu counters"),
30*1031c584SApple OSS Distributions T_META_OWNER("mwidmann"),
31*1031c584SApple OSS Distributions T_META_CHECK_LEAKS(false),
32*1031c584SApple OSS Distributions XNU_T_META_SOC_SPECIFIC,
33*1031c584SApple OSS Distributions T_META_ENABLED(HAS_CPC_SECURITY),
34*1031c584SApple OSS Distributions _T_META_REQUIRES_CPC_SUPPORT);
35*1031c584SApple OSS Distributions
36*1031c584SApple OSS Distributions // Several of these tests have two variants to support running on development and release kernels.
37*1031c584SApple 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.
38*1031c584SApple OSS Distributions
39*1031c584SApple OSS Distributions // Metadata for running on a development kernel in CPC secure mode.
40*1031c584SApple OSS Distributions #define _T_META_CPC_SECURE_ON_DEV \
41*1031c584SApple OSS Distributions XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL, T_META_SYSCTL_INT("kern.cpc.secure=1")
42*1031c584SApple OSS Distributions
43*1031c584SApple OSS Distributions static void
_assert_kpep_ok(int kpep_err,const char * fmt,...)44*1031c584SApple OSS Distributions _assert_kpep_ok(int kpep_err, const char *fmt, ...)
45*1031c584SApple OSS Distributions {
46*1031c584SApple OSS Distributions char msg[1024] = "";
47*1031c584SApple OSS Distributions va_list args;
48*1031c584SApple OSS Distributions va_start(args, fmt);
49*1031c584SApple OSS Distributions vsnprintf(msg, sizeof(msg), fmt, args);
50*1031c584SApple OSS Distributions va_end(args);
51*1031c584SApple OSS Distributions T_QUIET;
52*1031c584SApple OSS Distributions T_ASSERT_EQ(kpep_err, KPEP_ERR_NONE, "%s: %s", msg, kpep_strerror(kpep_err));
53*1031c584SApple OSS Distributions }
54*1031c584SApple OSS Distributions
55*1031c584SApple OSS Distributions static void
_skip_for_db(const char * kind,int kpep_err)56*1031c584SApple OSS Distributions _skip_for_db(const char *kind, int kpep_err)
57*1031c584SApple OSS Distributions {
58*1031c584SApple OSS Distributions const char * const public_kpep_path = "/usr/share/kpep";
59*1031c584SApple OSS Distributions const char * const internal_kpep_path = "/usr/local/share/kpep";
60*1031c584SApple OSS Distributions const char * const paths[2] = { public_kpep_path, internal_kpep_path, };
61*1031c584SApple OSS Distributions for (int i = 0; i < 2; i++) {
62*1031c584SApple OSS Distributions const char * const path = paths[i];
63*1031c584SApple OSS Distributions T_LOG("contents of %s:", path);
64*1031c584SApple OSS Distributions DIR *dir = opendir(path);
65*1031c584SApple OSS Distributions if (dir) {
66*1031c584SApple OSS Distributions struct dirent *entry = NULL;
67*1031c584SApple OSS Distributions while ((entry = readdir(dir)) != NULL) {
68*1031c584SApple OSS Distributions T_LOG(" %s", entry->d_name);
69*1031c584SApple OSS Distributions }
70*1031c584SApple OSS Distributions (void)closedir(dir);
71*1031c584SApple OSS Distributions } else {
72*1031c584SApple OSS Distributions T_LOG("failed to open directory: %s", strerror(errno));
73*1031c584SApple OSS Distributions }
74*1031c584SApple OSS Distributions }
75*1031c584SApple OSS Distributions int cpu_family = 0;
76*1031c584SApple OSS Distributions size_t family_size = sizeof(cpu_family);
77*1031c584SApple OSS Distributions int ret = sysctlbyname("hw.cpufamily", &cpu_family, &family_size, NULL, 0);
78*1031c584SApple OSS Distributions if (ret != 0) {
79*1031c584SApple OSS Distributions T_LOG("HW CPU family: 0x%8x", cpu_family);
80*1031c584SApple OSS Distributions } else {
81*1031c584SApple OSS Distributions T_LOG("failed to get hw.cpufamily: %s", strerror(errno));
82*1031c584SApple OSS Distributions }
83*1031c584SApple OSS Distributions T_SKIP("cannot open %s event database: %s", kind, kpep_strerror(kpep_err));
84*1031c584SApple OSS Distributions }
85*1031c584SApple OSS Distributions
86*1031c584SApple OSS Distributions // Check that a secure kernel disallows restricted events.
87*1031c584SApple OSS Distributions
88*1031c584SApple OSS Distributions static void
check_secure_cpmu(void)89*1031c584SApple OSS Distributions check_secure_cpmu(void)
90*1031c584SApple OSS Distributions {
91*1031c584SApple OSS Distributions kpep_db_t public_db = NULL;
92*1031c584SApple OSS Distributions int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &public_db);
93*1031c584SApple OSS Distributions if (ret != KPEP_ERR_NONE) {
94*1031c584SApple OSS Distributions _skip_for_db("public", ret);
95*1031c584SApple OSS Distributions }
96*1031c584SApple OSS Distributions kpep_db_t internal_db = NULL;
97*1031c584SApple OSS Distributions ret = kpep_db_createx(NULL, KPEP_DB_FLAG_INTERNAL_ONLY, &internal_db);
98*1031c584SApple OSS Distributions if (ret != KPEP_ERR_NONE) {
99*1031c584SApple OSS Distributions _skip_for_db("internal", ret);
100*1031c584SApple OSS Distributions }
101*1031c584SApple OSS Distributions const char *na = NULL;
102*1031c584SApple OSS Distributions kpep_db_name(public_db, &na);
103*1031c584SApple OSS Distributions
104*1031c584SApple OSS Distributions size_t internal_event_count = 0;
105*1031c584SApple OSS Distributions ret = kpep_db_events_count(internal_db, &internal_event_count);
106*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting internal event count");
107*1031c584SApple OSS Distributions
108*1031c584SApple OSS Distributions kpep_event_t *internal_events = calloc(internal_event_count,
109*1031c584SApple OSS Distributions sizeof(internal_events[0]));
110*1031c584SApple OSS Distributions T_QUIET; T_WITH_ERRNO;
111*1031c584SApple OSS Distributions T_ASSERT_NOTNULL(internal_events, "allocate space for internal events");
112*1031c584SApple OSS Distributions
113*1031c584SApple OSS Distributions ret = kpep_db_events(internal_db, internal_events,
114*1031c584SApple OSS Distributions internal_event_count * sizeof(internal_events[0]));
115*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting internal events");
116*1031c584SApple OSS Distributions
117*1031c584SApple OSS Distributions kpep_config_t config = NULL;
118*1031c584SApple OSS Distributions ret = kpep_config_create(internal_db, &config);
119*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "creating event configuration");
120*1031c584SApple OSS Distributions ret = kpep_config_force_counters(config);
121*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "forcing counters with configuration");
122*1031c584SApple OSS Distributions
123*1031c584SApple OSS Distributions unsigned int tested = 0;
124*1031c584SApple OSS Distributions unsigned int filtered = 0;
125*1031c584SApple OSS Distributions unsigned int public_tested = 0;
126*1031c584SApple OSS Distributions for (size_t i = 0; i < internal_event_count; i++) {
127*1031c584SApple OSS Distributions kpep_event_t event = internal_events[i];
128*1031c584SApple OSS Distributions const char *name = NULL;
129*1031c584SApple OSS Distributions ret = kpep_event_alias(event, &name);
130*1031c584SApple OSS Distributions if (!name) {
131*1031c584SApple OSS Distributions ret = kpep_event_name(event, &name);
132*1031c584SApple OSS Distributions }
133*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting event name");
134*1031c584SApple OSS Distributions if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
135*1031c584SApple OSS Distributions T_LOG("skipping non-configurable %s event", name);
136*1031c584SApple OSS Distributions continue;
137*1031c584SApple OSS Distributions }
138*1031c584SApple OSS Distributions bool empty_event = strcmp(name, "NO_EVNT") == 0;
139*1031c584SApple OSS Distributions if (empty_event) {
140*1031c584SApple OSS Distributions continue;
141*1031c584SApple OSS Distributions }
142*1031c584SApple OSS Distributions
143*1031c584SApple OSS Distributions kpep_event_t public_event = NULL;
144*1031c584SApple OSS Distributions ret = kpep_db_event(public_db, name, &public_event);
145*1031c584SApple OSS Distributions bool internal_only = ret == KPEP_ERR_EVENT_NOT_FOUND;
146*1031c584SApple OSS Distributions ret = kpep_config_add_event(config, &event, 0, NULL);
147*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "adding event %s to configuration", name);
148*1031c584SApple OSS Distributions
149*1031c584SApple OSS Distributions ret = kpep_config_apply(config);
150*1031c584SApple OSS Distributions bool not_permitted = ret == KPEP_ERR_ERRNO && errno == EPERM;
151*1031c584SApple OSS Distributions if (not_permitted) {
152*1031c584SApple OSS Distributions if (!internal_only) {
153*1031c584SApple OSS Distributions T_LOG("failed to configure public event %s", name);
154*1031c584SApple OSS Distributions }
155*1031c584SApple OSS Distributions filtered++;
156*1031c584SApple OSS Distributions } else if (internal_only) {
157*1031c584SApple OSS Distributions T_FAIL("configured internal-only event %s with secure CPC", name);
158*1031c584SApple OSS Distributions } else {
159*1031c584SApple OSS Distributions public_tested++;
160*1031c584SApple OSS Distributions }
161*1031c584SApple OSS Distributions ret = kpep_config_remove_event(config, 0);
162*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "removing event %s from configuration", name);
163*1031c584SApple OSS Distributions tested++;
164*1031c584SApple OSS Distributions }
165*1031c584SApple OSS Distributions
166*1031c584SApple OSS Distributions T_LOG("tested %u internal/public events", tested);
167*1031c584SApple OSS Distributions T_LOG("correctly permitted to configure %u public events", public_tested);
168*1031c584SApple OSS Distributions T_LOG("correctly not permitted to configure %u internal-only events",
169*1031c584SApple OSS Distributions filtered);
170*1031c584SApple OSS Distributions kpep_config_free(config);
171*1031c584SApple OSS Distributions kpep_db_free(public_db);
172*1031c584SApple OSS Distributions kpep_db_free(internal_db);
173*1031c584SApple OSS Distributions }
174*1031c584SApple OSS Distributions
175*1031c584SApple OSS Distributions T_DECL(secure_cpmu_event_restrictions, "secured CPMU should be restricted to known events",
176*1031c584SApple OSS Distributions _T_META_CPC_SECURE_ON_DEV)
177*1031c584SApple OSS Distributions {
178*1031c584SApple OSS Distributions check_secure_cpmu();
179*1031c584SApple OSS Distributions }
180*1031c584SApple OSS Distributions
181*1031c584SApple OSS Distributions T_DECL(release_cpmu_event_restrictions, "release CPMU should be restricted to known events",
182*1031c584SApple OSS Distributions XNU_T_META_REQUIRES_RELEASE_KERNEL)
183*1031c584SApple OSS Distributions {
184*1031c584SApple OSS Distributions check_secure_cpmu();
185*1031c584SApple OSS Distributions }
186*1031c584SApple OSS Distributions
187*1031c584SApple OSS Distributions #define UNCORE_DEV_PATH "/dev/monotonic/uncore"
188*1031c584SApple OSS Distributions #define UPMU_REF_CYCLES 0x02
189*1031c584SApple OSS Distributions
190*1031c584SApple OSS Distributions static void
check_secure_upmu(void)191*1031c584SApple OSS Distributions check_secure_upmu(void)
192*1031c584SApple OSS Distributions {
193*1031c584SApple OSS Distributions guardid_t guard;
194*1031c584SApple OSS Distributions int fd;
195*1031c584SApple OSS Distributions
196*1031c584SApple OSS Distributions guard = 0xa5adcafe;
197*1031c584SApple OSS Distributions
198*1031c584SApple OSS Distributions T_SETUPBEGIN;
199*1031c584SApple OSS Distributions
200*1031c584SApple OSS Distributions fd = guarded_open_np(UNCORE_DEV_PATH, &guard,
201*1031c584SApple OSS Distributions GUARD_CLOSE | GUARD_DUP | GUARD_WRITE, O_CLOEXEC | O_EXCL);
202*1031c584SApple OSS Distributions if (fd < 0 && errno == ENOENT) {
203*1031c584SApple OSS Distributions T_SKIP("uncore counters are unsupported");
204*1031c584SApple OSS Distributions }
205*1031c584SApple OSS Distributions
206*1031c584SApple OSS Distributions union monotonic_ctl_add add_ctl = {
207*1031c584SApple OSS Distributions .in.config.event = UPMU_REF_CYCLES,
208*1031c584SApple OSS Distributions .in.config.allowed_ctr_mask = 0xffff,
209*1031c584SApple OSS Distributions };
210*1031c584SApple OSS Distributions
211*1031c584SApple OSS Distributions T_SETUPEND;
212*1031c584SApple OSS Distributions
213*1031c584SApple OSS Distributions int ret = ioctl(fd, MT_IOC_ADD, &add_ctl);
214*1031c584SApple OSS Distributions T_EXPECT_POSIX_FAILURE(ret, EPERM,
215*1031c584SApple OSS Distributions "should not be allowed to count any events on UPMU");
216*1031c584SApple OSS Distributions }
217*1031c584SApple OSS Distributions
218*1031c584SApple OSS Distributions T_DECL(secure_upmu_event_restrictions, "secured UPMU should be restricted to no events",
219*1031c584SApple OSS Distributions _T_META_CPC_SECURE_ON_DEV)
220*1031c584SApple OSS Distributions {
221*1031c584SApple OSS Distributions check_secure_upmu();
222*1031c584SApple OSS Distributions }
223*1031c584SApple OSS Distributions
224*1031c584SApple OSS Distributions T_DECL(release_upmu_event_restrictions, "release UPMU should be restricted to no events",
225*1031c584SApple OSS Distributions XNU_T_META_REQUIRES_RELEASE_KERNEL)
226*1031c584SApple OSS Distributions {
227*1031c584SApple OSS Distributions check_secure_upmu();
228*1031c584SApple OSS Distributions }
229*1031c584SApple OSS Distributions
230*1031c584SApple OSS Distributions // Check that events which are exposed publicly are allowed to be configured.
231*1031c584SApple OSS Distributions
232*1031c584SApple OSS Distributions static void
check_event_coverage(kpep_db_flags_t flag,const char * kind)233*1031c584SApple OSS Distributions check_event_coverage(kpep_db_flags_t flag, const char *kind)
234*1031c584SApple OSS Distributions {
235*1031c584SApple OSS Distributions kpep_db_t db = NULL;
236*1031c584SApple OSS Distributions int ret = kpep_db_createx(NULL, flag, &db);
237*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "creating %s event database", kind);
238*1031c584SApple OSS Distributions
239*1031c584SApple OSS Distributions size_t event_count = 0;
240*1031c584SApple OSS Distributions ret = kpep_db_events_count(db, &event_count);
241*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting %s event count", kind);
242*1031c584SApple OSS Distributions
243*1031c584SApple OSS Distributions kpep_event_t *events = calloc(event_count, sizeof(events[0]));
244*1031c584SApple OSS Distributions T_QUIET; T_WITH_ERRNO;
245*1031c584SApple OSS Distributions T_ASSERT_NOTNULL(events, "allocate space for events");
246*1031c584SApple OSS Distributions
247*1031c584SApple OSS Distributions ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
248*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting public events");
249*1031c584SApple OSS Distributions
250*1031c584SApple OSS Distributions kpep_config_t config = NULL;
251*1031c584SApple OSS Distributions ret = kpep_config_create(db, &config);
252*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "creating event configuration");
253*1031c584SApple OSS Distributions ret = kpep_config_force_counters(config);
254*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "forcing counters with configuration");
255*1031c584SApple OSS Distributions
256*1031c584SApple OSS Distributions unsigned int tested = 0;
257*1031c584SApple OSS Distributions for (size_t i = 0; i < event_count; i++) {
258*1031c584SApple OSS Distributions kpep_event_t event = events[i];
259*1031c584SApple OSS Distributions const char *name = NULL;
260*1031c584SApple OSS Distributions ret = kpep_event_name(event, &name);
261*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "getting event name");
262*1031c584SApple OSS Distributions if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
263*1031c584SApple OSS Distributions T_LOG("skipping non-configurable %s event", name);
264*1031c584SApple OSS Distributions continue;
265*1031c584SApple OSS Distributions }
266*1031c584SApple OSS Distributions
267*1031c584SApple OSS Distributions ret = kpep_config_add_event(config, &event, 0, NULL);
268*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "adding event %s to configuration", name);
269*1031c584SApple OSS Distributions
270*1031c584SApple OSS Distributions ret = kpep_config_apply(config);
271*1031c584SApple OSS Distributions if (ret == KPEP_ERR_ERRNO && errno == EPERM) {
272*1031c584SApple OSS Distributions T_FAIL("failed to configure %s event %s with secure CPC", kind, name);
273*1031c584SApple OSS Distributions } else {
274*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "applying configuration with event %s", name);
275*1031c584SApple OSS Distributions }
276*1031c584SApple OSS Distributions ret = kpep_config_remove_event(config, 0);
277*1031c584SApple OSS Distributions _assert_kpep_ok(ret, "removing event %s from configuration", name);
278*1031c584SApple OSS Distributions tested++;
279*1031c584SApple OSS Distributions }
280*1031c584SApple OSS Distributions
281*1031c584SApple OSS Distributions T_LOG("successfully configured %u %s events", tested, kind);
282*1031c584SApple OSS Distributions kpep_config_free(config);
283*1031c584SApple OSS Distributions kpep_db_free(db);
284*1031c584SApple OSS Distributions }
285*1031c584SApple OSS Distributions
286*1031c584SApple OSS Distributions T_DECL(secure_public_event_coverage, "all public events in kpep should be allowed",
287*1031c584SApple OSS Distributions _T_META_CPC_SECURE_ON_DEV)
288*1031c584SApple OSS Distributions {
289*1031c584SApple OSS Distributions check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
290*1031c584SApple OSS Distributions }
291*1031c584SApple OSS Distributions
292*1031c584SApple OSS Distributions T_DECL(release_public_event_coverage, "all public events in kpep should be allowed",
293*1031c584SApple OSS Distributions XNU_T_META_REQUIRES_RELEASE_KERNEL)
294*1031c584SApple OSS Distributions {
295*1031c584SApple OSS Distributions check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
296*1031c584SApple OSS Distributions }
297*1031c584SApple OSS Distributions
298*1031c584SApple OSS Distributions // Check for internal development behaviors.
299*1031c584SApple OSS Distributions
300*1031c584SApple OSS Distributions T_DECL(insecure_cpmu_unrestricted, "insecure CPMU should be unrestricted",
301*1031c584SApple OSS Distributions XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL, T_META_SYSCTL_INT("kern.cpc.secure=0"))
302*1031c584SApple OSS Distributions {
303*1031c584SApple OSS Distributions check_event_coverage(KPEP_DB_FLAG_INTERNAL_ONLY, "internal");
304*1031c584SApple OSS Distributions }
305