This would be the way to start from Uefi shell directly. But really, the
initrd= is missing! I had exactly the same problem: The SATA/SCSI modules are not in the kernels, they are modules in the initramfs (mostly). Copy the initrd/initramfs beside your kernel and add the
initrd=initram-cpio.gz (adapt "image" name!) option to the command line.
The VFS error says: the root (/dev/sda2) you give me is on a block device I cannot read.
After looking up "yocto" I want to add: you should configure the needed modules as builtin ("y") and not as modules ("m"). That way you don't need a initrd, and that seems to be the idea behind this "embedded" project.
added after looking up yocto some more...
This yocto (and your Q) did not leave me alone. I found this from a 2016 conference/article (on lwn.net):
At the basic level [...] Yocto and Buildroot can both give
you the same end product: a root filesystem image for your embedded
device, a kernel, a bootloader, and a compatible toolchain.
As I see it, you rather want to use "make" to compile a kernel, plus modules, plus initrd if you like.
Can you tell me why you wrote this:
I used dd to put [the kernel image] on the hard drive
Now this is very confusing to me. As if you had done
dd ... of=/dev/sda. I would say: I copied the kernel image into a directory on partition x.
So you went all through this Yocto build stuff, bitbaked a kernel and initrd, and now you do not know how to boot a linux kernel?!?
I started out with a new mini-pc kit, so the disk was empty. I had decided to use GPT and Uefi. My last expereince had been MBR long time ago. First I booted a slackware installer from USB flash.
With fdisk I made a few partitions, including a "EFI" or "ESP" type. I wisely made it 2GB. Some recommend only 100MB. The ESP needs vfat formatting, because it is read also by BIOS.
The only (compiled) Linux kernel I found were the ones inside the ISO images of the installers and live systems. From a opensuse live usb system I could mount any ISO, then mount the squashs rootfs with the two files: kernel and initrd.
These two files (kernel and initial ram disk) I copied to my 2GB ESP. A kernel is 3 to 8 MB, a initrd 10 to 30 MB.
I made my BIOS boot into Uefi shell, where I can start a kernel by typing it's name like a uefi shell command.
This all took me some time to figure out, so when I got a kernel panic this was a big success.
This is how I start the kernel with filename "vmlinuz" from uefi prompt, from the ESP which is "fs0".
Because root= is missing, this should also give a panic. I soon tried:
fs0: vmlinuz root=/dev/sda2
And this gave me the kernel panic you originally described:
VFS: unable to mount rootfs...
From here on you can take two paths: use the initrd that "comes with" the kernel by adding
initrd=...cpio.gz as KCL option (kernel command line). Or use the Fedora 29 kernel --- it was the only one that could mount my sda device. The reason is: it has a few Kconfig options built-in, while all the other distros put them as modules in the initrd (sata, scsi, sd-mod).
While I am quite happy with a quick archlinux install, I really think I "have to" compile a kernel myself so I can boot without a initrd. A kernel should be able to mount the root fs by itself.
You see: without compiling or yocto, without grub, just by going into the ISO from the distros, I was able to do a lot of experiments. These are the boot options that matter:
The original kernel doc from 1996-2000 is misleading in some places. But Almesberger clearly defines: his initrd "allows" the bootup to happen in two stages. And: the
rdinit= script inside the ramdisk can mount the 'real' root and switch_root to it. There are situations where this two-step-bootup is necessary.
I followed the "VFS:" kernel panic in the kernel source. A function "prepare_namespace" is central. This part where the kernel mounts root= is skipped when a initrd= is given; there is a comment in the sources "if there is a initrd, let it to all the work".
without a initrd=, this tells the kernel directly into which (block) device it should "boot". With a initrd, it can be used to switch_root to. A
root=/dev/ram0 has special meaning: it makes the initial ram disk the real root from the start. This is useful for a lot of embedded systems, I guess: the rootfs lives in a cpio file and not on a partition.
This is the last thing the kernel does at bootup: call the first process. As with root=, if you give a initrd=, it is up to the ramdisk to parse that argument. Note that the kernel only can make sense of "/" after a root= is mounted.
Once your BIOS is switched to "Uefi shell" and your disk is GPT formatted (or hybrid/protective MBR) and you have added a small (but not too small) ESP vfat partition, you are ready to start a kernel plus initrd.
My plan now is simply to compile a kernel that can mount my standard SSD partitions, so I don't need a initrd. I do not need yocto or grub to do some softcore "embedding". Unless I want to compile the newest kernel source twice a month...but hey, I want to compile a kernel, not create my own distro!
I stop here and hope one can get some linux bootup ideas from my kernel command line adventures.