xref: /openbmc/linux/tools/testing/selftests/firmware/fw_namespace.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1901cff7cSTopi Miettinen // SPDX-License-Identifier: GPL-2.0
2901cff7cSTopi Miettinen /* Test triggering of loading of firmware from different mount
3901cff7cSTopi Miettinen  * namespaces. Expect firmware to be always loaded from the mount
4901cff7cSTopi Miettinen  * namespace of PID 1. */
5901cff7cSTopi Miettinen #define _GNU_SOURCE
6901cff7cSTopi Miettinen #include <errno.h>
7901cff7cSTopi Miettinen #include <fcntl.h>
8901cff7cSTopi Miettinen #include <sched.h>
9901cff7cSTopi Miettinen #include <stdarg.h>
10901cff7cSTopi Miettinen #include <stdbool.h>
11901cff7cSTopi Miettinen #include <stdio.h>
12901cff7cSTopi Miettinen #include <stdlib.h>
13901cff7cSTopi Miettinen #include <string.h>
14901cff7cSTopi Miettinen #include <sys/mount.h>
15901cff7cSTopi Miettinen #include <sys/stat.h>
16901cff7cSTopi Miettinen #include <sys/types.h>
17901cff7cSTopi Miettinen #include <sys/wait.h>
18901cff7cSTopi Miettinen #include <unistd.h>
19901cff7cSTopi Miettinen 
20901cff7cSTopi Miettinen #ifndef CLONE_NEWNS
21901cff7cSTopi Miettinen # define CLONE_NEWNS 0x00020000
22901cff7cSTopi Miettinen #endif
23901cff7cSTopi Miettinen 
24901cff7cSTopi Miettinen static char *fw_path = NULL;
25901cff7cSTopi Miettinen 
die(char * fmt,...)26901cff7cSTopi Miettinen static void die(char *fmt, ...)
27901cff7cSTopi Miettinen {
28901cff7cSTopi Miettinen 	va_list ap;
29901cff7cSTopi Miettinen 
30901cff7cSTopi Miettinen 	va_start(ap, fmt);
31901cff7cSTopi Miettinen 	vfprintf(stderr, fmt, ap);
32901cff7cSTopi Miettinen 	va_end(ap);
33901cff7cSTopi Miettinen 	if (fw_path)
34901cff7cSTopi Miettinen 		unlink(fw_path);
35901cff7cSTopi Miettinen 	umount("/lib/firmware");
36901cff7cSTopi Miettinen 	exit(EXIT_FAILURE);
37901cff7cSTopi Miettinen }
38901cff7cSTopi Miettinen 
trigger_fw(const char * fw_name,const char * sys_path)39901cff7cSTopi Miettinen static void trigger_fw(const char *fw_name, const char *sys_path)
40901cff7cSTopi Miettinen {
41901cff7cSTopi Miettinen 	int fd;
42901cff7cSTopi Miettinen 
43901cff7cSTopi Miettinen 	fd = open(sys_path, O_WRONLY);
44901cff7cSTopi Miettinen 	if (fd < 0)
45901cff7cSTopi Miettinen 		die("open failed: %s\n",
46901cff7cSTopi Miettinen 		    strerror(errno));
47901cff7cSTopi Miettinen 	if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name))
48901cff7cSTopi Miettinen 		exit(EXIT_FAILURE);
49901cff7cSTopi Miettinen 	close(fd);
50901cff7cSTopi Miettinen }
51901cff7cSTopi Miettinen 
setup_fw(const char * fw_path)52901cff7cSTopi Miettinen static void setup_fw(const char *fw_path)
53901cff7cSTopi Miettinen {
54901cff7cSTopi Miettinen 	int fd;
55901cff7cSTopi Miettinen 	const char fw[] = "ABCD0123";
56901cff7cSTopi Miettinen 
57901cff7cSTopi Miettinen 	fd = open(fw_path, O_WRONLY | O_CREAT, 0600);
58901cff7cSTopi Miettinen 	if (fd < 0)
59901cff7cSTopi Miettinen 		die("open failed: %s\n",
60901cff7cSTopi Miettinen 		    strerror(errno));
61901cff7cSTopi Miettinen 	if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1)
62901cff7cSTopi Miettinen 		die("write failed: %s\n",
63901cff7cSTopi Miettinen 		    strerror(errno));
64901cff7cSTopi Miettinen 	close(fd);
65901cff7cSTopi Miettinen }
66901cff7cSTopi Miettinen 
test_fw_in_ns(const char * fw_name,const char * sys_path,bool block_fw_in_parent_ns)67901cff7cSTopi Miettinen static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns)
68901cff7cSTopi Miettinen {
69901cff7cSTopi Miettinen 	pid_t child;
70901cff7cSTopi Miettinen 
71901cff7cSTopi Miettinen 	if (block_fw_in_parent_ns)
72901cff7cSTopi Miettinen 		if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
73901cff7cSTopi Miettinen 			die("blocking firmware in parent ns failed\n");
74901cff7cSTopi Miettinen 
75901cff7cSTopi Miettinen 	child = fork();
76901cff7cSTopi Miettinen 	if (child == -1) {
77901cff7cSTopi Miettinen 		die("fork failed: %s\n",
78901cff7cSTopi Miettinen 			strerror(errno));
79901cff7cSTopi Miettinen 	}
80901cff7cSTopi Miettinen 	if (child != 0) { /* parent */
81901cff7cSTopi Miettinen 		pid_t pid;
82901cff7cSTopi Miettinen 		int status;
83901cff7cSTopi Miettinen 
84901cff7cSTopi Miettinen 		pid = waitpid(child, &status, 0);
85901cff7cSTopi Miettinen 		if (pid == -1) {
86901cff7cSTopi Miettinen 			die("waitpid failed: %s\n",
87901cff7cSTopi Miettinen 				strerror(errno));
88901cff7cSTopi Miettinen 		}
89901cff7cSTopi Miettinen 		if (pid != child) {
90901cff7cSTopi Miettinen 			die("waited for %d got %d\n",
91901cff7cSTopi Miettinen 				child, pid);
92901cff7cSTopi Miettinen 		}
93901cff7cSTopi Miettinen 		if (!WIFEXITED(status)) {
94901cff7cSTopi Miettinen 			die("child did not terminate cleanly\n");
95901cff7cSTopi Miettinen 		}
96901cff7cSTopi Miettinen 		if (block_fw_in_parent_ns)
97901cff7cSTopi Miettinen 			umount("/lib/firmware");
982c137388SJiapeng Chong 		return WEXITSTATUS(status) == EXIT_SUCCESS;
99901cff7cSTopi Miettinen 	}
100901cff7cSTopi Miettinen 
101901cff7cSTopi Miettinen 	if (unshare(CLONE_NEWNS) != 0) {
102901cff7cSTopi Miettinen 		die("unshare(CLONE_NEWNS) failed: %s\n",
103901cff7cSTopi Miettinen 			strerror(errno));
104901cff7cSTopi Miettinen 	}
105901cff7cSTopi Miettinen 	if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1)
106901cff7cSTopi Miettinen 		die("remount root in child ns failed\n");
107901cff7cSTopi Miettinen 
108901cff7cSTopi Miettinen 	if (!block_fw_in_parent_ns) {
109901cff7cSTopi Miettinen 		if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
110901cff7cSTopi Miettinen 			die("blocking firmware in child ns failed\n");
111901cff7cSTopi Miettinen 	} else
112901cff7cSTopi Miettinen 		umount("/lib/firmware");
113901cff7cSTopi Miettinen 
114901cff7cSTopi Miettinen 	trigger_fw(fw_name, sys_path);
115901cff7cSTopi Miettinen 
116901cff7cSTopi Miettinen 	exit(EXIT_SUCCESS);
117901cff7cSTopi Miettinen }
118901cff7cSTopi Miettinen 
main(int argc,char ** argv)119901cff7cSTopi Miettinen int main(int argc, char **argv)
120901cff7cSTopi Miettinen {
121901cff7cSTopi Miettinen 	const char *fw_name = "test-firmware.bin";
122901cff7cSTopi Miettinen 	char *sys_path;
123901cff7cSTopi Miettinen 	if (argc != 2)
124901cff7cSTopi Miettinen 		die("usage: %s sys_path\n", argv[0]);
125901cff7cSTopi Miettinen 
126901cff7cSTopi Miettinen 	/* Mount tmpfs to /lib/firmware so we don't have to assume
127901cff7cSTopi Miettinen 	   that it is writable for us.*/
128901cff7cSTopi Miettinen 	if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1)
129901cff7cSTopi Miettinen 		die("mounting tmpfs to /lib/firmware failed\n");
130901cff7cSTopi Miettinen 
131901cff7cSTopi Miettinen 	sys_path = argv[1];
132*fe968ca2SShuah Khan 	if (asprintf(&fw_path, "/lib/firmware/%s", fw_name) < 0)
133*fe968ca2SShuah Khan 		die("error: failed to build full fw_path\n");
134901cff7cSTopi Miettinen 
135901cff7cSTopi Miettinen 	setup_fw(fw_path);
136901cff7cSTopi Miettinen 
137901cff7cSTopi Miettinen 	setvbuf(stdout, NULL, _IONBF, 0);
138901cff7cSTopi Miettinen 	/* Positive case: firmware in PID1 mount namespace */
139901cff7cSTopi Miettinen 	printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n");
140901cff7cSTopi Miettinen 	if (!test_fw_in_ns(fw_name, sys_path, false))
141901cff7cSTopi Miettinen 		die("error: failed to access firmware\n");
142901cff7cSTopi Miettinen 
143901cff7cSTopi Miettinen 	/* Negative case: firmware in child mount namespace, expected to fail */
144901cff7cSTopi Miettinen 	printf("Testing with firmware in child namespace\n");
145901cff7cSTopi Miettinen 	if (test_fw_in_ns(fw_name, sys_path, true))
146901cff7cSTopi Miettinen 		die("error: firmware access did not fail\n");
147901cff7cSTopi Miettinen 
148901cff7cSTopi Miettinen 	unlink(fw_path);
149901cff7cSTopi Miettinen 	free(fw_path);
150901cff7cSTopi Miettinen 	umount("/lib/firmware");
151901cff7cSTopi Miettinen 	exit(EXIT_SUCCESS);
152901cff7cSTopi Miettinen }
153