Linux 内核在 ZTE zx297520v3 SoC 上的引导实践:硬件剖析、U-Boot 构建与 CPU/GIC 初始化 Linux 内核在 ZTE zx297520v3 SoC 上的引导实践硬件剖析、U-Boot 构建与 CPU/GIC 初始化【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本篇技术指南以 Linux 内核源码树中的 Documentation/arch/arm/zte/zx297520v3.rst 为骨架系统讲解如何在 ZTE zx297520v3 SoC 上引导 Linux从该 SoC 的硬件组成Cortex-A53 32 位模式、GICv3、ZSP880 基带 DSP、Cortex-M0 协处理器出发依次覆盖 USB Boot ROM 下载引导、基于设备自带 U-Boot 的 NAND 镜像构建九步流程以及满足 Linux 引导要求的 CPU/GICv3 汇编初始化代码。读完本文你将掌握为这类廉价 LTE 路由器 SoC 制作可引导内核镜像的完整方法并能理解为何需要手工完成 GIC 安全配置与 PPI 极性修正。1. 硬件背景一台“32 位内核、64 位硬件”的廉价路由器 SoCzx297520v3 是一颗使用64 位能力 Cortex-A53 CPU 与 GICv3的 SoC但实际仅以arm32 模式运行对应内核配置ARCH_MULTI_V7见 arch/arm/mach-zte/Kconfig。CPU 支持 EL3但没有 EL2 虚拟化层hypervisor且看起来缺少 VFP 与 NEON 浮点单元——这意味着内核构建时需避免依赖这些特性。该 SoC 被大量用于廉价 LTE 转 WiFi 路由器包括电池供电的 MiFi 与固定式 CPECustomer Premises Equipment客户驻地设备。典型硬件配置为64 MB RAM其中一部分与 LTE 芯片共享也存在 32 MB 与 128 MB 的变体128 MB NAND flash也存在 8 MB / 16 MB NOR flash 的变体SDIO 连接的RTL8192 类 WiFi 芯片仅支持 2.4 GHzUSB 2.0 与按键部分固定式设备带 100 Mbit 以太网及以太网交换机多数设备有状态 LED个别使用 SPI 或 I2C 连接的显示屏部分设备带SD 卡槽——若存在建议将根文件系统放在 SD 卡上因其性能明显优于板载 NAND。1.1 LTE 基带 DSPZSP880LTE 接口运行在独立 DSPZSP880上其指令集未公开推测源自 LSI ZSP 系列。ZSP 与主 CPU 通过SRAM、DRAM 及一个可在两端产生 IRQ 的 mailbox 硬件通信。这意味着内核侧对 LTE 的控制需要遵循该 mailbox 协议。1.2 Cortex-M0 协处理器SoC 内还有一个Cortex M0 CPU负责早期硬件初始化并启动 Cortex-A53。一旦 U-Boot 启动它对系统运行已无必要用途不过存在基于 SRAM 的交接协议可以在其上运行自定义代码。1.3 仓库中的对应支持代码主线上游对该 SoC 的支持已落地可从源码结构中印证设备树公共定义 arch/arm/boot/dts/zte/zx297520v3.dtsi声明单核arm,cortex-a53、26 MHz 固定时钟的 UART、arm,armv7-timerPPI 13/14/11/10IRQ_TYPE_LEVEL_LOW以及 GICv3 中断控制器reg 0xf2000000 0x10000, 0xf2040000 0x20000具体设备 arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dtsD-Link DWR-932Mcompatible dlink,dwr932m, zte,zx297520v3内存 0x20000000 起 64 MB并使能uart1板级机器描述 arch/arm/mach-zte/zx297520v3.cDT_MACHINE_START匹配zte,zx297520v3Kconfig 开关SOC_ZX297520V3arch/arm/mach-zte/Kconfig默认启用并select ARM_GIC_V3 / ARM_PSCI_FW / ARM_AMBA / HAVE_ARM_ARCH_TIMER构建规则 arch/arm/boot/dts/zte/MakefileCONFIG_SOC_ZX297520V3使能zx297520v3-dlink-dwr932m.dtb的生成。2. 通过 USB 引导Boot ROM 下载模式Boot ROM 原生支持通过USB 下载自定义代码。进入该模式有两种方式将Boot PIN 接地GND修改 NAND 上第三个字节将其设为除0x5A即字符Z以外的任何值。配套的自由软件工具链有两个均为社区维护的外部项目仓库中未包含可按名称检索zx297520v3-loader用于启动自定义 U-Boot 与内核的 USB 加载工具u-boot-mainline可与 USB loader 配合使用的 U-Boot 版本负责按 Linux 的引导要求配置 CPU 与中断控制器。一个实用技巧若进入 USB 下载模式但数秒内未通过 USB 收到任何引导命令设备会自动继续正常引导。因此可以把 USB 引导永久开启同时保留默认启动文件不动——一旦需要刷机或救砖插入 USB 即可中断平时则不影响正常开机。3. 为内置 U-Boot 构建镜像NAND 启动设备出厂自带古老 U-Boot它从 NAND 加载 legacy uImage 并直接启动用户无法中断。镜像存放在 jffs2 分区imagefs通常为mtd4中的两个文件ap_cpuap.bin正常启动模式ap_recovery.bin恢复模式开发推荐见下文文件fotaflag用于切换这两种模式。3.1 384 字节签名头在 uImage 头之外镜像还带一个384 字节签名头用于部分设备上的镜像认证。大多数设备默认关闭认证因此只需在 uImage 文件前填充 384 个零字节即可。3.2 内置 U-Boot 的缺陷与对策内置 U-Boot 对 CPU 的设置也很糟糕详见第 4 节并且不支持加载 DTB因此内核必须开启CONFIG_ARM_APPEND_DTB追加式设备树。3.3 九步构建流程按原文档构建一个可从 NAND 启动的镜像需要以下步骤1) 将第 4 节的汇编代码补丁打入 arch/arm/kernel/head.S 2) make zx29_defconfig 3) make [-j x] 4) cat arch/arm/boot/zImage arch/arm/boot/dts/zte/[device].dtb kerneldtb 5) mkimage -A arm -O linux -T kernel -C none -a 0x20008000 -d kerneldtb uimg 6) dd if/dev/zero bs1 count384 ofap_recovery.bin 7) cat uimg ap_recovery.bin 8) 将该文件放到设备上的 imagefs 分区若空间不足删除 ap_cpuap.bin 9) 创建 fotaflag 文件echo -n FOTA-RECOVERY fotaflag要点说明第 4 步的[device]对应设备树文件例如本仓库已收录的zx297520v3-dlink-dwr932m.dtb由 arch/arm/boot/dts/zte/Makefile 生成第 5 步mkimage使用 legacy uImage 格式-T kernel -C none加载地址0x20008000与本仓库 DTS 中memory20000000的起始地址一致zx297520v3-dlink-dwr932m.dts开发阶段强烈建议引导ap_recovery.bin因为正常启动模式会在启动内核前启用看门狗watchdog不利于调试。4. CPU 与 GIC 设置让中断真正工作起来zx297520v3 的 CPU 与 GICv3 需要按照 Documentation/arch/arm64/booting.rst 的要求进行初始化。针对本 SoC具体为GICD_CTLR.DS1禁用 GIC 安全security功能开放对ICC_SRE的访问禁止将 IRQ 陷入 monitor 模式trapping IRQs into monitor mode将EL2 及以下配置为非安全模式insecure mode将定时器 PPIs 配置为低电平有效active-low。原文档特别指出ZTE 官方提供的内核源码本身无法启动中断完全不工作且在其他方面也不完整因此可以推断在官方二进制 blob 中必然存在与本文档描述类似的某种 workaround。4.1 汇编初始化代码示例以下是实现上述配置的汇编代码来自原文档可补丁进arch/arm/kernel/head.S的开头引导阶段#include linux/irqchip/arm-gic-v3.h #include asm/assembler.h #include asm/cp15.h Detect sane bootloaders and skip the hack ldr r3, 0xf2000000 ldr r3, [r3] ldr r4, (GICD_CTLR_ARE_NS | GICD_CTLR_DS) cmp r3, r4 beq skip_zx_hack This allows EL1 to handle ints hat are normally handled by EL2/3. ldr r3, 0xf2000000 str r4, [r3] cps #MON_MODE Work in non-secure physical address space: SCR_EL3.NS 1. At least the UART seems to respond only to non-secure addresses. I have taken insipiration from Raspberry pis armstub7.S here. mov r3, #0x131 non-secure, Make F, A bits in CPSR writeable Allow hypervisor call. mcr p15, 0, r3, c1, c1, 0 AP_PPI_MODE_REG: Configure timer PPIs (10, 11, 13, 14) to active-low. ldr r3, 0xF22020a8 ldr r4, 0x50 str r4, [r3] ldr r3, 0xF22020ac ldr r4, 0x14 str r4, [r3] Enable EL2 access to ICC_SRE (bit 3, ICC_SRE_EL3.Enable). Enable system reg access to GICv3 registers (bit 0, ICC_SRE_EL3.SRE) for EL1 and EL3. mrc p15, 6, r3, c12, c12, 5 ICC_SRE_EL3 orr r3, #0x9 FIXME: No defines for SRE_EL3 values? mcr p15, 6, r3, c12, c12, 5 mrc p15, 0, r3, c12, c12, 5 ICC_SRE_EL1 orr r3, #(ICC_SRE_EL1_SRE) mcr p15, 0, r3, c12, c12, 5 Like ICC_SRE_EL3, enable EL1 access to ICC_SRE and system register access for EL2. mrc p15, 4, r3, c12, c9, 5 ICC_SRE_EL2 aka ICC_HSRE orr r3, r3, #(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE) mcr p15, 4, r3, c12, c9, 5 isb Back to SVC mode cps #SVC_MODE skip_zx_hack:关键点逐段解读检测与跳过逻辑先读0xf2000000GICD_CTLR 寄存器若已等于GICD_CTLR_ARE_NS | GICD_CTLR_DS说明引导加载程序已正确初始化如主线的 u-boot-mainline直接跳到skip_zx_hack避免重复配置GIC 安全禁用将GICD_CTLR_ARE_NS | GICD_CTLR_DS写入 GICD_CTLR使 EL1 能处理通常由 EL2/EL3 处理的中断进入 monitor 模式并设置SCR_EL3.NS1写入值0x131工作于非安全物理地址空间——至少 UART 只响应非安全地址同时使 CPSR 的 F、A 位可写并允许 hypervisor 调用定时器 PPI 极性修正向0xF22020a8写入0x50、向0xF22020ac写入0x14把定时器 PPI10、11、13、14配置为低电平有效。这一点与设备树中timer节点声明的IRQ_TYPE_LEVEL_LOW相呼应见 zx297520v3.dtsiICC_SRE 访问使能分别对ICC_SRE_EL3bit3 使能 EL2 访问、bit0 使能 EL1/EL3 系统寄存器访问、ICC_SRE_EL1设置ICC_SRE_EL1_SRE、ICC_SRE_EL2akaICC_HSRE设置ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE进行 orr 后写回并执行isb同步屏障最后cps #SVC_MODE回到 SVC 模式继续正常启动流程。4.2 为何这些工作在仓库的 DTS 中可见其必要性在 arch/arm/boot/dts/zte/zx297520v3.dtsi 的 GIC 节点注释中明确说明该 GIC 在0xf2202070及之后地址存在非标准的电平/边沿极性配置寄存器对应 ZTE 内核中的AP_INT_MODE_BASE与AP_PPI_MODE_REG且 ZTE 源码中的偏移疑似有误一切默认是高电平有效/上升沿唯独定时器是低电平有效——目前依赖引导加载程序代为完成定时器 IRQ 极性切换。这正是上述汇编代码第 5 步配置定时器 PPI 为 active-low存在的根本原因。5. 常见问题与排障要点内核无法接收任何中断优先检查第 4 节的 5 项 GIC/CPU 配置是否全部执行——尤其是GICD_CTLR.DS与定时器 PPI 极性。ZTE 官方内核同样存在此问题说明这不是个别板卡差异而是 SoC 级缺陷必须由引导代码修复UART 无输出确认运行在非安全物理地址空间SCR_EL3.NS1UART 仅响应非安全地址正常启动模式被杀内置 U-Boot 在启动内核前会武装看门狗开发阶段请改用ap_recovery.binDTS 未生效内置 U-Boot 不支持加载 DTB务必开启CONFIG_ARM_APPEND_DTB并把 zImage 与 dtb 拼接后制作 uImage启动卡在 armv7-timer若引导加载程序未配置CNTVOFF由于是单 CPU 系统偏移随机通常不影响使用设备树已声明arm,cpu-registers-not-fw-configured以告知内核不要依赖固件完成寄存器配置。6. 小结zx297520v3 的上游 Linux 支持目前覆盖了设备树、板级机器代码与 Kconfig 配置见 arch/arm/boot/dts/zte/、arch/arm/mach-zte/但引导环节仍高度依赖外部工具链USB 模式下用 zx297520v3-loader u-boot-mainline 最省事从 NAND 启动则需遵循本文的九步流程并用第 4 节的汇编代码弥补 SoC 的 GIC/CPU 初始化缺陷。对开发者而言推荐路径是USB 下载模式调试 → 恢复分区镜像开发 → 最后再固化到正常启动分区。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考