xref: /linux/tools/testing/selftests/liveupdate/init.c (revision 85cdaca6970028bf6f544c355c90035586836ddf)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * Copyright (c) 2025, Google LLC.
5  * Pasha Tatashin <pasha.tatashin@soleen.com>
6  */
7 #include <fcntl.h>
8 #include <linux/kexec.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/mount.h>
13 #include <sys/reboot.h>
14 #include <sys/syscall.h>
15 #include <sys/wait.h>
16 #include <unistd.h>
17 
18 #define COMMAND_LINE_SIZE 2048
19 #define KERNEL_IMAGE "/kernel"
20 #define INITRD_IMAGE "/initrd.img"
21 #define TEST_BINARY "/test_binary"
22 
23 static int mount_filesystems(void)
24 {
25 	if (mount("devtmpfs", "/dev", "devtmpfs", 0, NULL) < 0) {
26 		fprintf(stderr, "INIT: Warning: Failed to mount devtmpfs\n");
27 		return -1;
28 	}
29 
30 	if (mount("debugfs", "/debugfs", "debugfs", 0, NULL) < 0) {
31 		fprintf(stderr, "INIT: Failed to mount debugfs\n");
32 		return -1;
33 	}
34 
35 	if (mount("proc", "/proc", "proc", 0, NULL) < 0) {
36 		fprintf(stderr, "INIT: Failed to mount proc\n");
37 		return -1;
38 	}
39 
40 	return 0;
41 }
42 
43 static long kexec_file_load(int kernel_fd, int initrd_fd,
44 			    unsigned long cmdline_len, const char *cmdline,
45 			    unsigned long flags)
46 {
47 	return syscall(__NR_kexec_file_load, kernel_fd, initrd_fd, cmdline_len,
48 		       cmdline, flags);
49 }
50 
51 static int kexec_load(void)
52 {
53 	char cmdline[COMMAND_LINE_SIZE];
54 	int kernel_fd, initrd_fd, err;
55 	ssize_t len;
56 	int fd;
57 
58 	fd = open("/proc/cmdline", O_RDONLY);
59 	if (fd < 0) {
60 		fprintf(stderr, "INIT: Failed to read /proc/cmdline\n");
61 
62 		return -1;
63 	}
64 
65 	len = read(fd, cmdline, sizeof(cmdline) - 1);
66 	close(fd);
67 	if (len < 0)
68 		return -1;
69 
70 	cmdline[len] = 0;
71 	if (len > 0 && cmdline[len - 1] == '\n')
72 		cmdline[len - 1] = 0;
73 
74 	strncat(cmdline, " luo_stage=2", sizeof(cmdline) - strlen(cmdline) - 1);
75 
76 	kernel_fd = open(KERNEL_IMAGE, O_RDONLY);
77 	if (kernel_fd < 0) {
78 		fprintf(stderr, "INIT: Failed to open kernel image\n");
79 		return -1;
80 	}
81 
82 	initrd_fd = open(INITRD_IMAGE, O_RDONLY);
83 	if (initrd_fd < 0) {
84 		fprintf(stderr, "INIT: Failed to open initrd image\n");
85 		close(kernel_fd);
86 		return -1;
87 	}
88 
89 	err = kexec_file_load(kernel_fd, initrd_fd, strlen(cmdline) + 1,
90 			      cmdline, 0);
91 
92 	close(initrd_fd);
93 	close(kernel_fd);
94 
95 	return err;
96 }
97 
98 static int run_test(int stage)
99 {
100 	char stage_arg[32];
101 	int status;
102 	pid_t pid;
103 
104 	snprintf(stage_arg, sizeof(stage_arg), "%d", stage);
105 
106 	pid = fork();
107 	if (pid < 0)
108 		return -1;
109 
110 	if (!pid) {
111 		char *const argv[] = {TEST_BINARY, "-s", stage_arg, NULL};
112 
113 		execve(TEST_BINARY, argv, NULL);
114 		fprintf(stderr, "INIT: execve failed\n");
115 		_exit(1);
116 	}
117 
118 	waitpid(pid, &status, 0);
119 
120 	return (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : -1;
121 }
122 
123 static int get_current_stage(void)
124 {
125 	char cmdline[COMMAND_LINE_SIZE];
126 	ssize_t len;
127 	int fd;
128 
129 	fd = open("/proc/cmdline", O_RDONLY);
130 	if (fd < 0)
131 		return -1;
132 
133 	len = read(fd, cmdline, sizeof(cmdline) - 1);
134 	close(fd);
135 
136 	if (len < 0)
137 		return -1;
138 
139 	cmdline[len] = 0;
140 
141 	return strstr(cmdline, "luo_stage=2") ? 2 : 1;
142 }
143 
144 int main(int argc, char *argv[])
145 {
146 	int current_stage;
147 	int err;
148 
149 	if (mount_filesystems())
150 		goto err_reboot;
151 
152 	current_stage = get_current_stage();
153 	if (current_stage < 0) {
154 		fprintf(stderr, "INIT: Failed to read cmdline");
155 		goto err_reboot;
156 	}
157 
158 	printf("INIT: Starting Stage %d\n", current_stage);
159 
160 	if (current_stage == 1 && kexec_load()) {
161 		fprintf(stderr, "INIT: Failed to load kexec kernel\n");
162 		goto err_reboot;
163 	}
164 
165 	if (run_test(current_stage)) {
166 		fprintf(stderr, "INIT: Test binary returned failure\n");
167 		goto err_reboot;
168 	}
169 
170 	printf("INIT: Stage %d completed successfully.\n", current_stage);
171 	reboot(current_stage == 1 ? RB_KEXEC : RB_AUTOBOOT);
172 
173 	return 0;
174 
175 err_reboot:
176 	reboot(RB_AUTOBOOT);
177 
178 	return -1;
179 }
180