赞
踩
QEMU(Quick EMUlator)是一个开源的虚拟化软件,它能够模拟多种硬件平台,并在这些平台上运行各种操作系统。QEMU可以在不同的主机架构之间进行虚拟化,例如x86、ARM、PowerPC、Risc-V等。QEMU是一个功能强大且灵活的虚拟化软件,可用于多种应用场景,包括系统仿真、硬件虚拟化、交叉编译以及设备模拟等。它广泛应用于开发、测试和部署各种软件和操作系统。
以下是QEMU的一些重要特点和用途:
QEMU适用于Windows、Linux和Mac,因为Linux开源的原因,QEMU在Linux上能够更好地利用虚拟化等技术,使得虚拟的性能相较其他平台会更好一些。通用情况下,可以直接使用预编译后的二进制版本,如果需要进行额外地扩展,可以自行编译版本。
● 可以从https://qemu.weilnetz.de/下载相应的版本。
● 可以在MSYS32中通过命令 pacman -S qemu 来安装。
Windows版本的QEMU需要使用mingw来编译,可以在Linux下编译,也可以在Windows下编译。Windows下推荐使用MSYS2进行编译。
pacman -Syu
pacman -Su
pacman -S base-devel mingw-w64-x86_64-toolchain git python ninja
pacman -S mingw-w64-x86_64-glib2 mingw-w64-x86_64-pixman python-setuptools
pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-SDL2 mingw-w64-x86_64-libslirp
git clone https://gitlab.com/qemu-project/qemu.git
// gitlab较慢时,可以使用国内镜像
git clone https://gitee.com/mirrors/qemu.git
cd qemu
./configure
make
更多编译方法参见:QEMU Build for Windows
此处主要以ubuntu 22.04版本为例。
Linux下的qemu各版本需要分别安装,如:
sudo apt install qemu-system-arm
sudo apt install qemu-system-aarch64
sudo apt install qemu-system-riscv32
sudo apt install qemu-system-riscv64
推荐从源码编译安装,更方便调试添加打印信息。推荐以tag版本安装,更可靠。以下测试是基于从gitee上克隆的版本进行测试。安装可以配置–enable-debug启用调试。
git clone https://gitee.com/mirrors/qemu.git
cd qemu
mkdir build
cd build
…/configure
make -j8
sudo make install
输入qemu-system-aarch64 -version显示以下信息表示安装成功。
QEMU emulator version 8.1.93 (v8.2.0-rc3)
Copyright © 2003-2023 Fabrice Bellard and the QEMU Project developers
此处以最小系统嵌入式固件来演示使用过程,并且分别演示Windows和Linux下的交叉编译,以及基于Cortext-M和risc-v两个当下流行的单片机为例。代码只执行一个Uart打印信息。
int main (void)
{
InitUart();
UartWrite("Hello World!\n", 13);
while (1)
{
/* code */
}
return 0;
}
qemu-system-arm.exe -machine mps2-an385 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel $(APP) -serial stdio -nographic
qemu-system-arm.exe -machine mps2-an385 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel $(APP) -serial stdio -nographic -S -s
gdb调试
结果
riscv-v使用Linux进行测试。
安装交叉编译工具链
在https://www.sifive.com/software上下载工具链,并将路径添加进环境路径。
编译并运行
调试
方法同cortext-m7。
gdb调试不太方便,利用VSCode可以更方便地查看变量,单步调试代码。添加lanch.json文件,将以下内容覆盖。按F5启动调试,代码即停止在main函数处。
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Launch QEMU to debug HelloWorld.elf", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/HelloWorld.elf", "cwd": "${workspaceFolder}", "miDebuggerPath": "arm-none-eabi-gdb", "miDebuggerServerAddress": "localhost:1234", "stopAtEntry": true, "preLaunchTask": "Run QEMU" } ] }
代码包括完整的固件,只需要安装交叉编译工具和qemu,即可以测试。
下面是Cortex-A57 CPU来安装Linux。
wget http://releases.linaro.org/components/kernel/uefi-linaro/16.02/release/qemu64/QEMU_EFI.fd -O /data/kvm/QEMU_EFI.fd
qemu-img create ubuntu20.04-arm64.img 32G
qemu-img create -f qcow2 nvme.qcow2 10G
qemu-system-aarch64 -machine virt
-cpu cortex-a57
-smp 2
-m 4G
-bios QEMU_EFI.fd
-device virtio-scsi-device
-device scsi-cd,drive=cdrom
-drive if=none,file=ubuntu-20.04-live-server-arm64.iso,id=cdrom,media=cdrom
-device virtio-blk-device,drive=vd0
-drive if=none,file=ubuntu20.04-arm64.img,id=vd0
-drive file=nvme.qcow2,if=none,id=nvme0 -device nvme,drive=nvme0,serial=foo
-net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
几分钟后安装界面,默认往下执行即可,设置用户名密码等,最终进入安装流程,安装会比较慢。安装完成之后,直接关闭当前控制台窗口,不要选Restart,会重新进行安装。
6. 启动虚拟机
启动虚拟机和安装差不多,只是不用cdrom系统文件,直接从镜像启动即可。
qemu-system-aarch64 -machine virt
-cpu cortex-a57
-smp 2
-m 4G
-bios QEMU_EFI.fd
-device virtio-blk-device,drive=vd0
-drive if=none,file=ubuntu20.04-arm64.img,id=vd0
-drive file=nvme.qcow2,if=none,id=nvme0 -device nvme,drive=nvme0,serial=foo
-net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
启动之后,等一两分钟,就会进入系统,输入用户名密码登录。
git clone https://gitee.com/mirrors/linux_old1.git
sudo apt-getinstall gcc-aarch64-linux-gnu
cd linux_old1
// arch/arm64/configs/defconfig 中将CONFIG_RANDOMIZE_BASE=y修改成CONFIG_RANDOMIZE_BASE=n
// 检查CONFIG_DEBUG_KERNEL=y
// 检查CONFIG_DEBUG_INFO_REDUCED=y
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
qemu-img create rootfs.img 512m
mkfs.ext4 rootfs.img
mkdir rootfs
sudo mount rootfs.img rootfs
sudo cp -rf _install/* rootfs
cd rootfs
sudo mkdir proc sys dev etc etc/init.d
sudo vim etc/init.d/rcS
sudo chmod +x etc/init.d/rcS
cd …
sudo umount rootfs
//# etc/init.d/rcS文件内容如下
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
qemu-system-aarch64 -machine virt,virtualization=true,gic-version=3 -nographic -m size=1024M -cpu cortex-a72 -smp 2 -kernel Image -drive format=raw,file=rootfs.img -append “root=/dev/vda rw”
qemu-system-aarch64 -machine virt,virtualization=true,gic-version=3 -nographic -m size=1024M -cpu cortex-a72 -smp 2 -kernel Image -drive format=raw,file=rootfs.img -append “root=/dev/vda rw nokaslr” -S -s
set auto-load safe-path /
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Launch QEMU to debug HelloWorld.elf", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/vmlinux", "cwd": "${workspaceFolder}", "miDebuggerPath": "gdb-multiarch", "miDebuggerServerAddress": "localhost:1234", "stopAtConnect": true, "stopAtEntry": true, } ] }
开发代码基于qemu 8.2.1版本。
git clone https://gitee.com/mirrors/qemu.git
要扩展QEMU的功能,肯定需要用到调试,所以编译时需要打开debug选项,并且可以指定编译的工具来减少编译时间。详细编译配置,参见./configure -help。如下只编译riscv版本。
./configure --target-list=riscv32-softmmu --enable-debug --disable-werror
make -j8
qemu是默认一块完整的开发板,其模拟的硬件不仅有cpu,还有各种外设。相关代码放在hw目录,uart外设则放 在char目录中。
ys_uart.c
ys_uart_read(uint64_t offset, uint64_t data, unsigned size) “CMSDK APB UART read: offset 0x%” PRIx64 " data 0x%" PRIx64 " size %u"
ys_uart_write(uint64_t offset, uint64_t data, unsigned size) “CMSDK APB UART write: offset 0x%” PRIx64 " data 0x%" PRIx64 " size %u"
ys_uart_reset(void) “CMSDK APB UART: reset”
ys_uart_receive(uint8_t c) “CMSDK APB UART: got character 0x%x from backend”
ys_uart_tx_pending(void) “CMSDK APB UART: character send to backend pending”
ys_uart_tx(uint8_t c) “CMSDK APB UART: character 0x%x sent to backend”
ys_uart_set_params(int speed) “CMSDK APB UART: params set to %d 8N1”
system_ss.add(when: ‘CONFIG_YS_UART’, if_true: files(‘ys_uart.c’))
Kconfig文件添加
config YS_UART
bool
#include "hw/char/ys_uart.h" // 添加头文件
// 在函数virt_machine_inti中添加如下代码
DeviceState *dev = qdev_new(TYPE_YS_UART);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
qdev_prop_set_chr(dev, "chardev", serial_hd(0));
qdev_prop_set_uint32(dev, "pclk-frq", 25000000);
sysbus_realize_and_unref(sbd, &error_fatal);
sysbus_mmio_map(sbd, 0, 0x40004000);
// 上面的代码为新添加
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193,
serial_hd(1), DEVICE_LITTLE_ENDIAN);
#define UART0_ADDRESS (0x40004000UL) #define UART0_DATA (*(((volatile uint32_t *)(UART0_ADDRESS + 0UL)))) #define UART0_STATE (*(((volatile uint32_t *)(UART0_ADDRESS + 4UL)))) #define UART0_CTRL (*(((volatile uint32_t *)(UART0_ADDRESS + 8UL)))) #define UART0_BAUDDIV (*(((volatile uint32_t *)(UART0_ADDRESS + 16UL)))) #define TX_BUFFER_MASK (1UL) static void InitUart(void) { UART0_BAUDDIV = 16; UART0_CTRL = 1; } /*-----------------------------------------------------------*/ static int UartWrite(char *pcString, int iStringLength) { int iNextChar; /* Output the formatted string to the UART. */ for (iNextChar = 0; iNextChar < iStringLength; iNextChar++) { while ((UART0_STATE & TX_BUFFER_MASK) != 0) { } UART0_DATA = *pcString; pcString++; } return iStringLength; } int main(void) { InitUart(); UartWrite("Hello World!\n", 13); while (1) { /* code */ } }
调试qemu,利用VSCode更,在.vscode目录中添加launch.json文件,添加如下内容:
{ "version": "0.2.0", "configurations": [ { "name": "Debug", "type": "cppdbg", "request": "launch", "args": ["-machine", "ys_virt", "-serial", "stdio", "-nodefaults", "-bios", "none", "-smp", "1", "-nographic", "-kernel", "e:\\HelloWorld_riscv.elf"], "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "stopAtEntry": false, "windows": { "MIMode": "gdb", "miDebuggerPath": "C:\\msys64\\mingw64\\bin\\gdb.exe", "program": "${workspaceFolder}\\build\\qemu-system-riscv32.exe", }, } ] }
在\system\main.c的int main(int argc, char **argv)处添加断点,按F5调试,显示如下:
./build/qemu-system-riscv32.exe -machine ys_virt -serial stdio -nodefaults -bios none -kernel /e/HelloWorld_riscv.elf -nographic
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。