1*bbb1b6f9SApple OSS Distributions /*
2*bbb1b6f9SApple OSS Distributions * Copyright (c) 2024 Apple Inc. All rights reserved.
3*bbb1b6f9SApple OSS Distributions *
4*bbb1b6f9SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*bbb1b6f9SApple OSS Distributions *
6*bbb1b6f9SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*bbb1b6f9SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*bbb1b6f9SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*bbb1b6f9SApple OSS Distributions * compliance with the License. The rights granted to you under the License
10*bbb1b6f9SApple OSS Distributions * may not be used to create, or enable the creation or redistribution of,
11*bbb1b6f9SApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to
12*bbb1b6f9SApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any
13*bbb1b6f9SApple OSS Distributions * terms of an Apple operating system software license agreement.
14*bbb1b6f9SApple OSS Distributions *
15*bbb1b6f9SApple OSS Distributions * Please obtain a copy of the License at
16*bbb1b6f9SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*bbb1b6f9SApple OSS Distributions *
18*bbb1b6f9SApple OSS Distributions * The Original Code and all software distributed under the License are
19*bbb1b6f9SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*bbb1b6f9SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*bbb1b6f9SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*bbb1b6f9SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*bbb1b6f9SApple OSS Distributions * Please see the License for the specific language governing rights and
24*bbb1b6f9SApple OSS Distributions * limitations under the License.
25*bbb1b6f9SApple OSS Distributions *
26*bbb1b6f9SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*bbb1b6f9SApple OSS Distributions */
28*bbb1b6f9SApple OSS Distributions
29*bbb1b6f9SApple OSS Distributions #include <arm_acle.h>
30*bbb1b6f9SApple OSS Distributions #include <libproc.h>
31*bbb1b6f9SApple OSS Distributions #include <signal.h>
32*bbb1b6f9SApple OSS Distributions #include <spawn_private.h>
33*bbb1b6f9SApple OSS Distributions #include <stddef.h>
34*bbb1b6f9SApple OSS Distributions #include <stdlib.h>
35*bbb1b6f9SApple OSS Distributions
36*bbb1b6f9SApple OSS Distributions #include <mach/mach.h>
37*bbb1b6f9SApple OSS Distributions #include <mach/mach_init.h>
38*bbb1b6f9SApple OSS Distributions #include <mach/mach_vm.h>
39*bbb1b6f9SApple OSS Distributions #include <mach/vm_map.h>
40*bbb1b6f9SApple OSS Distributions
41*bbb1b6f9SApple OSS Distributions #include <darwintest.h>
42*bbb1b6f9SApple OSS Distributions
43*bbb1b6f9SApple OSS Distributions #include <stdint.h>
44*bbb1b6f9SApple OSS Distributions #include <unistd.h>
45*bbb1b6f9SApple OSS Distributions #include <sys/types.h>
46*bbb1b6f9SApple OSS Distributions #include <sys/sysctl.h>
47*bbb1b6f9SApple OSS Distributions #define HAS_MTE 1
48*bbb1b6f9SApple OSS Distributions
49*bbb1b6f9SApple OSS Distributions #include <vm/vm_compressor_info.h>
50*bbb1b6f9SApple OSS Distributions
51*bbb1b6f9SApple OSS Distributions #include "arm_mte_utilities.h"
52*bbb1b6f9SApple OSS Distributions #include "test_utils.h"
53*bbb1b6f9SApple OSS Distributions
54*bbb1b6f9SApple OSS Distributions T_GLOBAL_META(
55*bbb1b6f9SApple OSS Distributions T_META_NAMESPACE("xnu.arm"),
56*bbb1b6f9SApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
57*bbb1b6f9SApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("arm"),
58*bbb1b6f9SApple OSS Distributions T_META_OWNER("s_shalom"),
59*bbb1b6f9SApple OSS Distributions T_META_RUN_CONCURRENTLY(false) /* test is sampling global sysctls */
60*bbb1b6f9SApple OSS Distributions );
61*bbb1b6f9SApple OSS Distributions
62*bbb1b6f9SApple OSS Distributions /*
63*bbb1b6f9SApple OSS Distributions * This test exercises internal functions that compress and decompress the MTE buffers
64*bbb1b6f9SApple OSS Distributions * These functions are not accessible from outside the kernel, so we include them here verbatim.
65*bbb1b6f9SApple OSS Distributions * In the future when user-land unit-test that runs kernel code we can move this there.
66*bbb1b6f9SApple OSS Distributions */
67*bbb1b6f9SApple OSS Distributions #define COMPRESSOR_TESTER 1
68*bbb1b6f9SApple OSS Distributions #define DEVELOPMENT 1
69*bbb1b6f9SApple OSS Distributions
70*bbb1b6f9SApple OSS Distributions // these need to be redefined since getting them from the XNU headers would create include conflicts
71*bbb1b6f9SApple OSS Distributions #define C_MTE_SIZE 512
72*bbb1b6f9SApple OSS Distributions #define C_SEG_OFFSET_ALIGNMENT_MASK (0x3FULL)
73*bbb1b6f9SApple OSS Distributions #define C_SEG_OFFSET_ALIGNMENT_BOUNDARY (64)
74*bbb1b6f9SApple OSS Distributions
75*bbb1b6f9SApple OSS Distributions #define C_SLOT_EXTRA_METADATA 16 /* 16 possible tags */
76*bbb1b6f9SApple OSS Distributions #define C_SLOT_C_MTE_SIZE_MAX (C_MTE_SIZE + C_SLOT_EXTRA_METADATA + 1)
77*bbb1b6f9SApple OSS Distributions
78*bbb1b6f9SApple OSS Distributions #define VM_MEMTAG_PTR_SIZE 56
79*bbb1b6f9SApple OSS Distributions #define VM_MEMTAG_TAG_SIZE 4
80*bbb1b6f9SApple OSS Distributions #define VM_MEMTAG_UPPER_SIZE 4
81*bbb1b6f9SApple OSS Distributions #define VM_MEMTAG_BYTES_PER_TAG 16
82*bbb1b6f9SApple OSS Distributions
83*bbb1b6f9SApple OSS Distributions #define C_SEG_ROUND_TO_ALIGNMENT(offset) \
84*bbb1b6f9SApple OSS Distributions (((offset) + C_SEG_OFFSET_ALIGNMENT_MASK) & ~C_SEG_OFFSET_ALIGNMENT_MASK)
85*bbb1b6f9SApple OSS Distributions
86*bbb1b6f9SApple OSS Distributions #define __assert_only __unused
87*bbb1b6f9SApple OSS Distributions
88*bbb1b6f9SApple OSS Distributions #include "../osfmk/arm64/vm_mte_compress.c"
89*bbb1b6f9SApple OSS Distributions
90*bbb1b6f9SApple OSS Distributions // masks only the tags bits out of a pointer
91*bbb1b6f9SApple OSS Distributions #define TAG_MASK (((1UL << VM_MEMTAG_TAG_SIZE) - 1UL) << VM_MEMTAG_PTR_SIZE)
92*bbb1b6f9SApple OSS Distributions
93*bbb1b6f9SApple OSS Distributions
94*bbb1b6f9SApple OSS Distributions static void
show_buf_diff(const uint8_t * a,const uint8_t * b,size_t sz)95*bbb1b6f9SApple OSS Distributions show_buf_diff(const uint8_t* a, const uint8_t* b, size_t sz)
96*bbb1b6f9SApple OSS Distributions {
97*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < sz; ++i) {
98*bbb1b6f9SApple OSS Distributions if (a[i] != b[i]) {
99*bbb1b6f9SApple OSS Distributions T_LOG(" byte diff at %u : %d != %d", i, (int)a[i], (int)b[i]);
100*bbb1b6f9SApple OSS Distributions break;
101*bbb1b6f9SApple OSS Distributions }
102*bbb1b6f9SApple OSS Distributions }
103*bbb1b6f9SApple OSS Distributions }
104*bbb1b6f9SApple OSS Distributions
105*bbb1b6f9SApple OSS Distributions // expected results of vm_mte_rle_compress_tags()
106*bbb1b6f9SApple OSS Distributions #define CASE_UNKNOWN 0
107*bbb1b6f9SApple OSS Distributions #define CASE_NON_COMP 1
108*bbb1b6f9SApple OSS Distributions #define CASE_SINGLE_TAG 2
109*bbb1b6f9SApple OSS Distributions #define CASE_NORMAL 3
110*bbb1b6f9SApple OSS Distributions
111*bbb1b6f9SApple OSS Distributions static uint32_t
test_compress_decompress_eq(const uint8_t * buf,const char * desc,int expect_case)112*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(const uint8_t *buf, const char *desc, int expect_case)
113*bbb1b6f9SApple OSS Distributions {
114*bbb1b6f9SApple OSS Distributions uint8_t compressed[C_MTE_SIZE] = {};
115*bbb1b6f9SApple OSS Distributions uint32_t compressed_size = vm_mte_rle_compress_tags((uint8_t *)buf, C_MTE_SIZE, compressed, C_MTE_SIZE);
116*bbb1b6f9SApple OSS Distributions if ((expect_case == CASE_NON_COMP && compressed_size != C_MTE_SIZE) ||
117*bbb1b6f9SApple OSS Distributions (expect_case == CASE_SINGLE_TAG && compressed_size <= C_MTE_SIZE) ||
118*bbb1b6f9SApple OSS Distributions (expect_case == CASE_NORMAL && compressed_size >= C_MTE_SIZE)) {
119*bbb1b6f9SApple OSS Distributions T_ASSERT_FAIL("case %s", desc);
120*bbb1b6f9SApple OSS Distributions }
121*bbb1b6f9SApple OSS Distributions
122*bbb1b6f9SApple OSS Distributions uint8_t decompressed[C_MTE_SIZE] = {};
123*bbb1b6f9SApple OSS Distributions bool ret = vm_mte_rle_decompress_tags(compressed, compressed_size, decompressed, C_MTE_SIZE);
124*bbb1b6f9SApple OSS Distributions if (!ret) {
125*bbb1b6f9SApple OSS Distributions show_buf_diff(buf, decompressed, C_MTE_SIZE);
126*bbb1b6f9SApple OSS Distributions T_ASSERT_FAIL("decompress return %s", desc);
127*bbb1b6f9SApple OSS Distributions }
128*bbb1b6f9SApple OSS Distributions
129*bbb1b6f9SApple OSS Distributions if (memcmp((char*)buf, (char*)decompressed, C_MTE_SIZE) != 0) {
130*bbb1b6f9SApple OSS Distributions show_buf_diff(buf, decompressed, C_MTE_SIZE);
131*bbb1b6f9SApple OSS Distributions T_ASSERT_FAIL("decompress equal original %s", desc);
132*bbb1b6f9SApple OSS Distributions } else {
133*bbb1b6f9SApple OSS Distributions bool quiet = (desc[0] == '_'); // don't want to spam the console during the many random runs
134*bbb1b6f9SApple OSS Distributions if (quiet) {
135*bbb1b6f9SApple OSS Distributions T_QUIET;
136*bbb1b6f9SApple OSS Distributions }
137*bbb1b6f9SApple OSS Distributions T_PASS("OK %s (size=%u)", desc, compressed_size);
138*bbb1b6f9SApple OSS Distributions }
139*bbb1b6f9SApple OSS Distributions
140*bbb1b6f9SApple OSS Distributions return compressed_size;
141*bbb1b6f9SApple OSS Distributions }
142*bbb1b6f9SApple OSS Distributions
143*bbb1b6f9SApple OSS Distributions
144*bbb1b6f9SApple OSS Distributions static void
simple_tests(void)145*bbb1b6f9SApple OSS Distributions simple_tests(void)
146*bbb1b6f9SApple OSS Distributions {
147*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
148*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "zeros", CASE_SINGLE_TAG);
149*bbb1b6f9SApple OSS Distributions
150*bbb1b6f9SApple OSS Distributions buf[0] = 0x01;
151*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 1", CASE_NORMAL);
152*bbb1b6f9SApple OSS Distributions
153*bbb1b6f9SApple OSS Distributions memset(buf, 0x22, C_MTE_SIZE);
154*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "twos", CASE_SINGLE_TAG);
155*bbb1b6f9SApple OSS Distributions
156*bbb1b6f9SApple OSS Distributions buf[0] = 0x21;
157*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 2", CASE_NORMAL);
158*bbb1b6f9SApple OSS Distributions
159*bbb1b6f9SApple OSS Distributions buf[0] = 0x01;
160*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 3", CASE_NORMAL);
161*bbb1b6f9SApple OSS Distributions
162*bbb1b6f9SApple OSS Distributions buf[0] = 0x11;
163*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 4", CASE_NORMAL);
164*bbb1b6f9SApple OSS Distributions
165*bbb1b6f9SApple OSS Distributions buf[0] = 0x31;
166*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 5", CASE_NORMAL);
167*bbb1b6f9SApple OSS Distributions
168*bbb1b6f9SApple OSS Distributions buf[1] = 0x01;
169*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 6", CASE_NORMAL);
170*bbb1b6f9SApple OSS Distributions
171*bbb1b6f9SApple OSS Distributions buf[0] = 0x11;
172*bbb1b6f9SApple OSS Distributions buf[1] = 0x11;
173*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 7", CASE_NORMAL);
174*bbb1b6f9SApple OSS Distributions
175*bbb1b6f9SApple OSS Distributions buf[2] = 0x01;
176*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 8", CASE_NORMAL);
177*bbb1b6f9SApple OSS Distributions
178*bbb1b6f9SApple OSS Distributions buf[2] = 0x11;
179*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 9", CASE_NORMAL);
180*bbb1b6f9SApple OSS Distributions
181*bbb1b6f9SApple OSS Distributions buf[3] = 0x01;
182*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 10", CASE_NORMAL);
183*bbb1b6f9SApple OSS Distributions
184*bbb1b6f9SApple OSS Distributions buf[3] = 0x11;
185*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 11", CASE_NORMAL);
186*bbb1b6f9SApple OSS Distributions
187*bbb1b6f9SApple OSS Distributions buf[3] = 0x21;
188*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 12", CASE_NORMAL);
189*bbb1b6f9SApple OSS Distributions
190*bbb1b6f9SApple OSS Distributions buf[3] = 0x12;
191*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 13", CASE_NORMAL);
192*bbb1b6f9SApple OSS Distributions
193*bbb1b6f9SApple OSS Distributions memset(buf, 0x22, C_MTE_SIZE);
194*bbb1b6f9SApple OSS Distributions buf[255] = 0x01;
195*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 14", CASE_NORMAL);
196*bbb1b6f9SApple OSS Distributions buf[255] = 0x21;
197*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 15", CASE_NORMAL);
198*bbb1b6f9SApple OSS Distributions buf[255] = 0x12;
199*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 16", CASE_NORMAL);
200*bbb1b6f9SApple OSS Distributions
201*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_MTE_SIZE; ++i) {
202*bbb1b6f9SApple OSS Distributions buf[i] = i % 16;
203*bbb1b6f9SApple OSS Distributions }
204*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "non-comp", CASE_NON_COMP);
205*bbb1b6f9SApple OSS Distributions
206*bbb1b6f9SApple OSS Distributions memset(buf, 0x22, C_MTE_SIZE);
207*bbb1b6f9SApple OSS Distributions buf[0] = 0x11;
208*bbb1b6f9SApple OSS Distributions buf[1] = 0x01;
209*bbb1b6f9SApple OSS Distributions buf[2] = 0x10;
210*bbb1b6f9SApple OSS Distributions buf[3] = 0x11;
211*bbb1b6f9SApple OSS Distributions buf[4] = 0x01;
212*bbb1b6f9SApple OSS Distributions buf[5] = 0x00;
213*bbb1b6f9SApple OSS Distributions test_compress_decompress_eq(buf, "simple 17", CASE_NORMAL);
214*bbb1b6f9SApple OSS Distributions }
215*bbb1b6f9SApple OSS Distributions
216*bbb1b6f9SApple OSS Distributions // run compress-decompress with input generated by the given callback
217*bbb1b6f9SApple OSS Distributions static void
218*bbb1b6f9SApple OSS Distributions gen_test(const char* name, int num_runs, int min_opt, int max_opt, void (^generate)(uint8_t *buf, int num_options))
219*bbb1b6f9SApple OSS Distributions {
220*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
221*bbb1b6f9SApple OSS Distributions uint32_t count_incomp = 0, count_normal = 0, count_single = 0;
222*bbb1b6f9SApple OSS Distributions uint64_t sum_normal = 0;
223*bbb1b6f9SApple OSS Distributions
224*bbb1b6f9SApple OSS Distributions for (int num_options = min_opt; num_options <= max_opt; ++num_options) {
225*bbb1b6f9SApple OSS Distributions for (int run = 0; run < num_runs; ++run) {
226*bbb1b6f9SApple OSS Distributions generate(buf, num_options);
227*bbb1b6f9SApple OSS Distributions
228*bbb1b6f9SApple OSS Distributions uint32_t sz = test_compress_decompress_eq(buf, name, CASE_UNKNOWN);
229*bbb1b6f9SApple OSS Distributions if (sz == C_MTE_SIZE) {
230*bbb1b6f9SApple OSS Distributions count_incomp++;
231*bbb1b6f9SApple OSS Distributions } else if (sz < C_MTE_SIZE) {
232*bbb1b6f9SApple OSS Distributions count_normal++;
233*bbb1b6f9SApple OSS Distributions sum_normal += sz;
234*bbb1b6f9SApple OSS Distributions } else {
235*bbb1b6f9SApple OSS Distributions count_single++;
236*bbb1b6f9SApple OSS Distributions }
237*bbb1b6f9SApple OSS Distributions }
238*bbb1b6f9SApple OSS Distributions }
239*bbb1b6f9SApple OSS Distributions
240*bbb1b6f9SApple OSS Distributions T_LOG("%s: incompressible:%u normal:%u (avg=%llu) sv:%u", name, count_incomp, count_normal, sum_normal / count_normal, count_single);
241*bbb1b6f9SApple OSS Distributions }
242*bbb1b6f9SApple OSS Distributions
243*bbb1b6f9SApple OSS Distributions static uint32_t rng_state = 0;
244*bbb1b6f9SApple OSS Distributions static void
my_srand(uint32_t seed)245*bbb1b6f9SApple OSS Distributions my_srand(uint32_t seed)
246*bbb1b6f9SApple OSS Distributions {
247*bbb1b6f9SApple OSS Distributions rng_state = seed;
248*bbb1b6f9SApple OSS Distributions }
249*bbb1b6f9SApple OSS Distributions static uint32_t
my_rand()250*bbb1b6f9SApple OSS Distributions my_rand()
251*bbb1b6f9SApple OSS Distributions {
252*bbb1b6f9SApple OSS Distributions rng_state = (rng_state * 1103515245) + 12345;
253*bbb1b6f9SApple OSS Distributions uint32_t r = (rng_state >> 15);
254*bbb1b6f9SApple OSS Distributions return r;
255*bbb1b6f9SApple OSS Distributions }
256*bbb1b6f9SApple OSS Distributions
257*bbb1b6f9SApple OSS Distributions
258*bbb1b6f9SApple OSS Distributions // fill a tags buffer with tags with values up to `num_options`
259*bbb1b6f9SApple OSS Distributions static void
random_tags_buf(uint8_t * buf,int num_options)260*bbb1b6f9SApple OSS Distributions random_tags_buf(uint8_t *buf, int num_options)
261*bbb1b6f9SApple OSS Distributions {
262*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(num_options > 0 && num_options <= 16, "unexpected num_options");
263*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_MTE_SIZE; ++i) {
264*bbb1b6f9SApple OSS Distributions uint8_t tag1 = (uint8_t)(my_rand() % num_options);
265*bbb1b6f9SApple OSS Distributions uint8_t tag2 = (uint8_t)(my_rand() % num_options);
266*bbb1b6f9SApple OSS Distributions buf[i] = (uint8_t)((tag1 << 4) | tag2);
267*bbb1b6f9SApple OSS Distributions }
268*bbb1b6f9SApple OSS Distributions }
269*bbb1b6f9SApple OSS Distributions
270*bbb1b6f9SApple OSS Distributions // fill a tags buffer with runs of tags with values up to `num_options` and up to `max_run` long
271*bbb1b6f9SApple OSS Distributions static void
random_tag_runs_buf(uint8_t * buf,int num_options,int max_run)272*bbb1b6f9SApple OSS Distributions random_tag_runs_buf(uint8_t *buf, int num_options, int max_run)
273*bbb1b6f9SApple OSS Distributions {
274*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GE(max_run, 1, "unexpected max_runs"); // sanity
275*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(num_options > 0 && num_options <= 16, "unexpected num_options");
276*bbb1b6f9SApple OSS Distributions
277*bbb1b6f9SApple OSS Distributions uint8_t cur_tag = 0;
278*bbb1b6f9SApple OSS Distributions int cur_run = 0;
279*bbb1b6f9SApple OSS Distributions int cur_repeat = 0; // will be set on the first iteration
280*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_MTE_SIZE; ++i) {
281*bbb1b6f9SApple OSS Distributions uint8_t tags[2];
282*bbb1b6f9SApple OSS Distributions for (int ti = 0; ti < 2; ++ti) {
283*bbb1b6f9SApple OSS Distributions if (cur_run == cur_repeat) {
284*bbb1b6f9SApple OSS Distributions cur_repeat = (my_rand() % max_run) + 1;
285*bbb1b6f9SApple OSS Distributions cur_tag = (uint8_t)(my_rand() % num_options);
286*bbb1b6f9SApple OSS Distributions cur_run = 0;
287*bbb1b6f9SApple OSS Distributions }
288*bbb1b6f9SApple OSS Distributions tags[ti] = cur_tag;
289*bbb1b6f9SApple OSS Distributions ++cur_run;
290*bbb1b6f9SApple OSS Distributions }
291*bbb1b6f9SApple OSS Distributions buf[i] = (uint8_t)((tags[1] << 4) | tags[0]);
292*bbb1b6f9SApple OSS Distributions }
293*bbb1b6f9SApple OSS Distributions }
294*bbb1b6f9SApple OSS Distributions
295*bbb1b6f9SApple OSS Distributions #define TAGS_IN_PAGE (C_MTE_SIZE * 2)
296*bbb1b6f9SApple OSS Distributions
297*bbb1b6f9SApple OSS Distributions // fill a buffer with the tags of the same repeat count
298*bbb1b6f9SApple OSS Distributions static void
same_repeat_buf(uint8_t * buf,int num_repeat)299*bbb1b6f9SApple OSS Distributions same_repeat_buf(uint8_t *buf, int num_repeat)
300*bbb1b6f9SApple OSS Distributions {
301*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(num_repeat >= 1 && num_repeat <= TAGS_IN_PAGE, "unexpected num_options");
302*bbb1b6f9SApple OSS Distributions uint8_t cur_tag = 0;
303*bbb1b6f9SApple OSS Distributions int cur_run = 0;
304*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_MTE_SIZE; ++i) {
305*bbb1b6f9SApple OSS Distributions uint8_t tags[2];
306*bbb1b6f9SApple OSS Distributions for (int ti = 0; ti < 2; ++ti) {
307*bbb1b6f9SApple OSS Distributions if (cur_run == num_repeat) {
308*bbb1b6f9SApple OSS Distributions cur_tag = (cur_tag + 1) % 0xf;
309*bbb1b6f9SApple OSS Distributions cur_run = 0;
310*bbb1b6f9SApple OSS Distributions }
311*bbb1b6f9SApple OSS Distributions tags[ti] = cur_tag;
312*bbb1b6f9SApple OSS Distributions ++cur_run;
313*bbb1b6f9SApple OSS Distributions }
314*bbb1b6f9SApple OSS Distributions buf[i] = (uint8_t)((tags[1] << 4) | tags[0]);
315*bbb1b6f9SApple OSS Distributions }
316*bbb1b6f9SApple OSS Distributions }
317*bbb1b6f9SApple OSS Distributions
318*bbb1b6f9SApple OSS Distributions // fill a buffer with the same tag
319*bbb1b6f9SApple OSS Distributions static void
same_tag_buf(uint8_t * buf,int num_options)320*bbb1b6f9SApple OSS Distributions same_tag_buf(uint8_t *buf, int num_options)
321*bbb1b6f9SApple OSS Distributions {
322*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(num_options >= 0 && num_options <= 16, "unexpected num_options");
323*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_MTE_SIZE; ++i) {
324*bbb1b6f9SApple OSS Distributions uint8_t tag = num_options;
325*bbb1b6f9SApple OSS Distributions buf[i] = (uint8_t)((tag << 4) | tag);
326*bbb1b6f9SApple OSS Distributions }
327*bbb1b6f9SApple OSS Distributions }
328*bbb1b6f9SApple OSS Distributions
329*bbb1b6f9SApple OSS Distributions static void
random_bytes_test(void)330*bbb1b6f9SApple OSS Distributions random_bytes_test(void)
331*bbb1b6f9SApple OSS Distributions {
332*bbb1b6f9SApple OSS Distributions my_srand(0);
333*bbb1b6f9SApple OSS Distributions gen_test("_rand_bytes", 10000, 2, 16, ^void (uint8_t *buf, int num_options) {
334*bbb1b6f9SApple OSS Distributions random_tags_buf(buf, num_options);
335*bbb1b6f9SApple OSS Distributions });
336*bbb1b6f9SApple OSS Distributions }
337*bbb1b6f9SApple OSS Distributions
338*bbb1b6f9SApple OSS Distributions static void
random_runs_test(int max_run)339*bbb1b6f9SApple OSS Distributions random_runs_test(int max_run)
340*bbb1b6f9SApple OSS Distributions {
341*bbb1b6f9SApple OSS Distributions my_srand(0);
342*bbb1b6f9SApple OSS Distributions gen_test("_rand_runs", 10000, 2, 16, ^void (uint8_t *buf, int num_options) {
343*bbb1b6f9SApple OSS Distributions random_tag_runs_buf(buf, num_options, max_run);
344*bbb1b6f9SApple OSS Distributions });
345*bbb1b6f9SApple OSS Distributions }
346*bbb1b6f9SApple OSS Distributions
347*bbb1b6f9SApple OSS Distributions static void
same_tag_test(void)348*bbb1b6f9SApple OSS Distributions same_tag_test(void)
349*bbb1b6f9SApple OSS Distributions {
350*bbb1b6f9SApple OSS Distributions gen_test("_same_tag", 1, 0, 16, ^void (uint8_t *buf, int num_options) {
351*bbb1b6f9SApple OSS Distributions same_tag_buf(buf, num_options);
352*bbb1b6f9SApple OSS Distributions });
353*bbb1b6f9SApple OSS Distributions }
354*bbb1b6f9SApple OSS Distributions
355*bbb1b6f9SApple OSS Distributions static void
every_repeat_len(void)356*bbb1b6f9SApple OSS Distributions every_repeat_len(void)
357*bbb1b6f9SApple OSS Distributions {
358*bbb1b6f9SApple OSS Distributions gen_test("_every_repeat", 1, 1, TAGS_IN_PAGE, ^void (uint8_t *buf, int num_options) {
359*bbb1b6f9SApple OSS Distributions same_repeat_buf(buf, num_options);
360*bbb1b6f9SApple OSS Distributions });
361*bbb1b6f9SApple OSS Distributions }
362*bbb1b6f9SApple OSS Distributions
363*bbb1b6f9SApple OSS Distributions T_DECL(mte_compress_tags,
364*bbb1b6f9SApple OSS Distributions "Test the MTE tags buffer compression and decompression functions")
365*bbb1b6f9SApple OSS Distributions {
366*bbb1b6f9SApple OSS Distributions simple_tests();
367*bbb1b6f9SApple OSS Distributions random_bytes_test();
368*bbb1b6f9SApple OSS Distributions random_runs_test(C_MTE_SIZE);
369*bbb1b6f9SApple OSS Distributions random_runs_test(C_MTE_SIZE / 2);
370*bbb1b6f9SApple OSS Distributions random_runs_test(20);
371*bbb1b6f9SApple OSS Distributions random_runs_test(3);
372*bbb1b6f9SApple OSS Distributions same_tag_test();
373*bbb1b6f9SApple OSS Distributions every_repeat_len();
374*bbb1b6f9SApple OSS Distributions }
375*bbb1b6f9SApple OSS Distributions
376*bbb1b6f9SApple OSS Distributions
377*bbb1b6f9SApple OSS Distributions static void
test_malformed(const uint8_t * compressed,uint32_t compressed_size,bool expected,const char * desc,uint32_t desc_arg)378*bbb1b6f9SApple OSS Distributions test_malformed(const uint8_t* compressed, uint32_t compressed_size, bool expected, const char* desc, uint32_t desc_arg)
379*bbb1b6f9SApple OSS Distributions {
380*bbb1b6f9SApple OSS Distributions char decompressed[C_MTE_SIZE] = {};
381*bbb1b6f9SApple OSS Distributions bool ret = vm_mte_rle_decompress_tags((uint8_t*)compressed, compressed_size, (uint8_t*)decompressed, C_MTE_SIZE);
382*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(ret, expected, "malformed decompressed %s %d", desc, desc_arg);
383*bbb1b6f9SApple OSS Distributions T_PASS("OK %s %d", desc, desc_arg);
384*bbb1b6f9SApple OSS Distributions }
385*bbb1b6f9SApple OSS Distributions
386*bbb1b6f9SApple OSS Distributions static void
simple_malformed(void)387*bbb1b6f9SApple OSS Distributions simple_malformed(void)
388*bbb1b6f9SApple OSS Distributions {
389*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
390*bbb1b6f9SApple OSS Distributions
391*bbb1b6f9SApple OSS Distributions buf[0] = 0x01;
392*bbb1b6f9SApple OSS Distributions test_malformed(buf, 1, false, "underflow only 1 byte output", 0);
393*bbb1b6f9SApple OSS Distributions
394*bbb1b6f9SApple OSS Distributions buf[0] = 0xF1;
395*bbb1b6f9SApple OSS Distributions buf[1] = 0xF2; // filled 1024 nibbles
396*bbb1b6f9SApple OSS Distributions test_malformed(buf, 2, true, "no overflow at edge", 0);
397*bbb1b6f9SApple OSS Distributions
398*bbb1b6f9SApple OSS Distributions // filled all the output, but there's another command that would overflow
399*bbb1b6f9SApple OSS Distributions buf[2] = 0x03;
400*bbb1b6f9SApple OSS Distributions test_malformed(buf, 3, false, "overflow by 1 nibble", 0);
401*bbb1b6f9SApple OSS Distributions
402*bbb1b6f9SApple OSS Distributions for (uint32_t i = 1; i <= 0xF; ++i) {
403*bbb1b6f9SApple OSS Distributions buf[2] = (uint8_t)(0x3 | (i << 4)); // every command should cause overflow
404*bbb1b6f9SApple OSS Distributions test_malformed(buf, 3, false, "overflow at edge", i);
405*bbb1b6f9SApple OSS Distributions }
406*bbb1b6f9SApple OSS Distributions
407*bbb1b6f9SApple OSS Distributions buf[0] = 0xF1; // 512
408*bbb1b6f9SApple OSS Distributions buf[1] = 0xE2; // + 256
409*bbb1b6f9SApple OSS Distributions buf[2] = 0xD3; // + 128
410*bbb1b6f9SApple OSS Distributions buf[3] = 0xC4; // + 64
411*bbb1b6f9SApple OSS Distributions buf[4] = 0xB5; // + 32
412*bbb1b6f9SApple OSS Distributions buf[5] = 0xA6; // + 16
413*bbb1b6f9SApple OSS Distributions buf[6] = 0x97; // + 8
414*bbb1b6f9SApple OSS Distributions buf[7] = 0x78; // + 6 = filled 1022 nibbles
415*bbb1b6f9SApple OSS Distributions test_malformed(buf, 8, false, "underflow missing 2 nibbles", 0);
416*bbb1b6f9SApple OSS Distributions buf[7] = 0x88; // + 7 = filled 1023 nibbles
417*bbb1b6f9SApple OSS Distributions test_malformed(buf, 8, false, "underflow missing 1 nibble", 0);
418*bbb1b6f9SApple OSS Distributions buf[8] = 0x09; // + 1 = filled 1024 nibbles
419*bbb1b6f9SApple OSS Distributions test_malformed(buf, 9, true, "no overflow from mid", 0);
420*bbb1b6f9SApple OSS Distributions
421*bbb1b6f9SApple OSS Distributions for (uint32_t i = 1; i <= 0xF; ++i) {
422*bbb1b6f9SApple OSS Distributions buf[8] = (uint8_t)(0x9 | (i << 4));
423*bbb1b6f9SApple OSS Distributions test_malformed(buf, 9, false, "overflow at mid", i);
424*bbb1b6f9SApple OSS Distributions }
425*bbb1b6f9SApple OSS Distributions }
426*bbb1b6f9SApple OSS Distributions
427*bbb1b6f9SApple OSS Distributions static void
random_malformed(int num_runs)428*bbb1b6f9SApple OSS Distributions random_malformed(int num_runs)
429*bbb1b6f9SApple OSS Distributions {
430*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
431*bbb1b6f9SApple OSS Distributions int fail = 0, success = 0;
432*bbb1b6f9SApple OSS Distributions my_srand(0);
433*bbb1b6f9SApple OSS Distributions for (int run = 0; run < num_runs; ++run) {
434*bbb1b6f9SApple OSS Distributions // fill buf with random bytes
435*bbb1b6f9SApple OSS Distributions uint32_t sz = my_rand() % C_MTE_SIZE;
436*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < sz; ++i) {
437*bbb1b6f9SApple OSS Distributions buf[i] = my_rand() % 0xFF;
438*bbb1b6f9SApple OSS Distributions }
439*bbb1b6f9SApple OSS Distributions
440*bbb1b6f9SApple OSS Distributions uint8_t decompressed[C_MTE_SIZE] = {};
441*bbb1b6f9SApple OSS Distributions bool ret = vm_mte_rle_decompress_tags(buf, sz, decompressed, C_MTE_SIZE);
442*bbb1b6f9SApple OSS Distributions // don't know if it's going to succeed or fail.
443*bbb1b6f9SApple OSS Distributions // we're testing that it doesn't assert or hang
444*bbb1b6f9SApple OSS Distributions if (ret) {
445*bbb1b6f9SApple OSS Distributions ++success;
446*bbb1b6f9SApple OSS Distributions } else {
447*bbb1b6f9SApple OSS Distributions ++fail;
448*bbb1b6f9SApple OSS Distributions }
449*bbb1b6f9SApple OSS Distributions }
450*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(fail > num_runs * 0.9 && fail < num_runs, "too many succeeded or failed %u,%u", success, fail);
451*bbb1b6f9SApple OSS Distributions T_PASS("OK random %u success, %u fail", success, fail);
452*bbb1b6f9SApple OSS Distributions }
453*bbb1b6f9SApple OSS Distributions
454*bbb1b6f9SApple OSS Distributions T_DECL(mte_decompress_malformed,
455*bbb1b6f9SApple OSS Distributions "Test that tags decompress returns an error")
456*bbb1b6f9SApple OSS Distributions {
457*bbb1b6f9SApple OSS Distributions simple_malformed();
458*bbb1b6f9SApple OSS Distributions random_malformed(1000000);
459*bbb1b6f9SApple OSS Distributions }
460*bbb1b6f9SApple OSS Distributions
461*bbb1b6f9SApple OSS Distributions
462*bbb1b6f9SApple OSS Distributions // This test isn't really useful for automatic testing, so it is disabled. It is useful for on-desk testing
463*bbb1b6f9SApple OSS Distributions // while trying to optimize these functions. For full optimization add this to the Makefile:
464*bbb1b6f9SApple OSS Distributions // arm_mte_compress: CFLAGS += -O3
465*bbb1b6f9SApple OSS Distributions T_DECL(mte_compress_tags_perf,
466*bbb1b6f9SApple OSS Distributions "Test formance of MTE tags compression",
467*bbb1b6f9SApple OSS Distributions T_META_ENABLED(false))
468*bbb1b6f9SApple OSS Distributions {
469*bbb1b6f9SApple OSS Distributions my_srand(0);
470*bbb1b6f9SApple OSS Distributions // compress worst case - random data of tags [0-F]
471*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
472*bbb1b6f9SApple OSS Distributions random_tags_buf(buf, 16);
473*bbb1b6f9SApple OSS Distributions uint8_t compressed[C_MTE_SIZE] = {};
474*bbb1b6f9SApple OSS Distributions uint32_t compressed_size = 0;
475*bbb1b6f9SApple OSS Distributions
476*bbb1b6f9SApple OSS Distributions // warmup cache
477*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < 50; ++i) {
478*bbb1b6f9SApple OSS Distributions compressed_size = vm_mte_rle_compress_tags((uint8_t *) buf, C_MTE_SIZE, compressed, C_MTE_SIZE);
479*bbb1b6f9SApple OSS Distributions }
480*bbb1b6f9SApple OSS Distributions T_LOG("compressed_size=%u", compressed_size);
481*bbb1b6f9SApple OSS Distributions
482*bbb1b6f9SApple OSS Distributions uint64_t startns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
483*bbb1b6f9SApple OSS Distributions
484*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < 300000; ++i) {
485*bbb1b6f9SApple OSS Distributions compressed_size = vm_mte_rle_compress_tags((uint8_t *) buf, C_MTE_SIZE, compressed, C_MTE_SIZE);
486*bbb1b6f9SApple OSS Distributions }
487*bbb1b6f9SApple OSS Distributions uint64_t elapsed = clock_gettime_nsec_np(CLOCK_MONOTONIC) - startns;
488*bbb1b6f9SApple OSS Distributions T_LOG("perf compress took: %llu msec", elapsed / NSEC_PER_MSEC);
489*bbb1b6f9SApple OSS Distributions
490*bbb1b6f9SApple OSS Distributions T_PASS("OK");
491*bbb1b6f9SApple OSS Distributions }
492*bbb1b6f9SApple OSS Distributions
493*bbb1b6f9SApple OSS Distributions T_DECL(mte_decompress_tags_perf,
494*bbb1b6f9SApple OSS Distributions "Test formance of MTE tags compression",
495*bbb1b6f9SApple OSS Distributions T_META_ENABLED(false))
496*bbb1b6f9SApple OSS Distributions {
497*bbb1b6f9SApple OSS Distributions my_srand(0);
498*bbb1b6f9SApple OSS Distributions uint8_t buf[C_MTE_SIZE] = {};
499*bbb1b6f9SApple OSS Distributions random_tag_runs_buf(buf, 16, 4);
500*bbb1b6f9SApple OSS Distributions
501*bbb1b6f9SApple OSS Distributions uint8_t compressed[C_MTE_SIZE] = {};
502*bbb1b6f9SApple OSS Distributions uint32_t compressed_size = vm_mte_rle_compress_tags((uint8_t *) buf, C_MTE_SIZE, compressed, C_MTE_SIZE);
503*bbb1b6f9SApple OSS Distributions T_LOG("compressed_size=%u", compressed_size);
504*bbb1b6f9SApple OSS Distributions // verify it's doing a decent amount of work
505*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(compressed_size < C_MTE_SIZE && compressed_size > C_MTE_SIZE / 5 * 4, "compressed to unexpected size %u", compressed_size);
506*bbb1b6f9SApple OSS Distributions
507*bbb1b6f9SApple OSS Distributions uint8_t decompressed[C_MTE_SIZE] = {};
508*bbb1b6f9SApple OSS Distributions bool ret = 0;
509*bbb1b6f9SApple OSS Distributions
510*bbb1b6f9SApple OSS Distributions uint64_t startns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
511*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < 300000; ++i) {
512*bbb1b6f9SApple OSS Distributions ret = vm_mte_rle_decompress_tags(compressed, compressed_size, (uint8_t*)decompressed, C_MTE_SIZE);
513*bbb1b6f9SApple OSS Distributions }
514*bbb1b6f9SApple OSS Distributions
515*bbb1b6f9SApple OSS Distributions uint64_t elapsed = clock_gettime_nsec_np(CLOCK_MONOTONIC) - startns;
516*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(ret, "decompress failed");
517*bbb1b6f9SApple OSS Distributions T_LOG("perf decompress took: %llu msec", elapsed / NSEC_PER_MSEC);
518*bbb1b6f9SApple OSS Distributions
519*bbb1b6f9SApple OSS Distributions T_PASS("OK");
520*bbb1b6f9SApple OSS Distributions }
521*bbb1b6f9SApple OSS Distributions
522*bbb1b6f9SApple OSS Distributions /****************************************************************************************
523*bbb1b6f9SApple OSS Distributions * Active compressor test
524*bbb1b6f9SApple OSS Distributions * This test runs creates different patterns of tags and data, triggers a page-out
525*bbb1b6f9SApple OSS Distributions * to the compressor, waits for the page to be compressed and then pages it back in
526*bbb1b6f9SApple OSS Distributions */
527*bbb1b6f9SApple OSS Distributions
528*bbb1b6f9SApple OSS Distributions #define countof(x) (sizeof(x) / sizeof(x[0]))
529*bbb1b6f9SApple OSS Distributions
530*bbb1b6f9SApple OSS Distributions static void
zero_tags(uint8_t * buf,size_t bufsize)531*bbb1b6f9SApple OSS Distributions zero_tags(uint8_t* buf, size_t bufsize)
532*bbb1b6f9SApple OSS Distributions {
533*bbb1b6f9SApple OSS Distributions for (uint32_t offset = 0; offset < bufsize; offset += 16) {
534*bbb1b6f9SApple OSS Distributions __arm_mte_set_tag(buf + offset);
535*bbb1b6f9SApple OSS Distributions }
536*bbb1b6f9SApple OSS Distributions }
537*bbb1b6f9SApple OSS Distributions
538*bbb1b6f9SApple OSS Distributions // state of single use case for convenient passing between functions
539*bbb1b6f9SApple OSS Distributions struct tag_pattern {
540*bbb1b6f9SApple OSS Distributions uint8_t* buf_start;
541*bbb1b6f9SApple OSS Distributions size_t buf_size;
542*bbb1b6f9SApple OSS Distributions // a tagged pointer per every 16 bytes of the buffer
543*bbb1b6f9SApple OSS Distributions uint8_t **tagged_ptrs;
544*bbb1b6f9SApple OSS Distributions size_t ptrs_count;
545*bbb1b6f9SApple OSS Distributions size_t ptrs_index;
546*bbb1b6f9SApple OSS Distributions };
547*bbb1b6f9SApple OSS Distributions
548*bbb1b6f9SApple OSS Distributions static void
tag_pattern_init(struct tag_pattern * t,uint8_t * buf_start,size_t buf_size)549*bbb1b6f9SApple OSS Distributions tag_pattern_init(struct tag_pattern *t, uint8_t *buf_start, size_t buf_size)
550*bbb1b6f9SApple OSS Distributions {
551*bbb1b6f9SApple OSS Distributions t->buf_start = buf_start;
552*bbb1b6f9SApple OSS Distributions t->buf_size = buf_size;
553*bbb1b6f9SApple OSS Distributions t->ptrs_count = t->buf_size / VM_MEMTAG_BYTES_PER_TAG;
554*bbb1b6f9SApple OSS Distributions T_LOG(" allocating %zu pointers", t->ptrs_count);
555*bbb1b6f9SApple OSS Distributions t->tagged_ptrs = (uint8_t**)calloc(t->ptrs_count, sizeof(uint8_t *));
556*bbb1b6f9SApple OSS Distributions t->ptrs_index = 0;
557*bbb1b6f9SApple OSS Distributions }
558*bbb1b6f9SApple OSS Distributions
559*bbb1b6f9SApple OSS Distributions static void
tag_pattern_push_ptr(struct tag_pattern * t,uint8_t * tagged_ptr)560*bbb1b6f9SApple OSS Distributions tag_pattern_push_ptr(struct tag_pattern *t, uint8_t* tagged_ptr)
561*bbb1b6f9SApple OSS Distributions {
562*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LT(t->ptrs_index, t->ptrs_count, "ptrs_index overflow"); // test sanity
563*bbb1b6f9SApple OSS Distributions t->tagged_ptrs[t->ptrs_index++] = tagged_ptr;
564*bbb1b6f9SApple OSS Distributions }
565*bbb1b6f9SApple OSS Distributions
566*bbb1b6f9SApple OSS Distributions static void
tag_pattern_destroy(struct tag_pattern * t)567*bbb1b6f9SApple OSS Distributions tag_pattern_destroy(struct tag_pattern *t)
568*bbb1b6f9SApple OSS Distributions {
569*bbb1b6f9SApple OSS Distributions free(t->tagged_ptrs);
570*bbb1b6f9SApple OSS Distributions }
571*bbb1b6f9SApple OSS Distributions
572*bbb1b6f9SApple OSS Distributions static uint8_t *
tag_pattern_get_ptr(struct tag_pattern * t,size_t offset)573*bbb1b6f9SApple OSS Distributions tag_pattern_get_ptr(struct tag_pattern *t, size_t offset)
574*bbb1b6f9SApple OSS Distributions {
575*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset, t->buf_size, "offset overflow"); // test sanity
576*bbb1b6f9SApple OSS Distributions uint8_t *chunk_p = t->tagged_ptrs[offset / VM_MEMTAG_BYTES_PER_TAG];
577*bbb1b6f9SApple OSS Distributions if (chunk_p == NULL) {
578*bbb1b6f9SApple OSS Distributions return t->buf_start + offset; // no tagged pointer filled in, return plain pointer
579*bbb1b6f9SApple OSS Distributions }
580*bbb1b6f9SApple OSS Distributions return chunk_p + (offset % VM_MEMTAG_BYTES_PER_TAG);
581*bbb1b6f9SApple OSS Distributions }
582*bbb1b6f9SApple OSS Distributions
583*bbb1b6f9SApple OSS Distributions // test the correctness of the data, use the tagged pointers if they are populated
584*bbb1b6f9SApple OSS Distributions static uint64_t
tag_pattern_read_verify(struct tag_pattern * t,const uint8_t * orig_data)585*bbb1b6f9SApple OSS Distributions tag_pattern_read_verify(struct tag_pattern *t, const uint8_t* orig_data)
586*bbb1b6f9SApple OSS Distributions {
587*bbb1b6f9SApple OSS Distributions uint64_t sum = 0;
588*bbb1b6f9SApple OSS Distributions for (size_t offset = 0; offset < t->buf_size; ++offset) {
589*bbb1b6f9SApple OSS Distributions uint8_t *tagged_ptr = tag_pattern_get_ptr(t, offset);
590*bbb1b6f9SApple OSS Distributions uint8_t c = *tagged_ptr;
591*bbb1b6f9SApple OSS Distributions sum += c;
592*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(c, orig_data[offset], "failed data comparison %zu : %d != %d", offset, (int)c, (int)orig_data[offset]);
593*bbb1b6f9SApple OSS Distributions }
594*bbb1b6f9SApple OSS Distributions return sum;
595*bbb1b6f9SApple OSS Distributions }
596*bbb1b6f9SApple OSS Distributions
597*bbb1b6f9SApple OSS Distributions // SV optimization that for sure ends up in the hash
598*bbb1b6f9SApple OSS Distributions static void
fill_zeros(uint8_t * buf,size_t bufsize)599*bbb1b6f9SApple OSS Distributions fill_zeros(uint8_t *buf, size_t bufsize)
600*bbb1b6f9SApple OSS Distributions {
601*bbb1b6f9SApple OSS Distributions // do nothing, test function zeros buffer after allocation
602*bbb1b6f9SApple OSS Distributions }
603*bbb1b6f9SApple OSS Distributions
604*bbb1b6f9SApple OSS Distributions // SV optimization
605*bbb1b6f9SApple OSS Distributions static void
fill_same(uint8_t * buf,size_t bufsize)606*bbb1b6f9SApple OSS Distributions fill_same(uint8_t *buf, size_t bufsize)
607*bbb1b6f9SApple OSS Distributions {
608*bbb1b6f9SApple OSS Distributions memset((void*)buf, 'A', bufsize);
609*bbb1b6f9SApple OSS Distributions }
610*bbb1b6f9SApple OSS Distributions
611*bbb1b6f9SApple OSS Distributions static void
fill_only_first_byte(uint8_t * buf,size_t bufsize)612*bbb1b6f9SApple OSS Distributions fill_only_first_byte(uint8_t *buf, size_t bufsize)
613*bbb1b6f9SApple OSS Distributions {
614*bbb1b6f9SApple OSS Distributions buf[0] = 'A';
615*bbb1b6f9SApple OSS Distributions }
616*bbb1b6f9SApple OSS Distributions
617*bbb1b6f9SApple OSS Distributions // should be nicely compressible by wkdm
618*bbb1b6f9SApple OSS Distributions static void
fill_counter(uint8_t * buf,size_t bufsize)619*bbb1b6f9SApple OSS Distributions fill_counter(uint8_t *buf, size_t bufsize)
620*bbb1b6f9SApple OSS Distributions {
621*bbb1b6f9SApple OSS Distributions uint32_t *ibuf = (uint32_t *)buf; // this cast is ok since buf has page alignment
622*bbb1b6f9SApple OSS Distributions for (size_t i = 0; i < bufsize / sizeof(uint32_t); ++i) {
623*bbb1b6f9SApple OSS Distributions ibuf[i] = 0x11111111 + (uint32_t)i;
624*bbb1b6f9SApple OSS Distributions }
625*bbb1b6f9SApple OSS Distributions }
626*bbb1b6f9SApple OSS Distributions
627*bbb1b6f9SApple OSS Distributions // should be uncompressible by wkdm
628*bbb1b6f9SApple OSS Distributions static void
fill_rand(uint8_t * buf,size_t bufsize)629*bbb1b6f9SApple OSS Distributions fill_rand(uint8_t *buf, size_t bufsize)
630*bbb1b6f9SApple OSS Distributions {
631*bbb1b6f9SApple OSS Distributions for (size_t i = 0; i < bufsize; ++i) {
632*bbb1b6f9SApple OSS Distributions buf[i] = my_rand() % 0xff;
633*bbb1b6f9SApple OSS Distributions }
634*bbb1b6f9SApple OSS Distributions }
635*bbb1b6f9SApple OSS Distributions
636*bbb1b6f9SApple OSS Distributions // increments vm.tags_below_align
637*bbb1b6f9SApple OSS Distributions static void
tag_pattern_single_at_start(struct tag_pattern * t)638*bbb1b6f9SApple OSS Distributions tag_pattern_single_at_start(struct tag_pattern *t)
639*bbb1b6f9SApple OSS Distributions {
640*bbb1b6f9SApple OSS Distributions uint8_t *buf = t->buf_start;
641*bbb1b6f9SApple OSS Distributions uint8_t *orig_tagged_ptr = __arm_mte_get_tag(buf);
642*bbb1b6f9SApple OSS Distributions uint64_t mask = __arm_mte_exclude_tag(orig_tagged_ptr, 0);
643*bbb1b6f9SApple OSS Distributions uint8_t *tagged_buf = __arm_mte_create_random_tag(buf, mask);
644*bbb1b6f9SApple OSS Distributions __arm_mte_set_tag(tagged_buf);
645*bbb1b6f9SApple OSS Distributions tag_pattern_push_ptr(t, tagged_buf);
646*bbb1b6f9SApple OSS Distributions // the rest remain NULL
647*bbb1b6f9SApple OSS Distributions }
648*bbb1b6f9SApple OSS Distributions
649*bbb1b6f9SApple OSS Distributions // every consecutive 16 bytes has a different tag (worst case for RLE algorithm)
650*bbb1b6f9SApple OSS Distributions // increments vm.tags_incompressible
651*bbb1b6f9SApple OSS Distributions static void
tag_pattern_max_mix(struct tag_pattern * t)652*bbb1b6f9SApple OSS Distributions tag_pattern_max_mix(struct tag_pattern *t)
653*bbb1b6f9SApple OSS Distributions {
654*bbb1b6f9SApple OSS Distributions uint8_t *prev_tagged_ptr = NULL;
655*bbb1b6f9SApple OSS Distributions for (size_t offset = 0; offset < t->buf_size; offset += VM_MEMTAG_BYTES_PER_TAG) {
656*bbb1b6f9SApple OSS Distributions uint8_t *ptr = t->buf_start + offset;
657*bbb1b6f9SApple OSS Distributions uint8_t *orig_tagged_ptr = __arm_mte_get_tag(ptr);
658*bbb1b6f9SApple OSS Distributions uint64_t mask = __arm_mte_exclude_tag(orig_tagged_ptr, 0);
659*bbb1b6f9SApple OSS Distributions mask = __arm_mte_exclude_tag(prev_tagged_ptr, mask); // don't want consecutive tags to be the same
660*bbb1b6f9SApple OSS Distributions uint8_t *tagged_ptr = __arm_mte_create_random_tag(ptr, mask);
661*bbb1b6f9SApple OSS Distributions __arm_mte_set_tag(tagged_ptr);
662*bbb1b6f9SApple OSS Distributions tag_pattern_push_ptr(t, tagged_ptr);
663*bbb1b6f9SApple OSS Distributions prev_tagged_ptr = tagged_ptr;
664*bbb1b6f9SApple OSS Distributions }
665*bbb1b6f9SApple OSS Distributions T_LOG(" got %zu pointers", t->ptrs_index);
666*bbb1b6f9SApple OSS Distributions }
667*bbb1b6f9SApple OSS Distributions
668*bbb1b6f9SApple OSS Distributions static uint8_t *
tag_fill(struct tag_pattern * t,uint8_t * buf,size_t buf_size,uint8_t * prev_ptr)669*bbb1b6f9SApple OSS Distributions tag_fill(struct tag_pattern *t, uint8_t* buf, size_t buf_size, uint8_t* prev_ptr)
670*bbb1b6f9SApple OSS Distributions {
671*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf_size % VM_MEMTAG_BYTES_PER_TAG, 0ul, "unexpected buf_size %zu", buf_size);
672*bbb1b6f9SApple OSS Distributions uint8_t *orig_tagged_ptr = __arm_mte_get_tag(buf);
673*bbb1b6f9SApple OSS Distributions uint64_t mask = __arm_mte_exclude_tag(orig_tagged_ptr, 0);
674*bbb1b6f9SApple OSS Distributions mask = __arm_mte_exclude_tag(prev_ptr, mask); // new tag should be different from previous
675*bbb1b6f9SApple OSS Distributions uint8_t *tagged_buf = __arm_mte_create_random_tag(buf, mask);
676*bbb1b6f9SApple OSS Distributions uintptr_t only_tag = (uintptr_t)tagged_buf & TAG_MASK;
677*bbb1b6f9SApple OSS Distributions
678*bbb1b6f9SApple OSS Distributions for (size_t offset = 0; offset < buf_size; offset += VM_MEMTAG_BYTES_PER_TAG) {
679*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset, t->buf_size, "fill_tag overflow"); // test sanity
680*bbb1b6f9SApple OSS Distributions uint8_t *ptr = buf + offset;
681*bbb1b6f9SApple OSS Distributions uint8_t *tagged_ptr = (uint8_t *)((uintptr_t)ptr | only_tag);
682*bbb1b6f9SApple OSS Distributions __arm_mte_set_tag(tagged_ptr);
683*bbb1b6f9SApple OSS Distributions tag_pattern_push_ptr(t, tagged_ptr);
684*bbb1b6f9SApple OSS Distributions }
685*bbb1b6f9SApple OSS Distributions return tagged_buf;
686*bbb1b6f9SApple OSS Distributions }
687*bbb1b6f9SApple OSS Distributions
688*bbb1b6f9SApple OSS Distributions // the entire page has the same non-zero tag
689*bbb1b6f9SApple OSS Distributions static void
tag_pattern_all_same(struct tag_pattern * t)690*bbb1b6f9SApple OSS Distributions tag_pattern_all_same(struct tag_pattern *t)
691*bbb1b6f9SApple OSS Distributions {
692*bbb1b6f9SApple OSS Distributions tag_fill(t, t->buf_start, t->buf_size, NULL);
693*bbb1b6f9SApple OSS Distributions }
694*bbb1b6f9SApple OSS Distributions
695*bbb1b6f9SApple OSS Distributions static void
tag_patten_all_zero(struct tag_pattern * t)696*bbb1b6f9SApple OSS Distributions tag_patten_all_zero(struct tag_pattern *t)
697*bbb1b6f9SApple OSS Distributions {
698*bbb1b6f9SApple OSS Distributions // do nothing, all tags are initialized to zero by the text function
699*bbb1b6f9SApple OSS Distributions }
700*bbb1b6f9SApple OSS Distributions
701*bbb1b6f9SApple OSS Distributions // increments vm.tags_below_align
702*bbb1b6f9SApple OSS Distributions static void
tag_pattern_half_and_half(struct tag_pattern * t)703*bbb1b6f9SApple OSS Distributions tag_pattern_half_and_half(struct tag_pattern *t)
704*bbb1b6f9SApple OSS Distributions {
705*bbb1b6f9SApple OSS Distributions size_t sz = t->buf_size / 2;
706*bbb1b6f9SApple OSS Distributions uint8_t *prev = tag_fill(t, t->buf_start, sz, NULL);
707*bbb1b6f9SApple OSS Distributions tag_fill(t, t->buf_start + sz, sz, prev);
708*bbb1b6f9SApple OSS Distributions }
709*bbb1b6f9SApple OSS Distributions
710*bbb1b6f9SApple OSS Distributions // increments vm.tags_above_align
711*bbb1b6f9SApple OSS Distributions static void
tag_pattern_odd_chunks(struct tag_pattern * t)712*bbb1b6f9SApple OSS Distributions tag_pattern_odd_chunks(struct tag_pattern *t)
713*bbb1b6f9SApple OSS Distributions {
714*bbb1b6f9SApple OSS Distributions size_t sizes[] = {31, 31, 63, 63, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31}; // should sum to less than 1024
715*bbb1b6f9SApple OSS Distributions size_t offset = 0;
716*bbb1b6f9SApple OSS Distributions uint8_t *prev = NULL;
717*bbb1b6f9SApple OSS Distributions for (size_t i = 0; i < countof(sizes); ++i) {
718*bbb1b6f9SApple OSS Distributions size_t sz = sizes[i] * VM_MEMTAG_BYTES_PER_TAG;
719*bbb1b6f9SApple OSS Distributions prev = tag_fill(t, t->buf_start + offset, sz, prev);
720*bbb1b6f9SApple OSS Distributions offset += sz;
721*bbb1b6f9SApple OSS Distributions }
722*bbb1b6f9SApple OSS Distributions T_LOG(" reached offset %zu, got %zu pointers", offset, t->ptrs_index);
723*bbb1b6f9SApple OSS Distributions }
724*bbb1b6f9SApple OSS Distributions
725*bbb1b6f9SApple OSS Distributions // --- the following functions uses the compressor sysctls to track that things are progressing as expected ---
726*bbb1b6f9SApple OSS Distributions
727*bbb1b6f9SApple OSS Distributions // keeps the state of the compressor sysctls
728*bbb1b6f9SApple OSS Distributions struct tags_sysctls {
729*bbb1b6f9SApple OSS Distributions uint64_t pages_compressed;
730*bbb1b6f9SApple OSS Distributions
731*bbb1b6f9SApple OSS Distributions uint64_t all_zero;
732*bbb1b6f9SApple OSS Distributions uint64_t same_value;
733*bbb1b6f9SApple OSS Distributions uint64_t below_align;
734*bbb1b6f9SApple OSS Distributions uint64_t above_align;
735*bbb1b6f9SApple OSS Distributions uint64_t incompressible;
736*bbb1b6f9SApple OSS Distributions
737*bbb1b6f9SApple OSS Distributions uint64_t pages_decompressed;
738*bbb1b6f9SApple OSS Distributions uint64_t pages_freed;
739*bbb1b6f9SApple OSS Distributions uint64_t pages_corrupted;
740*bbb1b6f9SApple OSS Distributions
741*bbb1b6f9SApple OSS Distributions int64_t overhead_bytes; // can be negative on diffs
742*bbb1b6f9SApple OSS Distributions int64_t start_overhead_bytes; // for comparing the very start to the end
743*bbb1b6f9SApple OSS Distributions int64_t tagged_pages;
744*bbb1b6f9SApple OSS Distributions
745*bbb1b6f9SApple OSS Distributions // unrelated to tagging, but interesting to see as well
746*bbb1b6f9SApple OSS Distributions uint64_t wk_compressions;
747*bbb1b6f9SApple OSS Distributions };
748*bbb1b6f9SApple OSS Distributions
749*bbb1b6f9SApple OSS Distributions // sample all the sysctls we're interested in
750*bbb1b6f9SApple OSS Distributions static void
tags_sysctls_sample(struct tags_sysctls * s)751*bbb1b6f9SApple OSS Distributions tags_sysctls_sample(struct tags_sysctls *s)
752*bbb1b6f9SApple OSS Distributions {
753*bbb1b6f9SApple OSS Distributions s->pages_compressed = sysctl_get_Q("vm.mte.compress_pages_compressed");
754*bbb1b6f9SApple OSS Distributions #if DEVELOPMENT || DEBUG
755*bbb1b6f9SApple OSS Distributions s->all_zero = sysctl_get_Q("vm.mte.compress_all_zero");
756*bbb1b6f9SApple OSS Distributions s->same_value = sysctl_get_Q("vm.mte.compress_same_value");
757*bbb1b6f9SApple OSS Distributions s->below_align = sysctl_get_Q("vm.mte.compress_below_align");
758*bbb1b6f9SApple OSS Distributions s->above_align = sysctl_get_Q("vm.mte.compress_above_align");
759*bbb1b6f9SApple OSS Distributions s->incompressible = sysctl_get_Q("vm.mte.compress_incompressible");
760*bbb1b6f9SApple OSS Distributions #endif /* DEVELOPMENT || DEBUG */
761*bbb1b6f9SApple OSS Distributions s->pages_decompressed = sysctl_get_Q("vm.mte.compress_pages_decompressed");
762*bbb1b6f9SApple OSS Distributions s->pages_freed = sysctl_get_Q("vm.mte.compress_pages_freed");
763*bbb1b6f9SApple OSS Distributions s->pages_corrupted = sysctl_get_Q("vm.mte.compress_pages_corrupted");
764*bbb1b6f9SApple OSS Distributions s->overhead_bytes = (int64_t)sysctl_get_Q("vm.mte.compress_overhead_bytes");
765*bbb1b6f9SApple OSS Distributions s->tagged_pages = (int64_t)sysctl_get_Q("vm.mte.compress_pages");
766*bbb1b6f9SApple OSS Distributions s->wk_compressions = sysctl_get_Q("vm.wk_compressions");
767*bbb1b6f9SApple OSS Distributions }
768*bbb1b6f9SApple OSS Distributions
769*bbb1b6f9SApple OSS Distributions static void
tags_sysctl_update(struct tags_sysctls * s,struct tags_sysctls * sample)770*bbb1b6f9SApple OSS Distributions tags_sysctl_update(struct tags_sysctls *s, struct tags_sysctls *sample)
771*bbb1b6f9SApple OSS Distributions { // update the sysctl state with the latest sample but preserve start_overhead_bytes
772*bbb1b6f9SApple OSS Distributions int64_t start_bytes = s->start_overhead_bytes;
773*bbb1b6f9SApple OSS Distributions *s = *sample;
774*bbb1b6f9SApple OSS Distributions s->start_overhead_bytes = start_bytes;
775*bbb1b6f9SApple OSS Distributions }
776*bbb1b6f9SApple OSS Distributions
777*bbb1b6f9SApple OSS Distributions // sample and diff with the previous sample
778*bbb1b6f9SApple OSS Distributions static void
tags_sysctls_sample_diff(const struct tags_sysctls * start,struct tags_sysctls * sample,struct tags_sysctls * d)779*bbb1b6f9SApple OSS Distributions tags_sysctls_sample_diff(const struct tags_sysctls *start, struct tags_sysctls *sample, struct tags_sysctls *d)
780*bbb1b6f9SApple OSS Distributions {
781*bbb1b6f9SApple OSS Distributions tags_sysctls_sample(sample);
782*bbb1b6f9SApple OSS Distributions #define SUB_FIELD(field) d->field = sample->field - start->field
783*bbb1b6f9SApple OSS Distributions SUB_FIELD(pages_compressed);
784*bbb1b6f9SApple OSS Distributions SUB_FIELD(all_zero);
785*bbb1b6f9SApple OSS Distributions SUB_FIELD(same_value);
786*bbb1b6f9SApple OSS Distributions SUB_FIELD(below_align);
787*bbb1b6f9SApple OSS Distributions SUB_FIELD(above_align);
788*bbb1b6f9SApple OSS Distributions SUB_FIELD(incompressible);
789*bbb1b6f9SApple OSS Distributions SUB_FIELD(pages_decompressed);
790*bbb1b6f9SApple OSS Distributions SUB_FIELD(pages_freed);
791*bbb1b6f9SApple OSS Distributions SUB_FIELD(pages_corrupted);
792*bbb1b6f9SApple OSS Distributions SUB_FIELD(overhead_bytes);
793*bbb1b6f9SApple OSS Distributions SUB_FIELD(tagged_pages);
794*bbb1b6f9SApple OSS Distributions SUB_FIELD(wk_compressions);
795*bbb1b6f9SApple OSS Distributions #undef SUB_FIELD
796*bbb1b6f9SApple OSS Distributions }
797*bbb1b6f9SApple OSS Distributions
798*bbb1b6f9SApple OSS Distributions static void
tags_sysctls_print(const struct tags_sysctls * s,const char * desc)799*bbb1b6f9SApple OSS Distributions tags_sysctls_print(const struct tags_sysctls *s, const char* desc)
800*bbb1b6f9SApple OSS Distributions {
801*bbb1b6f9SApple OSS Distributions T_LOG(" %s comp: %llu | zero: %llu same: %llu below: %llu above: %llu incomp: %llu | decomp:%llu freed:%llu corrupt:%llu | bytes:%lld pages:%lld | wk_comp:%llu",
802*bbb1b6f9SApple OSS Distributions desc, s->pages_compressed, s->all_zero, s->same_value, s->below_align, s->above_align, s->incompressible,
803*bbb1b6f9SApple OSS Distributions s->pages_decompressed, s->pages_freed, s->pages_corrupted, s->overhead_bytes, s->tagged_pages, s->wk_compressions);
804*bbb1b6f9SApple OSS Distributions }
805*bbb1b6f9SApple OSS Distributions
806*bbb1b6f9SApple OSS Distributions // called before the compressor work
807*bbb1b6f9SApple OSS Distributions static void
tags_sysctl_start(struct tags_sysctls * s)808*bbb1b6f9SApple OSS Distributions tags_sysctl_start(struct tags_sysctls *s)
809*bbb1b6f9SApple OSS Distributions {
810*bbb1b6f9SApple OSS Distributions tags_sysctls_sample(s);
811*bbb1b6f9SApple OSS Distributions s->start_overhead_bytes = s->overhead_bytes;
812*bbb1b6f9SApple OSS Distributions tags_sysctls_print(s, "START ");
813*bbb1b6f9SApple OSS Distributions }
814*bbb1b6f9SApple OSS Distributions
815*bbb1b6f9SApple OSS Distributions #define SYSCTL_ALL_ZERO 1
816*bbb1b6f9SApple OSS Distributions #define SYSCTL_SAME_VALUE 2
817*bbb1b6f9SApple OSS Distributions #define SYSCTL_BELOW_ALIGN 3
818*bbb1b6f9SApple OSS Distributions #define SYSCTL_ABOVE_ALIGN 4
819*bbb1b6f9SApple OSS Distributions #define SYSCTL_INCOMPRESSIBLE 5
820*bbb1b6f9SApple OSS Distributions
821*bbb1b6f9SApple OSS Distributions // uncomment this to make the asserts on the incremented statistics strict to the expected value. This assumes the
822*bbb1b6f9SApple OSS Distributions // tester is the only MTE enabled process not idle.
823*bbb1b6f9SApple OSS Distributions // This is undesirable if there are other MTE enabled processes which might page-out to the compressor
824*bbb1b6f9SApple OSS Distributions // while the test is running, which is the case in BATS.
825*bbb1b6f9SApple OSS Distributions // #define STRICT_STATS_EQ
826*bbb1b6f9SApple OSS Distributions
827*bbb1b6f9SApple OSS Distributions #ifdef STRICT_STATS_EQ
828*bbb1b6f9SApple OSS Distributions #define ASSERT_STAT_ATLEAST T_ASSERT_EQ
829*bbb1b6f9SApple OSS Distributions #else
830*bbb1b6f9SApple OSS Distributions #define ASSERT_STAT_ATLEAST T_ASSERT_GE
831*bbb1b6f9SApple OSS Distributions #endif
832*bbb1b6f9SApple OSS Distributions
833*bbb1b6f9SApple OSS Distributions static void
wait_compressed(struct tags_sysctls * start,uint32_t expected_increment)834*bbb1b6f9SApple OSS Distributions wait_compressed(struct tags_sysctls* start, uint32_t expected_increment)
835*bbb1b6f9SApple OSS Distributions {
836*bbb1b6f9SApple OSS Distributions // start with a sleep to give it a first chance to settle
837*bbb1b6f9SApple OSS Distributions usleep(10000);
838*bbb1b6f9SApple OSS Distributions struct tags_sysctls sample, d;
839*bbb1b6f9SApple OSS Distributions int iter = 1; // on account of the usleep above
840*bbb1b6f9SApple OSS Distributions while (true) {
841*bbb1b6f9SApple OSS Distributions tags_sysctls_sample_diff(start, &sample, &d);
842*bbb1b6f9SApple OSS Distributions if (d.pages_compressed > 0) {
843*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_compressed, 1ull, "compressed more than 1 page, are you running something in parallel?");
844*bbb1b6f9SApple OSS Distributions break;
845*bbb1b6f9SApple OSS Distributions }
846*bbb1b6f9SApple OSS Distributions usleep(10000);
847*bbb1b6f9SApple OSS Distributions ++iter;
848*bbb1b6f9SApple OSS Distributions if (iter > 10) {
849*bbb1b6f9SApple OSS Distributions T_ASSERT_FAIL("waiting too long for page-out. is MTE in the compressor enabled?");
850*bbb1b6f9SApple OSS Distributions break;
851*bbb1b6f9SApple OSS Distributions }
852*bbb1b6f9SApple OSS Distributions }
853*bbb1b6f9SApple OSS Distributions T_LOG(" waited for tags compression after %d msec", iter * 10);
854*bbb1b6f9SApple OSS Distributions tags_sysctls_print(&d, "WAITED");
855*bbb1b6f9SApple OSS Distributions
856*bbb1b6f9SApple OSS Distributions // check the expected sysctl was incremented
857*bbb1b6f9SApple OSS Distributions #define CHECK_INC(field_name, field_num) \
858*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.field_name, (expected_increment == field_num) ? 1ull : 0ull, "unexpected increment value")
859*bbb1b6f9SApple OSS Distributions CHECK_INC(all_zero, SYSCTL_ALL_ZERO);
860*bbb1b6f9SApple OSS Distributions CHECK_INC(same_value, SYSCTL_SAME_VALUE);
861*bbb1b6f9SApple OSS Distributions CHECK_INC(below_align, SYSCTL_BELOW_ALIGN);
862*bbb1b6f9SApple OSS Distributions CHECK_INC(above_align, SYSCTL_ABOVE_ALIGN);
863*bbb1b6f9SApple OSS Distributions CHECK_INC(incompressible, SYSCTL_INCOMPRESSIBLE);
864*bbb1b6f9SApple OSS Distributions #undef CHECK_INC
865*bbb1b6f9SApple OSS Distributions tags_sysctl_update(start, &sample); // reset it for the next check
866*bbb1b6f9SApple OSS Distributions }
867*bbb1b6f9SApple OSS Distributions
868*bbb1b6f9SApple OSS Distributions static void
check_sysctls_after_pagein(struct tags_sysctls * start)869*bbb1b6f9SApple OSS Distributions check_sysctls_after_pagein(struct tags_sysctls* start)
870*bbb1b6f9SApple OSS Distributions {
871*bbb1b6f9SApple OSS Distributions struct tags_sysctls sample, d;
872*bbb1b6f9SApple OSS Distributions tags_sysctls_sample_diff(start, &sample, &d);
873*bbb1b6f9SApple OSS Distributions tags_sysctls_print(&d, "PAGEIN");
874*bbb1b6f9SApple OSS Distributions
875*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_decompressed, 1ull, "check counter");
876*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_freed, 0ull, "check counter");
877*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_corrupted, 0ull, "check counter");
878*bbb1b6f9SApple OSS Distributions // after page-in overhead returns to 0
879*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(start->start_overhead_bytes - sample.overhead_bytes, 0ll, "check overhead bytes");
880*bbb1b6f9SApple OSS Distributions tags_sysctl_update(start, &sample);;
881*bbb1b6f9SApple OSS Distributions }
882*bbb1b6f9SApple OSS Distributions
883*bbb1b6f9SApple OSS Distributions static void
check_sysctls_after_dealloc(struct tags_sysctls * start,bool did_pagein)884*bbb1b6f9SApple OSS Distributions check_sysctls_after_dealloc(struct tags_sysctls* start, bool did_pagein)
885*bbb1b6f9SApple OSS Distributions {
886*bbb1b6f9SApple OSS Distributions struct tags_sysctls sample, d;
887*bbb1b6f9SApple OSS Distributions tags_sysctls_sample_diff(start, &sample, &d);
888*bbb1b6f9SApple OSS Distributions tags_sysctls_print(&d, "DEALLOC");
889*bbb1b6f9SApple OSS Distributions
890*bbb1b6f9SApple OSS Distributions if (!did_pagein) {
891*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_freed, 1ull, "check counter");
892*bbb1b6f9SApple OSS Distributions } else {
893*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_freed, 0ull, "check counter");
894*bbb1b6f9SApple OSS Distributions }
895*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_decompressed, 0ull, "check counter");
896*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(d.pages_corrupted, 0ull, "check counter");
897*bbb1b6f9SApple OSS Distributions T_QUIET; ASSERT_STAT_ATLEAST(start->start_overhead_bytes - sample.overhead_bytes, 0ll, "check overhead bytes");
898*bbb1b6f9SApple OSS Distributions tags_sysctl_update(start, &sample);;
899*bbb1b6f9SApple OSS Distributions }
900*bbb1b6f9SApple OSS Distributions
901*bbb1b6f9SApple OSS Distributions // --- main test function ---
902*bbb1b6f9SApple OSS Distributions
903*bbb1b6f9SApple OSS Distributions typedef void (*fn_fill)(uint8_t *buf, size_t bufsize);
904*bbb1b6f9SApple OSS Distributions typedef void (*fn_do_tags)(struct tag_pattern *t);
905*bbb1b6f9SApple OSS Distributions
906*bbb1b6f9SApple OSS Distributions struct tags_fill_t {
907*bbb1b6f9SApple OSS Distributions fn_do_tags do_tags_func;
908*bbb1b6f9SApple OSS Distributions const char *name;
909*bbb1b6f9SApple OSS Distributions uint32_t expect_sysctl_increment;
910*bbb1b6f9SApple OSS Distributions };
911*bbb1b6f9SApple OSS Distributions
912*bbb1b6f9SApple OSS Distributions struct data_fill_t {
913*bbb1b6f9SApple OSS Distributions fn_fill fill_func;
914*bbb1b6f9SApple OSS Distributions const char *name;
915*bbb1b6f9SApple OSS Distributions };
916*bbb1b6f9SApple OSS Distributions
917*bbb1b6f9SApple OSS Distributions #define WAIT_INTERACTIVE 1
918*bbb1b6f9SApple OSS Distributions #define DONT_PAGEIN 2
919*bbb1b6f9SApple OSS Distributions #define PRELOAD_COMPRESSED_BYTES 4
920*bbb1b6f9SApple OSS Distributions
921*bbb1b6f9SApple OSS Distributions static void
test_pattern(struct data_fill_t data_fill,struct tags_fill_t tags_fill,uint32_t flags)922*bbb1b6f9SApple OSS Distributions test_pattern(struct data_fill_t data_fill, struct tags_fill_t tags_fill, uint32_t flags)
923*bbb1b6f9SApple OSS Distributions {
924*bbb1b6f9SApple OSS Distributions T_LOG("---------- Running: fill:%s tags:%s... ----------", data_fill.name, tags_fill.name);
925*bbb1b6f9SApple OSS Distributions size_t bufsize = PAGE_SIZE;
926*bbb1b6f9SApple OSS Distributions vm_address_t address = 0;
927*bbb1b6f9SApple OSS Distributions kern_return_t kr = vm_allocate(mach_task_self(), &address, bufsize, VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
928*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate(VM_FLAGS_MTE)");
929*bbb1b6f9SApple OSS Distributions
930*bbb1b6f9SApple OSS Distributions uint8_t *buf = (uint8_t*)address;
931*bbb1b6f9SApple OSS Distributions uint8_t *copy_buf = (uint8_t *)malloc(bufsize); // will hold a copy of the data for comparing after page-in
932*bbb1b6f9SApple OSS Distributions
933*bbb1b6f9SApple OSS Distributions memset((void*)buf, 0, bufsize);
934*bbb1b6f9SApple OSS Distributions zero_tags(buf, bufsize);
935*bbb1b6f9SApple OSS Distributions
936*bbb1b6f9SApple OSS Distributions // fill page with data
937*bbb1b6f9SApple OSS Distributions data_fill.fill_func(buf, bufsize);
938*bbb1b6f9SApple OSS Distributions memcpy(copy_buf, buf, bufsize); // make a copy for later comparison
939*bbb1b6f9SApple OSS Distributions
940*bbb1b6f9SApple OSS Distributions struct tag_pattern t;
941*bbb1b6f9SApple OSS Distributions tag_pattern_init(&t, buf, bufsize);
942*bbb1b6f9SApple OSS Distributions T_LOG(" tagging");
943*bbb1b6f9SApple OSS Distributions tags_fill.do_tags_func(&t);
944*bbb1b6f9SApple OSS Distributions T_LOG(" verify-read");
945*bbb1b6f9SApple OSS Distributions // verify we can indeed read all tags
946*bbb1b6f9SApple OSS Distributions tag_pattern_read_verify(&t, copy_buf);
947*bbb1b6f9SApple OSS Distributions
948*bbb1b6f9SApple OSS Distributions struct tags_sysctls ts; // updated with the latest sysctl sample after each phase
949*bbb1b6f9SApple OSS Distributions tags_sysctl_start(&ts);
950*bbb1b6f9SApple OSS Distributions
951*bbb1b6f9SApple OSS Distributions T_LOG(" paging-out");
952*bbb1b6f9SApple OSS Distributions kr = mach_vm_behavior_set(mach_task_self(), (mach_vm_address_t)buf, bufsize, VM_BEHAVIOR_PAGEOUT);
953*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "failed mach_vm_behavior_set() %p,%zu - %d", buf, bufsize, kr);
954*bbb1b6f9SApple OSS Distributions
955*bbb1b6f9SApple OSS Distributions wait_compressed(&ts, tags_fill.expect_sysctl_increment);
956*bbb1b6f9SApple OSS Distributions
957*bbb1b6f9SApple OSS Distributions if (flags & WAIT_INTERACTIVE) {
958*bbb1b6f9SApple OSS Distributions getchar();
959*bbb1b6f9SApple OSS Distributions }
960*bbb1b6f9SApple OSS Distributions
961*bbb1b6f9SApple OSS Distributions if (!(flags & DONT_PAGEIN)) {
962*bbb1b6f9SApple OSS Distributions T_LOG(" paging-in");
963*bbb1b6f9SApple OSS Distributions tag_pattern_read_verify(&t, copy_buf);
964*bbb1b6f9SApple OSS Distributions check_sysctls_after_pagein(&ts);
965*bbb1b6f9SApple OSS Distributions }
966*bbb1b6f9SApple OSS Distributions T_LOG(" deallocating");
967*bbb1b6f9SApple OSS Distributions kr = vm_deallocate(mach_task_self(), address, bufsize);
968*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate");
969*bbb1b6f9SApple OSS Distributions
970*bbb1b6f9SApple OSS Distributions check_sysctls_after_dealloc(&ts, !(flags & DONT_PAGEIN));
971*bbb1b6f9SApple OSS Distributions
972*bbb1b6f9SApple OSS Distributions tag_pattern_destroy(&t);
973*bbb1b6f9SApple OSS Distributions T_PASS("OK");
974*bbb1b6f9SApple OSS Distributions }
975*bbb1b6f9SApple OSS Distributions
976*bbb1b6f9SApple OSS Distributions struct test_buf {
977*bbb1b6f9SApple OSS Distributions vm_address_t address;
978*bbb1b6f9SApple OSS Distributions size_t bufsize;
979*bbb1b6f9SApple OSS Distributions };
980*bbb1b6f9SApple OSS Distributions // this is just a simpler version of the above, split to two functions. This is meant so that there would be already
981*bbb1b6f9SApple OSS Distributions // something in the compressor while the test is running
982*bbb1b6f9SApple OSS Distributions static void
preload_compressed_bytes(struct test_buf * b)983*bbb1b6f9SApple OSS Distributions preload_compressed_bytes(struct test_buf *b)
984*bbb1b6f9SApple OSS Distributions {
985*bbb1b6f9SApple OSS Distributions T_LOG("---- preloading the compressor ----");
986*bbb1b6f9SApple OSS Distributions size_t bufsize = PAGE_SIZE;
987*bbb1b6f9SApple OSS Distributions vm_address_t address = 0;
988*bbb1b6f9SApple OSS Distributions kern_return_t kr = vm_allocate(mach_task_self(), &address, bufsize, VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
989*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate(VM_FLAGS_MTE)");
990*bbb1b6f9SApple OSS Distributions
991*bbb1b6f9SApple OSS Distributions uint8_t *buf = (uint8_t*)address;
992*bbb1b6f9SApple OSS Distributions memset((void*)buf, 0, bufsize);
993*bbb1b6f9SApple OSS Distributions zero_tags(buf, bufsize);
994*bbb1b6f9SApple OSS Distributions
995*bbb1b6f9SApple OSS Distributions // set non-zero tags
996*bbb1b6f9SApple OSS Distributions struct tag_pattern t;
997*bbb1b6f9SApple OSS Distributions tag_pattern_init(&t, buf, bufsize);
998*bbb1b6f9SApple OSS Distributions tag_pattern_max_mix(&t);
999*bbb1b6f9SApple OSS Distributions tag_pattern_destroy(&t); // don't need to verify it later
1000*bbb1b6f9SApple OSS Distributions
1001*bbb1b6f9SApple OSS Distributions struct tags_sysctls ts;
1002*bbb1b6f9SApple OSS Distributions tags_sysctl_start(&ts);
1003*bbb1b6f9SApple OSS Distributions
1004*bbb1b6f9SApple OSS Distributions T_LOG(" paging-out (preload)");
1005*bbb1b6f9SApple OSS Distributions kr = mach_vm_behavior_set(mach_task_self(), (mach_vm_address_t)buf, bufsize, VM_BEHAVIOR_PAGEOUT);
1006*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "failed mach_vm_behavior_set() %p,%zu - %d", buf, bufsize, kr);
1007*bbb1b6f9SApple OSS Distributions
1008*bbb1b6f9SApple OSS Distributions wait_compressed(&ts, SYSCTL_INCOMPRESSIBLE);
1009*bbb1b6f9SApple OSS Distributions b->address = address;
1010*bbb1b6f9SApple OSS Distributions b->bufsize = bufsize;
1011*bbb1b6f9SApple OSS Distributions }
1012*bbb1b6f9SApple OSS Distributions
1013*bbb1b6f9SApple OSS Distributions static void
un_preload_compressed_bytes(struct test_buf * b)1014*bbb1b6f9SApple OSS Distributions un_preload_compressed_bytes(struct test_buf *b)
1015*bbb1b6f9SApple OSS Distributions {
1016*bbb1b6f9SApple OSS Distributions T_LOG("---- un-preloading ----");
1017*bbb1b6f9SApple OSS Distributions kern_return_t kr = vm_deallocate(mach_task_self(), b->address, b->bufsize);
1018*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate");
1019*bbb1b6f9SApple OSS Distributions }
1020*bbb1b6f9SApple OSS Distributions
1021*bbb1b6f9SApple OSS Distributions static struct tags_fill_t tags_fills[] = {
1022*bbb1b6f9SApple OSS Distributions { &tag_pattern_single_at_start, "single_at_start", SYSCTL_BELOW_ALIGN },
1023*bbb1b6f9SApple OSS Distributions { &tag_pattern_max_mix, "max-mix", SYSCTL_INCOMPRESSIBLE },
1024*bbb1b6f9SApple OSS Distributions { &tag_patten_all_zero, "all-zero", SYSCTL_ALL_ZERO },
1025*bbb1b6f9SApple OSS Distributions { &tag_pattern_all_same, "all-same", SYSCTL_SAME_VALUE},
1026*bbb1b6f9SApple OSS Distributions { &tag_pattern_half_and_half, "halfs", SYSCTL_BELOW_ALIGN },
1027*bbb1b6f9SApple OSS Distributions { &tag_pattern_odd_chunks, "odd-chunks", SYSCTL_ABOVE_ALIGN }
1028*bbb1b6f9SApple OSS Distributions };
1029*bbb1b6f9SApple OSS Distributions
1030*bbb1b6f9SApple OSS Distributions static struct data_fill_t data_fills[] = {
1031*bbb1b6f9SApple OSS Distributions { &fill_zeros, "zeros" },
1032*bbb1b6f9SApple OSS Distributions { &fill_same, "same" },
1033*bbb1b6f9SApple OSS Distributions { &fill_only_first_byte, "first-byte" },
1034*bbb1b6f9SApple OSS Distributions { &fill_counter, "counter" },
1035*bbb1b6f9SApple OSS Distributions { &fill_rand, "rand" }
1036*bbb1b6f9SApple OSS Distributions };
1037*bbb1b6f9SApple OSS Distributions
1038*bbb1b6f9SApple OSS Distributions void
run_all_patterns(int flags)1039*bbb1b6f9SApple OSS Distributions run_all_patterns(int flags)
1040*bbb1b6f9SApple OSS Distributions {
1041*bbb1b6f9SApple OSS Distributions my_srand(0);
1042*bbb1b6f9SApple OSS Distributions struct test_buf b;
1043*bbb1b6f9SApple OSS Distributions if (flags & PRELOAD_COMPRESSED_BYTES) {
1044*bbb1b6f9SApple OSS Distributions preload_compressed_bytes(&b);
1045*bbb1b6f9SApple OSS Distributions }
1046*bbb1b6f9SApple OSS Distributions if (flags & PRELOAD_COMPRESSED_BYTES) {
1047*bbb1b6f9SApple OSS Distributions preload_compressed_bytes(&b);
1048*bbb1b6f9SApple OSS Distributions }
1049*bbb1b6f9SApple OSS Distributions for (size_t fpi = 0; fpi < countof(data_fills); ++fpi) {
1050*bbb1b6f9SApple OSS Distributions for (size_t tpi = 0; tpi < countof(tags_fills); ++tpi) {
1051*bbb1b6f9SApple OSS Distributions test_pattern(data_fills[fpi], tags_fills[tpi], flags);
1052*bbb1b6f9SApple OSS Distributions }
1053*bbb1b6f9SApple OSS Distributions }
1054*bbb1b6f9SApple OSS Distributions if (flags & PRELOAD_COMPRESSED_BYTES) {
1055*bbb1b6f9SApple OSS Distributions un_preload_compressed_bytes(&b);
1056*bbb1b6f9SApple OSS Distributions }
1057*bbb1b6f9SApple OSS Distributions }
1058*bbb1b6f9SApple OSS Distributions
1059*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_paging,
1060*bbb1b6f9SApple OSS Distributions "Test paging out to the compressor and paging in from the compressor of MTE pages",
1061*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1062*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1063*bbb1b6f9SApple OSS Distributions {
1064*bbb1b6f9SApple OSS Distributions run_all_patterns(0);
1065*bbb1b6f9SApple OSS Distributions run_all_patterns(PRELOAD_COMPRESSED_BYTES);
1066*bbb1b6f9SApple OSS Distributions }
1067*bbb1b6f9SApple OSS Distributions
1068*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_no_pageing,
1069*bbb1b6f9SApple OSS Distributions "Test what happens if the tagged memory is not paged-in before being deallocated",
1070*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1071*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1072*bbb1b6f9SApple OSS Distributions {
1073*bbb1b6f9SApple OSS Distributions run_all_patterns(DONT_PAGEIN);
1074*bbb1b6f9SApple OSS Distributions run_all_patterns(DONT_PAGEIN | PRELOAD_COMPRESSED_BYTES);
1075*bbb1b6f9SApple OSS Distributions }
1076*bbb1b6f9SApple OSS Distributions
1077*bbb1b6f9SApple OSS Distributions static size_t
read_big_sysctl(const char * name,char ** buf)1078*bbb1b6f9SApple OSS Distributions read_big_sysctl(const char *name, char **buf)
1079*bbb1b6f9SApple OSS Distributions {
1080*bbb1b6f9SApple OSS Distributions size_t len = 0;
1081*bbb1b6f9SApple OSS Distributions int rc = sysctlbyname(name, NULL, &len, NULL, 0); // get the length of the needed buffer
1082*bbb1b6f9SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(rc, "query size of sysctl `%s`", name);
1083*bbb1b6f9SApple OSS Distributions T_ASSERT_GT(len, (size_t)0, "sysctl got size 0");
1084*bbb1b6f9SApple OSS Distributions len += 4096; // allocate a bit extra in case the size changed between the two calls
1085*bbb1b6f9SApple OSS Distributions *buf = (char*)malloc(len);
1086*bbb1b6f9SApple OSS Distributions T_ASSERT_NE_PTR((void*)*buf, NULL, "allocation for sysctl %zu", len);
1087*bbb1b6f9SApple OSS Distributions rc = sysctlbyname(name, *buf, &len, NULL, 0);
1088*bbb1b6f9SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(rc, "query of sysctl `%s`", name);
1089*bbb1b6f9SApple OSS Distributions return len;
1090*bbb1b6f9SApple OSS Distributions }
1091*bbb1b6f9SApple OSS Distributions
1092*bbb1b6f9SApple OSS Distributions //#define CSEGS_VERBOSE
1093*bbb1b6f9SApple OSS Distributions #ifdef CSEGS_VERBOSE
1094*bbb1b6f9SApple OSS Distributions #define T_LOG_VERBOSE(...) T_LOG(__VA_ARGS__)
1095*bbb1b6f9SApple OSS Distributions #else
1096*bbb1b6f9SApple OSS Distributions #define T_LOG_VERBOSE(...)
1097*bbb1b6f9SApple OSS Distributions #endif
1098*bbb1b6f9SApple OSS Distributions
1099*bbb1b6f9SApple OSS Distributions // this uses the sysctl that dumps all the compressor metadata to calculate the MTE bytes overhead
1100*bbb1b6f9SApple OSS Distributions static void
get_mte_size_from_csegs(uint64_t * bytes_overhead,uint64_t * tagged_pages)1101*bbb1b6f9SApple OSS Distributions get_mte_size_from_csegs(uint64_t *bytes_overhead, uint64_t *tagged_pages)
1102*bbb1b6f9SApple OSS Distributions {
1103*bbb1b6f9SApple OSS Distributions uint64_t compressed_bytes = 0; // before alignment
1104*bbb1b6f9SApple OSS Distributions *bytes_overhead = 0;
1105*bbb1b6f9SApple OSS Distributions *tagged_pages = 0;
1106*bbb1b6f9SApple OSS Distributions
1107*bbb1b6f9SApple OSS Distributions char *buf = NULL;
1108*bbb1b6f9SApple OSS Distributions size_t sz = read_big_sysctl("vm.compressor_segments", &buf);
1109*bbb1b6f9SApple OSS Distributions
1110*bbb1b6f9SApple OSS Distributions size_t offset = 0;
1111*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GE_ULONG(sz, sizeof(uint32_t), "got buffer shorter than the magic value");
1112*bbb1b6f9SApple OSS Distributions uint32_t hdr_magic = *((uint32_t*)buf);
1113*bbb1b6f9SApple OSS Distributions T_ASSERT_EQ_UINT(hdr_magic, VM_C_SEGMENT_INFO_MAGIC, "match magic value");
1114*bbb1b6f9SApple OSS Distributions offset += sizeof(uint32_t);
1115*bbb1b6f9SApple OSS Distributions while (offset < sz) {
1116*bbb1b6f9SApple OSS Distributions // read next c_segment
1117*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset + sizeof(struct c_segment_info), sz, "unexpected offset for c_segment_info");
1118*bbb1b6f9SApple OSS Distributions const struct c_segment_info* cseg = (const struct c_segment_info*)(buf + offset);
1119*bbb1b6f9SApple OSS Distributions offset += sizeof(struct c_segment_info);
1120*bbb1b6f9SApple OSS Distributions // read its slots
1121*bbb1b6f9SApple OSS Distributions bool logged_segment = false;
1122*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset + cseg->csi_slots_len * sizeof(struct c_slot_info), sz, "unexpected offset for c_slot_info");
1123*bbb1b6f9SApple OSS Distributions for (int i = 0; i < cseg->csi_slots_len; ++i) {
1124*bbb1b6f9SApple OSS Distributions const struct c_slot_info *slot = (const struct c_slot_info*)&cseg->csi_slots[i];
1125*bbb1b6f9SApple OSS Distributions if (slot->csi_mte_size == 0) {
1126*bbb1b6f9SApple OSS Distributions continue;
1127*bbb1b6f9SApple OSS Distributions }
1128*bbb1b6f9SApple OSS Distributions ++(*tagged_pages);
1129*bbb1b6f9SApple OSS Distributions uint32_t actual_size = vm_mte_compressed_tags_actual_size(slot->csi_mte_size);
1130*bbb1b6f9SApple OSS Distributions if (actual_size > 0) {
1131*bbb1b6f9SApple OSS Distributions compressed_bytes += slot->csi_mte_size;
1132*bbb1b6f9SApple OSS Distributions *bytes_overhead += C_SEG_ROUND_TO_ALIGNMENT(slot->csi_mte_size);
1133*bbb1b6f9SApple OSS Distributions }
1134*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_FALSE(slot->csi_mte_has_data, "unexpected has_data");
1135*bbb1b6f9SApple OSS Distributions
1136*bbb1b6f9SApple OSS Distributions if (!logged_segment) {
1137*bbb1b6f9SApple OSS Distributions T_LOG_VERBOSE("segment %u bytes-used: %d", cseg->csi_mysegno, cseg->csi_bytes_used);
1138*bbb1b6f9SApple OSS Distributions logged_segment = true;
1139*bbb1b6f9SApple OSS Distributions }
1140*bbb1b6f9SApple OSS Distributions T_LOG_VERBOSE(" slot %d: size=%u mte_size=%u", i, (uint32_t) slot->csi_size, (uint32_t) slot->csi_mte_size);
1141*bbb1b6f9SApple OSS Distributions }
1142*bbb1b6f9SApple OSS Distributions offset += cseg->csi_slots_len * sizeof(struct c_slot_info);
1143*bbb1b6f9SApple OSS Distributions }
1144*bbb1b6f9SApple OSS Distributions T_LOG("compressed_bytes=%llu aligned=%llu tagged_pages=%llu", compressed_bytes, *bytes_overhead, *tagged_pages);
1145*bbb1b6f9SApple OSS Distributions }
1146*bbb1b6f9SApple OSS Distributions
1147*bbb1b6f9SApple OSS Distributions static void
counters_verify()1148*bbb1b6f9SApple OSS Distributions counters_verify()
1149*bbb1b6f9SApple OSS Distributions {
1150*bbb1b6f9SApple OSS Distributions // this comparison may fail since it is inherently racy, getting the same number in 2 sligtly different times.
1151*bbb1b6f9SApple OSS Distributions T_MAYFAIL;
1152*bbb1b6f9SApple OSS Distributions uint64_t bytes_from_csegs = 0, pages_from_csegs = 0;
1153*bbb1b6f9SApple OSS Distributions get_mte_size_from_csegs(&bytes_from_csegs, &pages_from_csegs);
1154*bbb1b6f9SApple OSS Distributions uint64_t bytes_from_sysctl = sysctl_get_Q("vm.mte.compress_overhead_bytes");
1155*bbb1b6f9SApple OSS Distributions uint64_t pages_from_sysctl = sysctl_get_Q("vm.mte.compress_pages");
1156*bbb1b6f9SApple OSS Distributions T_ASSERT_EQ(bytes_from_csegs, bytes_from_sysctl, "overhead bytes count match");
1157*bbb1b6f9SApple OSS Distributions T_ASSERT_EQ(pages_from_csegs, pages_from_sysctl, "tagged pages count match");
1158*bbb1b6f9SApple OSS Distributions }
1159*bbb1b6f9SApple OSS Distributions
1160*bbb1b6f9SApple OSS Distributions static vm_address_t
make_rand_tagged_buf(size_t bufsize)1161*bbb1b6f9SApple OSS Distributions make_rand_tagged_buf(size_t bufsize)
1162*bbb1b6f9SApple OSS Distributions {
1163*bbb1b6f9SApple OSS Distributions my_srand(0);
1164*bbb1b6f9SApple OSS Distributions T_LOG("filling buffer size 0x%zx", bufsize);
1165*bbb1b6f9SApple OSS Distributions vm_address_t address;
1166*bbb1b6f9SApple OSS Distributions kern_return_t kr = vm_allocate(mach_task_self(), &address, bufsize, VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
1167*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate(VM_FLAGS_MTE)");
1168*bbb1b6f9SApple OSS Distributions
1169*bbb1b6f9SApple OSS Distributions uint8_t *buf = (uint8_t*)address;
1170*bbb1b6f9SApple OSS Distributions memset((void*)buf, 0, bufsize);
1171*bbb1b6f9SApple OSS Distributions zero_tags(buf, bufsize);
1172*bbb1b6f9SApple OSS Distributions
1173*bbb1b6f9SApple OSS Distributions // fill each page with different fill and tag patterns
1174*bbb1b6f9SApple OSS Distributions for (int i = 0; i < bufsize / PAGE_SIZE; ++i) {
1175*bbb1b6f9SApple OSS Distributions struct tag_pattern t;
1176*bbb1b6f9SApple OSS Distributions uint8_t *it_buf = buf + i * PAGE_SIZE;
1177*bbb1b6f9SApple OSS Distributions size_t it_size = PAGE_SIZE;
1178*bbb1b6f9SApple OSS Distributions tag_pattern_init(&t, it_buf, it_size);
1179*bbb1b6f9SApple OSS Distributions
1180*bbb1b6f9SApple OSS Distributions struct data_fill_t *df = &data_fills[(my_rand() >> 1) % countof(data_fills)];
1181*bbb1b6f9SApple OSS Distributions df->fill_func(it_buf, it_size);
1182*bbb1b6f9SApple OSS Distributions int tf_ind = (my_rand() >> 1) % countof(tags_fills);
1183*bbb1b6f9SApple OSS Distributions struct tags_fill_t *tf = &tags_fills[tf_ind];
1184*bbb1b6f9SApple OSS Distributions tf->do_tags_func(&t);
1185*bbb1b6f9SApple OSS Distributions
1186*bbb1b6f9SApple OSS Distributions tag_pattern_destroy(&t);
1187*bbb1b6f9SApple OSS Distributions }
1188*bbb1b6f9SApple OSS Distributions return address;
1189*bbb1b6f9SApple OSS Distributions }
1190*bbb1b6f9SApple OSS Distributions
1191*bbb1b6f9SApple OSS Distributions static void
page_out(vm_address_t address,size_t bufsize)1192*bbb1b6f9SApple OSS Distributions page_out(vm_address_t address, size_t bufsize)
1193*bbb1b6f9SApple OSS Distributions {
1194*bbb1b6f9SApple OSS Distributions T_LOG("paging-out");
1195*bbb1b6f9SApple OSS Distributions kern_return_t kr = mach_vm_behavior_set(mach_task_self(), (mach_vm_address_t)address, bufsize, VM_BEHAVIOR_PAGEOUT);
1196*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "failed mach_vm_behavior_set() %lx,%zu - %d", address, bufsize, kr);
1197*bbb1b6f9SApple OSS Distributions }
1198*bbb1b6f9SApple OSS Distributions
1199*bbb1b6f9SApple OSS Distributions static void
print_stats()1200*bbb1b6f9SApple OSS Distributions print_stats()
1201*bbb1b6f9SApple OSS Distributions {
1202*bbb1b6f9SApple OSS Distributions struct tags_sysctls ts;
1203*bbb1b6f9SApple OSS Distributions tags_sysctls_sample(&ts);
1204*bbb1b6f9SApple OSS Distributions tags_sysctls_print(&ts, "STATS");
1205*bbb1b6f9SApple OSS Distributions }
1206*bbb1b6f9SApple OSS Distributions
1207*bbb1b6f9SApple OSS Distributions static void
dealloc(vm_address_t address,size_t bufsize)1208*bbb1b6f9SApple OSS Distributions dealloc(vm_address_t address, size_t bufsize)
1209*bbb1b6f9SApple OSS Distributions {
1210*bbb1b6f9SApple OSS Distributions T_LOG(" deallocating");
1211*bbb1b6f9SApple OSS Distributions kern_return_t kr = vm_deallocate(mach_task_self(), address, bufsize);
1212*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate");
1213*bbb1b6f9SApple OSS Distributions }
1214*bbb1b6f9SApple OSS Distributions
1215*bbb1b6f9SApple OSS Distributions // This test is useful for running after a some heavy MTE processes have ran and finished
1216*bbb1b6f9SApple OSS Distributions // the make sure that the bytes number maintained in the sysctl is the same as the actual mte_sizes in the segments
1217*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_counters_verify,
1218*bbb1b6f9SApple OSS Distributions "Verify that the overhead bytes statistics match the size as it appears in the segments",
1219*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1220*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1221*bbb1b6f9SApple OSS Distributions {
1222*bbb1b6f9SApple OSS Distributions counters_verify();
1223*bbb1b6f9SApple OSS Distributions print_stats();
1224*bbb1b6f9SApple OSS Distributions }
1225*bbb1b6f9SApple OSS Distributions
1226*bbb1b6f9SApple OSS Distributions
1227*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_exercise_counters_verify,
1228*bbb1b6f9SApple OSS Distributions "Exericise the MTE tags compress, then verify that the overhead bytes statistics match the size as it appears in the segments",
1229*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1230*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1231*bbb1b6f9SApple OSS Distributions {
1232*bbb1b6f9SApple OSS Distributions size_t bufsize = 100 * PAGE_SIZE;
1233*bbb1b6f9SApple OSS Distributions vm_address_t address = make_rand_tagged_buf(bufsize);
1234*bbb1b6f9SApple OSS Distributions page_out(address, bufsize);
1235*bbb1b6f9SApple OSS Distributions usleep(20000); // wait for the compressor to finish
1236*bbb1b6f9SApple OSS Distributions counters_verify();
1237*bbb1b6f9SApple OSS Distributions print_stats();
1238*bbb1b6f9SApple OSS Distributions dealloc(address, bufsize);
1239*bbb1b6f9SApple OSS Distributions }
1240*bbb1b6f9SApple OSS Distributions
1241*bbb1b6f9SApple OSS Distributions static void
dump_buffer(const char * path,const char * buf,size_t sz)1242*bbb1b6f9SApple OSS Distributions dump_buffer(const char *path, const char *buf, size_t sz)
1243*bbb1b6f9SApple OSS Distributions {
1244*bbb1b6f9SApple OSS Distributions FILE *f = fopen(path, "w");
1245*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(f, "Failed to open file %s", path);
1246*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fwrite(buf, 1, sz, f), sz, "Failed to write to file %s", path);
1247*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fclose(f), 0, "Failed to close file %s", path);
1248*bbb1b6f9SApple OSS Distributions }
1249*bbb1b6f9SApple OSS Distributions
1250*bbb1b6f9SApple OSS Distributions static size_t
read_file(const char * path,char ** buf_ptr)1251*bbb1b6f9SApple OSS Distributions read_file(const char *path, char **buf_ptr)
1252*bbb1b6f9SApple OSS Distributions {
1253*bbb1b6f9SApple OSS Distributions FILE *f = fopen(path, "r");
1254*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(f, "Failed to open file %s", path);
1255*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fseek(f, 0, SEEK_END), 0, "Faile to seek in file %s", path);
1256*bbb1b6f9SApple OSS Distributions size_t sz = ftell(f);
1257*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GT(sz, (size_t)0, "Empty file %s", path);
1258*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fseek(f, 0, SEEK_SET), 0, "Faile to seek in file %s", path);
1259*bbb1b6f9SApple OSS Distributions *buf_ptr = (char *)malloc(sz);
1260*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fread(*buf_ptr, 1, sz, f), sz, "Failed to read from file %s", path);
1261*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(fclose(f), 0, "Failed to close file %s", path);
1262*bbb1b6f9SApple OSS Distributions return sz;
1263*bbb1b6f9SApple OSS Distributions }
1264*bbb1b6f9SApple OSS Distributions
1265*bbb1b6f9SApple OSS Distributions static void
1266*bbb1b6f9SApple OSS Distributions get_mte_compressed_tags(
1267*bbb1b6f9SApple OSS Distributions void (^process_cseg)(uint32_t state),
1268*bbb1b6f9SApple OSS Distributions void (^process_cslot)(int slot_idx, uint8_t *compressed_buf, uint32_t compressed_size, uint32_t actual_size),
1269*bbb1b6f9SApple OSS Distributions const char *load_from_file, const char *dump_to_file)
1270*bbb1b6f9SApple OSS Distributions {
1271*bbb1b6f9SApple OSS Distributions char *buf = NULL;
1272*bbb1b6f9SApple OSS Distributions size_t sz = 0;
1273*bbb1b6f9SApple OSS Distributions if (load_from_file == NULL) {
1274*bbb1b6f9SApple OSS Distributions // if this buffer gets big reading it may fail when under memory pressure since it requires a big
1275*bbb1b6f9SApple OSS Distributions // memory allocation in the kernel
1276*bbb1b6f9SApple OSS Distributions T_MAYFAIL;
1277*bbb1b6f9SApple OSS Distributions sz = read_big_sysctl("vm.compressor_segments_data", &buf);
1278*bbb1b6f9SApple OSS Distributions } else {
1279*bbb1b6f9SApple OSS Distributions sz = read_file(load_from_file, &buf);
1280*bbb1b6f9SApple OSS Distributions }
1281*bbb1b6f9SApple OSS Distributions if (dump_to_file != NULL) {
1282*bbb1b6f9SApple OSS Distributions dump_buffer(dump_to_file, buf, sz);
1283*bbb1b6f9SApple OSS Distributions }
1284*bbb1b6f9SApple OSS Distributions
1285*bbb1b6f9SApple OSS Distributions size_t offset = 0;
1286*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GE_ULONG(sz, sizeof(uint32_t), "got buffer shorter than the magic value");
1287*bbb1b6f9SApple OSS Distributions uint32_t hdr_magic = *((uint32_t*)buf);
1288*bbb1b6f9SApple OSS Distributions T_ASSERT_EQ_UINT(hdr_magic, VM_C_SEGMENT_INFO_MAGIC_WITH_TAGS, "match magic value");
1289*bbb1b6f9SApple OSS Distributions offset += sizeof(uint32_t);
1290*bbb1b6f9SApple OSS Distributions while (offset < sz) {
1291*bbb1b6f9SApple OSS Distributions // read next c_segment
1292*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset + sizeof(struct c_segment_info), sz, "unexpected offset for c_segment_info");
1293*bbb1b6f9SApple OSS Distributions const struct c_segment_info* cseg = (const struct c_segment_info*)(buf + offset);
1294*bbb1b6f9SApple OSS Distributions process_cseg(cseg->csi_state);
1295*bbb1b6f9SApple OSS Distributions offset += sizeof(struct c_segment_info);
1296*bbb1b6f9SApple OSS Distributions // read its slots
1297*bbb1b6f9SApple OSS Distributions for (int si = 0; si < cseg->csi_slots_len; ++si) {
1298*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset + sizeof(struct c_slot_info), sz, "unexpected offset for c_slot_info");
1299*bbb1b6f9SApple OSS Distributions const struct c_slot_info *slot = (const struct c_slot_info*)(buf + offset);
1300*bbb1b6f9SApple OSS Distributions offset += sizeof(struct c_slot_info);
1301*bbb1b6f9SApple OSS Distributions if (slot->csi_mte_size == 0 || !slot->csi_mte_has_data) {
1302*bbb1b6f9SApple OSS Distributions continue;
1303*bbb1b6f9SApple OSS Distributions }
1304*bbb1b6f9SApple OSS Distributions uint32_t actual_size = vm_mte_compressed_tags_actual_size(slot->csi_mte_size);
1305*bbb1b6f9SApple OSS Distributions uint8_t *data_ptr = NULL;
1306*bbb1b6f9SApple OSS Distributions if (actual_size > 0) {
1307*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LE(offset + actual_size, sz, "unexpected offset for tags data");
1308*bbb1b6f9SApple OSS Distributions // compressed tag data is at the end of the c_slot_info
1309*bbb1b6f9SApple OSS Distributions data_ptr = (uint8_t *)slot + sizeof(struct c_slot_info);
1310*bbb1b6f9SApple OSS Distributions }
1311*bbb1b6f9SApple OSS Distributions process_cslot(si, data_ptr, slot->csi_mte_size, actual_size);
1312*bbb1b6f9SApple OSS Distributions offset += actual_size;
1313*bbb1b6f9SApple OSS Distributions }
1314*bbb1b6f9SApple OSS Distributions }
1315*bbb1b6f9SApple OSS Distributions free(buf);
1316*bbb1b6f9SApple OSS Distributions }
1317*bbb1b6f9SApple OSS Distributions
1318*bbb1b6f9SApple OSS Distributions static void
print_comp_hist(struct comp_histogram * comp_hist)1319*bbb1b6f9SApple OSS Distributions print_comp_hist(struct comp_histogram *comp_hist)
1320*bbb1b6f9SApple OSS Distributions {
1321*bbb1b6f9SApple OSS Distributions T_LOG("RLE cmd histogram:");
1322*bbb1b6f9SApple OSS Distributions for (int i = 0; i < countof(comp_hist->cmd_bins); ++i) {
1323*bbb1b6f9SApple OSS Distributions T_LOG("| %x, %llu", i, comp_hist->cmd_bins[i]);
1324*bbb1b6f9SApple OSS Distributions }
1325*bbb1b6f9SApple OSS Distributions T_LOG("Total: %llu cmds", comp_hist->cmd_total);
1326*bbb1b6f9SApple OSS Distributions T_LOG("Compressed size histogram:");
1327*bbb1b6f9SApple OSS Distributions T_LOG("| sv, %llu", comp_hist->same_value_count);
1328*bbb1b6f9SApple OSS Distributions for (int i = 0; i < countof(comp_hist->comp_size_bins); ++i) {
1329*bbb1b6f9SApple OSS Distributions T_LOG("| %d, %llu", (i + 1) * C_SEG_OFFSET_ALIGNMENT_BOUNDARY, comp_hist->comp_size_bins[i]);
1330*bbb1b6f9SApple OSS Distributions }
1331*bbb1b6f9SApple OSS Distributions }
1332*bbb1b6f9SApple OSS Distributions
1333*bbb1b6f9SApple OSS Distributions #define C_STATE_COUNT 11
1334*bbb1b6f9SApple OSS Distributions
1335*bbb1b6f9SApple OSS Distributions struct cseg_histogram {
1336*bbb1b6f9SApple OSS Distributions uint64_t csegs_per_state[C_STATE_COUNT + 1];
1337*bbb1b6f9SApple OSS Distributions };
1338*bbb1b6f9SApple OSS Distributions
1339*bbb1b6f9SApple OSS Distributions static void
analyse_rle_runs(const char * load_from_file,const char * dump_to_file,bool show_lens,bool show_recompress,bool show_cseg_state)1340*bbb1b6f9SApple OSS Distributions analyse_rle_runs(const char* load_from_file, const char* dump_to_file, bool show_lens, bool show_recompress, bool show_cseg_state)
1341*bbb1b6f9SApple OSS Distributions {
1342*bbb1b6f9SApple OSS Distributions struct comp_histogram comp_hist = {}, *comp_hist_ptr = &comp_hist;
1343*bbb1b6f9SApple OSS Distributions struct runs_histogram run_hist = {}, *run_hist_ptr = &run_hist;
1344*bbb1b6f9SApple OSS Distributions struct comp_histogram re_comp_hist = {}, *re_comp_hist_ptr = &re_comp_hist;
1345*bbb1b6f9SApple OSS Distributions struct cseg_histogram cseg_hist = {}, *cseg_hist_ptr = &cseg_hist;
1346*bbb1b6f9SApple OSS Distributions get_mte_compressed_tags(
1347*bbb1b6f9SApple OSS Distributions ^void (uint32_t cseg_state) {
1348*bbb1b6f9SApple OSS Distributions cseg_hist_ptr->csegs_per_state[MIN(cseg_state, C_STATE_COUNT)]++;
1349*bbb1b6f9SApple OSS Distributions },
1350*bbb1b6f9SApple OSS Distributions ^void (int slot_idx, uint8_t *compressed_buf, uint32_t compressed_size, uint32_t actual_size) {
1351*bbb1b6f9SApple OSS Distributions T_LOG_VERBOSE(" got compressed %d: %u(%x) bytes actual=%d", slot_idx, compressed_size, compressed_size, actual_size);
1352*bbb1b6f9SApple OSS Distributions // first verify that it decompresses to the correct size
1353*bbb1b6f9SApple OSS Distributions uint8_t decompressed[C_MTE_SIZE] = {};
1354*bbb1b6f9SApple OSS Distributions bool ret = vm_mte_rle_decompress_tags(compressed_buf, compressed_size, (uint8_t*)decompressed, C_MTE_SIZE);
1355*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(ret, "decompress failed");
1356*bbb1b6f9SApple OSS Distributions
1357*bbb1b6f9SApple OSS Distributions ret = vm_mte_rle_comp_histogram(compressed_buf, compressed_size, comp_hist_ptr);
1358*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(ret, "vm_mte_rle_cmd_histogram");
1359*bbb1b6f9SApple OSS Distributions vm_mte_rle_runs_histogram(decompressed, C_MTE_SIZE, run_hist_ptr);
1360*bbb1b6f9SApple OSS Distributions
1361*bbb1b6f9SApple OSS Distributions uint8_t re_compressed[C_MTE_SIZE] = {};
1362*bbb1b6f9SApple OSS Distributions uint32_t re_compress_sz = vm_mte_rle_compress_tags(decompressed, C_MTE_SIZE, re_compressed, C_MTE_SIZE);
1363*bbb1b6f9SApple OSS Distributions ret = vm_mte_rle_comp_histogram(re_compressed, re_compress_sz, re_comp_hist_ptr);
1364*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_TRUE(ret, "re-vm_mte_rle_cmd_histogram");
1365*bbb1b6f9SApple OSS Distributions }, load_from_file, dump_to_file);
1366*bbb1b6f9SApple OSS Distributions
1367*bbb1b6f9SApple OSS Distributions print_comp_hist(&comp_hist);
1368*bbb1b6f9SApple OSS Distributions
1369*bbb1b6f9SApple OSS Distributions if (show_lens) {
1370*bbb1b6f9SApple OSS Distributions T_LOG("RLE run lengths histogram:");
1371*bbb1b6f9SApple OSS Distributions for (int i = 0; i < countof(run_hist.rh_bins); ++i) {
1372*bbb1b6f9SApple OSS Distributions T_LOG("| %d, %llu", i, run_hist.rh_bins[i]);
1373*bbb1b6f9SApple OSS Distributions }
1374*bbb1b6f9SApple OSS Distributions }
1375*bbb1b6f9SApple OSS Distributions if (show_recompress) {
1376*bbb1b6f9SApple OSS Distributions T_LOG("*** recompressed ***");
1377*bbb1b6f9SApple OSS Distributions print_comp_hist(&re_comp_hist);
1378*bbb1b6f9SApple OSS Distributions }
1379*bbb1b6f9SApple OSS Distributions if (show_cseg_state) {
1380*bbb1b6f9SApple OSS Distributions T_LOG("cseg-state histogram:");
1381*bbb1b6f9SApple OSS Distributions for (int i = 0; i < C_STATE_COUNT + 1; ++i) {
1382*bbb1b6f9SApple OSS Distributions T_LOG("| %d, %llu", i, cseg_hist.csegs_per_state[i]);
1383*bbb1b6f9SApple OSS Distributions }
1384*bbb1b6f9SApple OSS Distributions }
1385*bbb1b6f9SApple OSS Distributions }
1386*bbb1b6f9SApple OSS Distributions
1387*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_analyze_rle,
1388*bbb1b6f9SApple OSS Distributions "Exercise the MTE tags compress, then read, verify and print the RLE commands stats and the runs stats",
1389*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1390*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1391*bbb1b6f9SApple OSS Distributions {
1392*bbb1b6f9SApple OSS Distributions const char *dump_to_file = NULL, *load_from_file = NULL;
1393*bbb1b6f9SApple OSS Distributions bool show_lens = false, show_recompress = false, show_state = false;
1394*bbb1b6f9SApple OSS Distributions for (int i = 0; i < argc; ++i) {
1395*bbb1b6f9SApple OSS Distributions if (strcmp(argv[i], "--in") == 0) {
1396*bbb1b6f9SApple OSS Distributions load_from_file = argv[++i];
1397*bbb1b6f9SApple OSS Distributions T_LOG("Loading data from `%s`", load_from_file);
1398*bbb1b6f9SApple OSS Distributions } else if (strcmp(argv[i], "--out") == 0) {
1399*bbb1b6f9SApple OSS Distributions dump_to_file = argv[++i];
1400*bbb1b6f9SApple OSS Distributions T_LOG("Dumping data to `%s`", dump_to_file);
1401*bbb1b6f9SApple OSS Distributions } else if (strcmp(argv[i], "--show-lens") == 0) {
1402*bbb1b6f9SApple OSS Distributions show_lens = true;
1403*bbb1b6f9SApple OSS Distributions } else if (strcmp(argv[i], "--recompress") == 0) {
1404*bbb1b6f9SApple OSS Distributions // this option allows testing new changes in the compression algorithm compared to
1405*bbb1b6f9SApple OSS Distributions // what's loaded from the input file/sysctl
1406*bbb1b6f9SApple OSS Distributions show_recompress = true;
1407*bbb1b6f9SApple OSS Distributions } else if (strcmp(argv[i], "--state") == 0) {
1408*bbb1b6f9SApple OSS Distributions // useful for stats on how many segments are in the swap
1409*bbb1b6f9SApple OSS Distributions show_state = true;
1410*bbb1b6f9SApple OSS Distributions }
1411*bbb1b6f9SApple OSS Distributions }
1412*bbb1b6f9SApple OSS Distributions analyse_rle_runs(load_from_file, dump_to_file, show_lens, show_recompress, show_state);
1413*bbb1b6f9SApple OSS Distributions if (load_from_file) {
1414*bbb1b6f9SApple OSS Distributions return; // don't want to print irrelevant stats when processing data from file
1415*bbb1b6f9SApple OSS Distributions }
1416*bbb1b6f9SApple OSS Distributions print_stats();
1417*bbb1b6f9SApple OSS Distributions }
1418*bbb1b6f9SApple OSS Distributions
1419*bbb1b6f9SApple OSS Distributions T_DECL(mte_compressor_exercise_analyze_rle,
1420*bbb1b6f9SApple OSS Distributions "Exercise the MTE tags compress, then read, verify and print the RLE commands stats and the runs stats",
1421*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
1422*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
1423*bbb1b6f9SApple OSS Distributions {
1424*bbb1b6f9SApple OSS Distributions size_t bufsize = 100 * PAGE_SIZE;
1425*bbb1b6f9SApple OSS Distributions vm_address_t address = make_rand_tagged_buf(bufsize);
1426*bbb1b6f9SApple OSS Distributions page_out(address, bufsize);
1427*bbb1b6f9SApple OSS Distributions usleep(20000); // wait for the compressor to finish
1428*bbb1b6f9SApple OSS Distributions analyse_rle_runs(NULL, NULL, false, false, false);
1429*bbb1b6f9SApple OSS Distributions print_stats();
1430*bbb1b6f9SApple OSS Distributions dealloc(address, bufsize);
1431*bbb1b6f9SApple OSS Distributions }
1432