162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/unistd.h>
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci#include <linux/fs.h>
562306a36Sopenharmony_ci#include <linux/minix_fs.h>
662306a36Sopenharmony_ci#include <linux/romfs_fs.h>
762306a36Sopenharmony_ci#include <linux/initrd.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/freezer.h>
1062306a36Sopenharmony_ci#include <linux/kmod.h>
1162306a36Sopenharmony_ci#include <uapi/linux/mount.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "do_mounts.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciunsigned long initrd_start, initrd_end;
1662306a36Sopenharmony_ciint initrd_below_start_ok;
1762306a36Sopenharmony_cistatic unsigned int real_root_dev;	/* do_proc_dointvec cannot handle kdev_t */
1862306a36Sopenharmony_cistatic int __initdata mount_initrd = 1;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciphys_addr_t phys_initrd_start __initdata;
2162306a36Sopenharmony_ciunsigned long phys_initrd_size __initdata;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
2462306a36Sopenharmony_cistatic struct ctl_table kern_do_mounts_initrd_table[] = {
2562306a36Sopenharmony_ci	{
2662306a36Sopenharmony_ci		.procname       = "real-root-dev",
2762306a36Sopenharmony_ci		.data           = &real_root_dev,
2862306a36Sopenharmony_ci		.maxlen         = sizeof(int),
2962306a36Sopenharmony_ci		.mode           = 0644,
3062306a36Sopenharmony_ci		.proc_handler   = proc_dointvec,
3162306a36Sopenharmony_ci	},
3262306a36Sopenharmony_ci	{ }
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic __init int kernel_do_mounts_initrd_sysctls_init(void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	register_sysctl_init("kernel", kern_do_mounts_initrd_table);
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_cilate_initcall(kernel_do_mounts_initrd_sysctls_init);
4162306a36Sopenharmony_ci#endif /* CONFIG_SYSCTL */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int __init no_initrd(char *str)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	mount_initrd = 0;
4662306a36Sopenharmony_ci	return 1;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci__setup("noinitrd", no_initrd);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int __init early_initrdmem(char *p)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	phys_addr_t start;
5462306a36Sopenharmony_ci	unsigned long size;
5562306a36Sopenharmony_ci	char *endp;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	start = memparse(p, &endp);
5862306a36Sopenharmony_ci	if (*endp == ',') {
5962306a36Sopenharmony_ci		size = memparse(endp + 1, NULL);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		phys_initrd_start = start;
6262306a36Sopenharmony_ci		phys_initrd_size = size;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ciearly_param("initrdmem", early_initrdmem);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int __init early_initrd(char *p)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	return early_initrdmem(p);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ciearly_param("initrd", early_initrd);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int __init init_linuxrc(struct subprocess_info *info, struct cred *new)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	ksys_unshare(CLONE_FS | CLONE_FILES);
7762306a36Sopenharmony_ci	console_on_rootfs();
7862306a36Sopenharmony_ci	/* move initrd over / and chdir/chroot in initrd root */
7962306a36Sopenharmony_ci	init_chdir("/root");
8062306a36Sopenharmony_ci	init_mount(".", "/", NULL, MS_MOVE, NULL);
8162306a36Sopenharmony_ci	init_chroot(".");
8262306a36Sopenharmony_ci	ksys_setsid();
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void __init handle_initrd(char *root_device_name)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct subprocess_info *info;
8962306a36Sopenharmony_ci	static char *argv[] = { "linuxrc", NULL, };
9062306a36Sopenharmony_ci	extern char *envp_init[];
9162306a36Sopenharmony_ci	int error;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	pr_warn("using deprecated initrd support, will be removed in 2021.\n");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	real_root_dev = new_encode_dev(ROOT_DEV);
9662306a36Sopenharmony_ci	create_dev("/dev/root.old", Root_RAM0);
9762306a36Sopenharmony_ci	/* mount initrd on rootfs' /root */
9862306a36Sopenharmony_ci	mount_root_generic("/dev/root.old", root_device_name,
9962306a36Sopenharmony_ci			   root_mountflags & ~MS_RDONLY);
10062306a36Sopenharmony_ci	init_mkdir("/old", 0700);
10162306a36Sopenharmony_ci	init_chdir("/old");
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
10462306a36Sopenharmony_ci					 GFP_KERNEL, init_linuxrc, NULL, NULL);
10562306a36Sopenharmony_ci	if (!info)
10662306a36Sopenharmony_ci		return;
10762306a36Sopenharmony_ci	call_usermodehelper_exec(info, UMH_WAIT_PROC|UMH_FREEZABLE);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* move initrd to rootfs' /old */
11062306a36Sopenharmony_ci	init_mount("..", ".", NULL, MS_MOVE, NULL);
11162306a36Sopenharmony_ci	/* switch root and cwd back to / of rootfs */
11262306a36Sopenharmony_ci	init_chroot("..");
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (new_decode_dev(real_root_dev) == Root_RAM0) {
11562306a36Sopenharmony_ci		init_chdir("/old");
11662306a36Sopenharmony_ci		return;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	init_chdir("/");
12062306a36Sopenharmony_ci	ROOT_DEV = new_decode_dev(real_root_dev);
12162306a36Sopenharmony_ci	mount_root(root_device_name);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
12462306a36Sopenharmony_ci	error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
12562306a36Sopenharmony_ci	if (!error)
12662306a36Sopenharmony_ci		printk("okay\n");
12762306a36Sopenharmony_ci	else {
12862306a36Sopenharmony_ci		if (error == -ENOENT)
12962306a36Sopenharmony_ci			printk("/initrd does not exist. Ignored.\n");
13062306a36Sopenharmony_ci		else
13162306a36Sopenharmony_ci			printk("failed\n");
13262306a36Sopenharmony_ci		printk(KERN_NOTICE "Unmounting old root\n");
13362306a36Sopenharmony_ci		init_umount("/old", MNT_DETACH);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cibool __init initrd_load(char *root_device_name)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (mount_initrd) {
14062306a36Sopenharmony_ci		create_dev("/dev/ram", Root_RAM0);
14162306a36Sopenharmony_ci		/*
14262306a36Sopenharmony_ci		 * Load the initrd data into /dev/ram0. Execute it as initrd
14362306a36Sopenharmony_ci		 * unless /dev/ram0 is supposed to be our actual root device,
14462306a36Sopenharmony_ci		 * in that case the ram disk is just set up here, and gets
14562306a36Sopenharmony_ci		 * mounted in the normal path.
14662306a36Sopenharmony_ci		 */
14762306a36Sopenharmony_ci		if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
14862306a36Sopenharmony_ci			init_unlink("/initrd.image");
14962306a36Sopenharmony_ci			handle_initrd(root_device_name);
15062306a36Sopenharmony_ci			return true;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	init_unlink("/initrd.image");
15462306a36Sopenharmony_ci	return false;
15562306a36Sopenharmony_ci}
156