1*4e1aa1a1SMark Brown // SPDX-License-Identifier: GPL-2.0-only
2*4e1aa1a1SMark Brown /*
3*4e1aa1a1SMark Brown  * Copyright (C) 2021 ARM Limited.
4*4e1aa1a1SMark Brown  */
5*4e1aa1a1SMark Brown #include <errno.h>
6*4e1aa1a1SMark Brown #include <stdbool.h>
7*4e1aa1a1SMark Brown #include <stddef.h>
8*4e1aa1a1SMark Brown #include <stdio.h>
9*4e1aa1a1SMark Brown #include <stdlib.h>
10*4e1aa1a1SMark Brown #include <string.h>
11*4e1aa1a1SMark Brown #include <unistd.h>
12*4e1aa1a1SMark Brown #include <sys/auxv.h>
13*4e1aa1a1SMark Brown #include <sys/prctl.h>
14*4e1aa1a1SMark Brown #include <sys/ptrace.h>
15*4e1aa1a1SMark Brown #include <sys/types.h>
16*4e1aa1a1SMark Brown #include <sys/uio.h>
17*4e1aa1a1SMark Brown #include <sys/wait.h>
18*4e1aa1a1SMark Brown #include <asm/sigcontext.h>
19*4e1aa1a1SMark Brown #include <asm/ptrace.h>
20*4e1aa1a1SMark Brown 
21*4e1aa1a1SMark Brown #include "../../kselftest.h"
22*4e1aa1a1SMark Brown 
23*4e1aa1a1SMark Brown /* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
24*4e1aa1a1SMark Brown #ifndef NT_ARM_ZA
25*4e1aa1a1SMark Brown #define NT_ARM_ZA 0x40c
26*4e1aa1a1SMark Brown #endif
27*4e1aa1a1SMark Brown #ifndef NT_ARM_ZT
28*4e1aa1a1SMark Brown #define NT_ARM_ZT 0x40d
29*4e1aa1a1SMark Brown #endif
30*4e1aa1a1SMark Brown 
31*4e1aa1a1SMark Brown #define EXPECTED_TESTS 3
32*4e1aa1a1SMark Brown 
33*4e1aa1a1SMark Brown static int sme_vl;
34*4e1aa1a1SMark Brown 
fill_buf(char * buf,size_t size)35*4e1aa1a1SMark Brown static void fill_buf(char *buf, size_t size)
36*4e1aa1a1SMark Brown {
37*4e1aa1a1SMark Brown 	int i;
38*4e1aa1a1SMark Brown 
39*4e1aa1a1SMark Brown 	for (i = 0; i < size; i++)
40*4e1aa1a1SMark Brown 		buf[i] = random();
41*4e1aa1a1SMark Brown }
42*4e1aa1a1SMark Brown 
do_child(void)43*4e1aa1a1SMark Brown static int do_child(void)
44*4e1aa1a1SMark Brown {
45*4e1aa1a1SMark Brown 	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
46*4e1aa1a1SMark Brown 		ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
47*4e1aa1a1SMark Brown 
48*4e1aa1a1SMark Brown 	if (raise(SIGSTOP))
49*4e1aa1a1SMark Brown 		ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
50*4e1aa1a1SMark Brown 
51*4e1aa1a1SMark Brown 	return EXIT_SUCCESS;
52*4e1aa1a1SMark Brown }
53*4e1aa1a1SMark Brown 
get_za(pid_t pid,void ** buf,size_t * size)54*4e1aa1a1SMark Brown static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)
55*4e1aa1a1SMark Brown {
56*4e1aa1a1SMark Brown 	struct user_za_header *za;
57*4e1aa1a1SMark Brown 	void *p;
58*4e1aa1a1SMark Brown 	size_t sz = sizeof(*za);
59*4e1aa1a1SMark Brown 	struct iovec iov;
60*4e1aa1a1SMark Brown 
61*4e1aa1a1SMark Brown 	while (1) {
62*4e1aa1a1SMark Brown 		if (*size < sz) {
63*4e1aa1a1SMark Brown 			p = realloc(*buf, sz);
64*4e1aa1a1SMark Brown 			if (!p) {
65*4e1aa1a1SMark Brown 				errno = ENOMEM;
66*4e1aa1a1SMark Brown 				goto error;
67*4e1aa1a1SMark Brown 			}
68*4e1aa1a1SMark Brown 
69*4e1aa1a1SMark Brown 			*buf = p;
70*4e1aa1a1SMark Brown 			*size = sz;
71*4e1aa1a1SMark Brown 		}
72*4e1aa1a1SMark Brown 
73*4e1aa1a1SMark Brown 		iov.iov_base = *buf;
74*4e1aa1a1SMark Brown 		iov.iov_len = sz;
75*4e1aa1a1SMark Brown 		if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))
76*4e1aa1a1SMark Brown 			goto error;
77*4e1aa1a1SMark Brown 
78*4e1aa1a1SMark Brown 		za = *buf;
79*4e1aa1a1SMark Brown 		if (za->size <= sz)
80*4e1aa1a1SMark Brown 			break;
81*4e1aa1a1SMark Brown 
82*4e1aa1a1SMark Brown 		sz = za->size;
83*4e1aa1a1SMark Brown 	}
84*4e1aa1a1SMark Brown 
85*4e1aa1a1SMark Brown 	return za;
86*4e1aa1a1SMark Brown 
87*4e1aa1a1SMark Brown error:
88*4e1aa1a1SMark Brown 	return NULL;
89*4e1aa1a1SMark Brown }
90*4e1aa1a1SMark Brown 
set_za(pid_t pid,const struct user_za_header * za)91*4e1aa1a1SMark Brown static int set_za(pid_t pid, const struct user_za_header *za)
92*4e1aa1a1SMark Brown {
93*4e1aa1a1SMark Brown 	struct iovec iov;
94*4e1aa1a1SMark Brown 
95*4e1aa1a1SMark Brown 	iov.iov_base = (void *)za;
96*4e1aa1a1SMark Brown 	iov.iov_len = za->size;
97*4e1aa1a1SMark Brown 	return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);
98*4e1aa1a1SMark Brown }
99*4e1aa1a1SMark Brown 
get_zt(pid_t pid,char zt[ZT_SIG_REG_BYTES])100*4e1aa1a1SMark Brown static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])
101*4e1aa1a1SMark Brown {
102*4e1aa1a1SMark Brown 	struct iovec iov;
103*4e1aa1a1SMark Brown 
104*4e1aa1a1SMark Brown 	iov.iov_base = zt;
105*4e1aa1a1SMark Brown 	iov.iov_len = ZT_SIG_REG_BYTES;
106*4e1aa1a1SMark Brown 	return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);
107*4e1aa1a1SMark Brown }
108*4e1aa1a1SMark Brown 
109*4e1aa1a1SMark Brown 
set_zt(pid_t pid,const char zt[ZT_SIG_REG_BYTES])110*4e1aa1a1SMark Brown static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])
111*4e1aa1a1SMark Brown {
112*4e1aa1a1SMark Brown 	struct iovec iov;
113*4e1aa1a1SMark Brown 
114*4e1aa1a1SMark Brown 	iov.iov_base = (void *)zt;
115*4e1aa1a1SMark Brown 	iov.iov_len = ZT_SIG_REG_BYTES;
116*4e1aa1a1SMark Brown 	return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZT, &iov);
117*4e1aa1a1SMark Brown }
118*4e1aa1a1SMark Brown 
119*4e1aa1a1SMark Brown /* Reading with ZA disabled returns all zeros */
ptrace_za_disabled_read_zt(pid_t child)120*4e1aa1a1SMark Brown static void ptrace_za_disabled_read_zt(pid_t child)
121*4e1aa1a1SMark Brown {
122*4e1aa1a1SMark Brown 	struct user_za_header za;
123*4e1aa1a1SMark Brown 	char zt[ZT_SIG_REG_BYTES];
124*4e1aa1a1SMark Brown 	int ret, i;
125*4e1aa1a1SMark Brown 	bool fail = false;
126*4e1aa1a1SMark Brown 
127*4e1aa1a1SMark Brown 	/* Disable PSTATE.ZA using the ZA interface */
128*4e1aa1a1SMark Brown 	memset(&za, 0, sizeof(za));
129*4e1aa1a1SMark Brown 	za.vl = sme_vl;
130*4e1aa1a1SMark Brown 	za.size = sizeof(za);
131*4e1aa1a1SMark Brown 
132*4e1aa1a1SMark Brown 	ret = set_za(child, &za);
133*4e1aa1a1SMark Brown 	if (ret != 0) {
134*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to disable ZA\n");
135*4e1aa1a1SMark Brown 		fail = true;
136*4e1aa1a1SMark Brown 	}
137*4e1aa1a1SMark Brown 
138*4e1aa1a1SMark Brown 	/* Read back ZT */
139*4e1aa1a1SMark Brown 	ret = get_zt(child, zt);
140*4e1aa1a1SMark Brown 	if (ret != 0) {
141*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to read ZT\n");
142*4e1aa1a1SMark Brown 		fail = true;
143*4e1aa1a1SMark Brown 	}
144*4e1aa1a1SMark Brown 
145*4e1aa1a1SMark Brown 	for (i = 0; i < ARRAY_SIZE(zt); i++) {
146*4e1aa1a1SMark Brown 		if (zt[i]) {
147*4e1aa1a1SMark Brown 			ksft_print_msg("zt[%d]: 0x%x != 0\n", i, zt[i]);
148*4e1aa1a1SMark Brown 			fail = true;
149*4e1aa1a1SMark Brown 		}
150*4e1aa1a1SMark Brown 	}
151*4e1aa1a1SMark Brown 
152*4e1aa1a1SMark Brown 	ksft_test_result(!fail, "ptrace_za_disabled_read_zt\n");
153*4e1aa1a1SMark Brown }
154*4e1aa1a1SMark Brown 
155*4e1aa1a1SMark Brown /* Writing then reading ZT should return the data written */
ptrace_set_get_zt(pid_t child)156*4e1aa1a1SMark Brown static void ptrace_set_get_zt(pid_t child)
157*4e1aa1a1SMark Brown {
158*4e1aa1a1SMark Brown 	char zt_in[ZT_SIG_REG_BYTES];
159*4e1aa1a1SMark Brown 	char zt_out[ZT_SIG_REG_BYTES];
160*4e1aa1a1SMark Brown 	int ret, i;
161*4e1aa1a1SMark Brown 	bool fail = false;
162*4e1aa1a1SMark Brown 
163*4e1aa1a1SMark Brown 	fill_buf(zt_in, sizeof(zt_in));
164*4e1aa1a1SMark Brown 
165*4e1aa1a1SMark Brown 	ret = set_zt(child, zt_in);
166*4e1aa1a1SMark Brown 	if (ret != 0) {
167*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to set ZT\n");
168*4e1aa1a1SMark Brown 		fail = true;
169*4e1aa1a1SMark Brown 	}
170*4e1aa1a1SMark Brown 
171*4e1aa1a1SMark Brown 	ret = get_zt(child, zt_out);
172*4e1aa1a1SMark Brown 	if (ret != 0) {
173*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to read ZT\n");
174*4e1aa1a1SMark Brown 		fail = true;
175*4e1aa1a1SMark Brown 	}
176*4e1aa1a1SMark Brown 
177*4e1aa1a1SMark Brown 	for (i = 0; i < ARRAY_SIZE(zt_in); i++) {
178*4e1aa1a1SMark Brown 		if (zt_in[i] != zt_out[i]) {
179*4e1aa1a1SMark Brown 			ksft_print_msg("zt[%d]: 0x%x != 0x%x\n", i,
180*4e1aa1a1SMark Brown 				       zt_in[i], zt_out[i]);
181*4e1aa1a1SMark Brown 			fail = true;
182*4e1aa1a1SMark Brown 		}
183*4e1aa1a1SMark Brown 	}
184*4e1aa1a1SMark Brown 
185*4e1aa1a1SMark Brown 	ksft_test_result(!fail, "ptrace_set_get_zt\n");
186*4e1aa1a1SMark Brown }
187*4e1aa1a1SMark Brown 
188*4e1aa1a1SMark Brown /* Writing ZT should set PSTATE.ZA */
ptrace_enable_za_via_zt(pid_t child)189*4e1aa1a1SMark Brown static void ptrace_enable_za_via_zt(pid_t child)
190*4e1aa1a1SMark Brown {
191*4e1aa1a1SMark Brown 	struct user_za_header za_in;
192*4e1aa1a1SMark Brown 	struct user_za_header *za_out;
193*4e1aa1a1SMark Brown 	char zt[ZT_SIG_REG_BYTES];
194*4e1aa1a1SMark Brown 	char *za_data;
195*4e1aa1a1SMark Brown 	size_t za_out_size;
196*4e1aa1a1SMark Brown 	int ret, i, vq;
197*4e1aa1a1SMark Brown 	bool fail = false;
198*4e1aa1a1SMark Brown 
199*4e1aa1a1SMark Brown 	/* Disable PSTATE.ZA using the ZA interface */
200*4e1aa1a1SMark Brown 	memset(&za_in, 0, sizeof(za_in));
201*4e1aa1a1SMark Brown 	za_in.vl = sme_vl;
202*4e1aa1a1SMark Brown 	za_in.size = sizeof(za_in);
203*4e1aa1a1SMark Brown 
204*4e1aa1a1SMark Brown 	ret = set_za(child, &za_in);
205*4e1aa1a1SMark Brown 	if (ret != 0) {
206*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to disable ZA\n");
207*4e1aa1a1SMark Brown 		fail = true;
208*4e1aa1a1SMark Brown 	}
209*4e1aa1a1SMark Brown 
210*4e1aa1a1SMark Brown 	/* Write ZT */
211*4e1aa1a1SMark Brown 	fill_buf(zt, sizeof(zt));
212*4e1aa1a1SMark Brown 	ret = set_zt(child, zt);
213*4e1aa1a1SMark Brown 	if (ret != 0) {
214*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to set ZT\n");
215*4e1aa1a1SMark Brown 		fail = true;
216*4e1aa1a1SMark Brown 	}
217*4e1aa1a1SMark Brown 
218*4e1aa1a1SMark Brown 	/* Read back ZA and check for register data */
219*4e1aa1a1SMark Brown 	za_out = NULL;
220*4e1aa1a1SMark Brown 	za_out_size = 0;
221*4e1aa1a1SMark Brown 	if (get_za(child, (void **)&za_out, &za_out_size)) {
222*4e1aa1a1SMark Brown 		/* Should have an unchanged VL */
223*4e1aa1a1SMark Brown 		if (za_out->vl != sme_vl) {
224*4e1aa1a1SMark Brown 			ksft_print_msg("VL changed from %d to %d\n",
225*4e1aa1a1SMark Brown 				       sme_vl, za_out->vl);
226*4e1aa1a1SMark Brown 			fail = true;
227*4e1aa1a1SMark Brown 		}
228*4e1aa1a1SMark Brown 		vq = __sve_vq_from_vl(za_out->vl);
229*4e1aa1a1SMark Brown 		za_data = (char *)za_out + ZA_PT_ZA_OFFSET;
230*4e1aa1a1SMark Brown 
231*4e1aa1a1SMark Brown 		/* Should have register data */
232*4e1aa1a1SMark Brown 		if (za_out->size < ZA_PT_SIZE(vq)) {
233*4e1aa1a1SMark Brown 			ksft_print_msg("ZA data less than expected: %u < %u\n",
234*4e1aa1a1SMark Brown 				       za_out->size, ZA_PT_SIZE(vq));
235*4e1aa1a1SMark Brown 			fail = true;
236*4e1aa1a1SMark Brown 			vq = 0;
237*4e1aa1a1SMark Brown 		}
238*4e1aa1a1SMark Brown 
239*4e1aa1a1SMark Brown 		/* That register data should be non-zero */
240*4e1aa1a1SMark Brown 		for (i = 0; i < ZA_PT_ZA_SIZE(vq); i++) {
241*4e1aa1a1SMark Brown 			if (za_data[i]) {
242*4e1aa1a1SMark Brown 				ksft_print_msg("ZA byte %d is %x\n",
243*4e1aa1a1SMark Brown 					       i, za_data[i]);
244*4e1aa1a1SMark Brown 				fail = true;
245*4e1aa1a1SMark Brown 			}
246*4e1aa1a1SMark Brown 		}
247*4e1aa1a1SMark Brown 	} else {
248*4e1aa1a1SMark Brown 		ksft_print_msg("Failed to read ZA\n");
249*4e1aa1a1SMark Brown 		fail = true;
250*4e1aa1a1SMark Brown 	}
251*4e1aa1a1SMark Brown 
252*4e1aa1a1SMark Brown 	ksft_test_result(!fail, "ptrace_enable_za_via_zt\n");
253*4e1aa1a1SMark Brown }
254*4e1aa1a1SMark Brown 
do_parent(pid_t child)255*4e1aa1a1SMark Brown static int do_parent(pid_t child)
256*4e1aa1a1SMark Brown {
257*4e1aa1a1SMark Brown 	int ret = EXIT_FAILURE;
258*4e1aa1a1SMark Brown 	pid_t pid;
259*4e1aa1a1SMark Brown 	int status;
260*4e1aa1a1SMark Brown 	siginfo_t si;
261*4e1aa1a1SMark Brown 
262*4e1aa1a1SMark Brown 	/* Attach to the child */
263*4e1aa1a1SMark Brown 	while (1) {
264*4e1aa1a1SMark Brown 		int sig;
265*4e1aa1a1SMark Brown 
266*4e1aa1a1SMark Brown 		pid = wait(&status);
267*4e1aa1a1SMark Brown 		if (pid == -1) {
268*4e1aa1a1SMark Brown 			perror("wait");
269*4e1aa1a1SMark Brown 			goto error;
270*4e1aa1a1SMark Brown 		}
271*4e1aa1a1SMark Brown 
272*4e1aa1a1SMark Brown 		/*
273*4e1aa1a1SMark Brown 		 * This should never happen but it's hard to flag in
274*4e1aa1a1SMark Brown 		 * the framework.
275*4e1aa1a1SMark Brown 		 */
276*4e1aa1a1SMark Brown 		if (pid != child)
277*4e1aa1a1SMark Brown 			continue;
278*4e1aa1a1SMark Brown 
279*4e1aa1a1SMark Brown 		if (WIFEXITED(status) || WIFSIGNALED(status))
280*4e1aa1a1SMark Brown 			ksft_exit_fail_msg("Child died unexpectedly\n");
281*4e1aa1a1SMark Brown 
282*4e1aa1a1SMark Brown 		if (!WIFSTOPPED(status))
283*4e1aa1a1SMark Brown 			goto error;
284*4e1aa1a1SMark Brown 
285*4e1aa1a1SMark Brown 		sig = WSTOPSIG(status);
286*4e1aa1a1SMark Brown 
287*4e1aa1a1SMark Brown 		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
288*4e1aa1a1SMark Brown 			if (errno == ESRCH)
289*4e1aa1a1SMark Brown 				goto disappeared;
290*4e1aa1a1SMark Brown 
291*4e1aa1a1SMark Brown 			if (errno == EINVAL) {
292*4e1aa1a1SMark Brown 				sig = 0; /* bust group-stop */
293*4e1aa1a1SMark Brown 				goto cont;
294*4e1aa1a1SMark Brown 			}
295*4e1aa1a1SMark Brown 
296*4e1aa1a1SMark Brown 			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
297*4e1aa1a1SMark Brown 					      strerror(errno));
298*4e1aa1a1SMark Brown 			goto error;
299*4e1aa1a1SMark Brown 		}
300*4e1aa1a1SMark Brown 
301*4e1aa1a1SMark Brown 		if (sig == SIGSTOP && si.si_code == SI_TKILL &&
302*4e1aa1a1SMark Brown 		    si.si_pid == pid)
303*4e1aa1a1SMark Brown 			break;
304*4e1aa1a1SMark Brown 
305*4e1aa1a1SMark Brown 	cont:
306*4e1aa1a1SMark Brown 		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
307*4e1aa1a1SMark Brown 			if (errno == ESRCH)
308*4e1aa1a1SMark Brown 				goto disappeared;
309*4e1aa1a1SMark Brown 
310*4e1aa1a1SMark Brown 			ksft_test_result_fail("PTRACE_CONT: %s\n",
311*4e1aa1a1SMark Brown 					      strerror(errno));
312*4e1aa1a1SMark Brown 			goto error;
313*4e1aa1a1SMark Brown 		}
314*4e1aa1a1SMark Brown 	}
315*4e1aa1a1SMark Brown 
316*4e1aa1a1SMark Brown 	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
317*4e1aa1a1SMark Brown 
318*4e1aa1a1SMark Brown 	ptrace_za_disabled_read_zt(child);
319*4e1aa1a1SMark Brown 	ptrace_set_get_zt(child);
320*4e1aa1a1SMark Brown 	ptrace_enable_za_via_zt(child);
321*4e1aa1a1SMark Brown 
322*4e1aa1a1SMark Brown 	ret = EXIT_SUCCESS;
323*4e1aa1a1SMark Brown 
324*4e1aa1a1SMark Brown error:
325*4e1aa1a1SMark Brown 	kill(child, SIGKILL);
326*4e1aa1a1SMark Brown 
327*4e1aa1a1SMark Brown disappeared:
328*4e1aa1a1SMark Brown 	return ret;
329*4e1aa1a1SMark Brown }
330*4e1aa1a1SMark Brown 
main(void)331*4e1aa1a1SMark Brown int main(void)
332*4e1aa1a1SMark Brown {
333*4e1aa1a1SMark Brown 	int ret = EXIT_SUCCESS;
334*4e1aa1a1SMark Brown 	pid_t child;
335*4e1aa1a1SMark Brown 
336*4e1aa1a1SMark Brown 	srandom(getpid());
337*4e1aa1a1SMark Brown 
338*4e1aa1a1SMark Brown 	ksft_print_header();
339*4e1aa1a1SMark Brown 
340*4e1aa1a1SMark Brown 	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2)) {
341*4e1aa1a1SMark Brown 		ksft_set_plan(1);
342*4e1aa1a1SMark Brown 		ksft_exit_skip("SME2 not available\n");
343*4e1aa1a1SMark Brown 	}
344*4e1aa1a1SMark Brown 
345*4e1aa1a1SMark Brown 	/* We need a valid SME VL to enable/disable ZA */
346*4e1aa1a1SMark Brown 	sme_vl = prctl(PR_SME_GET_VL);
347*4e1aa1a1SMark Brown 	if (sme_vl == -1) {
348*4e1aa1a1SMark Brown 		ksft_set_plan(1);
349*4e1aa1a1SMark Brown 		ksft_exit_skip("Failed to read SME VL: %d (%s)\n",
350*4e1aa1a1SMark Brown 			       errno, strerror(errno));
351*4e1aa1a1SMark Brown 	}
352*4e1aa1a1SMark Brown 
353*4e1aa1a1SMark Brown 	ksft_set_plan(EXPECTED_TESTS);
354*4e1aa1a1SMark Brown 
355*4e1aa1a1SMark Brown 	child = fork();
356*4e1aa1a1SMark Brown 	if (!child)
357*4e1aa1a1SMark Brown 		return do_child();
358*4e1aa1a1SMark Brown 
359*4e1aa1a1SMark Brown 	if (do_parent(child))
360*4e1aa1a1SMark Brown 		ret = EXIT_FAILURE;
361*4e1aa1a1SMark Brown 
362*4e1aa1a1SMark Brown 	ksft_print_cnts();
363*4e1aa1a1SMark Brown 
364*4e1aa1a1SMark Brown 	return ret;
365*4e1aa1a1SMark Brown }
366