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