Qemu使用技巧

Last Updated: 2023-09-24 06:18:35 Sunday

-- TOC --

Windows平台使用VMware,Linux平台使用Qemu,特别是在需要调试与kernel有关的场景下。

Qemu官网:https://www.qemu.org

Qemu可以做3件事情:

  1. 完整的系统模拟,即我们熟悉的虚拟机;
  2. 用户态程序模拟,比如在x86的平台上模拟运行arm或risc-v程序;
  3. 虚拟化方案;(还不太懂)

通过Qemu程序的名称,可以知道它对应的ISA架构和应用场景:

$ apt list | grep qemu
$ sudo apt install qemu-system-misc  # RISC-V
$ sudo apt install qemu-user         # RISC-V user-mode

Or:

$ ll /usr/bin/qemu-*
-rwxr-xr-x. 1 root root 526K Sep 21 16:53 /usr/bin/qemu-ga
-rwxr-xr-x. 1 root root 2.0M Sep 21 16:53 /usr/bin/qemu-img
-rwxr-xr-x. 1 root root 2.0M Sep 21 16:53 /usr/bin/qemu-io
lrwxrwxrwx. 1 root root   18 Sep 21 16:48 /usr/bin/qemu-kvm -> qemu-system-x86_64
-rwxr-xr-x. 1 root root 2.1M Sep 21 16:53 /usr/bin/qemu-nbd
-rwxr-xr-x. 1 root root 915K Sep 21 16:53 /usr/bin/qemu-pr-helper
-rwxr-xr-x. 1 root root 2.9M Sep 21 16:53 /usr/bin/qemu-storage-daemon
-rwxr-xr-x. 1 root root  14M Sep 21 16:53 /usr/bin/qemu-system-i386
-rwxr-xr-x. 1 root root  14M Sep 21 16:53 /usr/bin/qemu-system-x86_64

Qemu命令行参数

-name:指定虚拟机的name。

-kernel:指定kernel文件。

-initrd:指定initramfs文件。

-bios:指定firmware,默认是seabios,可以在此指定OVMF,让qemu按UEFI的方式启动。

-hd[a|b|c|d],指定Hard Disk,指向device file或image file,而不是其分区。即要写成/dev/sdc,而不是/dev/sdc1,可以是/dev/loop0这样的loop device

-cdrom,指定iso镜像。

-m:设置qemu虚拟机内存大小,默认单位MiB,默认128MiB,还可在此参数后面配置memory hotplug,暂略。

Optionally, a suffix of "M" or "G" can be used to signify a value in megabytes or gigabytes respectively.

-append:设置kernel启动参数。console=ttyS0,将启动期间所有打印重定向到串口,以方便查看,举例如下:

$ qemu-system-x86_64 -kernel bzImage_3rd \
> -initrd initrd_3rd.img \
> -m 1024 -append \
> 'bootenv=1 console=ttyS0'

Linux kernel会将自己不认识的启动参数,传递给init。默认kernel的打印都在vga上,无法向上翻!但串口可以。以上述方式启动kernel,跳过了grub阶段!

-nographic:启动时没有guest window,qemu成为一个命令行程序

-vga std:设置VGA为std类型

-smp:设置cpu core number

-S:Do not start CPU at startup (you must type 'c' in the monitor).

-s:Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234.

-enable-kvm,表示启动kvm虚拟机,这需要在BIOS中打开支持虚拟化的开关,这种模式下虚拟机运行速度更快

kvm_qemu.png

-vnc host:d,开启VNC,端口号为5900+d,监听在host上(ip地址),开启其参数后,本地的显示就没有了。

-net none,配置为不支持网络

-boot,指定虚拟机启动顺序,具体细节见manpage。

-drive

$ sudo qemu-system-x86_64 -drive format=raw,file=helloOS.img
$ sudo ... -drive file=fat:rw:/path/to/esp,index=0,format=vvfat

Qemu快捷键

Ctrl+Alt+g:将鼠标从qemu虚拟机窗口中弹出来

Ctrl+Alt+f:focus,去掉qemu窗口的周边,只留下中间部分,分辨率小的时候可以试试。

Ctrl-A, then x:退出nographic启动的虚拟机

用Qemu安装Win虚拟机

首先,需要创建一块硬盘文件,建议使用qcow2格式:

$ qemu-img create -f qcow2 win10.qcow2 40G

40G表示此文件的size上限,qcow2格式的文件,是动态增长的。

然后启动虚拟机:

$ qemu-system-x86_64 -enable-kvm -m 4G -smp 2 -hda win10.qcow2

用Qemu运行RISC-V程序

记录一下这个过程遇到的问题,以及解决办法。

$ sudo apt install qemu-user
$ sudo apt install crossbuild-essential-riscv64

然后risc-v gnu toolchain就有了,省了自己下载编译,用ll /usr/bin/riscv64-*查看交叉编译工具箱里的工具。

用C写一个hello world。交叉编译:

$ riscv64-linux-gnu-gcc -static test.c -o test_static
$ riscv64-linux-gnu-gcc test.c -o test -Wl,--dynamic-linker=/usr/riscv64-linux-gnu/lib/ld-linux-riscv64-lp64d.so.1

动态链接的时候,使用-Wl,--dynamic-linker给ld传递参数,指定ld的版本。(应该叫runtime linker,这个细节很重要)

可执行的elf文件,在.interp section中,存放了runtime linker的路径:

$ riscv64-linux-gnu-readelf -p1 test

String dump of section '.interp':
  [     0]  /usr/riscv64-linux-gnu/lib/ld-2.31.so

然后就可以运行了,两个版本都可以用qemu运行:

$ qemu-riscv64 test_static
$ qemu-riscv64 test

用Qemu启动OVMF

OVMF,Open Virutal Machine Firmware,就是一个用于虚拟机启动的UEFI固件,来自EDK2项目。Qemu默认使用seaBios启动虚拟机,可以通过-bios参数指定OVMF。

首先在系统中找到OVMF文件:

$ sudo find / -iname '*ovmf*.fd'

如果找不到,可以通过dnf或apt安装软件包得到。

$ sudo qemu-system-x86_64 -bios /usr/share/OVMF/OVMF_CODE.fd -net none ...

如果没有找到启动app,就会进入UEFI Shell(这是个很像Windows cmd的shell)。

用OVMF启动UEFI应用

$ sudo qemu-system-x86_64 -bios /usr/share/OVMF/OVMF_CODE.fd -net none \ 
                     -drive file=fat:rw:/path/to/esp,index=0,format=vvfat

QEMU有将目录挂载为虚拟FAT驱动器的功能。例如,创建一个名为esp的目录,将efi文件放到esp/EFI/BOOT/BOOTX64.EFI并使用-drive file=fat:rw:/path/to/esp,index=0,format=vvfat选项运行QEMU。虚拟机将会把esp目录当作是一个FAT分区,并会自动启动到其中的BOOTX64.EFI文件。

用Qemu启动loop设备中的grub

这是搭建完全虚拟化测试环境的方法,而且运行速度还飞快!

思路:用dd创建大文件,mkfs,然后mount到某个位置,将grub安装到这个位置。这个位置对应一个loop设备,用df -h可以查看。通过qemu的-hda参数指向此loop设备,启动grub。(UEFI亲测OK,BIOS要稍微复杂一些,此时不能仅用一个分区启动,必须要用大文件模拟整块硬盘)

坑:在编译grub/grub.cfg配置的文件的时候,发现此文件会莫名其妙的被修改成乱码,导致配置文件失效。在U盘上测试,也是这个效果。不明觉厉。。。后来发现,如果将U盘拿到其它电脑上去编辑此文件,再过来启动grub,就OK。如果在编辑了/dev/loop设备文件后,umount,再mount后使用,也OK!规避的策略:编辑完后umount,qemu直接使用image file启动!

本文链接:https://cs.pynote.net/sf/202201121/

-- EOF --

-- MORE --