匿名
未登录
登录
百问网嵌入式Linux wiki
搜索
查看“Qemu”的源代码
来自百问网嵌入式Linux wiki
名字空间
页面
讨论
更多
更多
页面选项
Read
查看源代码
历史
←
Qemu
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
= QEMU使用手册 = == QEMU简介 == <div style="margin-left:0cm;margin-right:0cm;">QEMU的英文单词是:Quick Emulator,它是一个小巧的模拟器。<br> 还有很多模拟器,比如VMWare、Virtual Box等。但是VMWare、Virtual Box只能模拟x86、AMD64/Intel64等PC系统;<br> 而QEMU可以模拟更多硬件:ARM、MIPS、PPC、x86、AMD64/Intel64。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU用途广泛,比如Xen、Android模拟器等都是基于QEMU的。</div> <div style="margin-left:0cm;margin-right:0cm;">在嵌入式领域,很多人使用QEMU来深研Linux,比如研究文件系统、优化等等。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU有两种模式:</div> === 用户模式(User Mode) === <div style="margin-left:0cm;margin-right:0cm;">简单地说,一个使用arm-xxx-gcc编译出来的程序,是给ARM板子使用的,它无法在PC机上运行,只能放到ARM板子上去运行。</div> <div style="margin-left:0cm;margin-right:0cm;">借助qemu,可以在PC机上运行ARM程序。比如:</div> <syntaxhighlight lang="bash"> $ 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! </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">在PC上使用qemu运行单个ARM程序时,这就是使用QEMU的用户模式。</div> <div style="margin-left:0cm;margin-right:0cm;">它会把ARM指令翻译为PC的指令去运行。</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span>,你可能无法做上述实验,因为:</div> # 你没有安装ARM交叉编译工具链 # 你没有安装QEMU <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">你根据下章《QEMU快速使用》安装QEMU后,就可以进行上述实验了。</div> === 系统模式(System Mode) === <div style="margin-left:0cm;margin-right:0cm;">很多时候我们并不满足于在PC上运行单个ARM程序,我们想模拟出整个ARM单板:在这个模拟出来的虚拟ARM单板上,运行Linux系统,在其中运行各种APP。</div> <div style="margin-left:0cm;margin-right:0cm;">这时候需要使用QEMU的系统模式。</div> <div style="margin-left:0cm;margin-right:0cm;">我们就是使用QEMU的系统模式来模拟IMX6ULL开发板,具体的使用请看下章。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 我们做的改进 === <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#222222;">QEMU可以模拟x86,也可以模拟各种ARM板子,还可以模拟各种外设。</span></div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#222222;">百问网对QEMU做了很多改进,支持更多硬件,支持更多GUI现实,支持更方便的调试。</span></div>* <div style="margin-left:0.677cm;margin-right:0cm;"><span style="color:#222222;">100ask-qemu特点</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">模拟网卡</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">模拟LCD显示功能</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">模拟led灯、按键</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">模拟at24cxx i2c存储芯片,直接可以通过用户态操作看到效果</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">增加逻辑分析仪显示功能</span></div> # <div style="margin-left:1.355cm;margin-right:0cm;"><span style="color:#222222;">后续会逐渐增加更多的模拟硬件模块</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.1 温湿度传感器</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.2 红外</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.3 超声波模块</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.4 ADC,DAC模块</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.5 I2C接口的传感器</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.6 SPI接口的OLED</span></div> <div style="margin-left:1.27cm;margin-right:0cm;"><span style="color:#222222;">6.7 你想加啥,跟我们说</span></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == QEMU快速使用 == <div style="margin-left:0cm;margin-right:0cm;">使用apt-get当然也可以安装QEMU,但是它版本太低,也不支持IMX6ULL。</div> <div style="margin-left:0cm;margin-right:0cm;">新版本QEMU已经支持IMX6ULL,我们在此基础上添加了更多功能,也修改了一些BUG。</div> <div style="margin-left:0cm;margin-right:0cm;">所以,请参考本文安装我们定制的QEMU。</div> === 准备工作 === <div style="margin-left:0.635cm;margin-right:0cm;"><span style="color:#ff0000;">必须</span>的:</div> # <span style="color:#222222;">一台可以上网的windows电脑</span> # <span style="color:#222222;">一个可以正常使用Ubuntu虚拟机VMWare系统,也要能上网</span> <div style="margin-left:0.635cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span><span style="color:#222222;">:很多人碰到VMWare中Ubuntu联网问题问题,可以参考此链接:</span></div> <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/VMwareAndUbuntuNetworkSetupGuide http://wiki.100ask.org/VMwareAndUbuntuNetworkSetupGuide]</div> <div style="margin-left:0.635cm;margin-right:0cm;"><span style="color:#ff0000;">可选</span>的(注意,如果想要有更快的效果,在ubuntu下可以安装KVM):</div> <div style="margin-left:0.635cm;margin-right:0cm;"><span style="color:#222222;">安装kvm加速qemu运行,在终端下执行如下命令:</span></div> <syntaxhighlight lang="bash"> $ sudo apt-get update $ sudo apt-get install qemu qemu-kvm libvirt-bin bridge-utils virt-manager </syntaxhighlight> === 获取镜像 === <div style="margin-left:0.635cm;margin-right:0cm;">我们只提供ubuntu 16.04和ubuntu 18.04系统的QEMU镜象文件。</div> ① ubuntu-18.04开发环境下qemu imx6ul系统镜像下载地址: <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/Download_ubuntu-18.04_imx6ul_qemu http://wiki.100ask.org/Download_ubuntu-18.04_imx6ul_qemu]</div> ② ubuntu-16.04开发环境下qemu imx6ul系统镜像下载地址: <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/Download_ubuntu-16.04_imx6ul_qemu http://wiki.100ask.org/Download_ubuntu-16.04_imx6ul_qemu]</div> <div style="margin-left:0.635cm;margin-right:0cm;">下载完成后上传到Ubuntu虚拟机中,执行如下命令进行解压缩操作。</div> ① ubuntu-16.04解压操作步骤 <syntaxhighlight lang="bash"> $ tar -xvf weidongshan-ubuntu-16.04_imx6ul_qemu_system-release.tgz </syntaxhighlight> <div style="margin-left:0.635cm;margin-right:0cm;">然后进入ubuntu-16.04_imx6ul_qemu_system目录执行后文介绍的命令。</div> ② ubuntu-18.04解压操作步骤 <syntaxhighlight lang="bash"> $ tar -xvf weidongshan-ubuntu-18.04_imx6ul_qemu_system-release.tgz </syntaxhighlight> <div style="margin-left:0.635cm;margin-right:0cm;">然后进入ubuntu-18.04_imx6ul_qemu_system目录执行后文介绍的命令。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 运行QEMU系统 === <div style="margin-left:0cm;margin-right:0cm;">假设你已经按照上文下载、解压好了QEMU镜像文件,你需要进入QEMU的目录,执行下列命令。</div> ==== 首次运行需要安装SDL环境以及解压文件系统镜像: ==== <div style="margin-left:0cm;margin-right:0cm;">使用脚本自动解压安装:</div> <syntaxhighlight lang="bash"> $ ./install_sdl.sh // 提示输入用户密码,等待安装完成 </syntaxhighlight> ==== 运行带GUI的imx6ul模拟器 ==== <div style="margin-left:0cm;margin-right:0cm;">① 模拟百问网imx6ul-qemu开发板</div> <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">② 模拟野火imx6ul-pro开发板</div> <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-gui.sh fire // 启动后,登录名是root,无需密码 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">③ 模拟正点原子imx6ul-alpha开发板</div> <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-gui.sh atk // 启动后,登录名是root,无需密码 </syntaxhighlight> ==== 运行不带GUI的imx6ul模拟器 ==== <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-nogui.sh // 启动后,登录名是root,无需密码 </syntaxhighlight> ==== 参数讲解 ==== <div style="margin-left:0cm;margin-right:0cm;">可以打开脚本文件qemu-imx6ul-gui.sh,它就是运行qemu-system-arm程序。其中用到了很多参数:</div> <code>-M mcimx6ul-evk</code> 指定需要模拟的单板型号。 <code>-m 512M</code> 指定板子的内存大小。 <code>-kernel zImage</code> 指定使用的内核镜像文件。 <code>-dtb 100ask_imx6ul_qemu.dtb</code> 指定使用的设备树文件。 <code>-display sdl</code> 指定使用那种图形显示输出。 <code>-serial mon:stdio</code> 指定串口信息输出。 <code>-drive file=rootfs.img,format=raw,id=mysdcard</code> 名为mysdcard的drive,源为rootfs.img <code>-device sd-card,drive=mysdcard</code> 添加一个sd-card设备,内容来自名为mysdcard的drive <code>-append “console=ttymxc0,115200 rootfstype=ext4 root=/dev/mmcblk1 rw rootwait init=/sbin/init loglevel=8”</code> 指定内核的命令行参数 <code>-nic user</code> 指定网卡为user mode <div style="margin-left:0cm;margin-right:0cm;">有了内核zImage、设备树、文件系统(rootfs.img),这就是一个完整的Linux系统。</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span>:QEMU中没有实现bootloader,以后我们会完全模拟SD卡,在SD卡上面放置u-boot、内核、设备树、文件系统。</div> === 百问网imx6ull-qemu开发板资料下载 === <div style="margin-left:0cm;margin-right:0cm;">百问网imx6ul-qemu开发板,并不是真实的开发板,我们可以在它上面添加任意硬件,当然也会提供原理图。</div> <div style="margin-left:0cm;margin-right:0cm;">这款虚拟开发板的资料可以从网盘中下载,打开以下链接:</div> <div style="margin-left:0cm;margin-right:0cm;">[http://wiki.100ask.org/Download_link_page http://wiki.100ask.org/Download_link_page]</div> <div style="margin-left:0cm;margin-right:0cm;">找到:找到“<span style="color:#ff0000;">韦东山升级版全系列视频开发板BSP包</span><span style="color:#222222;">”,打开对应的网盘链接后,可以看到一个目录“100ask_imx6ull-qemu”</span>。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === QEMU操作示例 === <div style="margin-left:0cm;margin-right:0cm;">先执行以下命令启动QEMU,它模拟百问网imx6ul-qemu开发板:</div> <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">它会弹出一个开发板的界面,并且运行该开发板的Linux系统,你可以在终端中操作该开发板:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 38.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span>:当你的鼠标点击QEMU的GUI界面时,鼠标将无法移出这个GUI界面。这时可以通过快捷键“Ctrl+Alt+g”把鼠标从GUI界面中退出来。</div> ==== 操作设备管理器 ==== <div style="margin-left:0cm;margin-right:0cm;">我们的计划是模拟更多的外设,为形象地操作这些外设,每个外设都会有一个GUI界面。可能会有多达几十、上百个外设,如果一下子显示那么多GUI界面,会很乱。</div> <div style="margin-left:0cm;margin-right:0cm;">所以我们实现了一个“设备管理”,如下所示。可以点击它上面的某个按钮,显示或隐藏某个外设的GUI界面。</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 39.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> ==== 操作LCD ==== <div style="margin-left:0cm;margin-right:0cm;">我们模拟的IMX6ULL板子,它的Linux系统中已经带有LCD的测试命令,可以执行以下命令测试:</div> <syntaxhighlight lang="bash"> [root@qemu_imx6ul:~]# fb-test </syntaxhighlight> 或 <syntaxhighlight lang="bash"> [root@qemu_imx6ul:~]# myfb-test /dev/fb0 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">效果如下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 41.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> ==== 操作LED ==== <div style="margin-left:0cm;margin-right:0cm;">我们模拟的IMX6ULL板子,它的Linux系统中已经带有LED驱动和测试命令,可以执行以下命令测试:</div> <syntaxhighlight lang="bash"> [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 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">效果如下:</div> <div style="text-align:center;">[[Image:图片 42.png|top]]</div> ==== 使用按键来控制LED ==== <div style="margin-left:0cm;margin-right:0cm;">我们模拟的IMX6ULL板子,它的Linux系统中已经带有LED驱动、按键驱动和测试命令。</div> <div style="margin-left:0cm;margin-right:0cm;">首先在“QEMU 设备管理器”中打开按键的界面,然后执行以下命令测试:</div> <syntaxhighlight lang="bash"> [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 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">效果如下图(只实现了左边的2个按键,右边的2个按键留待以后实现其他目的):</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 43.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> ==== 读写I2C EEPROM AT24C02 ==== <div style="margin-left:0cm;margin-right:0cm;">我们模拟的IMX6ULL板子,它已经实现了对I2C设备AT24C02的模拟。</div> <div style="margin-left:0cm;margin-right:0cm;">首先在“QEMU 设备管理器”中打开at24c02的界面,然后执行以下命令测试:</div> <syntaxhighlight lang="bash"> // 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 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">效果如下图:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 44.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 使用QEMU进行嵌入式Linux开发 == <div style="margin-left:0cm;margin-right:0cm;">完整的嵌入式Linux系统包含:bootloader、Linux内核、设备树、根文件系统。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU可以略过bootloader而直接启动内核,这给开发带来了便利。</div> <div style="margin-left:0cm;margin-right:0cm;">但是以后我们会修改QEMU,让它支持从SD卡上启动bootloader,让bootloader来启动内核。</div> <div style="margin-left:0cm;margin-right:0cm;">在本章中,我们介绍如何编译并替换内核、编译并替换设备树,修改根文件系统。</div> === 获取源码 === <div style="margin-left:0cm;margin-right:0cm;">我们的源码都是存放在GIT中,所以需要确保你的Ubuntu可以上网。如果不能上网,请参考以下链接:</div> <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/VMwareAndUbuntuNetworkSetupGuide http://wiki.100ask.org/VMwareAndUbuntuNetworkSetupGuide]</div> ==== 设置git邮箱账号和用户名 ==== <div style="margin-left:0cm;margin-right:0cm;">在Ubuntu中执行如下命令:</div> <syntaxhighlight lang="bash"> book@100ask:~$ git config --global user.email "you@example.com" book@100ask:~$ git config --global user.name "Your Name" </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">初次使用GIT时,需要配置邮箱帐号和用户名,可以随意指定。</div> ==== 下载源码 ==== <div style="margin-left:0cm;margin-right:0cm;">考虑到代码仓库过多,特使用repo工具管理代码。</div> <div style="margin-left:0cm;margin-right:0cm;">先用git clone下载repo工具,再用repo工具下载源码:</div> <syntaxhighlight lang="bash"> 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 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">上面使用的repo管理的是国内coding仓库,从国内仓库下载会快很多。</div> <div style="margin-left:0cm;margin-right:0cm;">如果一切正常,你在/home/book目录下创建了一个100ask_imx6ull-qemu目录,里有如下内容:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 45.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 设置工具链 === <div style="margin-left:0cm;margin-right:0cm;">交叉编译工具链主要是用于在ubuntu主机上编译可以在其它平台上运行的系统,比如在PC上为ARM板子编译程序。</div> <div style="margin-left:0cm;margin-right:0cm;">设置交叉编译工具主要是设置PATH, ARCH和CROSS_COMPILE三个环境变量,下面介绍具体设置方法。</div> ==== 永久生效 ==== <div style="margin-left:0cm;margin-right:0cm;">如需永久修改,请修改用户配置文件。在Ubuntu系统下,修改如下:</div> <syntaxhighlight lang="bash"> book@100ask:~$ vi ~/.bashrc </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">在行尾添加或修改:</div> <syntaxhighlight lang="bash"> 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 </syntaxhighlight> ==== 临时生效 ==== <div style="margin-left:0cm;margin-right:0cm;">如果你有很多单板,为了不冲突,你不能使用“永久生效”的办法。比如你有32位的ARM板,也有64位的ARM板,在使用前者时需要设置ARCH=arm,在使用后者时需要设置ARCH=arm64。</div> <div style="margin-left:0cm;margin-right:0cm;">这种情况下,可以使用“export”命令设置环境变量,这种设置方法只对当前终端有效:</div> <syntaxhighlight lang="bash"> 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- </syntaxhighlight> ==== 手动指定 ==== <div style="margin-left:0cm;margin-right:0cm;">执行make命令时,可以手工指定ARCH架构、CROSS_COMPILE等变量:</div> <syntaxhighlight lang="bash"> 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- </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">不过这种方法效率太低,不建议使用。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 编译内核及设备树 === <div style="margin-left:0cm;margin-right:0cm;">前面我们下载了源码,设置好工具链后,即可编译:</div> <syntaxhighlight lang="bash"> 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 //编译设备树文件 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;"><span style="background-color:#ffffff;color:#222222;">编译成功后,可以得到如下文件:</span></div> <syntaxhighlight lang="bash"> arch/arm/boot/zImage // 内核 arch/arm/boot/dts/100ask_imx6ul_qemu.dtb // 设备树 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">如果你修改过内核,或是修改过设备树文件,那么可以用上面2个文件去替换QEMU中的zImage和100ask_imx6ul_qemu.dtb。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU中的zImage和100ask_imx6ul_qemu.dtb在哪?你安装我们提供的QEMU时,可以得到这样的脚本:qemu-imx6ul-gui.sh。打开它就可以知道这2个文件在哪里了。</div> <div style="margin-left:0cm;margin-right:0cm;">一般位于imx6ul-system-image目录下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 46.png|top]]</div> === 修改文件系统 === <div style="margin-left:0cm;margin-right:0cm;">安装好我们提供的QEMU后,你可以得到一个imx6ul-system-image目录,里面有名为rootfs.img的文件,它就是根文件系统:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 49.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">你可以在Ubuntu下直接修改rootfs.img,不过要先挂载,执行以下命令:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 50.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">主要命令就是:</div> <syntaxhighlight lang="bash"> sudo mount -o loop rootfs.img /mnt </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">你就可以在/mnt目录下对其中的文件进行操作了,也可以把Ubuntu中的文件复制进去。</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span>:修改完毕后,要执行以下命令:</div> <syntaxhighlight lang="bash"> sudo umount /mnt </syntaxhighlight> === 启动模拟器后使用NFS === <div style="margin-left:0cm;margin-right:0cm;">安装好我们提供的QEMU后,可以执行如下命令启动开发板:</div> <syntaxhighlight lang="bash"> $ ./qemu-imx6ul-gui.sh // 启动后,登录名是root,无需密码 </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">等待进入系统后,就可以使用网络命令挂载Ubuntu的NFS目录了。</div> ==== 在Ubuntu上安装、配置NFS服务 ==== <div style="margin-left:0cm;margin-right:0cm;">如果你使用的是我们提供的Ubuntu,那么已经安装好了NFS服务。</div> <div style="margin-left:0cm;margin-right:0cm;">如果你的Ubuntu未安装NFS服务,那么在确保Ubuntu可以上网的前提下,执行以下命令:</div> <syntaxhighlight lang="bash"> sudo apt-get install nfs-kernel-server </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">然后,还得修改<span style="color:#ff0000;">/etc/exports</span>,添加类似以下的内容,下面的例子里允许开发板通过NFS访问Ubuntu的/home/book、/work两个目录:</div> <syntaxhighlight lang="bash"> /home/book *(rw,nohide,insecure,no_subtree_check,async,no_root_squash) /work *(rw,nohide,insecure,no_subtree_check,async,no_root_squash) </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">最后,重启NFS服务,在Ubuntu上执行以下命令:</div> <syntaxhighlight lang="bash"> sudo /etc/init.d/nfs-kernel-server restart </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">可以在Ubuntu上通过NFS挂载自己,验证一下NFS可用:</div> <syntaxhighlight lang="bash"> sudo mount -t nfs -o nolock,vers=3 127.0.0.1:/home/book /mnt ls /mnt </syntaxhighlight> ==== 开发板获取IP地址 ==== <div style="margin-left:0cm;margin-right:0cm;">QEMU运行时,Ubuntu是Host即宿主机,QEMU给它分配的IP是10.0.2.2。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU模拟的imx6ull板子是Guest即客户机,它会自动获取IP,也可以自己设置。</div> <div style="margin-left:0cm;margin-right:0cm;">Guest可以通过10.0.2.2访问Host,Host不能访问Guest。</div> <div style="margin-left:0cm;margin-right:0cm;">Guest中可以使用ifconfig命令查看IP,如果没有IP,可以如下设置:</div> <syntaxhighlight lang="bash"> [root@qemu_imx6ul:~]# ifconfig eth0 10.0.2.15 </syntaxhighlight> ==== 挂载主机nfs目录 ==== <div style="margin-left:0cm;margin-right:0cm;">QEMU模拟的imx6ull开发板,可以去访问10.0.2.2,比如使用NFS挂载:</div> <syntaxhighlight lang="bash"> [root@qemu_imx6ul:~]# mount -t nfs -o nolock,vers=3 10.0.2.2:/home/book/nfs_rootfs /mnt </syntaxhighlight> <div style="margin-left:0cm;margin-right:0cm;">如果一切正常,在开发板上就可以通过/mnt目录访问Ubuntu的/home/book/nfs_rootfs目录了。</div> == 更新QEMU == <div style="margin-left:0cm;margin-right:0cm;">我们使用QEMU来模拟IMX6ULL开发板,目的是学习嵌入式Linux系统。<br> 随着我们课程的陆续发布,我们也会给QEMU添加更多的功能。<br> 比如后面要讲到触摸屏时,就会修改QEMU让它支持触摸屏。</div> <div style="margin-left:0cm;margin-right:0cm;">所以你需要不断地更新QEMU,有3个方法。</div> === 下载最新的release包 === <div style="margin-left:0.635cm;margin-right:0cm;">从下面2个地址就可以下载到最新的release包,解压开后即可使用:</div> ① ubuntu-18.04开发环境下qemu imx6ul系统镜像下载地址: <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/Download_ubuntu-18.04_imx6ul_qemu http://wiki.100ask.org/Download_ubuntu-18.04_imx6ul_qemu]</div> ② ubuntu-16.04开发环境下qemu imx6ul系统镜像下载地址: <div style="margin-left:0.635cm;margin-right:0cm;">[http://wiki.100ask.org/Download_ubuntu-16.04_imx6ul_qemu http://wiki.100ask.org/Download_ubuntu-16.04_imx6ul_qemu]</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">注意</span>:如果你之前对内核、设备树或是文件系统进行过修改,想继续使用这些修改过的文件的话,需要用它们来覆盖release包中解压出来的对应文件。 </div> === 下载最新的qemu-system-arm可执行程序及配置文件 === <div style="margin-left:0cm;margin-right:0cm;">如果你不想下载整个release包,可以只替换其中的某些文件。</div> <div style="margin-left:0cm;margin-right:0cm;">可以打开下面的某个网址(根据你的ubuntu版本号选择):</div> [https://dev.tencent.com/u/weidongshan/p/ubuntu-16.04_imx6ul_qemu_system/git 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 https://dev.tencent.com/u/weidongshan/p/ubuntu-18.04_imx6ul_qemu_system/git] <div style="margin-left:0cm;margin-right:0cm;">以ubuntu 18.04为例,进入下图所示的bin、etc目录:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 54.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">对于bin目录,只需要下载其中的qemu-sysmte-arm;对于etc目录,里面所有的bmp文件都要下载。</div> <div style="margin-left:0cm;margin-right:0cm;">你之前在Ubuntu中曾经安排过我们提供的QEMU,你用下载的文件覆盖QEMU中的对应文件即可。</div> === 下载最新的qemu源码并编译 === <div style="margin-left:0cm;margin-right:0cm;">请参考后续章节,这属于对QEMU的开发了。对于嵌入式Linux初学者,没必要研究QEMU,使用我们提供的可执行程序即可。</div> = QEMU开发手册 = <div style="margin-left:0cm;margin-right:0cm;">重要的事情说三遍:</div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">对于嵌入式Linux初学者,对于QEMU无感者,对于急于找工作的人,</span></div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">不需要深入研究QEMU,不需要看本开发手册,看前面的使用手册就可以了,会用就行。</span></div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">不需要深入研究QEMU,不需要看本开发手册,看前面的使用手册就可以了,会用就行。</span></div> <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">不需要深入研究QEMU,不需要看本开发手册,看前面的使用手册就可以了,会用就行。</span></div> <div style="margin-left:0cm;margin-right:0cm;">我们使用QEMU来模拟IMX6ULL开发板,就像去制作一个开发板一样。作为初学者,你并不需要去设计开发板,会用开发板就可以了。</div> <div style="margin-left:0cm;margin-right:0cm;">如果你想深入研究QEMU,想承接我们发布的QEMU外包项目,那么请阅读本手册。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU框架简单,功能却很强大。</div> <div style="margin-left:0cm;margin-right:0cm;">理解了它的框架之后,添加新的外设并不困难。当然这需要你对外设的原理有清楚的认识。</div> <div style="margin-left:0cm;margin-right:0cm;">我们开发QEMU,要做的事情主要有3部分:</div># <span style="color:#ff0000;">'''添加外设'''</span> <div style="margin-left:0cm;margin-right:0cm;">比如添加一个LED,那么在QEMU中得有对应的源码,这些源码要监测LED对应的寄存器的值:当APP写寄存器时,这些源码要把那些值记录下来。</div> # <span style="color:#ff0000;">'''添加外设的GUI'''</span> <div style="margin-left:0cm;margin-right:0cm;">QEMU的界面很简陋,它默认只实现了LCD的GUI显示。你想点亮LED,在QEMU上是看不出效果的。</div> <div style="margin-left:0cm;margin-right:0cm;">所以我们需要给外设添加GUI界面。</div> <div style="margin-left:0cm;margin-right:0cm;">比如上面举的LED例子,APP写寄存器时,除了把寄存器的值记录下来之后,我们还要在GUI界面显示一个LED,并且把它点亮或熄灭。</div> # <span style="color:#ff0000;">'''在虚拟开发板上开发测试程序'''</span> <div style="margin-left:0cm;margin-right:0cm;">这个测试程序可以是裸机,也可以是Linux驱动+应用程序。</div> <div style="margin-left:0cm;margin-right:0cm;">我们在QEMU模拟出来的板子上运行这些测试程序时,跟真实板子应该没有差别。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">上述列的第1、2部分,需要对QEMU有所了解,请看本文的“开发手册”。</div> <div style="margin-left:0cm;margin-right:0cm;">上述列的第3部分,跟QEMU无关,跟在真实板子上写的程序是一样的。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 1. QEMU下载与编译 == === 1.1 源码下载 === <div style="margin-left:0cm;margin-right:0cm;">官方的QEMU对IMX6ULL的支持还太弱,没有更形象化的GUI,也没有更多的外设。我们对它进行了大量的改进。</div> <div style="margin-left:0cm;margin-right:0cm;">修改后的源码位于这2个GIT中:</div> <div style="margin-left:0cm;margin-right:0cm;">[https://github.com/100askTeam/qemu.git https://github.com/100askTeam/qemu.git]</div> <div style="margin-left:0cm;margin-right:0cm;">[https://gitee.com/weidongshan/qemu.git https://gitee.com/weidongshan/qemu.git]</div> <div style="margin-left:0cm;margin-right:0cm;">实际上,如果你之前根据《使用手册:3. 使用QEMU进行嵌入式Linux开发》里的说明使用repo下载过源码,那么在下图中的qemu目录就是源码了:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 40.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">你当然可以自行下载。在Ubuntu下,执行如下命令即可下载:</div> $ git clone [https://gitee.com/weidongshan/qemu.git https://gitee.com/weidongshan/qemu.git] 或 $ git clone [https://github.com/100askTeam/qemu.git https://github.com/100askTeam/qemu.git] <div style="margin-left:0cm;margin-right:0cm;">我们会不断更新QEMU,你下载过QEMU之后,可以进入qemu目录执行下述命令更新代码:</div> $ git pull origin === 1.2 配置、编译、安装 === <div style="margin-left:0cm;margin-right:0cm;">进入qemu目录,执行如下配置命令:</div> ./configure --prefix=$PWD/ --target-list="arm-softmmu arm-linux-user" --enable-debug --enable-sdl --enable-kvm --enable-tools --disable-curl <div style="margin-left:0cm;margin-right:0cm;">编译、安装命令如下:</div> $ make $ make install <div style="margin-left:0cm;margin-right:0cm;">如果一切正常,会在qemu源码目录下生成bin子目录,里面存放有各种可执行程序。</div> <div style="margin-left:0cm;margin-right:0cm;">配置、编译过程中有可能出错,一般就是缺少库文件。</div> <div style="margin-left:0cm;margin-right:0cm;">如果你的ubuntu能上网,那么使用apt-get命令即可安装这些库。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">错误示例,提示信息如下:</div> ERROR: pixman >= 0.21.8 not present. Please install the pixman devel package. <div style="margin-left:0cm;margin-right:0cm;">解决方法:</div> ① 确定库的名称: <div style="margin-left:0cm;margin-right:0cm;">执行如下命令:</div> $ 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) <div style="margin-left:0cm;margin-right:0cm;">根据输出信息,需要安装开发包(dev表示开发包): libpixman-1-dev。</div> ② 安装开发包: $ sudo apt-get install libpixman-1-dev <div style="margin-left:0cm;margin-right:0cm;">可能你的ubuntu中已经安装了某些开发包,下面列出一些必须的包:</div> $ sudo apt-get install pkg-config $ sudo apt-get install libsdl2-dev $ sudo apt-get install libpixman-1-dev <div style="margin-left:0cm;margin-right:0cm;">每次出错后,根据提示信息安装开发包,然后重新配置、编译、安装。 </div> <div style="margin-left:0cm;margin-right:0cm;">如果一切正常,在当前目录下会生成bin子目录, 里面有生成的QEMU程序:</div> qemu-system-arm === 1.3 使用新的qemu-system-arm === <div style="margin-left:0cm;margin-right:0cm;">将上面编译出来的bin/qemu-system-arm 可执行文件复制到如下目录:</div> ubuntu-18.04_imx6ul_qemu_system/qemu-system-arm/bin 或 ubuntu-16.04_imx6ul_qemu_system/qemu-system-arm/bin <div style="margin-left:0cm;margin-right:0cm;">我们也可能添加了更多的GUI显示,这些GUI所用图片位于源码目录的etc子目录下,这些图片也需要复制到如下目录去:</div> ubuntu-18.04_imx6ul_qemu_system/qemu-system-arm/etc 或 ubuntu-16.04_imx6ul_qemu_system/qemu-system-arm/etc <div style="margin-left:0cm;margin-right:0cm;">然后就可以执行 qemu-imx6ul-gui.sh 或 qemu-imx6ul-nogui.sh来使用您编译出来的QEMU了。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 1.4 QEMU源码目录 === <div style="margin-left:0cm;margin-right:0cm;">我们只罗列出涉及的少许文件,一般来说一个.c文件会有一个.h文件,它们的目录类似。</div> <div style="margin-left:0cm;margin-right:0cm;">比如hw/gpio/imx_gpio.c对应的头文件为include/hw/gpio/imx_gpio.h</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 22.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 2. QEMU的设备创建过程 == === 2.1 重要结构体TypeInfo === <div style="margin-left:0cm;margin-right:0cm;">一个板子上有很多硬件:IMX6ULL、LED、按键、LCD、触摸屏、网卡等等。</div> <div style="margin-left:0cm;margin-right:0cm;">IMX6ULL这类芯片被称为SoC(System on Chip),它里面也有很多部件,比如CPU、GPIO、SD控制器、中断控制器等等。</div> <div style="margin-left:0cm;margin-right:0cm;">这些硬件,或是部件,各有不同。怎么描述它们?</div> <div style="margin-left:0cm;margin-right:0cm;">每一个都使用一个TypeInfo结构体来描述。</div> <div style="margin-left:0cm;margin-right:0cm;">比如对于CPU,有这样的结构体(hw/cpu/a15mpcore.c):</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 47.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">对于LCD,有这样的结构体(hw/display/100ask_qemu_fb.c):</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 48.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">对于GPIO,也有这样的结构体(hw/gpio/imx_gpio.c):</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 51.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">由此可见,在QEMU中,每一个硬件都由一个TypeInfo来描述。这些结构体都会被注册进程序里,在一个链表中保存着,<span style="color:#ff0000;">备用</span>。<span style="color:#ff0000;">'''注意'''</span>,是<span style="color:#ff0000;">备用</span>,它们并不一定会用到。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">怎么注册这些TypeInfo结构体呢?不需要我们去调用注册函数,以GPIO为例,在hw/gpio/imx_gpio.c中有如下代码:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 52.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">关键点在于type_init,这个宏在include/qemu/module.h中定义:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 55.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">对于属性为“constructor”函数,它在main函数之前被调用。</div> <div style="margin-left:0cm;margin-right:0cm;">对于上述的type_init(imx_gpio_register_types),它的宏展开如下:</div> static void __attribute__((constructor)) do_qemu_init_imx_gpio_register_types (void) \ { \ register_module_init(imx_gpio_register_types, MODULE_INIT_QOM); \ } <div style="margin-left:0cm;margin-right:0cm;">这就得到了一个属性为“constructor”的函数do_qemu_init_imx_gpio_register_types,这个函数将在main函数之前被调用,它调用了register_module_init。</div> <div style="margin-left:0cm;margin-right:0cm;">register_module_init中构造了一个ModuleEntry结构体,并把它添加进某个链表里,这个链表是init_type_list[MODULE_INIT_QOM]:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 56.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">这样就得到了一个链表:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 61.png|top]]</div> === 2.2 生成TypeImpl === <div style="margin-left:0cm;margin-right:0cm;">从名字上讲,TypeImpl就是TypeInfo的实现,就是使用TypeInfo的信息构造一个TypeImpl结构体。</div> <div style="margin-left:0cm;margin-right:0cm;">在init_type_list[MODULE_INIT_QOM]链表中,有一系列的ModuleEntry结构体,每个ModuleEntry结构体中有一个init函数,它指向某个xxx_register_types,比如:</div> a15mp_register_types ask100fb_register_types <div style="margin-left:0cm;margin-right:0cm;">这些ModuleEntry结构体中的init函数何时被调用?</div> <div style="margin-left:0cm;margin-right:0cm;">vl.c:</div> main <div style="margin-left:0cm;margin-right:0cm;">module_call_init(MODULE_INIT_QOM);</div> <div style="margin-left:0cm;margin-right:0cm;">QTAILQ_FOREACH(e, l, node) {</div> <div style="margin-left:0cm;margin-right:0cm;">e->init();</div> <div style="margin-left:0cm;margin-right:0cm;">}</div> <div style="margin-left:0cm;margin-right:0cm;">这些xxx_register_types执行后,又得到了什么?</div> ① 分配一个TypeImpl结构体,使用TypeInfo来设置它: <div style="text-align:center;">[[Image:图片 62.png|top]]</div> ② 把TypeImpl放入链表:type_table <div style="margin-left:0cm;margin-right:0cm;">于是,在程序中就有了这样的链表:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 63.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 2.3 使用TypeImpl:实例化 === <div style="margin-left:0cm;margin-right:0cm;">在程序的type_table链表中,有很多TypeImpl结构体,比如CPU、GPIO、LED、LCD对应的TypeImpl结构体。</div> <div style="margin-left:0cm;margin-right:0cm;">但是这并不表示QEMU模拟的板子上有这些硬件,必竟它们只是“TypeImpl”,表示“类型”,需要在“<span style="color:#ff0000;">实例化</span>”之后,才表示板子上有了这些硬件。</div> <div style="margin-left:0cm;margin-right:0cm;">以CPU为例,代码为hw/cpu/a15mpcore.c,里面声明了一个A15MPPrivState结构体,还定义了一个TypeInfo结构体:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 58.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">A15MPPrivState和TypeInfo、TypeImpl之间有什么关系?</div> <div style="margin-left:0cm;margin-right:0cm;">TypeImpl的信息基本来自TypeInfo,</div> <div style="margin-left:0cm;margin-right:0cm;">所以问题转为:A15MPPrivState和TypeInfo之间有什么关系?</div> <div style="margin-left:0cm;margin-right:0cm;">A15MPPrivState用来表示一个CPU,你要在板子上添加一个CPU,必须分配、设置一个A15MPPrivState结构体。</div> <div style="margin-left:0cm;margin-right:0cm;">板子上的主芯片可能是单核CPU的,也可能是多核CPU的。</div> <div style="margin-left:0cm;margin-right:0cm;">假设有2个CPU,那么应该有对应的2个A15MPPrivState结构体。这2个CPU是类似的,同属于某类:用TypeImpl来描述。</div> <div style="margin-left:0cm;margin-right:0cm;">所以,可以得到下面的图:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 65.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">谁来分配、设置A15MPPrivState结构体?</div> ① 分配: <div style="margin-left:0cm;margin-right:0cm;">猜测是根据TypeInfo中的instrance_size来malloc出A15MPPrivState结构体。</div> ② 设置: <div style="margin-left:0cm;margin-right:0cm;">猜测是调用TypeInfo中的instrance_init函数来设置刚malloc出A15MPPrivState结构体。</div> <div style="margin-left:0cm;margin-right:0cm;">谁来malloc、谁来调用TypeInfo中的instrance_init函数?</div> <div style="margin-left:0cm;margin-right:0cm;">有2种方法:</div> ① qdev_create/qdev_init_nofail ② sysbus_init_child_obj/object_property_set_bool <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 2.4 实例化方法1:qdev_create/qdev_init_nofail === <div style="margin-left:0cm;margin-right:0cm;">这2个函数是成对出现的,以hw/display/100ask_qemu_fb.c为例:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 59.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">这2个函数的作用如下图所示:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 60.png|top]]</div> ==== 2.4.1 qdev_create分析 ==== ① 第2个参数是name,会被用来找到对应的TypeInfo结构体 ② 分配instance_size大小的内存,即分配ASK100FbState结构体,这用来表示LCD ③ 调用TypeInfo结构体中的class_init函数 <div style="margin-left:0cm;margin-right:0cm;">class_init,顾名思义,这个设备属于什么类别?先初始化一下它的类别。</div> <div style="margin-left:0cm;margin-right:0cm;">这些class_init函数都很类似,都是设置dc->realize函数,比如:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 64.png|top]]</div> ④ 调用TypeInfo结构体中的instance_init函数 <div style="margin-left:0.635cm;margin-right:0cm;"></div> <div style="margin-left:0.635cm;margin-right:0cm;"></div> <div style="margin-left:0.635cm;margin-right:0cm;"></div> <div style="margin-left:0.635cm;margin-right:0cm;"></div> <div style="margin-left:0.635cm;margin-right:0cm;"></div> ==== 2.4.2 qdev_init_nofail分析 ==== <div style="margin-left:0.63cm;margin-right:0cm;">qdev_init_nofail做的事情很简单:</div> <div style="text-align:center;margin-left:0.635cm;margin-right:0cm;">[[Image:图片 66.png|top]]</div> <div style="margin-left:0.63cm;margin-right:0cm;">只是把设备的realized属性设置为true,表示可以对它进行realize(变为现实)了。这会导致dc->realize函数被调用,即设备的类里的realize函数被调用。</div> ==== 2.4.3 总结:怎么创建设备 ==== <div style="margin-left:0.63cm;margin-right:0cm;">怎么定义一个设备?如下图:</div> <div style="text-align:center;margin-left:0.63cm;margin-right:0cm;">[[Image:图片 67.png|top]]</div> ① 先定义一个TypeInfo结构体 <div style="margin-left:0cm;margin-right:0cm;">里面有name,表示它的类型名。</div> <div style="margin-left:0cm;margin-right:0cm;">有class_init,这是“类别的初始化函数”,该类下可能有多个设备。class_init函数中通常给该类设置一个realize函数。</div> <div style="margin-left:0cm;margin-right:0cm;">有instance_size,每一个设备都用一个结构体来表示,比如LCD用ASK100FbState来描述。Instance_size表示这个结构体的大小。</div> <div style="margin-left:0cm;margin-right:0cm;">有instance_init,这是“实例的初始化函数”,它会被用来初始化设备结构体,比如初始化ASK100FbState结构体。</div> ② 注册这个TypeInfo结构体: <div style="margin-left:0cm;margin-right:0cm;">定义一个ask100fb_register_types函数,里面会注册TypeInfo结构体。</div> ③ 使用type_init,把ask100fb_register_types函数放入链表中 ④ 调用qdev_create创建设备,这会传入type name ⑤ 调用qdev_init_nofail设置设备的状态为realized,这会导致类别的realize函数被调用。 <div style="margin-left:0cm;margin-right:0cm;">简单地说,一个设备被创建时,这些函数被依次调用:</div> ① 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 === <div style="margin-left:0cm;margin-right:0cm;">参考hw/arm/fsl-imx6ul.c,里面大量的成对代码,比如:</div> [[Image:图片 68.png|top]] <div style="margin-left:0cm;margin-right:0cm;">上述函数的内部调用过程,跟qdev_create/qdev_init_nofail是类似的:</div> [[Image:图片 69.png|top]] ==== 2.5.1 object_initialize_child分析 ==== <div style="margin-left:0cm;margin-right:0cm;">该函数的第5个参数是type,表示type name,它会被用来找到对应的TypeImpl。</div> <div style="margin-left:0cm;margin-right:0cm;">找到后,会分配instance_size大小的结构体;</div> <div style="margin-left:0cm;margin-right:0cm;">然后调用TypeImpl中的class_init函数,这一般是设置dc->realize。</div> <div style="margin-left:0cm;margin-right:0cm;">最后调用TypeImpl中的instance_init函数。</div> ==== 2.5.2 object_property_set_bool分析 ==== <div style="margin-left:0.63cm;margin-right:0cm;">比如:</div> <div style="margin-left:0.63cm;margin-right:0cm;">[[Image:图片 70.png|top]]</div> <div style="margin-left:0.63cm;margin-right:0cm;">只是把设备的realized属性设置为true,表示可以对它进行realize(变为现实)了。这会导致dc->realize函数被调用,即设备的类里的realize函数被调用。</div> ==== 2.5.3 总结:怎么创建设备 ==== <div style="margin-left:0cm;margin-right:0cm;">怎么定义一个设备?如下图所示:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 71.png|top]]</div> ① 先定义一个TypeInfo结构体 <div style="margin-left:0cm;margin-right:0cm;">里面有name,表示它的类型名。</div> <div style="margin-left:0cm;margin-right:0cm;">有class_init,这是“类别的初始化函数”,该类下可能有多个设备。class_init函数中通常给该类设置一个realize函数。</div> <div style="margin-left:0cm;margin-right:0cm;">有instance_size,每一个设备都用一个结构体来表示,比如CPU用A15MPPrivState来描述。Instance_size表示这个结构体的大小。</div> <div style="margin-left:0cm;margin-right:0cm;">有instance_init,这是“实例的初始化函数”,它会被用来初始化设备结构体,比如初始化A15MPPrivState结构体。</div> ② 注册这个TypeInfo结构体: <div style="margin-left:0cm;margin-right:0cm;">定义一个a15mp_register_types函数,里面会注册TypeInfo结构体。</div> ③ 使用type_init,把a15mp_register_types函数放入链表中 ④ 调用object_initialize_child创建设备,这会传入type name ⑤ 调用object_property_set_bool设置设备的状态为realized,这会导致类别的realize函数被调用。 <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 2.6 怎么创建设备 === <div style="margin-left:0cm;margin-right:0cm;">其实在2.4.3、2.5.3已经整理出来了,在这里只是再次总结一下。</div> ==== 2.6.1 设置TypeInfo结构体 ==== <div style="margin-left:0cm;margin-right:0cm;">比如LCD:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 72.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">要注意到,里面有一个ASK100FbState结构体,这个结构体由我们自己设置。但是它的格式有一定要求:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 73.png|top]]</div> ==== 2.6.2 注册TypeInfo结构体 ==== <div style="margin-left:0cm;margin-right:0cm;">比如:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 74.png|top]]</div> ==== 2.6.3 使用TypeInfo创建设备/设置设备的realized属性为true ==== <div style="margin-left:0cm;margin-right:0cm;">这有2种方法,前面介绍过:</div> qdev_create/qdev_init_nofail object_initialize_child/object_property_set_bool == 3. QEMU的设备模拟 == === 3.1 QEMU模拟外设的原理 === <div style="margin-left:0cm;margin-right:0cm;">QEMU主要是实现了CPU核的模拟,可以读写某个地址。</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU的模拟外设的原理很简单:<span style="color:#ff0000;">硬件即内存</span>。</div> <div style="margin-left:0cm;margin-right:0cm;">要在QEMU上模拟某个外设,思路就是:</div> ① CPU读某个地址时,QEMU模拟外设的行为,把数据返回给CPU ② CPU写某个地址时,QEMU获得数据,用来模拟外设的行为。 <div style="margin-left:0cm;margin-right:0cm;">即:要模拟外设备,我们只需要针对外设的地址提供对应的读写函数即可。</div> <div style="margin-left:0cm;margin-right:0cm;">以GPIO为例:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 77.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">以LCD控制器为例,它主要有2大功能:</div> ① 写LCD控制器,根据外接的LCD设置参数,比如分辨率、BPP、各种时序 ② 从FrameBuffer中不断获得数据发给LCD,在LCD上显示出来。 <div style="margin-left:0cm;margin-right:0cm;">站在Linux LCD驱动的角度,上述2大功能可以分为:</div> ① 写LCD控制器的相关寄存器 ② 分配FrameBuffer,写FrameBuffer <div style="margin-left:0cm;margin-right:0cm;">QEMU中要模拟LCD控制器,需要:</div> ① 记录驱动程序写寄存器的值,解析出分辨率等信息 ② 记录FrameBuffer的地址,并持续不断地从中得到图像数据并显示出来 <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">框图如下:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 1.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">简单地说,</div> ① 设置LCD控制器时: <div style="margin-left:0cm;margin-right:0cm;">在QEMU中可以给LCD控制器的访问地址A1~A2提供读写回调函数,比如:</div> qemu_a1a2_read qemu_a1a2_write <div style="margin-left:0cm;margin-right:0cm;">当Linux LCD驱动程序写LCD控制器的寄存器时,就会导致qemu中的qemu_a1a2_write函数被调用,在函数中分析、记录这些值,得到分辨率等信息。</div> ② 写FrameBuffer时: <div style="margin-left:0cm;margin-right:0cm;">在QEMU中针对FrameBuffer提供一个刷新函数。</div> <div style="margin-left:0cm;margin-right:0cm;">当LCD驱动写FrameBuffer时,QEMU会使用这些数据更新GUI窗口的图像。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 3.2 给某段地址提供读写函数 === <div style="margin-left:0cm;margin-right:0cm;">怎么描述某段地址:基地址、大小;还得给这段地址提供读写函数。</div> <div style="margin-left:0cm;margin-right:0cm;">这段地址设置好后,需要添加进system_memory去。</div> <div style="margin-left:0cm;margin-right:0cm;">有2种方法。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> ==== 3.2.1 memory_region_init_io/memory_region_add_subregion ==== <div style="margin-left:0cm;margin-right:0cm;">比如:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 80.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">上图中memory_region_init_io被用来初始化一块内存s->iomem,指定了它的读写函数、大小。</div> <div style="margin-left:0cm;margin-right:0cm;">然后给s->iomem指定了基地址,并添加进system_memory中。</div> <div style="margin-left:0cm;margin-right:0cm;">以后,客户机上的程序读写这块地址时,就会导致对应的读写函数被调用。</div> ==== 3.2.2 memory_region_init_io/sysbus_init_mmio/sysbus_mmio_map ==== <div style="margin-left:0cm;margin-right:0cm;">以hw/net/smc91c111.c为例:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 81.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 4. 模拟LED == === 4.1 主要函数 === <div style="margin-left:0cm;margin-right:0cm;">客户机上的程序要操作LED时,会先设置GPIO为output,然后把值写入某个数据寄存器。</div> <div style="margin-left:0cm;margin-right:0cm;">所以我们只需要针对数据寄存器提供对应的write函数即可</div> <div style="margin-left:0cm;margin-right:0cm;">QEMU中已经实现IMX6ULL的GPIO模拟,代码在hw\gpio\imx_gpio.c中。</div> <div style="margin-left:0cm;margin-right:0cm;">对应的write函数如下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 82.png|top]]</div> === 4.2 添加一段内存 === <div style="margin-left:0cm;margin-right:0cm;">客户机的程序要设置GPIO5_IO03为输出引脚时,需要访问这个寄存器:</div> IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 <div style="margin-left:0cm;margin-right:0cm;">它的基地址是0x2290014,QEMU中并没有添加这个地址,客户机访问它时就会发生硬件错误。</div> <div style="margin-left:0cm;margin-right:0cm;">所以在hw/arm/fsl-imx6ul.c中使用如下代码添加了这块空间:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 83.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 5. 模拟LCD == <div style="margin-left:0cm;margin-right:0cm;">我们添加的文件是:hw/display/100ask_qemu_fb.c,新加一个文件时要把它编进QEMU中,需要修改同目录的Makefile.objs,比如修改hw/display/Makefile.objs,添加一行:</div> common-obj-$(CONFIG_FSL_IMX6UL) += 100ask_qemu_fb.o === 5.1 在Linux上编写LCD驱动程序 === <div style="margin-left:0cm;margin-right:0cm;">既然操作的不是真实的LCD控制器,那么LCD驱动程序可以极大精简。</div> ① 对于LCD控制器,只需要操作4个寄存器: <div style="margin-left:0cm;margin-right:0cm;">分别用来保存:framebuffer的物理地址、宽度、高度、BPP。</div> <div style="margin-left:0cm;margin-right:0cm;">你需要记住这些寄存器的物理地址(可以自己指定地址是什么)。</div> ② 对于FrameBuffer: <div style="margin-left:0cm;margin-right:0cm;">驱动程序分配得到FrameBuffer后,要把它的物理地址写到上述第1个寄存器里。</div> <div style="margin-left:0cm;margin-right:0cm;">部分代码如下,其他的时钟使能、GPIO设置等等都不再需要:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 2.png|top]]</div> === 5.2 在QEMU中创建LCD控制器的设备 === <div style="text-align:center;">[[Image:图片 4.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 5.3 重要函数分析 === ==== 5.3.1 给LCD控制器的寄存器提供回调函数 ==== <div style="text-align:center;">[[Image:图片 5.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 7.png|top]]</div> ==== 5.3.2 给FrameBuffer提供更新函数 ==== <div style="text-align:center;">[[Image:图片 6.png|top]]</div> ==== 5.3.3 更新函数 ==== <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 9.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 6. QEMU的输出:GUI系统 == <div style="margin-left:0cm;margin-right:0cm;">QEMU的GUI系统,支持SDL、GTK等。SDL使用比较简单,我们就使用SDL来显示GUI。</div> <div style="margin-left:0cm;margin-right:0cm;">SDL的显示原理跟LCD FrameBuffer是一样的,可以认为每一个SDL窗口都有一个显存。你可以在显存中修改每一个象素的颜色。</div> <div style="margin-left:0cm;margin-right:0cm;">本文以hw/display/100ask_qemu_fb.c为例进行讲解。</div> === 6.1 创建GUI Console === <div style="margin-left:0cm;margin-right:0cm;">创建GUI Console的函数有2个:graphic_console_init、graphic_console_init_hidden。</div> <div style="margin-left:0cm;margin-right:0cm;">后者是我们添加的,它创建的GUI Console默认是隐藏的,要显示的话需要在“Device Manager”中点击对应的按钮。</div> <div style="margin-left:0cm;margin-right:0cm;">LCD代码如下,主要是graphic_console_init函数:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 84.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">调用graphic_console_init时,要传入一个GraphicHwOps结构体,如下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 85.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 6.2 GUI更新函数 === <div style="margin-left:0cm;margin-right:0cm;">ask100fb_update函数被GUI系统周期性地调用:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 86.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">效果如下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 87.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 6.3 GUI函数详解 === ==== 6.3.1 解析BMP文件得到RGB数据 ==== <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 88.png|top]]</div> ==== 6.3.2 显示RGB数据 ==== <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 89.png|top]]</div> ==== 6.3.3 显示LCD数据 ==== <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 90.png|top]]</div> == 7. QEMU的输入:鼠标事件 == <div style="margin-left:0cm;margin-right:0cm;">点击buttons界面时,对应的按钮会被按下或松开:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 91.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">这是怎么实现的?</div> <div style="margin-left:0cm;margin-right:0cm;">我们需要给这个GUI界面添加鼠标处理函数,代码在ui/button_ui.c中。这是新加的文件,要把它编进QEMU中需要修改同目录下的Makefile.objs,比如修改ui/Makefile.objs,添加一行:</div> common-obj-$(CONFIG_FSL_IMX6UL) += button_ui.o === 7.1 分配、设置、注册QemuInputHandler === <div style="text-align:center;">[[Image:图片 92.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 7.2 QemuInputHandler处理函数分析 === <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 96.png|top]]</div> === 7.3 输入事件处理流程 === <div style="margin-left:0cm;margin-right:0cm;">输入事件的处理源头是sdl2_2d_refresh函数:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 16.png|top]]</div> sdl2_2d_refresh <div style="margin-left:0cm;margin-right:0cm;">sdl2_poll_events(scon);</div> <div style="margin-left:0cm;margin-right:0cm;">SDL_PollEvent(ev)</div> <div style="margin-left:0cm;margin-right:0cm;">handle_keydown(ev);</div> <div style="margin-left:0cm;margin-right:0cm;">handle_keyup(ev);</div> <div style="margin-left:0cm;margin-right:0cm;">handle_textinput(ev);</div> <div style="margin-left:0cm;margin-right:0cm;">handle_mousemotion(ev);</div> <div style="margin-left:0cm;margin-right:0cm;">handle_mousebutton(ev); // SDL_MOUSEBUTTONDOWN,UP</div> <div style="margin-left:0cm;margin-right:0cm;">sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);</div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 7.4 QEMU捕获的输入事件,可以作为ABS也可以作为REL事件上传 === <div style="margin-left:0cm;margin-right:0cm;">作为哪类事件上传,取决于qemu_input_is_absolute()的返回值:</div> <div style="text-align:center;">[[Image:图片 26.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">qemu_input_is_absolute函数只会判断第1个GUI Console中的QemuInputHandler。我们的第1个GUI Console是“Device Manager”,它的代码是hw/display/device_manager.c。</div> <div style="margin-left:0cm;margin-right:0cm;">对应的QemuInputHandler的mask必须设置为INPUT_EVENT_MASK_ABS:</div> <div style="text-align:center;">[[Image:图片 95.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 8. 使用中断 == <div style="margin-left:0cm;margin-right:0cm;">在QEMU中模拟一个外设时,这个外设要使用中断的话,并不复杂。</div> <div style="margin-left:0cm;margin-right:0cm;">先看看硬件框架:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 97.png|top]]</div> === 8.1 外设发出哪一个中断? === <div style="margin-left:0cm;margin-right:0cm;">GIC从多个中断源获得中断信号,它会发信号给CPU,这样CPU才会处理中断。 </div> <div style="margin-left:0cm;margin-right:0cm;">作为外设备,它要发出哪一个中断呢?</div> <div style="margin-left:0cm;margin-right:0cm;">在include/hw/arm/fsl-imx6ul.h中列出了各个中断源,如下:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 98.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">你需要查看这些中断源,确定你的外设要发出哪一个中断。</div> <div style="margin-left:0cm;margin-right:0cm;">你的外设,要跟中断控制器建立联系,即确定使用哪一个中断。</div> ==== 8.8.1 调用sysbus_init_irq初始化qemu_irq ==== <div style="margin-left:0cm;margin-right:0cm;">在QEMU中,用qemu_irq结构体来描述中断,需要初始化qemu_irq结构体。</div> <div style="margin-left:0cm;margin-right:0cm;">以GPIO为例,代码在hw/gpio/imx_gpio.c中:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 99.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">sysbus_init_irq函数很有意思:</div> ① 它会给设备dev添加一个属性prop,prop的名字是“sysbus-irq[0]”或“sysbus-irq[1]” ② 会给这个prop添加一个link,link到<span style="color:#ff0000;">&s->irq[0]</span>或<span style="color:#ff0000;">&s->irq[1]</span> ==== 8.8.2 调用sysbus_connect_irq连接中断 ==== <div style="margin-left:0cm;margin-right:0cm;">在hw/arm/fsl-imx6ul.c中,给GPIO模块和GIC建立联系:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 100.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">sysbus_connect_irq也很神奇,它是通过名字“sysbus-irq[0]”或“sysbus-irq[1]”找到设备的属性。</div> <div style="margin-left:0cm;margin-right:0cm;">然后根据属性的link找到之前传入的<span style="color:#ff0000;">&s->irq[0]</span>或&<span style="color:#ff0000;">s->irq[1]</span>,最后设置它。</div> <div style="margin-left:0cm;margin-right:0cm;">设置为什么呢?下面的函数会返回一个qemu_irq,第2个参数用来指定是哪一个中断:</div> qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX6UL_I2Cn_IRQ[i]) <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="text-align:center;"></div> === 8.2 中断触发 === 发出中断: <div style="margin-left:0cm;margin-right:0cm;">qemu_set_irq(irq, 1);</div> 清除中断: <div style="margin-left:0cm;margin-right:0cm;">qemu_set_irq(irq, 0);</div> <div style="margin-left:0cm;margin-right:0cm;">上面的2个调用必须成双出现,如果多次发出中断而没有清除中断,那后面发出的中断是无效的。</div> <div style="margin-left:0cm;margin-right:0cm;">这里说的成双出现,并不是说它们必须前后出现。流程是这样的:</div> ① 用户按下按键后,使用“qemu_set_irq(irq, 1)”发出中断 ② 客户机的程序处理完中断后,它会写ISR寄存器来清除中断: <div style="margin-left:0cm;margin-right:0cm;">对ISR寄存器的写操作会导致QEMU中对应的write函数被调用,在里面执行“qemu_set_irq(irq, 0)”。</div> <div style="margin-left:0cm;margin-right:0cm;">对于hw/gpio/imx_gpio.c,发出中断的函数流程为:</div> notify_imx_gpio_change <div style="margin-left:0cm;margin-right:0cm;">imx_gpio_set(s, pin, level);</div> <div style="margin-left:0cm;margin-right:0cm;">imx_gpio_update_int(s);</div> qemu_set_irq(s->irq[0], 1); <div style="margin-left:0cm;margin-right:0cm;">清除中断的流程为:</div> imx_gpio_write <div style="margin-left:0cm;margin-right:0cm;">case ISR_ADDR:</div> <div style="margin-left:0cm;margin-right:0cm;">s->isr &= ~value;</div> <div style="margin-left:0cm;margin-right:0cm;">imx_gpio_set_all_int_lines(s);</div> <div style="margin-left:0cm;margin-right:0cm;">qemu_set_irq(s->irq[0], 0);</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 9. 模拟按键 == <div style="margin-left:0cm;margin-right:0cm;">我们模拟的按键涉及GUI、鼠标输入、中断,值得仔细研究。</div> <div style="margin-left:0cm;margin-right:0cm;">GUI和鼠标的处理,都在这个文件里:ui/button_ui.c</div> <div style="margin-left:0cm;margin-right:0cm;">当用户在GUI中点击某个按键时,它对应哪个引脚?</div> <div style="margin-left:0cm;margin-right:0cm;">这由这个文件决定:hw/gpio/100ask_imx6ull_buttons.c</div> <div style="margin-left:0cm;margin-right:0cm;">上述文件确定是哪一个引脚之后,要通知hw/gpio/imx_gpio.c来处理。</div> === 9.1 按键GUI === <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 3.png|top]]</div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="color:#ff0000;margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 9.2 点击按键 === <div style="text-align:center;">[[Image:图片 8.png|top]]</div> === 9.3 更新按键图片的同时 === <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 10.png|top]]</div> === 9.4 单板的代码怎么处理按键 === <div style="margin-left:0cm;margin-right:0cm;">对应的代码在hw/gpio/100ask_imx6ull_buttons.c中:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 11.png|top]]</div> === 9.5 IMX6ULL的代码怎么处理输入引脚 === <div style="margin-left:0cm;margin-right:0cm;">对应的代码在hw/gpio/imx_gpio.c中:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 13.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 10. QEMU启动过程 == === 10.1 各个模块的注册 === <div style="margin-left:0cm;margin-right:0cm;">各个模块都定义了一个TypeInfo结构体,比如LCD:</div> <div style="text-align:center;">[[Image:图片 21.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">并且都使用type_init定义了一个属性为<span style="color:#ff0000;">constructor</span>的函数(include/qemu/module.h):</div> <nowiki>#define type_init(function) module_init(function, MODULE_INIT_QOM)</nowiki> <nowiki>#define module_init(function, type) </nowiki>\ static void __attribute__((<span style="color:#ff0000;">constructor</span>)) do_qemu_init_ ## function(void) \ { \ register_dso_module_init(function, type); \ } <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 23.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">这些属性为<span style="color:#ff0000;">constructor</span>的函数会在main函数之前被调用,这样就得到了一个链表:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 14.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 10.2 各个模块的初始化 === <div style="margin-left:0cm;margin-right:0cm;">main函数位于vl.c中,它有如下调用:</div> main <div style="margin-left:0cm;margin-right:0cm;">module_call_init(MODULE_INIT_QOM);</div> <div style="margin-left:0cm;margin-right:0cm;">QTAILQ_FOREACH(e, l, node) {</div> <div style="margin-left:0cm;margin-right:0cm;">e->init();</div> <div style="margin-left:0cm;margin-right:0cm;">}</div> <div style="margin-left:0cm;margin-right:0cm;">这些xxx_register_types执行后,又得到了什么?</div> ① 分配一个TypeImpl结构体,使用TypeInfo来设置它: <div style="text-align:center;">[[Image:图片 18.png|top]]</div> ② 把TypeImpl放入链表:type_table <div style="margin-left:0cm;margin-right:0cm;">于是,在程序中就有了这样的链表:</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 19.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;">程序中有一系列的TypeImpl,但是并不表示会用到它们。要用某个TypeImpl,需要创建对应的设备。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 10.3 选择machine === <div style="margin-left:0cm;margin-right:0cm;">启动qemu时,会传入参数“-M mcimx6ul-evk”,对应的文件为:hw/arm/mcimx6ul-evk.c</div> <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 27.png|top]]</div> === 10.4 选择CPU === <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 28.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">CPU在中hw/arm/fsl-imx6ul.c定义:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 29.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 10.5 CPU的初始化 === <div style="margin-left:0cm;margin-right:0cm;">hw/arm/mcimx6ul-evk.c mcimx6ul_evk_init:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 31.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">这会导致hw/arm/fsl-imx6ul.c中fsl_imx6ul_init函数被调用:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 32.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">在fsl_imx6ul_init函数中,会去创建名为a7mpcore的设备,它的类型是TYPE_A15MPCORE_PRIV:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 33.png|top]]</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 34.png|top]]</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 35.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">a7mpcore设备对应的realize函数被调用时,里面会实例化GIC等。</div> === 10.6 在system memory中添加内存 === <div style="margin-left:0cm;margin-right:0cm;">分配system memory,代码为hw/arm/mcimx6ul-evk.c中的mcimx6ul_evk_init函数:</div> <div style="text-align:center;margin-left:0cm;margin-right:0cm;">[[Image:图片 30.png|top]]</div> <div style="margin-left:0cm;margin-right:0cm;">其中的ram_size来自QEMU运行时的参数“-m 512M”;内存基地址为0x80000000。</div> <div style="margin-left:0cm;margin-right:0cm;">从这里可以知道,system memory中既含有可读可写的内存,也含有各种模块的寄存器。</div> <div style="margin-left:0cm;margin-right:0cm;"></div> == 11. 调试 == === 11.1 要调试,运行gdb程序之前,必须先进入源码目录 === <div style="margin-left:0cm;margin-right:0cm;">无论是使用gdb调试PC程序,还是使用arm-xxx-gdb调试ARM程序,运行这些gdb程序前,必须进入要调试的源码的目录。</div> <div style="margin-left:0cm;margin-right:0cm;">然后再启动gdb或arm-xxx-gdb。</div> === 11.2 调试QEMU本身 === <div style="margin-left:0cm;margin-right:0cm;">先进入QEMU源码目录,执行如下命令。</div> <div style="margin-left:0cm;margin-right:0cm;">这个命令中涉及qemu-system-arm、zImage、设备树、文件系统这4个文件的路径,你要改成你的路径。</div> 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 <div style="margin-left:0cm;margin-right:0cm;">关键点在于:gdb --args ,它后面就可以跟一连串的命令及参数了。</div> <div style="margin-left:0cm;margin-right:0cm;">这种方法有一个缺点,就是客户机上的Linux系统启动后,你无法再在终端中输入gdb命令。可以使用下一种方法来调试。</div> === 11.3 调试之前就运行的QEMU === <div style="margin-left:0cm;margin-right:0cm;">先进入QEMU源码目录。</div> ① 首先确定qemu-system-arm进程的PID <div style="margin-left:0cm;margin-right:0cm;">执行如下命令:</div> ps -A | grep qemu <div style="margin-left:0cm;margin-right:0cm;">假设得到PID为9527</div> ② 在QEMU源码目录下执行如下命令: sudo gdb -p 9527 <div style="margin-left:0cm;margin-right:0cm;"><span style="color:#ff0000;">'''注意'''</span>:一定要加上sudo</div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> <div style="margin-left:0cm;margin-right:0cm;"></div> === 11.4 调试客户机上的Linux内核 === ==== 11.4.1 首先运行qemu-system-arm ==== <div style="margin-left:0cm;margin-right:0cm;">执行QEMU的命令时,在最后加上参数“-s -S”。</div> <div style="margin-left:0cm;margin-right:0cm;">-s是“-gdb tcp::1234”的缩写,表示QEMU将会在本机端口1234上开启gdbserver。</div> <div style="margin-left:0cm;margin-right:0cm;">-S表示不启动CPU,当你使用arm-xxx-gdb连接上QEMU后,必须执行c命令才可以运行内核。</div> <div style="margin-left:0cm;margin-right:0cm;">我用的命令如下所示。</div> <div style="margin-left:0cm;margin-right:0cm;">这个命令中涉及qemu-system-arm、zImage、设备树、文件系统这4个文件的路径,你要改成你的路径。</div> 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 ==== 14.4.2 进入内核源码目录,运行arm-xxx-gdb vmlinux ==== <div style="margin-left:0cm;margin-right:0cm;">[[Image:图片 20.png|top]]</div>
该页面使用的模板:
模板:Redtext
(
查看源代码
)
返回至
Qemu
。
导航
导航
WIKI首页
官方店铺
资料下载
交流社区
所有页面
所有产品
MPU-Linux开发板
MCU-单片机开发板
Linux开发系列视频
单片机开发系列视频
所有模块配件
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志