ARMv8体系结构基础02:搭建实验环境

目录

1 实验环境概述

1.1 实验环境种类

1.2 树莓派4b简介

2 实验代码分析

2.1 实验代码结构

2.2 Makefile文件分析

2.3 linker.ld文件分析

2.4 程序流程分析

2.4.1 启动代码

2.4.2 kernel_main函数

3 基于qemu的实验环境搭建

3.1 使用qemu + gdb调试

3.1.1 启动qemu调试

3.1.2 启动gdb

3.1.3 连接gdb server

3.1.4 开始调试

3.2 使用qemu + eclipse调试

3.2.1 创建新工程

3.2.2 创建调试配置

3.2.3 开始调试

4 基于树莓派的实验环境搭建

4.1 调试工具概述

4.1.2 OpenOCD

4.2 烧写树莓派基础镜像

4.3 更新树莓派SPI BootRom

4.3.1 配置WiFi

4.3.2 更新系统软件包

4.4 树莓派4b启动流程

4.5 更新测试镜像

4.6 更新调试镜像

4.7.1 JLink升级

4.7.2 连接JLink和树莓派

4.7.3 JLink接入虚拟机

4.8 开始调试

4.8.1 启动OpenOCD

4.8.2 连接OpenOCD

4.8.3 暂停CPU

4.8.4 加载调试镜像到树莓派

4.8.5 设置CPU执行位置

4.8.5 连接gdb调试

5 实验验证

5.1 currentEL验证

5.2 SP指针验证


1 实验环境概述

1.1 实验环境种类

《奔跑吧Linux第3季》课程共提供2种实验环境

1. 使用qemu模拟树莓派4b的实验环境

2. 实际基于树莓派4b的实验环境

1.2 树莓派4b简介

我们的软硬件实验环境均基于树莓派4b,此处进行简要介绍

ARMv8体系结构基础02:搭建实验环境_第1张图片

1. 使用BCM2711作为主芯片

2. 内置4核Cortex-A72

3. 内置GICv2中断控制器

4. 支持丰富的外设接口

说明:树莓派4b address map

介绍树莓派4b的address map主要是为了帮助理解下文种设置外设寄存器的操作

ARMv8体系结构基础02:搭建实验环境_第2张图片

① BCM2711芯片手册中列出的外设地址是Legacy Master模式下的,Main peripherals的基地址为0x7c000000

以下文代码要设置GPFSEL寄存器为例,在BCM2711芯片手册中的地址为0x7e200000 + 0x04

ARMv8体系结构基础02:搭建实验环境_第3张图片

② 树莓派4b启动后,根据config.txt中的设置,如果arm_peri_high为1,则使用High Peripheral模式,也就是图中的Full 35-bit Address Map模式

③ 如果不设置arm_peri_high,树莓派默认以Low Peripheral模式启动,我们的实验中就是这种情况,此时Main peripherals的基地址为0x0_fc000000

因此GPFSEL寄存器的地址就被映射到0xFC000000 + (0x7e200000 - 0x7c000000) + 0x04,即0xfc000000 + 0x02200000 + 0x04 = 0xfe200004

这与代码中的设置是一致的

ARMv8体系结构基础02:搭建实验环境_第4张图片

2 实验代码分析

2.1 实验代码结构

ARMv8体系结构基础02:搭建实验环境_第5张图片

2.2 Makefile文件分析

ARMv8体系结构基础02:搭建实验环境_第6张图片

ARMv8体系结构基础02:搭建实验环境_第7张图片

说明:启动qemu调试参数

可见与启动qemu运行相比,进行调试时增加了-S和-s参数

① -S:freeze CPU at startup,此时CPU会停在我们要调试程序的第1条指令处

② -s:shorthand for -gdb tcp::1234,此时qemu会启动内部的gdbServer,并等待gdb调试器通过网络连接,默认TCP端口号为1234

2.3 linker.ld文件分析

ARMv8体系结构基础02:搭建实验环境_第8张图片

1. 链接起始地址为0x80000

之所以选择该地址,是因为在树莓派4b配置中,会将编译生成的镜像加载到0x80000处运行

2. 首先链接.text.boot段的内容,这也是镜像中最先执行的部分,我们对程序流程的分析也从这里开始

2.4 程序流程分析

2.4.1 启动代码

启动代码位于boot.S文件中

ARMv8体系结构基础02:搭建实验环境_第9张图片

说明1:mpidr_el1寄存

ARMv8体系结构基础02:搭建实验环境_第10张图片

对于CPU0(Primary CPU),该寄存器的bit[7:0]为0

说明2:cbz指令

ARMv8体系结构基础02:搭建实验环境_第11张图片

CBZ指令当寄存器值为0时,跳转到

说明3:清空BSS段

首先获取bss_begin & bss_end的地址,之后调用memzero函数对该区域清零。调用时,x0中存储的是BSS段起始地址,x1中存储的是BSS段大小

说明4:栈指针设置

ARMv8体系结构基础02:搭建实验环境_第12张图片

① SECTION_SHIFT = PAGE_SHIFT + TABLE_SHIFT = 12 + 9 = 21

② SECTION_SIZE = 1 << SECTION_SHIFT = 1 << 21 = 2MB

③ LOW_MEMORY = 2 * SECTION_SIZE = 2 * 1MB = 4MB

可见此时栈设置在内存地址4MB处

2.4.2 kernel_main函数

ARMv8体系结构基础02:搭建实验环境_第13张图片

kernel_main函数的主要流程如下,

1. 初始化串口

2. 向串口发送"Welcon BenOS!"字符串

3. 在循环中等待用户输入并回显

说明1:实验代码中的串口使用轮询方式工作

说明2:benos.elf文件符号表

ARMv8体系结构基础02:搭建实验环境_第14张图片

① _start标号位于镜像文件起始处

② BBS段此时为空

3 基于qemu的实验环境搭建

3.1 使用qemu + gdb调试

3.1.1 启动qemu调试

在一个端口启动qemu调试,命令如下

# 在实验源码Makefile所在目录

make debug

说明1:-machine raspi4参数

① -machine参数用于设置要模拟的设备

② 使用如下命令可以查看当前qemu支持的设备列表

qemu-system-aarch64 -machine help

ARMv8体系结构基础02:搭建实验环境_第15张图片

说明2:-nographic参数

disable图像输出,将串口输入输出作为控制台(console)

说明3:-kernel benos.bin参数

将benos.bin作为内核镜像

3.1.2 启动gdb

在另一个端口启动gdb,命令如下

gdb-multiarch --tui build/benos.elf

ARMv8体系结构基础02:搭建实验环境_第16张图片

说明1:--tui参数

① 使用终端界面(terminal user interface)

② 如果不加--tui参数,将不会有显示对应源码的窗口

ARMv8体系结构基础02:搭建实验环境_第17张图片

说明2:gdb启动调试时,需要使用带有符号表的可执行文件,此处即为benos.elf

3.1.3 连接gdb server

在gdb命令行中输入如下命令,用于连接qemu调试时启动的gdb server

target remote localhost:1234

ARMv8体系结构基础02:搭建实验环境_第18张图片

3.1.4 开始调试

1. 设置断点

使用如下命令将断点设置在_start标号处,也就是内核镜像的首条指令处

b _start

c # 运行到断点处

2. 打开寄存器观察窗口

使用如下命令打开寄存器观察窗口

layout reg

ARMv8体系结构基础02:搭建实验环境_第19张图片

3. 调试程序

之后便可以单步调试程序,观察程序运行状态

我们以获取bss_begin & bss_end为例,可见x0 & x1寄存器中正确获取了BSS段的起止地址

ARMv8体系结构基础02:搭建实验环境_第20张图片

说明1:adr指令

ARMv8体系结构基础02:搭建实验环境_第21张图片

adr指令用于获取标号相对于PC的地址(地址无关指令),此处获取的就是bss_begin & bss_end标号的地址,也就是BSS段的范围

需要注意的是,此时链接地址和运行地址是匹配的,所以使用地址无关操作获取的标号地址,与实际的运行地址是一致的

说明2:如果只是使用qemu运行内核镜像,则使用如下名

# 在实验源码Makefile所在目录

make run

可见实验结果与之前的分析是一致的

说明3:此处调试的原理是使用了qemu内置的gdbserver

ARMv8体系结构基础02:搭建实验环境_第22张图片

说明4:qemu不能100%模拟硬件行为

3.2 使用qemu + eclipse调试

在使用qemu + eclipse调试时,仍然是在一个端口启动qemu调试,之后启动eclipse进行如下配置

3.2.1 创建新工程

ARMv8体系结构基础02:搭建实验环境_第23张图片

ARMv8体系结构基础02:搭建实验环境_第24张图片

ARMv8体系结构基础02:搭建实验环境_第25张图片

工程创建完成后,可见工程中包含了之前调试的源码

ARMv8体系结构基础02:搭建实验环境_第26张图片

3.2.2 创建调试配置

ARMv8体系结构基础02:搭建实验环境_第27张图片

ARMv8体系结构基础02:搭建实验环境_第28张图片

ARMv8体系结构基础02:搭建实验环境_第29张图片

ARMv8体系结构基础02:搭建实验环境_第30张图片

ARMv8体系结构基础02:搭建实验环境_第31张图片

ARMv8体系结构基础02:搭建实验环境_第32张图片

配置完成Apply之后,就会生成新建的调试配置

ARMv8体系结构基础02:搭建实验环境_第33张图片

3.2.3 开始调试

ARMv8体系结构基础02:搭建实验环境_第34张图片

点击Debug之后便进入调试界面,可见模拟的树莓派4b中有4个CPU核

ARMv8体系结构基础02:搭建实验环境_第35张图片

之后在Debugger Console中加载符号表

ARMv8体系结构基础02:搭建实验环境_第36张图片

此处要注意ELF文件的路径

ARMv8体系结构基础02:搭建实验环境_第37张图片

之后在Debugger Console中将断点设置在_start标号处,并运行到断点处,此时就会出现调试的源码界面,此时便可以进行单步调试,并观察处理器状态

ARMv8体系结构基础02:搭建实验环境_第38张图片

此时可以查看Registers窗口,观察寄存器状态

ARMv8体系结构基础02:搭建实验环境_第39张图片

4 基于树莓派的实验环境搭建

4.1 调试工具概述

1. 硬件仿真器使用仿真头完全取代目标板上的CPU,从而完全仿真目标板上的芯片行为,提供更加深入的调试功能

2. JTAG(Joint Test Action Group)是一种国际标准测试协议,主要用于芯片内部测试。JTAG仿真器通过边界扫描接口与CPU进行通信,实现对CPU和外设的调试功能

3. JLink是SEGGER公司开发的基于JTAG协议的仿真器,其V11版本支持树莓派4b主芯片的Cortex-A72调试

说明:JTAG 20pin接口

ARMv8体系结构基础02:搭建实验环境_第40张图片

ARMv8体系结构基础02:搭建实验环境_第41张图片

4.1.2 OpenOCD

1. OpenOCD(Open On-Chip Debbugger,开源片上调试器)是一款开源软件,提供针对嵌入式设备的调试、系统编程和边界扫描功能

2. OpenOCD的功能在仿真器的辅助下完成,仿真器是必须的,因为调试主机(运行OpenOCD的PC)通常不具备调试电信号的解析能力

4.2 烧写树莓派基础镜像

首先使用一张SD卡烧写树莓派基础镜像

烧写工具:Win32DiskImager

镜像文件:2020-08-20-raspios-buster-arm64.img

ARMv8体系结构基础02:搭建实验环境_第42张图片

注意:打开Win32DiskImager工具时,需要使用管理员权限

烧写完成后,SD卡中将自动生成FAT32格式的boot分区,其中的文件如下图所示

ARMv8体系结构基础02:搭建实验环境_第43张图片

说明1:使能串口输出

修改boot分区中的config.txt配置文件,增加如下2行,用于使能串口输出

ARMv8体系结构基础02:搭建实验环境_第44张图片

此时启动树莓派,可通过串口登录,其中树莓派镜像默认的用户名和密码如下

用户名:pi

密码:raspberry

ARMv8体系结构基础02:搭建实验环境_第45张图片

说明2:使能串口配置项说明

① uart_2ndstage

ARMv8体系结构基础02:搭建实验环境_第46张图片

设置该参数,可以使得树莓派从second-stage loader就开始打印调试信息

② enable_uart

设置该参数,可以配合cmdline.txt中的内核启动参数,使得内核创建控制台串口

4.3 更新树莓派SPI BootRom

通过WiFi更新树莓派4b全系统的软件包,这样会自动更新SPI BootRom固件

4.3.1 配置WiFi

使用如下命令配置WiFi

sudo raspi-config

我这里指令该命令时,会显示乱码,而不是配置界面

ARMv8体系结构基础02:搭建实验环境_第47张图片

这是因为串口工具的编码方式需要修改,我们将编码方式选为UTF-8,重新执行上述命令,即可显示配置界面

ARMv8体系结构基础02:搭建实验环境_第48张图片

WiFi配置流程如下,

ARMv8体系结构基础02:搭建实验环境_第49张图片

ARMv8体系结构基础02:搭建实验环境_第50张图片

ARMv8体系结构基础02:搭建实验环境_第51张图片

ARMv8体系结构基础02:搭建实验环境_第52张图片

此时可见wlan0 interface已经分配到IP地址,且可以ping通百度,这说明网络配置已经成功

ARMv8体系结构基础02:搭建实验环境_第53张图片

说明:树莓派4b只能连接2.4GHzWiFi网络

4.3.2 更新系统软件包

使用如下命令更新整个系统的软件包

sudo apt update

sudo apt full-upgrade

之后重启树莓派即可

4.4 树莓派4b启动流程

1. 当树莓派4b启动时,Cortex-A72处于standby状态,GPU将负责启动系统。GPU会加载片上ROM code并执行,ROM code的主要功能为初始化SD host controller,为后续读取SD卡上的文件做准备

2. SD host controller初始化完成后,ROM code会加载SD卡上的bootcode.bin文件并执行

3. bootcode.bin文件负责加载SD卡上的start.elf文件并执行

4. start.elf文件解析SD卡上的config.txt配置文件,加载SD卡上的kernel.img文件到内存指定地址,然后CPU结束standby状态开始执行内核

说明1:bootcode.bin和star.elf文件为树莓派官方提供,不开源

说明2:如何指定kernel文件并加载到内存指定地址

要完成这个目标,就需要用到config.txt配置文件中的2个参数

① kernel

kernel参数用于指定加载的内核镜像文件,如果不指定,对于树莓派4b,默认使用kernel7l.img文件

② kernel_old & kernel_address

ARMv8体系结构基础02:搭建实验环境_第54张图片

这2个参数用于指定kernel文件的加载地址,如果这2个参数均不指定,则默认将64位kernel文件加载到0x200000地址处(但是根据树莓派4b上级验证情况,实际是加载到0x80000地址处

4.5 更新测试镜像

在理解了树莓派4b的启动流程后,我们将课程提供的测试用的镜像拷贝到SD卡的boot分区

启动树莓派后,可见已经运行测试镜像

ARMv8体系结构基础02:搭建实验环境_第55张图片

说明:测试镜像config.txt

ARMv8体系结构基础02:搭建实验环境_第56张图片

此时启动使用的内核镜像为benos4.bin

4.6 更新调试镜像

下面我们更新调试镜像,该镜像是为了,也是将相关文件拷贝到SD卡的boot分区

同样分析一下调试镜像的config.txt配置文件

ARMv8体系结构基础02:搭建实验环境_第57张图片

1. 启动的内核文件为loop.bin,我们查看该文件的二进制内容,对照ARMv8 AArch64指令集编码,该文件仅包含一条无条件跳转的B语句,用于实现无限循环,类似如下指令的效果

label:

    b label

这么做的目的,是让CPU处于循环状态,等待JLink获取CPU运行的控制权

2. enable_jtag_gpio=1

ARMv8体系结构基础02:搭建实验环境_第58张图片

该选项用于设置JTAG相关的GPIO功能,同时在SoC内部建立相应连接

3. gpio=22-27=a4

将GPIO22 ~ GPIO27的功能设置为Alt4,该配置项与上面的enable_jtag_gpio=1功能一样

4. init_uart_clock=48000000

该选项设置串口UART0的时钟为48MHz,也就是默认值

5. init_uart_baud=115200

该选项设置串口的波特率为115200,也是默认值

4.7.1 JLink升级

我们实验使用的JLink为V11版本,但是需要将固件更新到最新版本,才能支持Cortex-A72。可以使用J-Link Commander程序进行升级

ARMv8体系结构基础02:搭建实验环境_第59张图片

如果当前固件版本不是最新,打开J-Link Commander程序后会自动提示升级,此时选择升级即可。当前使用的JLink固件版本已更新至2021年6月29日

ARMv8体系结构基础02:搭建实验环境_第60张图片

4.7.2 连接JLink和树莓派

要在树莓派上使用JLink仿真器,需要将JLink仿真器的JTAG接口连接到树莓派的扩展板上。树莓派的扩展接口中已经内置了JTAG接口,可以使用杜邦线连接

树莓派与JLink仿真器的连接如下,

ARMv8体系结构基础02:搭建实验环境_第61张图片

4.7.3 JLink接入虚拟机

1. 首先将JLink的连接接入虚拟机

2. 使用lsusb命令查看JLink是否已接入虚拟机

ARMv8体系结构基础02:搭建实验环境_第62张图片

3. 使用如下命令,验证能否访问JLink

sudo openocd -f jlink.cfg # 在jlink.cfg文件所在目录执行

ARMv8体系结构基础02:搭建实验环境_第63张图片

 可见已经识别到JLink硬件版本为V11,且探测到目标CPU电压为3.244V,后续的错误是因为还没有指定要调试的设备信息

说明:jlink.cfg文件

ARMv8体系结构基础02:搭建实验环境_第64张图片

可见该配置文件只是指定使用的调试接口类型为jlink(这里之所以要配置,是因为JLink硬件也可以使用SWD接口进行调试)

4.8 开始调试

4.8.1 启动OpenOCD

使用如下命令启动OpenOCD

sudo openocd -f jlink.cfg -f raspi4.cfg # 在配置文件所在目录执行

ARMv8体系结构基础02:搭建实验环境_第65张图片

可见JLink已经识别到树莓派4b上的bcm2711芯片,且在3333端口启动了gdb server

说明:raspi4.cfg配置文件

ARMv8体系结构基础02:搭建实验环境_第66张图片

4.8.2 连接OpenOCD

在另一个shell中使用telnet连接上一步中启动的OpenOCD,且根据raspi4.cfg配置文件,telnet的端口号为4444

telnet localhost 4444

ARMv8体系结构基础02:搭建实验环境_第67张图片

可见telnet连接成功

4.8.3 暂停CPU

使用halt命令先让CPU暂停运行

ARMv8体系结构基础02:搭建实验环境_第68张图片

可见bcm2711中的2个CPU核已因为JLink的debug请求暂停运行,且暂停前处于EL2异常等级,并使用SP_EL2栈指针

4.8.4 加载调试镜像到树莓派

使用如下命令加载要调试的进行到树莓派,,此处加载的是可运行的二进制文件

load_image /home/rlk/rlk/armv8_trainning/lab01/benos.elf 0x80000

ARMv8体系结构基础02:搭建实验环境_第69张图片

4.8.5 设置CPU执行位置

使用如下指令让CPU执行流停止在指定位置,此处停止在可执行镜像的加载位置

step 0x80000

说明:根据上级验证,要想调试bin文件中的第1条指令,step应该将CPU执行位置设置在0x7FFFC

step 0x7FFFC

其实从上面的截图也可以看出,当使用step 0x80000命令时,CPU halt时的PC值为0x80004,也就是bin文件的第2条指令处,这与实际测试结果也是一致的

而使用step 0x7FFFC命令时,CPU halt时的PC值为0x80000,这是我们期望的结果

4.8.5 连接gdb调试

在另一个端口启动gdb,命令如下,此处加载的是带有符号表的ELF文件

gdb-multiarch --tui build/benos.elf



# 在gdb命令行中

target remote localhost:3333

ARMv8体系结构基础02:搭建实验环境_第70张图片

此时便可以进行单步调试,可见在实际的树莓派4b中,寄存器值与qemu虚拟机不同

5 实验验证

5.1 currentEL验证

ARMv8体系结构基础02:搭建实验环境_第71张图片

执行完mrs x0, CurrentEL指令后,X0的值为0x8

ARMv8体系结构基础02:搭建实验环境_第72张图片

对照CurrentEL寄存器定义,此时异常等级为EL2

5.2 SP指针验证

ARMv8体系结构基础02:搭建实验环境_第73张图片

1. SP寄存器值

在执行完mov sp, #LOW_MEMORY指令之后,可见SP指针被设置为0x400000,也就是4MB地址处

2. SPSel寄存器值

此时我们获取SPSel寄存器的值,该值为1,说明使用的是当前异常等级的栈指针,也就是SP_EL2

ARMv8体系结构基础02:搭建实验环境_第74张图片

你可能感兴趣的:(计算机体系结构,计算机体系结构)