18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/unistd.h>
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <linux/fs.h>
58c2ecf20Sopenharmony_ci#include <linux/minix_fs.h>
68c2ecf20Sopenharmony_ci#include <linux/romfs_fs.h>
78c2ecf20Sopenharmony_ci#include <linux/initrd.h>
88c2ecf20Sopenharmony_ci#include <linux/sched.h>
98c2ecf20Sopenharmony_ci#include <linux/freezer.h>
108c2ecf20Sopenharmony_ci#include <linux/kmod.h>
118c2ecf20Sopenharmony_ci#include <uapi/linux/mount.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "do_mounts.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciunsigned long initrd_start, initrd_end;
168c2ecf20Sopenharmony_ciint initrd_below_start_ok;
178c2ecf20Sopenharmony_ciunsigned int real_root_dev;	/* do_proc_dointvec cannot handle kdev_t */
188c2ecf20Sopenharmony_cistatic int __initdata mount_initrd = 1;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciphys_addr_t phys_initrd_start __initdata;
218c2ecf20Sopenharmony_ciunsigned long phys_initrd_size __initdata;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int __init no_initrd(char *str)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	mount_initrd = 0;
268c2ecf20Sopenharmony_ci	return 1;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci__setup("noinitrd", no_initrd);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int __init early_initrdmem(char *p)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	phys_addr_t start;
348c2ecf20Sopenharmony_ci	unsigned long size;
358c2ecf20Sopenharmony_ci	char *endp;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	start = memparse(p, &endp);
388c2ecf20Sopenharmony_ci	if (*endp == ',') {
398c2ecf20Sopenharmony_ci		size = memparse(endp + 1, NULL);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci		phys_initrd_start = start;
428c2ecf20Sopenharmony_ci		phys_initrd_size = size;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci	return 0;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ciearly_param("initrdmem", early_initrdmem);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int __init early_initrd(char *p)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	return early_initrdmem(p);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ciearly_param("initrd", early_initrd);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int __init init_linuxrc(struct subprocess_info *info, struct cred *new)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	ksys_unshare(CLONE_FS | CLONE_FILES);
578c2ecf20Sopenharmony_ci	console_on_rootfs();
588c2ecf20Sopenharmony_ci	/* move initrd over / and chdir/chroot in initrd root */
598c2ecf20Sopenharmony_ci	init_chdir("/root");
608c2ecf20Sopenharmony_ci	init_mount(".", "/", NULL, MS_MOVE, NULL);
618c2ecf20Sopenharmony_ci	init_chroot(".");
628c2ecf20Sopenharmony_ci	ksys_setsid();
638c2ecf20Sopenharmony_ci	return 0;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void __init handle_initrd(void)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct subprocess_info *info;
698c2ecf20Sopenharmony_ci	static char *argv[] = { "linuxrc", NULL, };
708c2ecf20Sopenharmony_ci	extern char *envp_init[];
718c2ecf20Sopenharmony_ci	int error;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	pr_warn("using deprecated initrd support, will be removed in 2021.\n");
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	real_root_dev = new_encode_dev(ROOT_DEV);
768c2ecf20Sopenharmony_ci	create_dev("/dev/root.old", Root_RAM0);
778c2ecf20Sopenharmony_ci	/* mount initrd on rootfs' /root */
788c2ecf20Sopenharmony_ci	mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
798c2ecf20Sopenharmony_ci	init_mkdir("/old", 0700);
808c2ecf20Sopenharmony_ci	init_chdir("/old");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/*
838c2ecf20Sopenharmony_ci	 * In case that a resume from disk is carried out by linuxrc or one of
848c2ecf20Sopenharmony_ci	 * its children, we need to tell the freezer not to wait for us.
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	current->flags |= PF_FREEZER_SKIP;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
898c2ecf20Sopenharmony_ci					 GFP_KERNEL, init_linuxrc, NULL, NULL);
908c2ecf20Sopenharmony_ci	if (!info)
918c2ecf20Sopenharmony_ci		return;
928c2ecf20Sopenharmony_ci	call_usermodehelper_exec(info, UMH_WAIT_PROC);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	current->flags &= ~PF_FREEZER_SKIP;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* move initrd to rootfs' /old */
978c2ecf20Sopenharmony_ci	init_mount("..", ".", NULL, MS_MOVE, NULL);
988c2ecf20Sopenharmony_ci	/* switch root and cwd back to / of rootfs */
998c2ecf20Sopenharmony_ci	init_chroot("..");
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (new_decode_dev(real_root_dev) == Root_RAM0) {
1028c2ecf20Sopenharmony_ci		init_chdir("/old");
1038c2ecf20Sopenharmony_ci		return;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	init_chdir("/");
1078c2ecf20Sopenharmony_ci	ROOT_DEV = new_decode_dev(real_root_dev);
1088c2ecf20Sopenharmony_ci	mount_root();
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
1118c2ecf20Sopenharmony_ci	error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
1128c2ecf20Sopenharmony_ci	if (!error)
1138c2ecf20Sopenharmony_ci		printk("okay\n");
1148c2ecf20Sopenharmony_ci	else {
1158c2ecf20Sopenharmony_ci		if (error == -ENOENT)
1168c2ecf20Sopenharmony_ci			printk("/initrd does not exist. Ignored.\n");
1178c2ecf20Sopenharmony_ci		else
1188c2ecf20Sopenharmony_ci			printk("failed\n");
1198c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "Unmounting old root\n");
1208c2ecf20Sopenharmony_ci		init_umount("/old", MNT_DETACH);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cibool __init initrd_load(void)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (mount_initrd) {
1278c2ecf20Sopenharmony_ci		create_dev("/dev/ram", Root_RAM0);
1288c2ecf20Sopenharmony_ci		/*
1298c2ecf20Sopenharmony_ci		 * Load the initrd data into /dev/ram0. Execute it as initrd
1308c2ecf20Sopenharmony_ci		 * unless /dev/ram0 is supposed to be our actual root device,
1318c2ecf20Sopenharmony_ci		 * in that case the ram disk is just set up here, and gets
1328c2ecf20Sopenharmony_ci		 * mounted in the normal path.
1338c2ecf20Sopenharmony_ci		 */
1348c2ecf20Sopenharmony_ci		if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
1358c2ecf20Sopenharmony_ci			init_unlink("/initrd.image");
1368c2ecf20Sopenharmony_ci			handle_initrd();
1378c2ecf20Sopenharmony_ci			return true;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	init_unlink("/initrd.image");
1418c2ecf20Sopenharmony_ci	return false;
1428c2ecf20Sopenharmony_ci}
143