xref: /linux/tools/testing/selftests/liveupdate/lib/lu_utils.c (revision 7b24dd46a70e94d8db83b50a04d46690fac216d0)
1*df1a0d06SVipin Sharma // SPDX-License-Identifier: GPL-2.0-only
2*df1a0d06SVipin Sharma 
3*df1a0d06SVipin Sharma /*
4*df1a0d06SVipin Sharma  * Copyright (c) 2025, Google LLC.
5*df1a0d06SVipin Sharma  * Pasha Tatashin <pasha.tatashin@soleen.com>
6*df1a0d06SVipin Sharma  */
7*df1a0d06SVipin Sharma 
8*df1a0d06SVipin Sharma #define _GNU_SOURCE
9*df1a0d06SVipin Sharma 
10*df1a0d06SVipin Sharma #include <stdio.h>
11*df1a0d06SVipin Sharma #include <stdlib.h>
12*df1a0d06SVipin Sharma #include <string.h>
13*df1a0d06SVipin Sharma #include <getopt.h>
14*df1a0d06SVipin Sharma #include <fcntl.h>
15*df1a0d06SVipin Sharma #include <unistd.h>
16*df1a0d06SVipin Sharma #include <sys/ioctl.h>
17*df1a0d06SVipin Sharma #include <sys/syscall.h>
18*df1a0d06SVipin Sharma #include <sys/mman.h>
19*df1a0d06SVipin Sharma #include <sys/types.h>
20*df1a0d06SVipin Sharma #include <sys/resource.h>
21*df1a0d06SVipin Sharma #include <sys/stat.h>
22*df1a0d06SVipin Sharma #include <errno.h>
23*df1a0d06SVipin Sharma #include <stdarg.h>
24*df1a0d06SVipin Sharma #include <linux/unistd.h>
25*df1a0d06SVipin Sharma 
26*df1a0d06SVipin Sharma #include <libliveupdate.h>
27*df1a0d06SVipin Sharma 
28*df1a0d06SVipin Sharma int luo_open_device(void)
29*df1a0d06SVipin Sharma {
30*df1a0d06SVipin Sharma 	return open(LUO_DEVICE, O_RDWR);
31*df1a0d06SVipin Sharma }
32*df1a0d06SVipin Sharma 
33*df1a0d06SVipin Sharma int luo_ensure_nofile_limit(long min_limit)
34*df1a0d06SVipin Sharma {
35*df1a0d06SVipin Sharma 	struct rlimit hl;
36*df1a0d06SVipin Sharma 
37*df1a0d06SVipin Sharma 	/* Allow to extra files to be used by test itself */
38*df1a0d06SVipin Sharma 	min_limit += 32;
39*df1a0d06SVipin Sharma 
40*df1a0d06SVipin Sharma 	if (getrlimit(RLIMIT_NOFILE, &hl) < 0)
41*df1a0d06SVipin Sharma 		return -errno;
42*df1a0d06SVipin Sharma 
43*df1a0d06SVipin Sharma 	if (hl.rlim_cur >= min_limit)
44*df1a0d06SVipin Sharma 		return 0;
45*df1a0d06SVipin Sharma 
46*df1a0d06SVipin Sharma 	hl.rlim_cur = min_limit;
47*df1a0d06SVipin Sharma 	if (hl.rlim_cur > hl.rlim_max)
48*df1a0d06SVipin Sharma 		hl.rlim_max = hl.rlim_cur;
49*df1a0d06SVipin Sharma 
50*df1a0d06SVipin Sharma 	if (setrlimit(RLIMIT_NOFILE, &hl) < 0)
51*df1a0d06SVipin Sharma 		return -errno;
52*df1a0d06SVipin Sharma 
53*df1a0d06SVipin Sharma 	return 0;
54*df1a0d06SVipin Sharma }
55*df1a0d06SVipin Sharma 
56*df1a0d06SVipin Sharma int luo_create_session(int luo_fd, const char *name)
57*df1a0d06SVipin Sharma {
58*df1a0d06SVipin Sharma 	struct liveupdate_ioctl_create_session arg = { .size = sizeof(arg) };
59*df1a0d06SVipin Sharma 
60*df1a0d06SVipin Sharma 	snprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, "%.*s",
61*df1a0d06SVipin Sharma 		 LIVEUPDATE_SESSION_NAME_LENGTH - 1, name);
62*df1a0d06SVipin Sharma 
63*df1a0d06SVipin Sharma 	if (ioctl(luo_fd, LIVEUPDATE_IOCTL_CREATE_SESSION, &arg))
64*df1a0d06SVipin Sharma 		return -errno;
65*df1a0d06SVipin Sharma 
66*df1a0d06SVipin Sharma 	return arg.fd;
67*df1a0d06SVipin Sharma }
68*df1a0d06SVipin Sharma 
69*df1a0d06SVipin Sharma int luo_retrieve_session(int luo_fd, const char *name)
70*df1a0d06SVipin Sharma {
71*df1a0d06SVipin Sharma 	struct liveupdate_ioctl_retrieve_session arg = { .size = sizeof(arg) };
72*df1a0d06SVipin Sharma 
73*df1a0d06SVipin Sharma 	snprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, "%.*s",
74*df1a0d06SVipin Sharma 		 LIVEUPDATE_SESSION_NAME_LENGTH - 1, name);
75*df1a0d06SVipin Sharma 
76*df1a0d06SVipin Sharma 	if (ioctl(luo_fd, LIVEUPDATE_IOCTL_RETRIEVE_SESSION, &arg))
77*df1a0d06SVipin Sharma 		return -errno;
78*df1a0d06SVipin Sharma 
79*df1a0d06SVipin Sharma 	return arg.fd;
80*df1a0d06SVipin Sharma }
81*df1a0d06SVipin Sharma 
82*df1a0d06SVipin Sharma int luo_session_preserve_fd(int session_fd, int fd, __u64 token)
83*df1a0d06SVipin Sharma {
84*df1a0d06SVipin Sharma 	struct liveupdate_session_preserve_fd arg = {
85*df1a0d06SVipin Sharma 		.size = sizeof(arg),
86*df1a0d06SVipin Sharma 		.fd = fd,
87*df1a0d06SVipin Sharma 		.token = token,
88*df1a0d06SVipin Sharma 	};
89*df1a0d06SVipin Sharma 
90*df1a0d06SVipin Sharma 	if (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &arg))
91*df1a0d06SVipin Sharma 		return -errno;
92*df1a0d06SVipin Sharma 
93*df1a0d06SVipin Sharma 	return 0;
94*df1a0d06SVipin Sharma }
95*df1a0d06SVipin Sharma 
96*df1a0d06SVipin Sharma int luo_session_retrieve_fd(int session_fd, __u64 token)
97*df1a0d06SVipin Sharma {
98*df1a0d06SVipin Sharma 	struct liveupdate_session_retrieve_fd arg = {
99*df1a0d06SVipin Sharma 		.size = sizeof(arg),
100*df1a0d06SVipin Sharma 		.token = token,
101*df1a0d06SVipin Sharma 	};
102*df1a0d06SVipin Sharma 
103*df1a0d06SVipin Sharma 	if (ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_FD, &arg))
104*df1a0d06SVipin Sharma 		return -errno;
105*df1a0d06SVipin Sharma 
106*df1a0d06SVipin Sharma 	return arg.fd;
107*df1a0d06SVipin Sharma }
108*df1a0d06SVipin Sharma 
109*df1a0d06SVipin Sharma /* Helper function to get a session name via ioctl. */
110*df1a0d06SVipin Sharma int luo_get_session_name(int session_fd, char *name, size_t name_len)
111*df1a0d06SVipin Sharma {
112*df1a0d06SVipin Sharma 	struct liveupdate_session_get_name args = {};
113*df1a0d06SVipin Sharma 
114*df1a0d06SVipin Sharma 	args.size = sizeof(args);
115*df1a0d06SVipin Sharma 
116*df1a0d06SVipin Sharma 	if (ioctl(session_fd, LIVEUPDATE_SESSION_GET_NAME, &args))
117*df1a0d06SVipin Sharma 		return -errno;
118*df1a0d06SVipin Sharma 
119*df1a0d06SVipin Sharma 	strncpy(name, (char *)args.name, name_len - 1);
120*df1a0d06SVipin Sharma 	name[name_len - 1] = '\0';
121*df1a0d06SVipin Sharma 
122*df1a0d06SVipin Sharma 	return 0;
123*df1a0d06SVipin Sharma }
124*df1a0d06SVipin Sharma 
125*df1a0d06SVipin Sharma int create_and_preserve_memfd(int session_fd, int token, const char *data)
126*df1a0d06SVipin Sharma {
127*df1a0d06SVipin Sharma 	long page_size = getpagesize();
128*df1a0d06SVipin Sharma 	void *map = MAP_FAILED;
129*df1a0d06SVipin Sharma 	int mfd = -1, ret = -1;
130*df1a0d06SVipin Sharma 
131*df1a0d06SVipin Sharma 	mfd = memfd_create("test_mfd", 0);
132*df1a0d06SVipin Sharma 	if (mfd < 0)
133*df1a0d06SVipin Sharma 		return -errno;
134*df1a0d06SVipin Sharma 
135*df1a0d06SVipin Sharma 	if (ftruncate(mfd, page_size) != 0)
136*df1a0d06SVipin Sharma 		goto out;
137*df1a0d06SVipin Sharma 
138*df1a0d06SVipin Sharma 	map = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, mfd, 0);
139*df1a0d06SVipin Sharma 	if (map == MAP_FAILED)
140*df1a0d06SVipin Sharma 		goto out;
141*df1a0d06SVipin Sharma 
142*df1a0d06SVipin Sharma 	snprintf(map, page_size, "%s", data);
143*df1a0d06SVipin Sharma 	munmap(map, page_size);
144*df1a0d06SVipin Sharma 
145*df1a0d06SVipin Sharma 	ret = luo_session_preserve_fd(session_fd, mfd, token);
146*df1a0d06SVipin Sharma 	if (ret)
147*df1a0d06SVipin Sharma 		goto out;
148*df1a0d06SVipin Sharma 
149*df1a0d06SVipin Sharma 	ret = 0;
150*df1a0d06SVipin Sharma out:
151*df1a0d06SVipin Sharma 	if (ret != 0 && errno != 0)
152*df1a0d06SVipin Sharma 		ret = -errno;
153*df1a0d06SVipin Sharma 	if (mfd >= 0)
154*df1a0d06SVipin Sharma 		close(mfd);
155*df1a0d06SVipin Sharma 	return ret;
156*df1a0d06SVipin Sharma }
157*df1a0d06SVipin Sharma 
158*df1a0d06SVipin Sharma int restore_and_verify_memfd(int session_fd, int token,
159*df1a0d06SVipin Sharma 			     const char *expected_data)
160*df1a0d06SVipin Sharma {
161*df1a0d06SVipin Sharma 	long page_size = getpagesize();
162*df1a0d06SVipin Sharma 	void *map = MAP_FAILED;
163*df1a0d06SVipin Sharma 	int mfd = -1, ret = -1;
164*df1a0d06SVipin Sharma 
165*df1a0d06SVipin Sharma 	mfd = luo_session_retrieve_fd(session_fd, token);
166*df1a0d06SVipin Sharma 	if (mfd < 0)
167*df1a0d06SVipin Sharma 		return mfd;
168*df1a0d06SVipin Sharma 
169*df1a0d06SVipin Sharma 	map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, mfd, 0);
170*df1a0d06SVipin Sharma 	if (map == MAP_FAILED)
171*df1a0d06SVipin Sharma 		goto out;
172*df1a0d06SVipin Sharma 
173*df1a0d06SVipin Sharma 	if (expected_data && strcmp(expected_data, map) != 0) {
174*df1a0d06SVipin Sharma 		ksft_print_msg("Data mismatch! Expected '%s', Got '%s'\n",
175*df1a0d06SVipin Sharma 			       expected_data, (char *)map);
176*df1a0d06SVipin Sharma 		ret = -EINVAL;
177*df1a0d06SVipin Sharma 		goto out_munmap;
178*df1a0d06SVipin Sharma 	}
179*df1a0d06SVipin Sharma 
180*df1a0d06SVipin Sharma 	ret = mfd;
181*df1a0d06SVipin Sharma out_munmap:
182*df1a0d06SVipin Sharma 	munmap(map, page_size);
183*df1a0d06SVipin Sharma out:
184*df1a0d06SVipin Sharma 	if (ret < 0 && errno != 0)
185*df1a0d06SVipin Sharma 		ret = -errno;
186*df1a0d06SVipin Sharma 	if (ret < 0 && mfd >= 0)
187*df1a0d06SVipin Sharma 		close(mfd);
188*df1a0d06SVipin Sharma 	return ret;
189*df1a0d06SVipin Sharma }
190*df1a0d06SVipin Sharma 
191*df1a0d06SVipin Sharma int luo_session_finish(int session_fd)
192*df1a0d06SVipin Sharma {
193*df1a0d06SVipin Sharma 	struct liveupdate_session_finish arg = { .size = sizeof(arg) };
194*df1a0d06SVipin Sharma 
195*df1a0d06SVipin Sharma 	if (ioctl(session_fd, LIVEUPDATE_SESSION_FINISH, &arg) < 0)
196*df1a0d06SVipin Sharma 		return -errno;
197*df1a0d06SVipin Sharma 
198*df1a0d06SVipin Sharma 	return 0;
199*df1a0d06SVipin Sharma }
200*df1a0d06SVipin Sharma 
201*df1a0d06SVipin Sharma void create_state_file(int luo_fd, const char *session_name, int token,
202*df1a0d06SVipin Sharma 		       int next_stage)
203*df1a0d06SVipin Sharma {
204*df1a0d06SVipin Sharma 	char buf[32];
205*df1a0d06SVipin Sharma 	int state_session_fd;
206*df1a0d06SVipin Sharma 
207*df1a0d06SVipin Sharma 	state_session_fd = luo_create_session(luo_fd, session_name);
208*df1a0d06SVipin Sharma 	if (state_session_fd < 0)
209*df1a0d06SVipin Sharma 		fail_exit("luo_create_session for state tracking");
210*df1a0d06SVipin Sharma 
211*df1a0d06SVipin Sharma 	snprintf(buf, sizeof(buf), "%d", next_stage);
212*df1a0d06SVipin Sharma 	if (create_and_preserve_memfd(state_session_fd, token, buf) < 0)
213*df1a0d06SVipin Sharma 		fail_exit("create_and_preserve_memfd for state tracking");
214*df1a0d06SVipin Sharma 
215*df1a0d06SVipin Sharma 	/*
216*df1a0d06SVipin Sharma 	 * DO NOT close session FD, otherwise it is going to be unpreserved
217*df1a0d06SVipin Sharma 	 */
218*df1a0d06SVipin Sharma }
219*df1a0d06SVipin Sharma 
220*df1a0d06SVipin Sharma void restore_and_read_stage(int state_session_fd, int token, int *stage)
221*df1a0d06SVipin Sharma {
222*df1a0d06SVipin Sharma 	char buf[32] = {0};
223*df1a0d06SVipin Sharma 	int mfd;
224*df1a0d06SVipin Sharma 
225*df1a0d06SVipin Sharma 	mfd = restore_and_verify_memfd(state_session_fd, token, NULL);
226*df1a0d06SVipin Sharma 	if (mfd < 0)
227*df1a0d06SVipin Sharma 		fail_exit("failed to restore state memfd");
228*df1a0d06SVipin Sharma 
229*df1a0d06SVipin Sharma 	if (read(mfd, buf, sizeof(buf) - 1) < 0)
230*df1a0d06SVipin Sharma 		fail_exit("failed to read state mfd");
231*df1a0d06SVipin Sharma 
232*df1a0d06SVipin Sharma 	*stage = atoi(buf);
233*df1a0d06SVipin Sharma 
234*df1a0d06SVipin Sharma 	close(mfd);
235*df1a0d06SVipin Sharma }
236*df1a0d06SVipin Sharma 
237*df1a0d06SVipin Sharma void daemonize_and_wait(void)
238*df1a0d06SVipin Sharma {
239*df1a0d06SVipin Sharma 	pid_t pid;
240*df1a0d06SVipin Sharma 
241*df1a0d06SVipin Sharma 	ksft_print_msg("[STAGE 1] Forking persistent child to hold sessions...\n");
242*df1a0d06SVipin Sharma 
243*df1a0d06SVipin Sharma 	pid = fork();
244*df1a0d06SVipin Sharma 	if (pid < 0)
245*df1a0d06SVipin Sharma 		fail_exit("fork failed");
246*df1a0d06SVipin Sharma 
247*df1a0d06SVipin Sharma 	if (pid > 0) {
248*df1a0d06SVipin Sharma 		ksft_print_msg("[STAGE 1] Child PID: %d. Resources are pinned.\n", pid);
249*df1a0d06SVipin Sharma 		ksft_print_msg("[STAGE 1] You may now perform kexec reboot.\n");
250*df1a0d06SVipin Sharma 		exit(EXIT_SUCCESS);
251*df1a0d06SVipin Sharma 	}
252*df1a0d06SVipin Sharma 
253*df1a0d06SVipin Sharma 	/* Detach from terminal so closing the window doesn't kill us */
254*df1a0d06SVipin Sharma 	if (setsid() < 0)
255*df1a0d06SVipin Sharma 		fail_exit("setsid failed");
256*df1a0d06SVipin Sharma 
257*df1a0d06SVipin Sharma 	close(STDIN_FILENO);
258*df1a0d06SVipin Sharma 	close(STDOUT_FILENO);
259*df1a0d06SVipin Sharma 	close(STDERR_FILENO);
260*df1a0d06SVipin Sharma 
261*df1a0d06SVipin Sharma 	/* Change dir to root to avoid locking filesystems */
262*df1a0d06SVipin Sharma 	if (chdir("/") < 0)
263*df1a0d06SVipin Sharma 		exit(EXIT_FAILURE);
264*df1a0d06SVipin Sharma 
265*df1a0d06SVipin Sharma 	while (1)
266*df1a0d06SVipin Sharma 		sleep(60);
267*df1a0d06SVipin Sharma }
268*df1a0d06SVipin Sharma 
269*df1a0d06SVipin Sharma static int parse_stage_args(int argc, char *argv[])
270*df1a0d06SVipin Sharma {
271*df1a0d06SVipin Sharma 	int stage = 1;
272*df1a0d06SVipin Sharma 	int opt;
273*df1a0d06SVipin Sharma 
274*df1a0d06SVipin Sharma 	optind = 1;
275*df1a0d06SVipin Sharma 	while ((opt = getopt(argc, argv, "s:")) != -1) {
276*df1a0d06SVipin Sharma 		switch (opt) {
277*df1a0d06SVipin Sharma 		case 's':
278*df1a0d06SVipin Sharma 			stage = atoi(optarg);
279*df1a0d06SVipin Sharma 			if (stage != 1 && stage != 2)
280*df1a0d06SVipin Sharma 				fail_exit("Invalid stage argument");
281*df1a0d06SVipin Sharma 			break;
282*df1a0d06SVipin Sharma 		default:
283*df1a0d06SVipin Sharma 			fail_exit("Unknown argument");
284*df1a0d06SVipin Sharma 		}
285*df1a0d06SVipin Sharma 	}
286*df1a0d06SVipin Sharma 
287*df1a0d06SVipin Sharma 	return stage;
288*df1a0d06SVipin Sharma }
289*df1a0d06SVipin Sharma 
290*df1a0d06SVipin Sharma int luo_test(int argc, char *argv[],
291*df1a0d06SVipin Sharma 	     const char *state_session_name,
292*df1a0d06SVipin Sharma 	     luo_test_stage1_fn stage1,
293*df1a0d06SVipin Sharma 	     luo_test_stage2_fn stage2)
294*df1a0d06SVipin Sharma {
295*df1a0d06SVipin Sharma 	int target_stage = parse_stage_args(argc, argv);
296*df1a0d06SVipin Sharma 	int luo_fd = luo_open_device();
297*df1a0d06SVipin Sharma 	int state_session_fd;
298*df1a0d06SVipin Sharma 	int detected_stage;
299*df1a0d06SVipin Sharma 
300*df1a0d06SVipin Sharma 	if (luo_fd < 0) {
301*df1a0d06SVipin Sharma 		ksft_exit_skip("Failed to open %s. Is the luo module loaded?\n",
302*df1a0d06SVipin Sharma 			       LUO_DEVICE);
303*df1a0d06SVipin Sharma 	}
304*df1a0d06SVipin Sharma 
305*df1a0d06SVipin Sharma 	state_session_fd = luo_retrieve_session(luo_fd, state_session_name);
306*df1a0d06SVipin Sharma 	if (state_session_fd == -ENOENT)
307*df1a0d06SVipin Sharma 		detected_stage = 1;
308*df1a0d06SVipin Sharma 	else if (state_session_fd >= 0)
309*df1a0d06SVipin Sharma 		detected_stage = 2;
310*df1a0d06SVipin Sharma 	else
311*df1a0d06SVipin Sharma 		fail_exit("Failed to check for state session");
312*df1a0d06SVipin Sharma 
313*df1a0d06SVipin Sharma 	if (target_stage != detected_stage) {
314*df1a0d06SVipin Sharma 		ksft_exit_fail_msg("Stage mismatch Requested stage %d, but system is in stage %d.\n"
315*df1a0d06SVipin Sharma 				   "(State session %s: %s)\n",
316*df1a0d06SVipin Sharma 				   target_stage, detected_stage, state_session_name,
317*df1a0d06SVipin Sharma 				   (detected_stage == 2) ? "EXISTS" : "MISSING");
318*df1a0d06SVipin Sharma 	}
319*df1a0d06SVipin Sharma 
320*df1a0d06SVipin Sharma 	if (target_stage == 1)
321*df1a0d06SVipin Sharma 		stage1(luo_fd);
322*df1a0d06SVipin Sharma 	else
323*df1a0d06SVipin Sharma 		stage2(luo_fd, state_session_fd);
324*df1a0d06SVipin Sharma 
325*df1a0d06SVipin Sharma 	return 0;
326*df1a0d06SVipin Sharma }
327