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