ARMv7-A小系统实践(十二):用QEMU和GDB把整条实现链验清楚

本章记录什么

最后一章不再增加新的架构功能,而是把整条系统路径重新走一遍,并建立一套可复用的验证与排错方法。目标是让“能够运行”变成一组可以复查的证据:ELF 段与符号正确,启动输出按阶段出现,任务切换有双向进展,Timer IRQ 能重复触发,设备清源与 EOI 都发生,MMU/Cache 状态可读,CPU1 能完成 SGI 往返。

在这一章之前做了什么

前十章已经依次建立处理器状态、ABI、启动、协同上下文、异常现场、GIC/Timer、抢占、MMU、Cache 和双核底座。系统越完整,故障越不适合靠“多加几行打印”猜测,因为串口本身可能不可重入,读取 IAR 还有副作用,Cache 又可能让共享状态的观察滞后。

本章按照静态检查、分层断点、寄存器观察、串口证据和已知 Bug 五条线组织。文章末尾给出能力边界,避免把教学闭环夸大成完整产品操作系统。

ARMv7-A小系统实践(十一):启动CPU1,走通SCU与SGI核间通信

本章记录什么

这一章把单核可运行系统扩展为 Cortex-A9 双核启动底座。重点是共享状态和每核状态的边界:BSS、页表和 Distributor 只能由主核负责共享初始化;模式栈、GICC、PPI、CP15、TLB 和 L1 Cache 则必须由每个 CPU 分别建立。

在这一章之前做了什么

单核路径已经能够启动 MMU、Cache、GIC 和任务调度,但直接让 CPU1 执行同一入口会引入竞争:两个核可能同时清 BSS、重复初始化全局对象,或者共用一份 SVC/IRQ 栈。为此启动入口先读取 MPIDR 分流,CPU1 在 release flag 上等待,CPU0 完成发布后再用 cache clean、屏障和事件唤醒次核。

本章给出启动时序、SGI 往返验证和次核初始化代码,同时明确“第二个核在线”不等于“已经完成 SMP 调度器”。

ARMv7-A小系统实践(十):L1、L2、屏障与Cache一致性

本章记录什么

这一章解释 L1 I/D Cache、分支预测、SCU 和 L2C-310 的启用与维护。核心问题不是“把 SCTLR 的某一位置 1”,而是旧缓存状态何时失效、页表何时需要 clean、跨核共享何时依赖 SCU,以及 DMA 或自修改代码出现后软件应怎样维护一致性。

在这一章之前做了什么

上一章已经为普通内存和 MMIO 建立了不同的页表属性。只有在属性正确后,Cache 的行为才有可推导基础。启动阶段还要先无效化旧状态,再按顺序启用 SCU、ACTLR.SMP、L1 和 L2;运行阶段则要区分 clean、invalidate 和 clean+invalidate。

本章从概念、顺序和屏障开始,随后给出完整的 Cache 实现代码。最后会明确当前小系统证明了哪些 CPU 一致性能力,以及尚未通过 DMA 场景证明什么。

ARMv7-A小系统实践(九):用短描述符页表建立MMU与内存保护

本章记录什么

这一章建立 ARMv7-A short-descriptor MMU 的最小可用映射。内容从虚拟地址的拆分开始,解释 16 KiB 一级表、粗页表、Section 与 Small Page 描述符,然后把链接脚本中的代码、只读数据、可写数据、栈、页表和 MMIO 映射成不同权限与内存类型。

在这一章之前做了什么

前面的启动、上下文和中断仍可在 MMU 关闭时运行,但所有地址都只有“数值”,缺少权限与类型约束。打开 Cache 前更不能回避这一层:同一物理区域被标成 Normal Memory 还是 Device Memory,会直接改变访问排序、缓存和推测行为。

本章采用恒等映射降低早期调试难度,但不会把恒等映射误解为“没有地址转换”。文章给出页表构造代码、开启顺序、PAR 验证方式和常见故障定位。

ARMv7-A小系统实践(八):从完整IRQ现场到按需切换,再看RTEMS

本章记录什么

这一章把主动切换与 IRQ 返回路径合并成抢占式调度,并比较两种异常帧方案:基础版在每次 IRQ 保存 64 字节完整现场;精简版常规 IRQ 只保存 32 字节 caller-saved 现场,只有确实切换任务时才保存 r4-r11。最后用 RTEMS 6.2 的 ARM 端口检查这些选择背后的工程条件。

在这一章之前做了什么

此时已经有两个彼此独立但尚未连接的能力:普通函数边界上的协同任务切换,以及能够重复进入的 Timer IRQ。抢占的关键不是在 handler 里直接调用普通 context switch,而是让定时器只提出调度请求,由最外层 IRQ 尾部决定恢复哪一个任务的异常现场。

本章会先用完整 frame 建立容易证明正确的版本,再讨论何时可以优化。性能数字不是重点;重点是每省掉一组保存动作,都必须说明是谁继续承担寄存器保护责任。

ARMv7-A小系统实践(七):GIC、定时器与一次完整的IRQ

本章记录什么

这一章沿着一枚定时器中断走完整条链:Private Timer 产生事件,GIC Distributor 和本核 CPU Interface 判断是否交付,处理器进入 IRQ,软件读取 IAR 完成应答,设备 handler 清除中断源,最后把原始 IAR 写回 EOIR。目标是理解一个状态机,而不是记住几组地址。

在这一章之前做了什么

上一章已经能够安全进入和离开 IRQ,但入口正确并不代表设备中断能够重复工作。还需要解决三层使能、优先级、目标 CPU、Pending/Active 状态以及设备侧状态。任何一层遗漏,现象都可能只是“没有输出”或“只进入一次”。

本章会先解释 SGI、PPI、SPI 和 GIC 两大组成,再用 ID29 建立周期 tick。所有关键寄存器语义、分发器骨架和排错顺序都在文章内展开,不要求读者另开工程才能补齐上下文。

ARMv7-A小系统实践(六):异常入口、返回地址与独立中断栈

本章记录什么

这一章解释异常入口与返回,并建立任务私有的 IRQ 异常帧。它要解决的不是“进入了哪个 C 函数”,而是任意指令执行到一半时,怎样保存足够的信息,使处理结束后能够像中断从未发生一样继续执行;如果调度器选择了另一个任务,又怎样改为恢复另一个任务的现场。

在这一章之前做了什么

上一章的协同切换发生在函数调用边界,因此可借助 AAPCS 只保存 callee-saved 状态。IRQ 没有这个便利:r0-r3r12、条件标志和返回地址都可能正处于有效计算中。硬件只完成模式切换、CPSR 到 SPSR 的保存以及返回地址写入 banked lr,其余现场必须由入口汇编建立。

本章先分清硬件动作和软件动作,再说明为什么返回 PC 需要修正、为什么异常帧应归属于被打断任务、为什么 C handler 可以使用独立中断栈但恢复现场不能长期停留在共享栈上。

ARMv7-A小系统实践(五):两个线程怎样真正切换起来

本章记录什么

这一章实现两个任务之间的协同式切换。重点不在调度算法,而在“暂停的计算怎样被表示”:哪些寄存器属于普通函数边界上的长期状态,任务栈与上下文对象分别保存什么,新任务从未运行过又怎样伪造出第一次恢复所需的现场。

在这一章之前做了什么

启动代码已经建立 SVC 栈并进入 C 环境,AAPCS32 也给出了 r4-r11splr 的保存责任。于是可以先在没有中断的情况下做最小闭环:任务 A 主动调用切换函数,汇编保存 A 的 callee-saved 状态,恢复 B 的状态;B 再切回 A,A 从原调用点之后继续执行。

这种主动切换故意避开 IRQ。原因是它把问题缩小到普通调用边界,容易观察、容易回退。下一章再处理任意指令点被打断时所需的完整异常现场。

ARMv7-A小系统实践(四):从ELF、模式栈到第一行串口输出

本章记录什么

这一章把前两章的寄存器规则和调用规范放进真正的启动路径:镜像被放到什么地址,向量表为何必须对齐,链接脚本如何给代码、数据、页表和各模式栈分配空间,启动汇编又如何把一个没有 C 运行时的处理器带到 kernel_main。这是后续一切功能的地基。

在这一章之前做了什么

此时已经知道处理器模式会选择不同的 banked sp,也知道调用 C 前必须满足 AAPCS 的栈对齐和寄存器约定。但这些知识仍是静态规则。启动阶段要把它们变成具体地址:先屏蔽异步事件,建立向量基址和每模式栈,清理 BSS,再由 CPU0 进入共享初始化;CPU1 不能重复执行同一套破坏性操作。

本章会从链接合同讲到第一行串口输出,并给出一份可独立阅读的启动汇编。代码不是“最终模板”,而是一条可以逐条断点验证的最小路径。

ARMv7-A小系统实践(三):AAPCS32、栈与上下文汇编

本章记录什么

这一章讨论 C 函数与汇编函数如何彼此信任。处理器不会自动知道哪几个寄存器应该由调用者保存,也不会自动保证栈满足编译器要求;这些规则来自 AAPCS32。只有把参数、返回值、caller-saved、callee-saved、栈对齐和 lr 的含义连起来,才能安全地从启动汇编调用 C、从 IRQ 入口调用分发器,或者实现一个与编译器兼容的上下文切换函数。

在这一章之前做了什么

上一章已经说明模式切换如何改变 splr 和 SPSR 的可见版本。那属于处理器架构。普通函数调用则通常不改变处理器模式,它依赖的是软件合同。两者都在操作寄存器,但保存集合、返回方式和错误症状完全不同。本章先把这条边界画清楚,再进入多寄存器访存和实际函数边界。

文章中的示例会从一个普通五参数函数开始,逐步走到汇编调用 C、C 调用汇编以及结构体偏移校验。读完后,即使没有工程源码,也应能独立判断一段汇编是否破坏了 ABI。


:D 一言句子获取中...