13

I had compiled a linux kernel and I wanted to debug it in QEMU. I created a file to boot from by doing the commands

$ qemu-img create -f raw disk.img 200M
$ mkfs.ext2 -F disk.img
# mkdir /mnt/rootfs
# mount -o loop disk.img /mnt/rootfs

Then I did qemu -kernel bzImage -initrd disk.img and got the screen below which says:

Kernel panic - not syncing: VFS: unable to mount root fs on unknown block

My QEMU screen

What have I done wrong and what can I do to fix it?

2

3 Answers 3

9

What's happening is that you're trying to boot Linux in the "Obsolete" way. That is where the initrd is a ramdisk as opposed to a compressed cpio archive unpacked by the kernel in a ramfs, and with the old way to switch to the end device.

In that mode, the kernel mounts the disk.img as a ramdisk as the root file system and then executes /linuxrc in there. Most likely in your case, there's no such file. When /linuxrc (which is supposed to do whatever's necessary to bring up the block device for the real root filesystem) exits, then the kernel mounts the real root file system.

The messages above show that it mounts the ram disk successfully (1,0: 1 is for ram, so /dev/ram0) but not the real root file system /dev/sda1 (8,1: 8 is sd, 1 is a1). Presumably since you didn't specify a kernel command line (-append), that /dev/sda1 comes from a CONFIG_CMDLINE passed at kernel compile time or using rdev.

If your disk.img is meant to contain a root file system of say a small Linux distribution with /sbin/init..., then you probably want to write it instead:

kvm -kernel kernel.img -initrd disk.img -append 'root=/dev/ram0`

Then, the kernel would treat the ram disk as the real root file system (though you could still pivot_root to another one).

To be able to see the kernel messages more easily, I'd recommend using serial output:

kvm -kernel kernel.img -initrd disk.img -nographic -append "root=/dev/ram0 console=ttyS0"

As an alternative you could use an init ramfs instead of an init ramdisk:

mkdir -p RAMFS/{bin,dev} 
cd RAMFS/bin
cp /bin/busybox .
"$PWD/busybox" --install .
cd ..
cp -a /dev/{null,tty,zero,console} dev
printf '%s\n' "#! /bin/sh -" "exec /bin/sh" > init
chmod +x init
find . | cpio -oHnewc | gzip > ../initramfs.gz
cd ..
kvm -kernel kernel.img -initrd initramfs.gz

(provided busybox is the statically linked version) and you'll get a shell and other busybox utilities in that kernel).

Note that the kernel now runs /init as opposed to /linuxrc or /sbin/init in that mode.

2
  • Line 3 of the shown output shows, that the kernel mounted the ext2 filesystem of the initramdisk. So it's probably not a missing module.
    – t-8ch
    Mar 11, 2013 at 17:36
  • Oh yes, I had missed that, thanks @t-8ch. I think I know what's going on and have updated my answer. Mar 11, 2013 at 22:16
8

The kernel is telling you, that it doesn't know which device holds the root file system. Your loop mount isn't necessary. (Unmount it before continuing).

Try a command like

qemu -kernel bzImage -hda disk.img -append root=/dev/sda

The -hda disk.img parameter tells qemu to simulate a disk device based on your disk.img.

The -append root=/dev/sda switch is used by qemu to tell the kernel about it's root device. This is done by appending the root=/dev/sda to the kernel commandline. You can compare this to the kernel commandline of your own kernel by doing cat /proc/cmdline (This is safe). You should see there a root parameter, too.

6
  • How would I unmount the files?
    – Coder404
    Mar 10, 2013 at 21:20
  • umount /mnt/rootfs
    – t-8ch
    Mar 10, 2013 at 21:27
  • When I do that I get umount: /mnt/rootfs is not mounted (according to mtab)
    – Coder404
    Mar 10, 2013 at 21:40
  • Presumably Coder404 doesn't want to attach a disk to that machine and just run the init in the initrd. Here you're passing the disk.img both as a hard disk and a initrd which doesn't make sense. Mar 10, 2013 at 22:53
  • @StephaneChazelas thanks about the hint about -initrd that shouldn't have been there.
    – t-8ch
    Mar 11, 2013 at 17:34
1

CONFIG_BLK_DEV_INITRD=y

This kernel config option is also required. It enables initrd support on the Linux kernel.

Luckly Buildroot sets it by default for us when BR2_TARGET_ROOTFS_CPIO=y is given.

You then pass the CPIO to QEMU with the qemu -initrd option. My full QEMU command is:

./buildroot/output.x86_64~/host/usr/bin/qemu-system-x86_64 -m 128M -monitor telnet::45454,server,nowait -netdev user,hostfwd=tcp::45455-:45455,id=net0 -smp 1  -M pc -append ' nopat nokaslr norandmaps printk.devkmsg=on printk.time=y console=ttyS0' -device edu -device lkmc_pci_min -device virtio-net-pci,netdev=net0 -kernel ./buildroot/output.x86_64~/images/bzImage  -nographic  -initrd './buildroot/output.x86_64~/images/rootfs.cpio'

Here is a minimalistic fully automated Buildroot + QEMU example: https://github.com/cirosantilli/linux-kernel-module-cheat/tree/b3868a3b009f2ab44fa6d3db3d174930b3cf7b69#initrd

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .