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?

Ciro Santilli OurBigBook.com
  • 18,092
  • 4
  • 117
  • 102
Coder404
  • 895

3 Answers3

10

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.

  • 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 '13 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. – Stéphane Chazelas Mar 11 '13 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.

t-8ch
  • 1,823
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

Ciro Santilli OurBigBook.com
  • 18,092
  • 4
  • 117
  • 102