Qemu
目录
- 1 QEMU使用手册
-
2 QEMU开发手册
- 2.1 1. QEMU下载与编译
- 2.2 2. QEMU的设备创建过程
- 2.3 3. QEMU的设备模拟
- 2.4 4. 模拟LED
- 2.5 5. 模拟LCD
- 2.6 6. QEMU的输出:GUI系统
- 2.7 7. QEMU的输入:鼠标事件
- 2.8 8. 使用中断
- 2.9 9. 模拟按键
- 2.10 10. QEMU启动过程
- 2.11 11. 调试
QEMU使用手册
QEMU简介
还有很多模拟器,比如VMWare、Virtual Box等。但是VMWare、Virtual Box只能模拟x86、AMD64/Intel64等PC系统;
用户模式(User Mode)
$ gcc -o hello hello.c -static
$ ./hello // 这个hello程序是使用gcc给PC机编译的,可以直接运行
Hello, world!
$ arm-linux-gnueabihf-gcc -o hello hello.c -static // 它是给ARM板子编译的
$ ./hello // 所以无法在PC上运行
bash: ./hello: cannot execute binary file: Exec format error
$ ./qemu-arm ./hello // 我们可以用QEMU在PC上运行它
Hello, world!
- 你没有安装ARM交叉编译工具链
- 你没有安装QEMU
系统模式(System Mode)
我们做的改进
*
- 模拟网卡
- 模拟LCD显示功能
- 模拟led灯、按键
- 模拟at24cxx i2c存储芯片,直接可以通过用户态操作看到效果
- 增加逻辑分析仪显示功能
- 后续会逐渐增加更多的模拟硬件模块
QEMU快速使用
准备工作
- 一台可以上网的windows电脑
- 一个可以正常使用Ubuntu虚拟机VMWare系统,也要能上网
$ sudo apt-get update
$ sudo apt-get install qemu qemu-kvm libvirt-bin bridge-utils virt-manager
获取镜像
① ubuntu-18.04开发环境下qemu imx6ul系统镜像下载地址:
② ubuntu-16.04开发环境下qemu imx6ul系统镜像下载地址:
① ubuntu-16.04解压操作步骤
$ tar -xvf weidongshan-ubuntu-16.04_imx6ul_qemu_system-release.tgz
② ubuntu-18.04解压操作步骤
$ tar -xvf weidongshan-ubuntu-18.04_imx6ul_qemu_system-release.tgz
运行QEMU系统
首次运行需要安装SDL环境以及解压文件系统镜像:
$ ./install_sdl.sh // 提示输入用户密码,等待安装完成
运行带GUI的imx6ul模拟器
$ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码
$ ./qemu-imx6ul-gui.sh fire // 启动后,登录名是root,无需密码
$ ./qemu-imx6ul-gui.sh atk // 启动后,登录名是root,无需密码
运行不带GUI的imx6ul模拟器
$ ./qemu-imx6ul-nogui.sh // 启动后,登录名是root,无需密码
参数讲解
-M mcimx6ul-evk
指定需要模拟的单板型号。
-m 512M
指定板子的内存大小。
-kernel zImage
指定使用的内核镜像文件。
-dtb 100ask_imx6ul_qemu.dtb
指定使用的设备树文件。
-display sdl
指定使用那种图形显示输出。
-serial mon:stdio
指定串口信息输出。
-drive file=rootfs.img,format=raw,id=mysdcard
名为mysdcard的drive,源为rootfs.img
-device sd-card,drive=mysdcard
添加一个sd-card设备,内容来自名为mysdcard的drive
-append “console=ttymxc0,115200 rootfstype=ext4 root=/dev/mmcblk1 rw rootwait init=/sbin/init loglevel=8”
指定内核的命令行参数
-nic user
指定网卡为user mode
百问网imx6ull-qemu开发板资料下载
QEMU操作示例
$ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码
操作设备管理器
操作LCD
[root@qemu_imx6ul:~]# fb-test
或
[root@qemu_imx6ul:~]# myfb-test /dev/fb0
操作LED
[root@qemu_imx6ul:~]# cd led_driver_qemu/
[root@qemu_imx6ul:~/led_driver_qemu]# insmod 100ask_led.ko
[root@qemu_imx6ul:~/led_driver_qemu]# ./ledtest /dev/100ask_led0 off
[root@qemu_imx6ul:~/led_driver_qemu]# ./ledtest /dev/100ask_led0 on
使用按键来控制LED
[root@qemu_imx6ul:~]# cd led_driver_qemu/
[root@qemu_imx6ul:~/led_driver_qemu]# insmod 100ask_led.ko
[root@qemu_imx6ul:~/led_driver_qemu]# cd ../button_driver_qemu/
[root@qemu_imx6ul:~/button_driver_qemu]# insmod button_drv.ko
[root@qemu_imx6ul:~/button_driver_qemu]# insmod board_100ask_qemu_imx6u
[root@qemu_imx6ul:~/button_driver_qemu]# ./button_led_test
读写I2C EEPROM AT24C02
// 0x50是AT24C02的I2C设备地址
[root@qemu_imx6ul:~]# i2c_usr_test /dev/i2c-0 0x50 r 0 // 读地址0
data: , 0, 0x00
[root@qemu_imx6ul:~]# i2c_usr_test /dev/i2c-0 0x50 w 1 0x58 // 写地址1,写入0x58
使用QEMU进行嵌入式Linux开发
获取源码
设置git邮箱账号和用户名
book@100ask:~$ git config --global user.email "you@example.com"
book@100ask:~$ git config --global user.name "Your Name"
下载源码
book@100ask:~$ git clone https://git.dev.tencent.com/codebug8/repo.git
book@100ask:~$ mkdir -p 100ask_imx6ull-qemu && cd 100ask_imx6ull-qemu
book@100ask:~/100ask_imx6ull-qemu$ ../repo/repo init -u https://dev.tencent.com/u/weidongshan/p/manifests/git -b linux-sdk -m imx6ull/100ask-imx6ull_qemu_release_v1.0.xml --no-repo-verify
book@100ask:~/100ask_imx6ull-qemu$ ../repo/repo sync -j4
设置工具链
永久生效
book@100ask:~$ vi ~/.bashrc
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
临时生效
book@100ask:~$ export PATH=$PATH:/home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
book@100ask:~$ export ARCH=arm
book@100ask:~$ export CROSS_COMPILE=arm-linux-gnueabihf-
手动指定
book@100ask:~$ export PATH=$PATH:/home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
book@100ask:~$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
编译内核及设备树
book@100ask:~/100ask_imx6ull-qemu$ cd linux-4.9.88
book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make mrproper
book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make 100ask_imx6ul_qemu_defconfig
book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make zImage -jN //编译zImage 内核镜像,其中N参数可以根据CPU个数,来加速编译系统。
book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make dtbs //编译设备树文件
arch/arm/boot/zImage // 内核
arch/arm/boot/dts/100ask_imx6ul_qemu.dtb // 设备树
修改文件系统
sudo mount -o loop rootfs.img /mnt
sudo umount /mnt
启动模拟器后使用NFS
$ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码
在Ubuntu上安装、配置NFS服务
sudo apt-get install nfs-kernel-server
/home/book *(rw,nohide,insecure,no_subtree_check,async,no_root_squash)
/work *(rw,nohide,insecure,no_subtree_check,async,no_root_squash)
sudo /etc/init.d/nfs-kernel-server restart
sudo mount -t nfs -o nolock,vers=3 127.0.0.1:/home/book /mnt
ls /mnt
开发板获取IP地址
[root@qemu_imx6ul:~]# ifconfig eth0 10.0.2.15
挂载主机nfs目录
[root@qemu_imx6ul:~]# mount -t nfs -o nolock,vers=3 10.0.2.2:/home/book/nfs_rootfs /mnt
更新QEMU
随着我们课程的陆续发布,我们也会给QEMU添加更多的功能。
下载最新的release包
① ubuntu-18.04开发环境下qemu imx6ul系统镜像下载地址:
② ubuntu-16.04开发环境下qemu imx6ul系统镜像下载地址:
下载最新的qemu-system-arm可执行程序及配置文件
https://dev.tencent.com/u/weidongshan/p/ubuntu-16.04_imx6ul_qemu_system/git
https://dev.tencent.com/u/weidongshan/p/ubuntu-18.04_imx6ul_qemu_system/git
下载最新的qemu源码并编译
QEMU开发手册
# 添加外设
- 添加外设的GUI
- 在虚拟开发板上开发测试程序
1. QEMU下载与编译
1.1 源码下载
$ git clone https://gitee.com/weidongshan/qemu.git
或
$ git clone https://github.com/100askTeam/qemu.git
$ git pull origin
1.2 配置、编译、安装
./configure --prefix=$PWD/ --target-list="arm-softmmu arm-linux-user" --enable-debug --enable-sdl --enable-kvm --enable-tools --disable-curl
$ make
$ make install
ERROR: pixman >= 0.21.8 not present.
Please install the pixman devel package.
① 确定库的名称:
$ apt-cache search pixman
libpixman-1-0 - pixel-manipulation library for X and cairo
libpixman-1-dev - pixel-manipulation library for X and cairo (development files)
② 安装开发包:
$ sudo apt-get install libpixman-1-dev
$ sudo apt-get install pkg-config
$ sudo apt-get install libsdl2-dev
$ sudo apt-get install libpixman-1-dev
qemu-system-arm
1.3 使用新的qemu-system-arm
ubuntu-18.04_imx6ul_qemu_system/qemu-system-arm/bin
或
ubuntu-16.04_imx6ul_qemu_system/qemu-system-arm/bin
ubuntu-18.04_imx6ul_qemu_system/qemu-system-arm/etc
或
ubuntu-16.04_imx6ul_qemu_system/qemu-system-arm/etc
1.4 QEMU源码目录
2. QEMU的设备创建过程
2.1 重要结构体TypeInfo
static void __attribute__((constructor)) do_qemu_init_imx_gpio_register_types (void) \
{ \
register_module_init(imx_gpio_register_types, MODULE_INIT_QOM); \
}
2.2 生成TypeImpl
a15mp_register_types
ask100fb_register_types
main
① 分配一个TypeImpl结构体,使用TypeInfo来设置它:
② 把TypeImpl放入链表:type_table
2.3 使用TypeImpl:实例化
① 分配:
② 设置:
① qdev_create/qdev_init_nofail
② sysbus_init_child_obj/object_property_set_bool
2.4 实例化方法1:qdev_create/qdev_init_nofail
2.4.1 qdev_create分析
① 第2个参数是name,会被用来找到对应的TypeInfo结构体
② 分配instance_size大小的内存,即分配ASK100FbState结构体,这用来表示LCD
③ 调用TypeInfo结构体中的class_init函数
④ 调用TypeInfo结构体中的instance_init函数
2.4.2 qdev_init_nofail分析
2.4.3 总结:怎么创建设备
① 先定义一个TypeInfo结构体
② 注册这个TypeInfo结构体:
③ 使用type_init,把ask100fb_register_types函数放入链表中
④ 调用qdev_create创建设备,这会传入type name
⑤ 调用qdev_init_nofail设置设备的状态为realized,这会导致类别的realize函数被调用。
① TypeInfo中的class_init:它会设置dc->realize = 某个函数
② TypeInfo中instance_size大小的对象被malloc
③ TypeInfo中的instance_init函数被调用,它被用来初始化步骤②中malloc出来的结构体
④ dc->realize被调用
2.5 实例化方法2:object_initialize_child/object_property_set_bool
2.5.1 object_initialize_child分析
2.5.2 object_property_set_bool分析
2.5.3 总结:怎么创建设备
① 先定义一个TypeInfo结构体
② 注册这个TypeInfo结构体:
③ 使用type_init,把a15mp_register_types函数放入链表中
④ 调用object_initialize_child创建设备,这会传入type name
⑤ 调用object_property_set_bool设置设备的状态为realized,这会导致类别的realize函数被调用。
2.6 怎么创建设备
2.6.1 设置TypeInfo结构体
2.6.3 使用TypeInfo创建设备/设置设备的realized属性为true
qdev_create/qdev_init_nofail
object_initialize_child/object_property_set_bool
3. QEMU的设备模拟
3.1 QEMU模拟外设的原理
① CPU读某个地址时,QEMU模拟外设的行为,把数据返回给CPU
② CPU写某个地址时,QEMU获得数据,用来模拟外设的行为。
① 写LCD控制器,根据外接的LCD设置参数,比如分辨率、BPP、各种时序
② 从FrameBuffer中不断获得数据发给LCD,在LCD上显示出来。
① 写LCD控制器的相关寄存器
② 分配FrameBuffer,写FrameBuffer
① 记录驱动程序写寄存器的值,解析出分辨率等信息
② 记录FrameBuffer的地址,并持续不断地从中得到图像数据并显示出来
① 设置LCD控制器时:
qemu_a1a2_read
qemu_a1a2_write
② 写FrameBuffer时:
3.2 给某段地址提供读写函数
3.2.1 memory_region_init_io/memory_region_add_subregion
4. 模拟LED
4.1 主要函数
4.2 添加一段内存
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3
5. 模拟LCD
common-obj-$(CONFIG_FSL_IMX6UL) += 100ask_qemu_fb.o
5.1 在Linux上编写LCD驱动程序
① 对于LCD控制器,只需要操作4个寄存器:
② 对于FrameBuffer:
5.3 重要函数分析
6. QEMU的输出:GUI系统
6.1 创建GUI Console
7. QEMU的输入:鼠标事件
common-obj-$(CONFIG_FSL_IMX6UL) += button_ui.o
7.3 输入事件处理流程
sdl2_2d_refresh
7.4 QEMU捕获的输入事件,可以作为ABS也可以作为REL事件上传
8. 使用中断
8.1 外设发出哪一个中断?
8.8.1 调用sysbus_init_irq初始化qemu_irq
① 它会给设备dev添加一个属性prop,prop的名字是“sysbus-irq[0]”或“sysbus-irq[1]”
② 会给这个prop添加一个link,link到&s->irq[0]或&s->irq[1]
8.8.2 调用sysbus_connect_irq连接中断
qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX6UL_I2Cn_IRQ[i])
8.2 中断触发
发出中断:
清除中断:
① 用户按下按键后,使用“qemu_set_irq(irq, 1)”发出中断
② 客户机的程序处理完中断后,它会写ISR寄存器来清除中断:
notify_imx_gpio_change
qemu_set_irq(s->irq[0], 1);
imx_gpio_write
9. 模拟按键
9.3 更新按键图片的同时
10. QEMU启动过程
10.1 各个模块的注册
#define type_init(function) module_init(function, MODULE_INIT_QOM)
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_dso_module_init(function, type); \
}
10.2 各个模块的初始化
main
① 分配一个TypeImpl结构体,使用TypeInfo来设置它:
② 把TypeImpl放入链表:type_table
10.5 CPU的初始化
10.6 在system memory中添加内存
11. 调试
11.1 要调试,运行gdb程序之前,必须先进入源码目录
11.2 调试QEMU本身
gdb --args /home/book/mywork/qemu/qemu_git/qemu/bin/qemu-system-arm -M \
mcimx6ul-evk -m 512M -kernel /home/book/nfs_rootfs/zImage \
-dtb /home/book/nfs_rootfs/mynet.dtb -serial stdio \
-drive file=/home/book/mywork/imx6ull_test_image/buildroot_rootfs.img,format=raw,id=mysdcard -device sd-card,drive=mysdcard \
-append "console=ttymxc0,115200 console=tty1 rootfstype=ext4 root=/dev/mmcblk1 rw rootwait init=/sbin/init loglevel=8" \
-nic user
11.3 调试之前就运行的QEMU
① 首先确定qemu-system-arm进程的PID
ps -A | grep qemu
② 在QEMU源码目录下执行如下命令:
sudo gdb -p 9527
11.4 调试客户机上的Linux内核
11.4.1 首先运行qemu-system-arm
gdb --args /home/book/mywork/qemu/qemu_git/qemu/bin/qemu-system-arm -M \
mcimx6ul-evk -m 512M -kernel /home/book/nfs_rootfs/zImage \
-dtb /home/book/nfs_rootfs/mynet.dtb -serial stdio \
-drive file=/home/book/mywork/imx6ull_test_image/buildroot_rootfs.img,format=raw,id=mysdcard -device sd-card,drive=mysdcard \
-append "console=ttymxc0,115200 console=tty1 rootfstype=ext4 root=/dev/mmcblk1 rw rootwait init=/sbin/init loglevel=8" \
-nic user -s -S