1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds #include <linux/unistd.h>
31da177e4SLinus Torvalds #include <linux/kernel.h>
41da177e4SLinus Torvalds #include <linux/fs.h>
51da177e4SLinus Torvalds #include <linux/minix_fs.h>
61da177e4SLinus Torvalds #include <linux/romfs_fs.h>
71da177e4SLinus Torvalds #include <linux/initrd.h>
81da177e4SLinus Torvalds #include <linux/sched.h>
97dfb7103SNigel Cunningham #include <linux/freezer.h>
10ba4df280SAl Viro #include <linux/kmod.h>
11e262e32dSDavid Howells #include <uapi/linux/mount.h>
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds #include "do_mounts.h"
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds unsigned long initrd_start, initrd_end;
161da177e4SLinus Torvalds int initrd_below_start_ok;
17d772cc2cStangmeng static unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
181da177e4SLinus Torvalds static int __initdata mount_initrd = 1;
191da177e4SLinus Torvalds
20b1ab95c6SFlorian Fainelli phys_addr_t phys_initrd_start __initdata;
21b1ab95c6SFlorian Fainelli unsigned long phys_initrd_size __initdata;
22b1ab95c6SFlorian Fainelli
23d772cc2cStangmeng #ifdef CONFIG_SYSCTL
24d772cc2cStangmeng static struct ctl_table kern_do_mounts_initrd_table[] = {
25d772cc2cStangmeng {
26d772cc2cStangmeng .procname = "real-root-dev",
27d772cc2cStangmeng .data = &real_root_dev,
28d772cc2cStangmeng .maxlen = sizeof(int),
29d772cc2cStangmeng .mode = 0644,
30d772cc2cStangmeng .proc_handler = proc_dointvec,
31d772cc2cStangmeng },
32d772cc2cStangmeng { }
33d772cc2cStangmeng };
34d772cc2cStangmeng
kernel_do_mounts_initrd_sysctls_init(void)35d772cc2cStangmeng static __init int kernel_do_mounts_initrd_sysctls_init(void)
36d772cc2cStangmeng {
37d772cc2cStangmeng register_sysctl_init("kernel", kern_do_mounts_initrd_table);
38d772cc2cStangmeng return 0;
39d772cc2cStangmeng }
40d772cc2cStangmeng late_initcall(kernel_do_mounts_initrd_sysctls_init);
41d772cc2cStangmeng #endif /* CONFIG_SYSCTL */
42d772cc2cStangmeng
no_initrd(char * str)431da177e4SLinus Torvalds static int __init no_initrd(char *str)
441da177e4SLinus Torvalds {
451da177e4SLinus Torvalds mount_initrd = 0;
461da177e4SLinus Torvalds return 1;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds __setup("noinitrd", no_initrd);
501da177e4SLinus Torvalds
early_initrdmem(char * p)51694cfd87SRonald G. Minnich static int __init early_initrdmem(char *p)
52229c55ccSFlorian Fainelli {
53229c55ccSFlorian Fainelli phys_addr_t start;
54229c55ccSFlorian Fainelli unsigned long size;
55229c55ccSFlorian Fainelli char *endp;
56229c55ccSFlorian Fainelli
57229c55ccSFlorian Fainelli start = memparse(p, &endp);
58229c55ccSFlorian Fainelli if (*endp == ',') {
59229c55ccSFlorian Fainelli size = memparse(endp + 1, NULL);
60229c55ccSFlorian Fainelli
61229c55ccSFlorian Fainelli phys_initrd_start = start;
62229c55ccSFlorian Fainelli phys_initrd_size = size;
63229c55ccSFlorian Fainelli }
64229c55ccSFlorian Fainelli return 0;
65229c55ccSFlorian Fainelli }
66694cfd87SRonald G. Minnich early_param("initrdmem", early_initrdmem);
67694cfd87SRonald G. Minnich
early_initrd(char * p)68694cfd87SRonald G. Minnich static int __init early_initrd(char *p)
69694cfd87SRonald G. Minnich {
70694cfd87SRonald G. Minnich return early_initrdmem(p);
71694cfd87SRonald G. Minnich }
72229c55ccSFlorian Fainelli early_param("initrd", early_initrd);
73229c55ccSFlorian Fainelli
init_linuxrc(struct subprocess_info * info,struct cred * new)74f0ea68f1SChristoph Hellwig static int __init init_linuxrc(struct subprocess_info *info, struct cred *new)
751da177e4SLinus Torvalds {
769b32105eSDominik Brodowski ksys_unshare(CLONE_FS | CLONE_FILES);
77b49a733dSDominik Brodowski console_on_rootfs();
78ba4df280SAl Viro /* move initrd over / and chdir/chroot in initrd root */
79db63f1e3SChristoph Hellwig init_chdir("/root");
80c60166f0SChristoph Hellwig init_mount(".", "/", NULL, MS_MOVE, NULL);
814b7ca501SChristoph Hellwig init_chroot(".");
82e2aaa9f4SDominik Brodowski ksys_setsid();
83ba4df280SAl Viro return 0;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds
handle_initrd(char * root_device_name)86*c8643c72SChristoph Hellwig static void __init handle_initrd(char *root_device_name)
871da177e4SLinus Torvalds {
88907ed132SLucas De Marchi struct subprocess_info *info;
89ba4df280SAl Viro static char *argv[] = { "linuxrc", NULL, };
90ba4df280SAl Viro extern char *envp_init[];
911da177e4SLinus Torvalds int error;
921da177e4SLinus Torvalds
939acc17baSChristoph Hellwig pr_warn("using deprecated initrd support, will be removed in 2021.\n");
949acc17baSChristoph Hellwig
951da177e4SLinus Torvalds real_root_dev = new_encode_dev(ROOT_DEV);
96bdaf8529SGreg Kroah-Hartman create_dev("/dev/root.old", Root_RAM0);
971da177e4SLinus Torvalds /* mount initrd on rootfs' /root */
98*c8643c72SChristoph Hellwig mount_root_generic("/dev/root.old", root_device_name,
99*c8643c72SChristoph Hellwig root_mountflags & ~MS_RDONLY);
10083ff98c3SChristoph Hellwig init_mkdir("/old", 0700);
101db63f1e3SChristoph Hellwig init_chdir("/old");
1021da177e4SLinus Torvalds
103907ed132SLucas De Marchi info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
104907ed132SLucas De Marchi GFP_KERNEL, init_linuxrc, NULL, NULL);
105907ed132SLucas De Marchi if (!info)
106907ed132SLucas De Marchi return;
1071fbcaa92SPeter Zijlstra call_usermodehelper_exec(info, UMH_WAIT_PROC|UMH_FREEZABLE);
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds /* move initrd to rootfs' /old */
110c60166f0SChristoph Hellwig init_mount("..", ".", NULL, MS_MOVE, NULL);
1111da177e4SLinus Torvalds /* switch root and cwd back to / of rootfs */
1124b7ca501SChristoph Hellwig init_chroot("..");
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds if (new_decode_dev(real_root_dev) == Root_RAM0) {
115db63f1e3SChristoph Hellwig init_chdir("/old");
1161da177e4SLinus Torvalds return;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds
119db63f1e3SChristoph Hellwig init_chdir("/");
1201da177e4SLinus Torvalds ROOT_DEV = new_decode_dev(real_root_dev);
121*c8643c72SChristoph Hellwig mount_root(root_device_name);
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
124c60166f0SChristoph Hellwig error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
1251da177e4SLinus Torvalds if (!error)
1261da177e4SLinus Torvalds printk("okay\n");
1271da177e4SLinus Torvalds else {
128f220ab2aSJay Lan if (error == -ENOENT)
129f220ab2aSJay Lan printk("/initrd does not exist. Ignored.\n");
130f220ab2aSJay Lan else
1311da177e4SLinus Torvalds printk("failed\n");
1321da177e4SLinus Torvalds printk(KERN_NOTICE "Unmounting old root\n");
13309267defSChristoph Hellwig init_umount("/old", MNT_DETACH);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds
initrd_load(char * root_device_name)137*c8643c72SChristoph Hellwig bool __init initrd_load(char *root_device_name)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds if (mount_initrd) {
140bdaf8529SGreg Kroah-Hartman create_dev("/dev/ram", Root_RAM0);
1411da177e4SLinus Torvalds /*
1421da177e4SLinus Torvalds * Load the initrd data into /dev/ram0. Execute it as initrd
1431da177e4SLinus Torvalds * unless /dev/ram0 is supposed to be our actual root device,
1441da177e4SLinus Torvalds * in that case the ram disk is just set up here, and gets
1451da177e4SLinus Torvalds * mounted in the normal path.
1461da177e4SLinus Torvalds */
1471da177e4SLinus Torvalds if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
1488fb9f73eSChristoph Hellwig init_unlink("/initrd.image");
149*c8643c72SChristoph Hellwig handle_initrd(root_device_name);
150f057f3b2SYaowei Bai return true;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds }
1538fb9f73eSChristoph Hellwig init_unlink("/initrd.image");
154f057f3b2SYaowei Bai return false;
1551da177e4SLinus Torvalds }
156