1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/unistd.h> |
3 | #include <linux/kernel.h> |
4 | #include <linux/fs.h> |
5 | #include <linux/minix_fs.h> |
6 | #include <linux/romfs_fs.h> |
7 | #include <linux/initrd.h> |
8 | #include <linux/sched.h> |
9 | #include <linux/freezer.h> |
10 | #include <linux/kmod.h> |
11 | #include <uapi/linux/mount.h> |
12 | |
13 | #include "do_mounts.h" |
14 | |
15 | unsigned long initrd_start, initrd_end; |
16 | int initrd_below_start_ok; |
17 | static unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */ |
18 | static int __initdata mount_initrd = 1; |
19 | |
20 | phys_addr_t phys_initrd_start __initdata; |
21 | unsigned long phys_initrd_size __initdata; |
22 | |
23 | #ifdef CONFIG_SYSCTL |
24 | static struct ctl_table kern_do_mounts_initrd_table[] = { |
25 | { |
26 | .procname = "real-root-dev" , |
27 | .data = &real_root_dev, |
28 | .maxlen = sizeof(int), |
29 | .mode = 0644, |
30 | .proc_handler = proc_dointvec, |
31 | }, |
32 | { } |
33 | }; |
34 | |
35 | static __init int kernel_do_mounts_initrd_sysctls_init(void) |
36 | { |
37 | register_sysctl_init("kernel" , kern_do_mounts_initrd_table); |
38 | return 0; |
39 | } |
40 | late_initcall(kernel_do_mounts_initrd_sysctls_init); |
41 | #endif /* CONFIG_SYSCTL */ |
42 | |
43 | static int __init no_initrd(char *str) |
44 | { |
45 | mount_initrd = 0; |
46 | return 1; |
47 | } |
48 | |
49 | __setup("noinitrd" , no_initrd); |
50 | |
51 | static int __init early_initrdmem(char *p) |
52 | { |
53 | phys_addr_t start; |
54 | unsigned long size; |
55 | char *endp; |
56 | |
57 | start = memparse(ptr: p, retptr: &endp); |
58 | if (*endp == ',') { |
59 | size = memparse(ptr: endp + 1, NULL); |
60 | |
61 | phys_initrd_start = start; |
62 | phys_initrd_size = size; |
63 | } |
64 | return 0; |
65 | } |
66 | early_param("initrdmem" , early_initrdmem); |
67 | |
68 | static int __init early_initrd(char *p) |
69 | { |
70 | return early_initrdmem(p); |
71 | } |
72 | early_param("initrd" , early_initrd); |
73 | |
74 | static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) |
75 | { |
76 | ksys_unshare(CLONE_FS | CLONE_FILES); |
77 | console_on_rootfs(); |
78 | /* move initrd over / and chdir/chroot in initrd root */ |
79 | init_chdir(filename: "/root" ); |
80 | init_mount(dev_name: "." , dir_name: "/" , NULL, MS_MOVE, NULL); |
81 | init_chroot(filename: "." ); |
82 | ksys_setsid(); |
83 | return 0; |
84 | } |
85 | |
86 | static void __init handle_initrd(char *root_device_name) |
87 | { |
88 | struct subprocess_info *info; |
89 | static char *argv[] = { "linuxrc" , NULL, }; |
90 | extern char *envp_init[]; |
91 | int error; |
92 | |
93 | pr_warn("using deprecated initrd support, will be removed in 2021.\n" ); |
94 | |
95 | real_root_dev = new_encode_dev(dev: ROOT_DEV); |
96 | create_dev(name: "/dev/root.old" , dev: Root_RAM0); |
97 | /* mount initrd on rootfs' /root */ |
98 | mount_root_generic(name: "/dev/root.old" , pretty_name: root_device_name, |
99 | flags: root_mountflags & ~MS_RDONLY); |
100 | init_mkdir(pathname: "/old" , mode: 0700); |
101 | init_chdir(filename: "/old" ); |
102 | |
103 | info = call_usermodehelper_setup(path: "/linuxrc" , argv, envp: envp_init, |
104 | GFP_KERNEL, init: init_linuxrc, NULL, NULL); |
105 | if (!info) |
106 | return; |
107 | call_usermodehelper_exec(info, UMH_WAIT_PROC|UMH_FREEZABLE); |
108 | |
109 | /* move initrd to rootfs' /old */ |
110 | init_mount(dev_name: ".." , dir_name: "." , NULL, MS_MOVE, NULL); |
111 | /* switch root and cwd back to / of rootfs */ |
112 | init_chroot(filename: ".." ); |
113 | |
114 | if (new_decode_dev(dev: real_root_dev) == Root_RAM0) { |
115 | init_chdir(filename: "/old" ); |
116 | return; |
117 | } |
118 | |
119 | init_chdir(filename: "/" ); |
120 | ROOT_DEV = new_decode_dev(dev: real_root_dev); |
121 | mount_root(root_device_name); |
122 | |
123 | printk(KERN_NOTICE "Trying to move old root to /initrd ... " ); |
124 | error = init_mount(dev_name: "/old" , dir_name: "/root/initrd" , NULL, MS_MOVE, NULL); |
125 | if (!error) |
126 | printk("okay\n" ); |
127 | else { |
128 | if (error == -ENOENT) |
129 | printk("/initrd does not exist. Ignored.\n" ); |
130 | else |
131 | printk("failed\n" ); |
132 | printk(KERN_NOTICE "Unmounting old root\n" ); |
133 | init_umount(name: "/old" , MNT_DETACH); |
134 | } |
135 | } |
136 | |
137 | bool __init initrd_load(char *root_device_name) |
138 | { |
139 | if (mount_initrd) { |
140 | create_dev(name: "/dev/ram" , dev: Root_RAM0); |
141 | /* |
142 | * Load the initrd data into /dev/ram0. Execute it as initrd |
143 | * unless /dev/ram0 is supposed to be our actual root device, |
144 | * in that case the ram disk is just set up here, and gets |
145 | * mounted in the normal path. |
146 | */ |
147 | if (rd_load_image(from: "/initrd.image" ) && ROOT_DEV != Root_RAM0) { |
148 | init_unlink(pathname: "/initrd.image" ); |
149 | handle_initrd(root_device_name); |
150 | return true; |
151 | } |
152 | } |
153 | init_unlink(pathname: "/initrd.image" ); |
154 | return false; |
155 | } |
156 | |