18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/fs.h>
48c2ecf20Sopenharmony_ci#include <linux/minix_fs.h>
58c2ecf20Sopenharmony_ci#include <linux/ext2_fs.h>
68c2ecf20Sopenharmony_ci#include <linux/romfs_fs.h>
78c2ecf20Sopenharmony_ci#include <uapi/linux/cramfs_fs.h>
88c2ecf20Sopenharmony_ci#include <linux/initrd.h>
98c2ecf20Sopenharmony_ci#include <linux/string.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "do_mounts.h"
138c2ecf20Sopenharmony_ci#include "../fs/squashfs/squashfs_fs.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/decompress/generic.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct file *in_file, *out_file;
188c2ecf20Sopenharmony_cistatic loff_t in_pos, out_pos;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic int __init prompt_ramdisk(char *str)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	pr_warn("ignoring the deprecated prompt_ramdisk= option\n");
238c2ecf20Sopenharmony_ci	return 1;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci__setup("prompt_ramdisk=", prompt_ramdisk);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciint __initdata rd_image_start;		/* starting block # of image */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int __init ramdisk_start_setup(char *str)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	rd_image_start = simple_strtol(str,NULL,0);
328c2ecf20Sopenharmony_ci	return 1;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci__setup("ramdisk_start=", ramdisk_start_setup);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int __init crd_load(decompress_fn deco);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * This routine tries to find a RAM disk image to load, and returns the
408c2ecf20Sopenharmony_ci * number of blocks to read for a non-compressed image, 0 if the image
418c2ecf20Sopenharmony_ci * is a compressed image, and -1 if an image with the right magic
428c2ecf20Sopenharmony_ci * numbers could not be found.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * We currently check for the following magic numbers:
458c2ecf20Sopenharmony_ci *	minix
468c2ecf20Sopenharmony_ci *	ext2
478c2ecf20Sopenharmony_ci *	romfs
488c2ecf20Sopenharmony_ci *	cramfs
498c2ecf20Sopenharmony_ci *	squashfs
508c2ecf20Sopenharmony_ci *	gzip
518c2ecf20Sopenharmony_ci *	bzip2
528c2ecf20Sopenharmony_ci *	lzma
538c2ecf20Sopenharmony_ci *	xz
548c2ecf20Sopenharmony_ci *	lzo
558c2ecf20Sopenharmony_ci *	lz4
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic int __init
588c2ecf20Sopenharmony_ciidentify_ramdisk_image(struct file *file, loff_t pos,
598c2ecf20Sopenharmony_ci		decompress_fn *decompressor)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	const int size = 512;
628c2ecf20Sopenharmony_ci	struct minix_super_block *minixsb;
638c2ecf20Sopenharmony_ci	struct romfs_super_block *romfsb;
648c2ecf20Sopenharmony_ci	struct cramfs_super *cramfsb;
658c2ecf20Sopenharmony_ci	struct squashfs_super_block *squashfsb;
668c2ecf20Sopenharmony_ci	int nblocks = -1;
678c2ecf20Sopenharmony_ci	unsigned char *buf;
688c2ecf20Sopenharmony_ci	const char *compress_name;
698c2ecf20Sopenharmony_ci	unsigned long n;
708c2ecf20Sopenharmony_ci	int start_block = rd_image_start;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
738c2ecf20Sopenharmony_ci	if (!buf)
748c2ecf20Sopenharmony_ci		return -ENOMEM;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	minixsb = (struct minix_super_block *) buf;
778c2ecf20Sopenharmony_ci	romfsb = (struct romfs_super_block *) buf;
788c2ecf20Sopenharmony_ci	cramfsb = (struct cramfs_super *) buf;
798c2ecf20Sopenharmony_ci	squashfsb = (struct squashfs_super_block *) buf;
808c2ecf20Sopenharmony_ci	memset(buf, 0xe5, size);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/*
838c2ecf20Sopenharmony_ci	 * Read block 0 to test for compressed kernel
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	pos = start_block * BLOCK_SIZE;
868c2ecf20Sopenharmony_ci	kernel_read(file, buf, size, &pos);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	*decompressor = decompress_method(buf, size, &compress_name);
898c2ecf20Sopenharmony_ci	if (compress_name) {
908c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "RAMDISK: %s image found at block %d\n",
918c2ecf20Sopenharmony_ci		       compress_name, start_block);
928c2ecf20Sopenharmony_ci		if (!*decompressor)
938c2ecf20Sopenharmony_ci			printk(KERN_EMERG
948c2ecf20Sopenharmony_ci			       "RAMDISK: %s decompressor not configured!\n",
958c2ecf20Sopenharmony_ci			       compress_name);
968c2ecf20Sopenharmony_ci		nblocks = 0;
978c2ecf20Sopenharmony_ci		goto done;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* romfs is at block zero too */
1018c2ecf20Sopenharmony_ci	if (romfsb->word0 == ROMSB_WORD0 &&
1028c2ecf20Sopenharmony_ci	    romfsb->word1 == ROMSB_WORD1) {
1038c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1048c2ecf20Sopenharmony_ci		       "RAMDISK: romfs filesystem found at block %d\n",
1058c2ecf20Sopenharmony_ci		       start_block);
1068c2ecf20Sopenharmony_ci		nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
1078c2ecf20Sopenharmony_ci		goto done;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (cramfsb->magic == CRAMFS_MAGIC) {
1118c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1128c2ecf20Sopenharmony_ci		       "RAMDISK: cramfs filesystem found at block %d\n",
1138c2ecf20Sopenharmony_ci		       start_block);
1148c2ecf20Sopenharmony_ci		nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
1158c2ecf20Sopenharmony_ci		goto done;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* squashfs is at block zero too */
1198c2ecf20Sopenharmony_ci	if (le32_to_cpu(squashfsb->s_magic) == SQUASHFS_MAGIC) {
1208c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1218c2ecf20Sopenharmony_ci		       "RAMDISK: squashfs filesystem found at block %d\n",
1228c2ecf20Sopenharmony_ci		       start_block);
1238c2ecf20Sopenharmony_ci		nblocks = (le64_to_cpu(squashfsb->bytes_used) + BLOCK_SIZE - 1)
1248c2ecf20Sopenharmony_ci			 >> BLOCK_SIZE_BITS;
1258c2ecf20Sopenharmony_ci		goto done;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * Read 512 bytes further to check if cramfs is padded
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	pos = start_block * BLOCK_SIZE + 0x200;
1328c2ecf20Sopenharmony_ci	kernel_read(file, buf, size, &pos);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (cramfsb->magic == CRAMFS_MAGIC) {
1358c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1368c2ecf20Sopenharmony_ci		       "RAMDISK: cramfs filesystem found at block %d\n",
1378c2ecf20Sopenharmony_ci		       start_block);
1388c2ecf20Sopenharmony_ci		nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
1398c2ecf20Sopenharmony_ci		goto done;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/*
1438c2ecf20Sopenharmony_ci	 * Read block 1 to test for minix and ext2 superblock
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	pos = (start_block + 1) * BLOCK_SIZE;
1468c2ecf20Sopenharmony_ci	kernel_read(file, buf, size, &pos);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Try minix */
1498c2ecf20Sopenharmony_ci	if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
1508c2ecf20Sopenharmony_ci	    minixsb->s_magic == MINIX_SUPER_MAGIC2) {
1518c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1528c2ecf20Sopenharmony_ci		       "RAMDISK: Minix filesystem found at block %d\n",
1538c2ecf20Sopenharmony_ci		       start_block);
1548c2ecf20Sopenharmony_ci		nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
1558c2ecf20Sopenharmony_ci		goto done;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* Try ext2 */
1598c2ecf20Sopenharmony_ci	n = ext2_image_size(buf);
1608c2ecf20Sopenharmony_ci	if (n) {
1618c2ecf20Sopenharmony_ci		printk(KERN_NOTICE
1628c2ecf20Sopenharmony_ci		       "RAMDISK: ext2 filesystem found at block %d\n",
1638c2ecf20Sopenharmony_ci		       start_block);
1648c2ecf20Sopenharmony_ci		nblocks = n;
1658c2ecf20Sopenharmony_ci		goto done;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	printk(KERN_NOTICE
1698c2ecf20Sopenharmony_ci	       "RAMDISK: Couldn't find valid RAM disk image starting at %d.\n",
1708c2ecf20Sopenharmony_ci	       start_block);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cidone:
1738c2ecf20Sopenharmony_ci	kfree(buf);
1748c2ecf20Sopenharmony_ci	return nblocks;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic unsigned long nr_blocks(struct file *file)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct inode *inode = file->f_mapping->host;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (!S_ISBLK(inode->i_mode))
1828c2ecf20Sopenharmony_ci		return 0;
1838c2ecf20Sopenharmony_ci	return i_size_read(inode) >> 10;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciint __init rd_load_image(char *from)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int res = 0;
1898c2ecf20Sopenharmony_ci	unsigned long rd_blocks, devblocks;
1908c2ecf20Sopenharmony_ci	int nblocks, i;
1918c2ecf20Sopenharmony_ci	char *buf = NULL;
1928c2ecf20Sopenharmony_ci	unsigned short rotate = 0;
1938c2ecf20Sopenharmony_ci	decompress_fn decompressor = NULL;
1948c2ecf20Sopenharmony_ci#if !defined(CONFIG_S390)
1958c2ecf20Sopenharmony_ci	char rotator[4] = { '|' , '/' , '-' , '\\' };
1968c2ecf20Sopenharmony_ci#endif
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	out_file = filp_open("/dev/ram", O_RDWR, 0);
1998c2ecf20Sopenharmony_ci	if (IS_ERR(out_file))
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	in_file = filp_open(from, O_RDONLY, 0);
2038c2ecf20Sopenharmony_ci	if (IS_ERR(in_file))
2048c2ecf20Sopenharmony_ci		goto noclose_input;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	in_pos = rd_image_start * BLOCK_SIZE;
2078c2ecf20Sopenharmony_ci	nblocks = identify_ramdisk_image(in_file, in_pos, &decompressor);
2088c2ecf20Sopenharmony_ci	if (nblocks < 0)
2098c2ecf20Sopenharmony_ci		goto done;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (nblocks == 0) {
2128c2ecf20Sopenharmony_ci		if (crd_load(decompressor) == 0)
2138c2ecf20Sopenharmony_ci			goto successful_load;
2148c2ecf20Sopenharmony_ci		goto done;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * NOTE NOTE: nblocks is not actually blocks but
2198c2ecf20Sopenharmony_ci	 * the number of kibibytes of data to load into a ramdisk.
2208c2ecf20Sopenharmony_ci	 */
2218c2ecf20Sopenharmony_ci	rd_blocks = nr_blocks(out_file);
2228c2ecf20Sopenharmony_ci	if (nblocks > rd_blocks) {
2238c2ecf20Sopenharmony_ci		printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n",
2248c2ecf20Sopenharmony_ci		       nblocks, rd_blocks);
2258c2ecf20Sopenharmony_ci		goto done;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/*
2298c2ecf20Sopenharmony_ci	 * OK, time to copy in the data
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	if (strcmp(from, "/initrd.image") == 0)
2328c2ecf20Sopenharmony_ci		devblocks = nblocks;
2338c2ecf20Sopenharmony_ci	else
2348c2ecf20Sopenharmony_ci		devblocks = nr_blocks(in_file);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (devblocks == 0) {
2378c2ecf20Sopenharmony_ci		printk(KERN_ERR "RAMDISK: could not determine device size\n");
2388c2ecf20Sopenharmony_ci		goto done;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
2428c2ecf20Sopenharmony_ci	if (!buf) {
2438c2ecf20Sopenharmony_ci		printk(KERN_ERR "RAMDISK: could not allocate buffer\n");
2448c2ecf20Sopenharmony_ci		goto done;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",
2488c2ecf20Sopenharmony_ci		nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");
2498c2ecf20Sopenharmony_ci	for (i = 0; i < nblocks; i++) {
2508c2ecf20Sopenharmony_ci		if (i && (i % devblocks == 0)) {
2518c2ecf20Sopenharmony_ci			pr_cont("done disk #1.\n");
2528c2ecf20Sopenharmony_ci			rotate = 0;
2538c2ecf20Sopenharmony_ci			fput(in_file);
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci		kernel_read(in_file, buf, BLOCK_SIZE, &in_pos);
2578c2ecf20Sopenharmony_ci		kernel_write(out_file, buf, BLOCK_SIZE, &out_pos);
2588c2ecf20Sopenharmony_ci#if !defined(CONFIG_S390)
2598c2ecf20Sopenharmony_ci		if (!(i % 16)) {
2608c2ecf20Sopenharmony_ci			pr_cont("%c\b", rotator[rotate & 0x3]);
2618c2ecf20Sopenharmony_ci			rotate++;
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	pr_cont("done.\n");
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cisuccessful_load:
2688c2ecf20Sopenharmony_ci	res = 1;
2698c2ecf20Sopenharmony_cidone:
2708c2ecf20Sopenharmony_ci	fput(in_file);
2718c2ecf20Sopenharmony_cinoclose_input:
2728c2ecf20Sopenharmony_ci	fput(out_file);
2738c2ecf20Sopenharmony_ciout:
2748c2ecf20Sopenharmony_ci	kfree(buf);
2758c2ecf20Sopenharmony_ci	init_unlink("/dev/ram");
2768c2ecf20Sopenharmony_ci	return res;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ciint __init rd_load_disk(int n)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	create_dev("/dev/root", ROOT_DEV);
2828c2ecf20Sopenharmony_ci	create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n));
2838c2ecf20Sopenharmony_ci	return rd_load_image("/dev/root");
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int exit_code;
2878c2ecf20Sopenharmony_cistatic int decompress_error;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic long __init compr_fill(void *buf, unsigned long len)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	long r = kernel_read(in_file, buf, len, &in_pos);
2928c2ecf20Sopenharmony_ci	if (r < 0)
2938c2ecf20Sopenharmony_ci		printk(KERN_ERR "RAMDISK: error while reading compressed data");
2948c2ecf20Sopenharmony_ci	else if (r == 0)
2958c2ecf20Sopenharmony_ci		printk(KERN_ERR "RAMDISK: EOF while reading compressed data");
2968c2ecf20Sopenharmony_ci	return r;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic long __init compr_flush(void *window, unsigned long outcnt)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	long written = kernel_write(out_file, window, outcnt, &out_pos);
3028c2ecf20Sopenharmony_ci	if (written != outcnt) {
3038c2ecf20Sopenharmony_ci		if (decompress_error == 0)
3048c2ecf20Sopenharmony_ci			printk(KERN_ERR
3058c2ecf20Sopenharmony_ci			       "RAMDISK: incomplete write (%ld != %ld)\n",
3068c2ecf20Sopenharmony_ci			       written, outcnt);
3078c2ecf20Sopenharmony_ci		decompress_error = 1;
3088c2ecf20Sopenharmony_ci		return -1;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	return outcnt;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic void __init error(char *x)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s\n", x);
3168c2ecf20Sopenharmony_ci	exit_code = 1;
3178c2ecf20Sopenharmony_ci	decompress_error = 1;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int __init crd_load(decompress_fn deco)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	int result;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (!deco) {
3258c2ecf20Sopenharmony_ci		pr_emerg("Invalid ramdisk decompression routine.  "
3268c2ecf20Sopenharmony_ci			 "Select appropriate config option.\n");
3278c2ecf20Sopenharmony_ci		panic("Could not decompress initial ramdisk image.");
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	result = deco(NULL, 0, compr_fill, compr_flush, NULL, NULL, error);
3318c2ecf20Sopenharmony_ci	if (decompress_error)
3328c2ecf20Sopenharmony_ci		result = 1;
3338c2ecf20Sopenharmony_ci	return result;
3348c2ecf20Sopenharmony_ci}
335