[Bug] ramdisk loading fails and the system crashes during Linux kernel startup.
Bug Description: ramdisk loading fails and the system crashes during Linux kernel startup.
Log information:
- RAMDISK: Couldn't find valid RAM disk image starting at 0.
- UDF-fs: No partition found (1)
- NILFS: Can't find nilfs on dev ram0.
- (1,15):ocfs2_fill_super:1001 ERROR: superblock probe
- VFS: Cannot open root device "ram0" or unknown-block(1,0)
- Please append a correct "root=" boot option; here are the available partitions:
- 0800 8003520 sda driver: sd
- 0801 14048 sda1
- 0804 1 sda4
- 0805 393057 sda5
- 0806 102400 sda6
- 0807 7488690 sda7
- Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)
- Unable to load '/system/dump '.
- Pid: 1, comm: swapper Not tainted 2.6.32.15-hermes-1 #23
- Call Trace:
- UTC time : 2005-1-1 0:14:52
- [ ] panic+0x7a/0x12d
- [ ] mount_block_root+0x257/0x275
- [ ] mount_root+0x56/0x5a
- [ ] prepare_namespace+0x16b/0x198
- [ ] kernel_init+0x178/0x188
- [ ] child_rip+0xa/0x20
- [ ] ? kernel_init+0x0/0x188
- [ ] ? child_rip+0x0/0x20
First see Couldn't find valid RAM disk image starting at 0.
Find the code to print this information:
- static int __init
- identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor)
- {
- const int size = 512;
- struct minix_super_block *minixsb;
- struct ext2_super_block *ext2sb;
- struct romfs_super_block *romfsb;
- struct cramfs_super *cramfsb;
- struct squashfs_super_block *squashfsb;
- int nblocks = -1;
- unsigned char *buf;
- const char *compress_name;
- int i = 0;
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return -1;
- minixsb = (struct minix_super_block *) buf;
- ext2sb = (struct ext2_super_block *) buf;
- romfsb = (struct romfs_super_block *) buf;
- cramfsb = (struct cramfs_super *) buf;
- squashfsb = (struct squashfs_super_block *) buf;
- memset(buf, 0xe5, size);
- /*
- * Read block 0 to test for compressed kernel
- */
- sys_lseek(fd, start_block * BLOCK_SIZE, 0);
- sys_read(fd, buf, size);
- // Eric Ju Jul 27th 2016
- printk("start_block:%d\n",start_block);
- for(i=0;i
- printk("0x%x ",*(buf+i));
- printk("\n");
- *decompressor = decompress_method(buf, size, &compress_name);
- if (compress_name) {
- printk(KERN_NOTICE "RAMDISK: %s image found at block %d\n",
- compress_name, start_block);
- if (!*decompressor)
- printk(KERN_EMERG
- "RAMDISK: %s decompressor not configured!\n",
- compress_name);
- nblocks = 0;
- goto done;
- }
- /* romfs is at block zero too */
- if (romfsb->word0 == ROMSB_WORD0 &&
- romfsb->word1 == ROMSB_WORD1) {
- printk(KERN_NOTICE
- "RAMDISK: romfs filesystem found at block %d\n",
- start_block);
- nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
- goto done;
- }
- if (cramfsb->magic == CRAMFS_MAGIC) {
- printk(KERN_NOTICE
- "RAMDISK: cramfs filesystem found at block %d\n",
- start_block);
- nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
- goto done;
- }
- /* squashfs is at block zero too */
- if (le32_to_cpu(squashfsb->s_magic) == SQUASHFS_MAGIC) {
- printk(KERN_NOTICE
- "RAMDISK: squashfs filesystem found at block %d\n",
- start_block);
- nblocks = (le64_to_cpu(squashfsb->bytes_used) + BLOCK_SIZE - 1)
- >> BLOCK_SIZE_BITS;
- goto done;
- }
- /*
- * Read block 1 to test for minix and ext2 superblock
- */
- sys_lseek(fd, (start_block+1) * BLOCK_SIZE, 0);
- sys_read(fd, buf, size);
- /* Try minix */
- if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
- minixsb->s_magic == MINIX_SUPER_MAGIC2) {
- printk(KERN_NOTICE
- "RAMDISK: Minix filesystem found at block %d\n",
- start_block);
- nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
- goto done;
- }
- /* Try ext2 */
- if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) {
- printk(KERN_NOTICE
- "RAMDISK: ext2 filesystem found at block %d\n",
- start_block);
- nblocks = le32_to_cpu(ext2sb->s_blocks_count) <<
- le32_to_cpu(ext2sb->s_log_block_size);
- goto done;
- }
- printk(KERN_NOTICE
- "RAMDISK: Couldn't find valid RAM disk image starting at %d.\n",
- start_block);
- done:
- sys_lseek(fd, start_block * BLOCK_SIZE, 0);
- kfree(buf);
- return nblocks;
- }
As you can see, this log is printed because all the branches in this function are not matched successfully. Normally, the function should go to the first branch and jump to the done.
Why didn't I go into the first branch? I guess fd should be the file descriptor pointing to initrd. The read before the first branch should read the content of the first sector of initrd and perform magic comparison. When the match is successful, it indicates that initrd is the correct image file and calls the corresponding decompression function to decompress it. Check whether the initrd file is correct by printing the read buf content. After the experiment, all the printed content is 0xFF, which proves that the initrd file is incorrect.
Why is the initrd file incorrect? The initrd. imgfile on the disk is correct. Continue to trace the call of identify_ramdisk_image to see what fd is? After tracking, we found that the following functions are located in the kernel source code/init/do_mounts_initrd.c.
- int __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) {
- sys_unlink("/initrd.image");
- handle_initrd();
- return 1;
- }
- }
- sys_unlink("/initrd.image");
- return 0;
- }
Initrd. image file? No. How can I change the file name of initrd. img on our disk to initrd. image? And the path is incorrect. I guess the initrd. image file should have some startup code that has created a symbolic link to initrd. img. Continue to find where the initrd. image is created? Find the following code at/init/initramfs. c
- static int __init populate_rootfs(void)
- {
- int i=0;
- char *err = unpack_to_rootfs(__initramfs_start,
- __initramfs_end - __initramfs_start);
- if (err)
- panic(err);/* Failed to decompress INTERNAL initramfs */
- if (initrd_start) {
- #ifdef CONFIG_BLK_DEV_RAM
- int fd;
- printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
- err = unpack_to_rootfs((char *)initrd_start,
- initrd_end - initrd_start);
- if (!err) {
- free_initrd();
- return 0;
- } else {
- clean_rootfs();
- unpack_to_rootfs(__initramfs_start,
- __initramfs_end - __initramfs_start);
- }
- printk(KERN_INFO "rootfs image is not initramfs (%s)"
- "; looks like an initrd\n", err);
- fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
- if (fd >= 0) {
- sys_write(fd, (char *)initrd_start,
- initrd_end - initrd_start);
- sys_close(fd);
- free_initrd();
- }
- #else
- printk(KERN_INFO "Unpacking initramfs...\n");
- err = unpack_to_rootfs((char *)initrd_start,
- initrd_end - initrd_start);
- if (err)
- printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
- free_initrd();
- #endif
- }
- return 0;
- }
The initrd. image file is created here. Another sentence is: sys_write (fd, (char *) initrd_start, initrd_end-initrd_start); it seems that the kernel writes the relevant data from the memory to/init. image. It is not a soft link. Where is initrd_start? Where is the data? After printing initrd_start, it is found that initrd_start is 0xffff880000100000, which is a virtual address that has been converted. Since we know that initrd. image is written to the root file system from the memory, there must be other programs that read our initrd. img into the memory. Where is initrd. img read into the memory? Where is the file path provided? Remember, the lilo. multi. conf file contains the path of the specified initrd. imgfile. It must be that when lilo is started, it reads initrd. img into the memory and passes the address to the kernel. Check the serial port log. The following information is contained in the log just started:
- RAMDISK: 7fa36000 - 7ffff40e
- Allocated new RAMDISK: 00100000 - 006c940e
- Move RAMDISK from 000000007fa36000 - 000000007ffff40d to 00100000 - 006c940d
As you can see, the starting address of RAMDISK is 0x7fa36000. Is the physical address converted from this virtual address? The third line looks like the kernel moves the RAMDISK content to the 0x00100000 address. After comparing the virtual address 0xffff880000100000, you can determine that the virtual address must be mapped from 0x00100000. Because the kernel uses address ing at low physical addresses, the actual physical address is offset after the virtual high-end address is set. Print the following content before and after RAMDISK is moved to see if it is not an error. It is found that the RAMDISK is 0xFF before it is moved. It can be concluded that LILO has made an error when turning initrd. img. From the perspective of 0xFF, the physical memory should be the initial state after power-on. Suddenly another message is displayed. After the kernel is replaced and the lilo64-C lilo. multi. conf-s 'pwd' is executed, lilo reports a warning message:
- Normally any initial ramdisk (initrd) loaded with a kernel is loaded as
- high in memory as possible, but never above 15Mb. This is due to a BIOS
- limitation on older systems. On newer systems, this option enables using
- memory above 15Mb (up to a kernel imposed limit, around 768Mb) for
- passing the initrd to the kernel. The presence of this option merely
- indicates that your system does not have the old BIOS limitation.
Let's look at the initial starting address of RAMDISK: 0x7fa36000. Obviously, this address is higher than the address of 15 MB, which indicates that LILO considers the kernel and initrd. the size of img exceeds a fixed limit and initrd is placed in high-end memory. Why does LILO fail to write data into the memory? From the above information, we can see that the BIOS does not support access to the high-end memory when it is just powered on. Therefore, LILO encountered an error when calling the BIOS Write Program, LILO does not care about this error.
Find a technical document on LILO's HomePage, which clearly states that LILO will load initrd. img At the end of the low-end address of the memory (less than 16 Mb ). The limit of 16 Mb is that the BIOS only uses 24-bit address space for data transmission. After reading the LILO code, LILO will be 3 times the kernel image size and initrd. the total size of the img is calculated. When the total size is greater than 14 MB, LILO considers that the low address space below 14 MB cannot be placed into the kernel and initrd image files, so the BIOS supports the address space above 16 MB, therefore, when LILO loads the initrd image, it places initrd in the high address space.
LILO Technical Documentation: commit.