基于QEMU的vexpress-a9开发调试环境搭建

本文详细介绍了在Windows环境中通过VirtualBox或VMware搭建LinuxUbuntu,安装必要的工具,编译kernel和U-Boot,制作根文件系统,以及使用QEMU进行kernel调试的过程,包括设置链接地址、断点和执行调试命令等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

准备工作

  1. 如果是window环境,则建议安装virtual box或vmware以便安装linux ubuntu。
  2. 在ubuntu上安装必要工具: sudo adb install qemu-system-arm gdb-multiarch libnl-3-dev libncurses5-dev binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi

编译kernel,uboot以及制作ramdisk

编kernel

去kernel.org下载kernel内核工程,我这里选的是3.18,下载地址:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/snapshot/linux-3.18.tar.gz
编译命令:
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm vexpress_defconfig
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm zImage
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm dtbs
编译完毕后,会生成zImage镜像

编uboot

下载地址:https://ftp.denx.de/pub/u-boot/,我这里选的是2016.01: https://ftp.denx.de/pub/u-boot/u-boot-2016.01.tar.bz2
编译命令:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_ca9x4_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
注意:如果环境里gcc版本是7,则需要 cp include/linux/compiler-gcc5.h include/linux/compiler-gcc7.h,因为uboot2016里没有适配gcc7

制作根文件系统

如果想正常运行起kernel,需要制作根文件系统,具体可参考:https://www.zhaixue.cc/qemu/qemu-build_busybox_rootfs.html

qemu正常运行kernel

将上面做个根文件rootfs.ext3移到kernel编完的根目录下,在kernel编译根目录执行执行命令:

qemu-system-arm \
        -M vexpress-a9 \
        -m 256 \
        -kernel arch/arm/boot/zImage \
        -nographic \
        -append "root=/dev/mmcblk0 rw console=ttyAMA0 memblock=debug printk.synchronous=1" \
        -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
        -sd rootfs.ext3

其中zImage是kernel镜像,vexpress-v2p-ca9.dtb是设备树文件(同内核一同编译出来)。
启动后的命令行
执行到shell这里,按回车,kernel就启动到控制台了。
注意:如果要退出,需要另起个shell,运行pkill qemu即可退出qemu

qemu从uboot运行

在编译好的uboot根目录下运行:
qemu-system-arm -M vexpress-a9 -m 256M -nographic -kernel u-boot
其中u-boot为编译出的uboot镜像
在这里插入图片描述

调试kernel

从start_kernel()开始调试

qemu-system-arm \
        -M vexpress-a9 \
        -m 256 \
        -kernel arch/arm/boot/zImage \
        -nographic \
        -append "root=/dev/mmcblk0 rw console=ttyAMA0 memblock=debug printk.synchronous=1" \
        -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
        -sd ../../rootfs.ext3 \
        -gdb tcp::1234  -S

在这里插入图片描述
不同于直接运行,调试运行执行后,shell会马上block住,等待调试器接入,我们需要另外启动一个shell到kernel根目录下,执行如下命令:

gdb-multiarch vmlinux

其中,vmlinux文件是编译kernel时,生成的
在这里插入图片描述

进入gdb命令行下,敲入:

target remote:1234

则连接上被调试的qemu环境。在这里插入图片描述
我们要从start_kernel()开始调试的话,则敲入

b start_kernel //在start_kernel设置断点
c //执行运行,运行到断点处会自动停住

在这里插入图片描述
可以看到gdb停到main.c的502行start_kernel()的入口处,这里我们可以用各种调试指令了,比如

i r pc

查看当前pc指令位置
在这里插入图片描述

从kernel起始位置compressed/vmlinux开始调试

了解kernel的人应该知道start_kernel其实已经从汇编执行到了C代码,并不是kernel真正意义上的“起始位置”。
我们知道编译完的kernel有两个可执行的vmlinux,一个在根目录,一个在arch/arm/compressed,
以我们当前例子这个arm架构的vexpress-a9举例,最先运行的是起始代码在kernel目录下的arch/arm/boot/compressed/head.S起始,那调试就应该用arch/arm/boot/compressed/vmlinux来调试。这个vmlinux的具体的加载地址,是由qemu内设定的vexpress-a9架构决定的。
我们可以下载qemu的源码:

git clone https://gitlab.com/qemu-project/qemu.git

找到vexpress-a9的架构所在源码(hw/arm/vexpress.c)查到内存起始地址是0x60000000
在这里插入图片描述
另外由于架构上会偏移一段位移地址,来启动内核
在这里插入图片描述
在这里插入图片描述
综上:qemu如果启动vexpress-a9上soc的内核镜像,会加载到地址为0x60010000的地址上,知道了这两个信息,我们可以将arch/arm/boot/compressed/vmlinux的链接地址设置成0x60010000。具体方法是,在之前我们编译过kernel镜像是,内核帮我们生成过一个arch/arm/boot/compressed/vmlinux.lds,我们改写一下其中的TEXT_OFFSET从0改成0x60010000即可,再重新编译一下kernel镜像(make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm zImage)即可将arch/arm/boot/compressed/vmlinux的链接地址从0修改到0x60010000上。
在这里插入图片描述
调试时,先要设置断点到0x60010000位置,再执行continue让pc运行到该地址后,才能开始单步调试:
在这里插入图片描述
与之对应上的arch/arm/boot/compressed/head.S:
在这里插入图片描述
compressed/vmlinux这段主要做的是自解压真正的内核镜像(vmlinux),并运行到arch/arm/kernel/head.S:
在这里插入图片描述
这里再介绍一下打断点的技巧,由于我们在调试汇编,所以一般断点都是打在地址上,而地址和代码需要对应起来,我们需要反汇编可执行程序。反汇编的方法为:

arm-linux-gnueabi-objdump -d arch/arm/boot/compressed/vmlinux > dump.s

第一列就是可执行程序的链接地址,第二列是可执行程序实际的hex,后一列就是反汇编代码含义。
可以看到,第一列的起始就是我们设置的compressed/vmlinux的链接地址。有了dump.s,我们想执行代码到哪里,只需要设置断点到对应地址就行了
在这里插入图片描述

如果希望完整调试compressed/vmlinux直到跳转到正真的vmlinux中(arch/arm/kernel/head.S)的话,由于在解压vmlinux之前需要腾出ram初始地址,以及使能mmu和cache,会做一次relocate。在relocate后由于我们debug的compressed/vmlinux的链接地址会再次不匹配,所以需要再次修改compressed/vmlinux的链接地址以便调试relocate后的compressed/vmlinux。

这里r6寄存器实测为0x6498d40, 重定向后的restart位置为0x60658e08
在这里插入图片描述

重定位到restart位置,相对原链接地址,偏移0x648d40位置。
在这里插入图片描述
所以,我们可以将compressed/vmlinux再次设置为0x6001000+0x648d40=0x60658d40,并且再次重新调试,将断点直接设置到重定向后的restart位置为0x60658e08
在这里插入图片描述
接下来我们就可以调试compressed/vmlinux的后半部分,直到arch/arm/kernel/head.S。
在这里插入图片描述
在这里插入图片描述
可以看到,最后pc会赋值r4(0x60008000)这个就是kernel起始的运行地址,这个是在下面这段代码里定义的。而TEXT_OFFSET则是在arch/arm/Makefile里定义的,为0x8000。
在这里插入图片描述
在这里插入图片描述
直到这里,compressed/vmlinux执行了最后一句 mov pc r4,跳转到了0x60080000,执行arch/arm/kernel/head.S真实的kernel运行起来了。

调试kernel汇编

有了前面的经验,相信我们已经知道该如何调试kernel(vmlinux)了吧。
我们需要将vmlinux的链接地址设置到0x60008000,设置方法是修改arch/arm/Kconfig中VMSPLIT_2G对应的值到0x60000000,并重新编译kernel。
在这里插入图片描述
接着就可以直接b *0x60008000,并且执行到arch/arm/kernel/head.S。
在这里插入图片描述
(本文完)

<think>好的,用户现在想了解如何学习Cortex-A9处理器。首先,我需要回顾之前的对话,用户之前已经询问过Cortex-A9的详细介绍,现在转向学习方法。这可能意味着用户有实际应用的需求,比如嵌入式开发或相关项目,或者他们正在准备进入这一领域的学习。 接下来,我需要考虑用户可能的背景。由于Cortex-A9属于较老的架构,但仍在许多嵌入式系统中使用,用户可能是在工作中遇到相关项目,或者是学生需要掌握这一技术。因此,回答需要既包含理论基础,又强调实践操作,同时可能涉及工具链和实际案例。 用户的需求可能不仅仅是学习架构本身,而是如何系统地掌握,包括开发环境搭建、编程、调试以及项目应用。需要分步骤引导,从基础到进阶,逐步深入。可能还需要推荐资源,如书籍、在线课程或实验平台,帮助用户找到学习材料。 另外,用户可能对实践环节特别关注,比如如何获取开发板,如何开始第一个程序,如何处理多核编程或性能优化等问题。需要提供具体的例子,比如代码片段、调试命令,以及常见问题的解决方法。 还需要考虑用户可能遇到的挑战,比如交叉编译环境的配置、硬件调试的复杂性、多核同步问题等。在回答中需要预先提及这些难点,并给出解决建议,帮助用户规避常见陷阱。 最后,结合之前的回答结构,保持一致的风格,使用清晰的标题、分点说明、代码示例和公式,增强可读性和实用性。确保语言口语化,避免过于技术化的术语,或在必要时进行解释,让不同层次的读者都能理解。</think>学习Cortex-A9处理器需要理论与实践相结合,以下是系统化的学习路径和关键实践方法,包含具体操作案例和开发技巧: --- ### 一、基础理论准备 1. **架构文档精读** - 必读手册: -ARM Cortex-A9 MPCore Technical Reference Manual》(ARM DDI 0407) -ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》 - 重点章节标注示例: ```markdown [!NOTE] 关键章节标记 - Chapter 3: Processor Modes (USR/SVC/IRQ等模式转换) - Chapter 4: Memory Management Unit (页表项bitmap解析) - Chapter 10: L1 Cache Controller (缓存替换策略) ``` 2. **指令集训练** - 编写基础汇编程序(使用GAS语法): ```assembly .global _start _start: MOV R0, #0x1000 @ 立即数加载 LDR R1, =data_block @ 伪指令地址加载 loop: LDR R2, [R1], #4 @ 自动后变址加载 ADD R0, R0, R2 @ 累加操作 CMP R1, #end_data @ 地址比较 BNE loop data_block: .word 0x1,0x2,0x3,0x4 end_data: ``` --- ### 二、开发环境搭建 1. **工具链配置** - 推荐使用Linaro GCC交叉编译链: ```bash # 安装命令 sudo apt-get install gcc-arm-linux-gnueabihf # 验证版本 arm-linux-gnueabihf-gcc -v ``` 2. **QEMU仿真环境** - 启动带Cortex-A9仿真的虚拟机: ```bash qemu-system-arm -M vexpress-a9 -kernel zImage \ -dtb vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0" ``` 3. **硬件开发板选择** | 开发板型号 | 特点 | 参考价格 | |------------------|-------------------------------|----------| | Zynq-7000系列 | FPGA+双核A9,适合异构开发 | $300 | | i.MX6 Solo | 工业级单核方案 | $150 | | STM32MP157 | 集成3D GPU,支持Android | $80 | --- ### 三、关键实验项目 1. **启动代码分析** - 分析Uboot启动流程(以vexpress板为例): ```c // arch/arm/cpu/armv7/start.S reset: bl save_boot_params mrc p15, 0, r0, c1, c0, 0 @ 读取SCTLR bic r0, r0, #0x2000 @ 关闭指令缓存 mcr p15, 0, r0, c1, c0, 0 @ 写回SCTLR ``` 2. **中断控制器实验** - 编写GIC中断处理程序: ```c void __irq_handler() { uint32_t int_id = readl(GIC_CPU_INTACK); switch(int_id) { case TIMER_IRQ: handle_timer(); // 定时器中断处理 break; } writel(int_id, GIC_CPU_EOI); } ``` 3. **多核通信实践** - 使用核间中断(IPI)实现任务分发: ```c // Core0发送信号 send_ipi(1, TASK_START); // 向Core1发送任务启动指令 // Core1接收处理 while(!pending_ipi()); task_id = get_ipi_msg(); ``` --- ### 四、性能优化技巧 1. **NEON加速实践** - 矩阵乘法优化(4x4单精度): ```c void neon_matmul(float *C, float *A, float *B) { asm volatile ( "vld1.32 {d16-d19}, [%1]! \n" // 加载A矩阵 "vld1.32 {d20-d23}, [%2]! \n" // 加载B矩阵 "vmla.f32 q12, q8, q10 \n" // 乘加运算 "vst1.32 {d24-d27}, [%0]! \n" // 存储结果 : "+r"(C) : "r"(A), "r"(B) : "q8","q9","q10","q11","q12" ); } ``` 2. **缓存优化策略** - 数据预取示例: ```c #define PREFETCH(addr) __builtin_prefetch(addr,0,3) for(int i=0; i<size; i+=64){ PREFETCH(&data[i+64]); // 提前预取下一缓存行 process(data[i]); } ``` --- ### 五、调试与问题排查 1. **JTAG调试配置** - OpenOCD配置文件示例(J-Link调试器): ```tcl interface jlink transport select jtag set CHIPNAME cortex_a9 jtag newtap $CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf targets cortex_a9.cpu ``` 2. **常见问题解决方案** | 问题现象 | 排查步骤 | 典型原因 | |--------------------------|-----------------------------------|------------------------| | 启动卡在uboot | 检查DDR初始化参数 | 时序配置错误 | | 多核系统死锁 | 分析spinlock使用情况 | 未正确释放锁 | | NEON计算结果异常 | 检查内存对齐(16字节对齐) | 未使用ALIGNED属性 | --- ### 六、进阶学习资源 1. **推荐书籍** -ARM System Developer&#39;s Guide》Andrew Sloss 著 - 《Cortex-A Series Programmer&#39;s Guide》ARM官方文档 2. **在线实验平台** - ARM DS-5 Development Studio:支持虚拟化Cortex-A9仿真 - Keil MDK-Arm:提供完整开发环境(需License) --- ### 七、学习路线图 ```mermaid graph TD A[基础理论] --> B(ARMv7架构) A --> C(汇编指令集) B --> D[开发环境搭建] C --> D D --> E[单核实验] D --> F[多核通信] E --> G[性能优化] F --> G G --> H[实际项目] ``` 通过以上系统性学习,建议在6个月内完成从基础到项目开发的过渡。重点掌握:**多核同步机制**、**NEON优化**和**异常处理流程**。实际操作中遇到问题时,建议结合ARM CoreSight技术进行实时追踪调试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值