mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
![Ronald G. Minnich](/assets/img/avatar_default.png)
Add the initrdmem option: initrdmem=ss[KMG],nn[KMG] which is used to specify the physical address of the initrd, almost always an address in FLASH. Also add code for x86 to use the existing phys_init_start and phys_init_size variables in the kernel. This is useful in cases where a kernel and an initrd is placed in FLASH, but there is no firmware file system structure in the FLASH. One such situation occurs when unused FLASH space on UEFI systems has been reclaimed by, e.g., taking it from the Management Engine. For example, on many systems, the ME is given half the FLASH part; not only is 2.75M of an 8M part unused; but 10.75M of a 16M part is unused. This space can be used to contain an initrd, but need to tell Linux where it is. This space is "raw": due to, e.g., UEFI limitations: it can not be added to UEFI firmware volumes without rebuilding UEFI from source or writing a UEFI device driver. It can be referenced only as a physical address and size. At the same time, if a kernel can be "netbooted" or loaded from GRUB or syslinux, the option of not using the physical address specification should be available. Then, it is easy to boot the kernel and provide an initrd; or boot the the kernel and let it use the initrd in FLASH. In practice, this has proven to be very helpful when integrating Linux into FLASH on x86. Hence, the most flexible and convenient path is to enable the initrdmem command line option in a way that it is the last choice tried. For example, on the DigitalLoggers Atomic Pi, an image into FLASH can be burnt in with a built-in command line which includes: initrdmem=0xff968000,0x200000 which specifies a location and size. [ bp: Massage commit message, make it passive. ] [akpm@linux-foundation.org: coding style fixes] Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: H. Peter Anvin (Intel) <hpa@zytor.com> Link: http://lkml.kernel.org/r/CAP6exYLK11rhreX=6QPyDQmW7wPHsKNEFtXE47pjx41xS6O7-A@mail.gmail.com Link: https://lkml.kernel.org/r/20200426011021.1cskg0AGd%akpm@linux-foundation.org
154 lines
3.7 KiB
C
154 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/unistd.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/minix_fs.h>
|
|
#include <linux/romfs_fs.h>
|
|
#include <linux/initrd.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/freezer.h>
|
|
#include <linux/kmod.h>
|
|
#include <uapi/linux/mount.h>
|
|
|
|
#include "do_mounts.h"
|
|
|
|
unsigned long initrd_start, initrd_end;
|
|
int initrd_below_start_ok;
|
|
unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
|
|
static int __initdata mount_initrd = 1;
|
|
|
|
phys_addr_t phys_initrd_start __initdata;
|
|
unsigned long phys_initrd_size __initdata;
|
|
|
|
static int __init no_initrd(char *str)
|
|
{
|
|
mount_initrd = 0;
|
|
return 1;
|
|
}
|
|
|
|
__setup("noinitrd", no_initrd);
|
|
|
|
static int __init early_initrdmem(char *p)
|
|
{
|
|
phys_addr_t start;
|
|
unsigned long size;
|
|
char *endp;
|
|
|
|
start = memparse(p, &endp);
|
|
if (*endp == ',') {
|
|
size = memparse(endp + 1, NULL);
|
|
|
|
phys_initrd_start = start;
|
|
phys_initrd_size = size;
|
|
}
|
|
return 0;
|
|
}
|
|
early_param("initrdmem", early_initrdmem);
|
|
|
|
/*
|
|
* This is here as the initrd keyword has been in use since 11/2018
|
|
* on ARM, PowerPC, and MIPS.
|
|
* It should not be; it is reserved for bootloaders.
|
|
*/
|
|
static int __init early_initrd(char *p)
|
|
{
|
|
return early_initrdmem(p);
|
|
}
|
|
early_param("initrd", early_initrd);
|
|
|
|
static int init_linuxrc(struct subprocess_info *info, struct cred *new)
|
|
{
|
|
ksys_unshare(CLONE_FS | CLONE_FILES);
|
|
console_on_rootfs();
|
|
/* move initrd over / and chdir/chroot in initrd root */
|
|
ksys_chdir("/root");
|
|
do_mount(".", "/", NULL, MS_MOVE, NULL);
|
|
ksys_chroot(".");
|
|
ksys_setsid();
|
|
return 0;
|
|
}
|
|
|
|
static void __init handle_initrd(void)
|
|
{
|
|
struct subprocess_info *info;
|
|
static char *argv[] = { "linuxrc", NULL, };
|
|
extern char *envp_init[];
|
|
int error;
|
|
|
|
real_root_dev = new_encode_dev(ROOT_DEV);
|
|
create_dev("/dev/root.old", Root_RAM0);
|
|
/* mount initrd on rootfs' /root */
|
|
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
|
|
ksys_mkdir("/old", 0700);
|
|
ksys_chdir("/old");
|
|
|
|
/*
|
|
* In case that a resume from disk is carried out by linuxrc or one of
|
|
* its children, we need to tell the freezer not to wait for us.
|
|
*/
|
|
current->flags |= PF_FREEZER_SKIP;
|
|
|
|
info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
|
|
GFP_KERNEL, init_linuxrc, NULL, NULL);
|
|
if (!info)
|
|
return;
|
|
call_usermodehelper_exec(info, UMH_WAIT_PROC);
|
|
|
|
current->flags &= ~PF_FREEZER_SKIP;
|
|
|
|
/* move initrd to rootfs' /old */
|
|
do_mount("..", ".", NULL, MS_MOVE, NULL);
|
|
/* switch root and cwd back to / of rootfs */
|
|
ksys_chroot("..");
|
|
|
|
if (new_decode_dev(real_root_dev) == Root_RAM0) {
|
|
ksys_chdir("/old");
|
|
return;
|
|
}
|
|
|
|
ksys_chdir("/");
|
|
ROOT_DEV = new_decode_dev(real_root_dev);
|
|
mount_root();
|
|
|
|
printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
|
|
error = do_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
|
|
if (!error)
|
|
printk("okay\n");
|
|
else {
|
|
int fd = ksys_open("/dev/root.old", O_RDWR, 0);
|
|
if (error == -ENOENT)
|
|
printk("/initrd does not exist. Ignored.\n");
|
|
else
|
|
printk("failed\n");
|
|
printk(KERN_NOTICE "Unmounting old root\n");
|
|
ksys_umount("/old", MNT_DETACH);
|
|
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
|
|
if (fd < 0) {
|
|
error = fd;
|
|
} else {
|
|
error = ksys_ioctl(fd, BLKFLSBUF, 0);
|
|
ksys_close(fd);
|
|
}
|
|
printk(!error ? "okay\n" : "failed\n");
|
|
}
|
|
}
|
|
|
|
bool __init initrd_load(void)
|
|
{
|
|
if (mount_initrd) {
|
|
create_dev("/dev/ram", Root_RAM0);
|
|
/*
|
|
* Load the initrd data into /dev/ram0. Execute it as initrd
|
|
* unless /dev/ram0 is supposed to be our actual root device,
|
|
* in that case the ram disk is just set up here, and gets
|
|
* mounted in the normal path.
|
|
*/
|
|
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
|
|
ksys_unlink("/initrd.image");
|
|
handle_initrd();
|
|
return true;
|
|
}
|
|
}
|
|
ksys_unlink("/initrd.image");
|
|
return false;
|
|
}
|