Lazy loaded image
Words 0Read Time 1 min
Invalid Date
The memory system consists of these components:
  • Level 1 cache—32 KB instruction, 32 KB data cache
  • Level 2 cache—unified instruction and data (128 KB)
  • On-Chip Memory:
    • Boot ROM, including High-Assurance Boot (HAB, 96 KB)
    • Internal fast access RAM (OCRAM, 128 KB)
  • External memory interfaces:
    • 16-bit LP-DDR2, 16-bit DDR3-400, and LV-DDR3-400
    • 8-bit NAND-flash, including support for Raw MLC/TLC, 2 KB ,4 KB, and 8 KB page size, BA-NAND, PBA-NAND, LBA-NAND, OneNAND™, and others
    • BCH ECC up to 40 bits
    • 16-bit NOR flash
    • 16-bit PSRAM, Cellular RAM
    • Dual-channel/single-channel QuadSPI flash
📍
这篇文档解决什么:嵌入式内存布局最难回答的不是"表长什么样",而是"为什么每个地址必须是这个值"。本文从硬件 / 工具链 / 运行时三组约束出发,推导 IMX6ULL 从 BootROM 到 Linux 运行时的完整内存布局,并对比 ARM64 的演进方向。
读完你能拿到
  1. 看懂任意 ARM32 SoC 内存布局的方法论(不再只是背表)。
  1. 面试 / 排错时能说清每个地址的"因"而不只是"果"。
  1. 向 ARM64 迁移驱动时知道哪些假设会失效(见 §5.7 对照表)。
如何阅读:§1 建立设计哲学(强烈建议先读);§2–§4 是硬件 + 启动阶段的静态视图;§5 是 Linux 运行时动态视图 + ARM64 对照。时间紧张只读 §1.0 TL;DR + §1.6 常见陷阱表即可应急。

1. 布局设计原因分析

1.0 要点速览 (TL;DR)

🧭
核心思想:嵌入式启动期的内存布局不是"随便放",而是被硬件 / 工具链 / 运行时安全三组约束挤出来的唯一解。
三条铁律
  1. 硬件决定起点——BootROM、OCRAM、DDR 窗口的物理地址都写死在 SoC RTL 里,软件不能挪。
  1. 阶段决定分区——SPL → U-Boot → Kernel → 用户态四个阶段互不干扰:每一阶段的代码 + 数据 + 堆栈必须独占一块不会被下一阶段覆盖的空间。
  1. 对齐与间距换稳健性——2GB / 1MB section / 32B 向量表对齐减少 MMU / 解码逻辑成本,大段保留区换 DMA / 解压 / 设备树修改的缓冲。
四个关键原则
  • 固定对齐(2GB 自然边界 / 1MB section / 32B 向量)
  • 安全间距(组件间几 MB~几十 MB 的空洞,容忍溢出)
  • 就近放置(BootROM 近 0、OCRAM 给 SPL、Kernel 在 DDR 低端、U-Boot 在 DDR 高端)
  • 早晚分离(OCRAM = 早期;DDR 低端 = 中期;DDR 高端 = 运行时)

1.1 设计哲学:约束驱动的唯一解

不要把这张表理解成"工程师品味",它是一组约束方程的解
约束来源
硬约束
软约束
硬件 (SoC)
上电 PC = 0x00000000;DDR 窗口 = 0x80000000+;MMU 一级页表 1MB 对齐;异常向量 32B 对齐
OCRAM 只有 128KB;DDR 实际容量 512MB
工具链 / 协议
Linux zImage 解压需偏移;DTB 要对齐;initramfs 自解析头
U-Boot CONFIG_SYS_TEXT_BASE 默认值、bootm 预期地址
运行时安全
DMA buffer 不能覆盖 kernel;栈不能溢出到 .bss;U-Boot 不能被解压到自身
调试期望固定地址;升级时镜像大小增长留 headroom
所以下面每一个"为什么",都应该读成:在这些约束全部成立时,这个地址 / 这个间距是当前硬件下唯一合理的落点。

1.2 起始地址选择原因

BootROM 0x00000000 — 完全由硬件锁死
  • CPU 复位后 PC 硬连线到 0x00000000,第一条指令必须在这里。
  • 厂商把 ROM 固化在低地址,任何改变都要改 SoC 硅版,软件层面零自由度。
  • 反例:没有哪款 ARM Cortex-A 可以把复位向量挪到 DDR,因为复位时 DDR 控制器还没初始化。
OCRAM 0x00900000 — 启动早期唯一可用 RAM
  • DDR 控制器需要软件 (SPL) 跑一段 DDR PHY 校准后才能用,在那之前 CPU 能写的 RAM 只有 OCRAM
  • 128KB 容量是 NXP 在成本和 SPL 实际大小之间的折中:SPL Code + Stack + Heap + ROM 工作区刚好塞进 128KB(见 §2.1)。
  • 地址 0x00900000 并非随意——在低 2GB 片内资源区划里,为 OCRAM 专门留出一个 1MB 对齐窗口,避免和 AIPS 寄存器区 (0x02000000+) 冲突。
Kernel Image 0x80080000 — DDR 基址 + 512KB 头部偏移
  • 0x80000000 由 MMDC 路由写死(已在 §3 展开),这是 DDR 的硬起点。
  • 为什么 +512KB 偏移
    • ARM Linux zImage 的解压器会原地解压到紧邻位置,头部需要空间放解压缓冲和 stub。
    • BootROM / SPL 返回时可能还在 0x80000000 附近留有临时数据,避免踩踏。
    • 刚好对齐 1MB section 边界 (0x80080000 可被 0x100000 整除的下一个位置是 0x80100000,实际用 0x80008000 的 ARM 默认起点加 512KB = 0x80080000 给 defconfig 留空间),让 MMU 早期页表建起来零碎片。
U-Boot 0x87800000 — DDR 高端避让 + 对齐自然边界
  • 放在 DDR 低 128MB 边界上方0x87800000 = 0x80000000 + 120MB),确保下面的 119MB 都给 Kernel Image 用。
  • 0x87800000 本身是 8MB 对齐,方便 U-Boot 内部用 1MB/2MB section 建页表。
  • 给 U-Boot 预留 8MB 是 NXP vendor defconfig 惯例:code + data + heap + env + stack + 命令缓冲累计 ≈ 7MB,留 1MB headroom 应付版本升级。

1.3 组件间距离设计:空洞就是保险

每一个间距都有明确的工程意义
  • Kernel ↔ DTB 之间 ~40MB:当你内核开了 KASAN / debug / 大驱动(如 GPU、ISP),vmlinux 可能从 ~8MB 膨胀到 30MB+。40MB 让你不用动 bootcmd。
  • DTB ↔ Ramdisk 之间 ~16MB:U-Boot 的 fdt resize / fdt set 会就地扩展 DTB;16MB 足够任何 overlay。
  • Ramdisk ↔ U-Boot 之间 ~56MB:initramfs 可能从几 MB 涨到几十 MB(busybox + modules + firmware),同时给 DMA 传输留 headroom——U-Boot 加载镜像时 SDHC 的 DMA 窗口容易覆盖相邻区域。
反直觉的点:这些间距看似"浪费",但在 512MB 板子上只占 约 23%,换来的是 bootcmd 可复用、镜像升级不挪地址、OTA 脚本一份走天下——这才是嵌入式批量生产真正值钱的地方。

1.4 异常向量表 0x87800020 地址原因

依据
硬/软
如果违反
32 字节对齐 (ARM 要求)
触发 Undefined Instruction,U-Boot 无法启动
位于 DDR (非 OCRAM)
跑在 OCRAM 要搬 128KB 限制;DDR 容量充足、cache 友好
远离内核 / 用户区
调试时不容易被误写;隔离让 fault 分析更容易
固定地址
JTAG / T32 脚本写好 watchpoint 后可复用
紧贴 U-Boot .text 起点
U-Boot 的 relocation 机制期望 vector + text 连续布局
关键理解:U-Boot 阶段的向量表并不等同于 Linux 内核的向量表(内核后来会把向量搬到 0xFFFF0000 高向量,见 §5.3)。两者分属不同运行阶段,互不冲突。

1.5 布局优势:从原则反推价值

优势
具体体现
底层原则
启动效率
OCRAM (SPL) → DDR (U-Boot) → DDR (Kernel) 一次性线性推进,没有二次搬移
早晚分离 + 就近放置
内存保护
组件间数十 MB 空洞 + 512KB 头部保留,镜像升级不越界
安全间距
扩展性
kernel 119MB / rootfs 32MB / U-Boot 8MB 留有足够 headroom
头部容量超配
调试友好
所有关键地址固定,T32/OpenOCD 脚本一份通吃全系列板
固定对齐 + 硬件决定起点
安全性
BootROM 只读 + 低地址;OCRAM 断电丢失;Kernel 远离用户区
早晚分离 + 物理特性
生态一致性
i.MX6 / 8 系列 DDR 起点一致,NXP 工具链通用
硬件决定起点

1.6 常见陷阱与排错线索

🔗
跨 SoC 通用的完整排错速查卡(SPL / U-Boot / Kernel / 运行时 四阶段)已抽取为独立资产:
🧰
ARM32 嵌入式启动排错速查卡
。本节只保留与 IMX6ULL 内存布局直接相关的 6 条。
把这张表贴在工位上,能挡掉 90% 的布局类 bug:
症状
根因
排查方向
SPL 跑到一半挂住,串口无输出
SPL 体积超 16KB,覆盖到 SPL stack
arm-none-eabi-size u-boot-spl 查段大小
Uncompressing Linux... 卡死
解压缓冲 0x80800000 被 initrd / DTB 覆盖
检查 bootm 参数地址是否互相重叠
U-Boot 启动 OK,启动内核后 hang
DTB 地址错误或 DTB 被内核镜像覆盖
md.l 0x83000000 看 DTB magic 0xd00dfeed
DMA 数据偶发损坏
DMA buffer 落在 Ramdisk / Kernel 尾部
/proc/iomem • dma-ranges
大版本升级后 bootcmd 挂了
kernel 或 initramfs 膨胀超出预留间隔
ls -l 镜像 + 对照 §1.3 空洞裕量
Kernel panic: "Unable to handle kernel paging request at virtual address 0xXXXX"
驱动硬编码地址没用 ioremap,或 ioremap 后走出 vmalloc 区
/proc/vmallocinfo (§5.3.1)

2. 内部存储器映射

起始地址
结束地址
容量
内容
说明
0x00000000
0x00017FFF
96KB
BootROM Code
CPU上电后执行的固化代码
0x00900000
0x0091FFFF
128KB
OCRAM (On-Chip RAM)
内部高速缓存,启动时使用

2.1 OCRAM 详细布局 (启动阶段)

起始地址
结束地址
容量
内容
用途
0x00900000
0x00903FFF
16KB
SPL Code
Secondary Program Loader
0x00904000
0x00907FFF
16KB
SPL Stack
SPL运行时栈空间
0x00908000
0x0091BFFF
80KB
SPL Heap/BSS
SPL数据段和堆空间
0x0091C000
0x0091FFFF
16KB
BootROM数据
ROM代码工作区域

3. 外部DDR3内存映射 (512MB)

起始地址
结束地址
容量
内容
说明
0x80000000
0x8007FFFF
512KB
保留区域
系统保留,避免冲突
0x80080000
0x8077FFFF
~119MB
Linux Kernel Image
内核镜像加载区域
0x80800000
0x8087FFFF
512KB
Kernel解压缓冲区
内核解压临时空间
0x83000000
0x8307FFFF
512KB
Device Tree Blob
设备树二进制文件
0x84000000
0x85FFFFFF
32MB
Initial Ramdisk
初始化RAM磁盘
0x87800000
0x87FFFFFF
8MB
U-Boot运行区域
详见下表
0x88000000
0x9FFFFFFF
384MB
Linux运行时内存
内核运行时动态分配
IMX6ULL 外部 DDR3 起始地址 0x80000000(即 2GB 偏移)由 SoC 硬件总线地址映射决定,不是软件可配置的。核心原因如下:

3.1 ARM 物理地址空间划分

ARM Cortex-A7 使用 32 位物理地址(4GB 空间)。NXP 将这 4GB 划分为多个固定区域:
  • 0x00000000 – 0x7FFFFFFF(低 2GB)→ 片内资源:BootROM、OCRAM、外设寄存器(AIPS/IP Bus)、GPU、PCIe 等
  • 0x80000000 – 0xFFFFFFFF(高 2GB)→ MMDC(Multi-Mode DDR Controller)映射窗口,即外部 DDR
这是 SoC 内部总线互联矩阵(AXI interconnect)的硬连线路由规则,写死在 RTL 中。

3.2 为何选 0x80000000

  • 对齐 2GB 自然边界。大功率对齐简化地址解码逻辑——只需看最高位 bit[31] 即可区分片内(0)和 DDR(1)。
  • 为片上外设预留充足空间。IMX6ULL 有大量 IP 模块(USB、ENET、UART、GPIO、CSI、LCDIF……),寄存器地址密集分布在 0x02000000–0x02200000 一带,加上 BootROM、OCRAM、GPU 等,低 2GB 刚好容纳。
  • NXP i.MX 家族一致性。从 i.MX6Q/DL 到 i.MX6ULL 再到 i.MX8 系列,DDR 起始地址均为 0x80000000(或 0x40000000 于部分 i.MX8 型号),保持软件生态兼容。

3.3 参考依据

IMX6ULL Reference Manual (Chapter 2, Memory Map) 明确规定:
MMDC - DDR Controller: 0x80000000 – 0xFFFFFFFF (up to 2 GB)
即使你的板子只焊了 512MB DDR3,地址窗口仍然从 0x80000000 开始,有效范围到 0x9FFFFFFF(512MB),高地址部分未映射。

3.4 小结

因素
说明
硬件决定
MMDC 端口在总线矩阵中路由到 bit[31]=1 的地址段
对齐优势
2GB 自然边界,解码仅需 1 bit
生态兼容
i.MX 全系列统一约定
不可修改
由 SoC RTL 固化,软件无法重映射

4. U-Boot详细内存布局 (0x87800000-0x87FFFFFF)

起始地址
结束地址
容量
内容
详细说明
0x87800000
0x8780001F
32B
异常向量表
ARM异常向量表(8个向量×4字节)
0x87800020
0x878003FF
992B
异常处理代码
各种异常的处理函数
0x87800400
0x8785FFFF
~384KB
U-Boot主代码段(.text)
主要功能代码
0x87860000
0x8787FFFF
128KB
只读数据段(.rodata)
常量、字符串等
0x87880000
0x878BFFFF
256KB
数据段(.data)
已初始化全局变量
0x878C0000
0x878FFFFF
256KB
BSS段(.bss)
未初始化全局变量
0x87900000
0x879FFFFF
1MB
堆空间(Heap)
动态内存分配
0x87A00000
0x87BFFFFF
2MB
栈空间(Stack)
函数调用栈
0x87C00000
0x87DFFFFF
2MB
环境变量区
U-Boot环境变量存储
0x87E00000
0x87EFFFFF
1MB
设备树临时区
设备树修改缓冲区
0x87F00000
0x87FFFFFF
1MB
命令缓冲区
命令行解析和缓存

5. Linux 运行时内存布局说明

Linux 内核从 0x80080000 解压启动后,会接管整段 DDR3(0x80000000 – 0x9FFFFFFF,共 512MB)的内存管理。其中 0x88000000 – 0x9FFFFFFF(384MB)是运行时可动态分配的主力池,承载内核对象、用户进程、页缓存等。

5.1 物理地址 vs 虚拟地址

IMX6ULL 跑 Linux (ARM 32-bit,默认 3G/1G 切分)时,地址空间是两套视角
视角
范围
说明
物理地址
0x80000000 – 0x9FFFFFFF
MMDC 路由到 DDR3 的真实硬件地址
内核虚拟地址
0xC0000000 – 0xFFFFFFFF
内核空间共 1GB;其中与物理内存等大的 512MB (lowmem) 做 1:1 线性映射,剩余给 vmalloc / ioremap / fixmap 使用
用户虚拟地址
0x00000000 – 0xBFFFFFFF
每个用户进程独立的 3GB 空间
线性映射关系:PAGE_OFFSET(0xC0000000)PHYS_OFFSET(0x80000000),即 内核虚拟地址 = 物理地址 + 0x40000000
⚠️
澄清误区:"1GB 内核空间全部线性映射"是错的
  • "线性映射"只要求 VA = PA + const,对实际存在的物理内存成立即可。
  • IMX6ULL 物理内存只有 512MB,所以 lowmem 线性映射区只有 512MB0xC0000000 – 0xDFFFFFFF)。
  • 1GB 内核空间 = 512MB lowmem 线性区 + ≈488MB vmalloc/ioremap 非线性区 + 顶部 fixmap 等
  • 对应不存在物理内存的虚拟地址,内核根本不建页表,访问直接 oops。
  • 只有当物理内存 > ~760MB,lowmem 装不下时,才需要 ZONE_HIGHMEM + kmap()。IMX6ULL 远不到阈值,全部 RAM 都能被 lowmem 吃下。

5.2 内核物理内存分区 (Zone)

ARM 32-bit + 512MB DDR 的典型配置不启用 HIGHMEM,全部物理内存直接线性映射进内核空间:
Zone
范围 (物理)
用途
ZONE_DMA
起始若干 MB
兼容老外设的 DMA 分配
ZONE_NORMAL
剩余全部
内核 / 用户的常规分配
ZONE_HIGHMEM
不存在
512MB 全部已直映射,无需高端内存

5.3 内核虚拟地址布局 (1GB)

区段
大致范围
用途
lowmem 线性映射区
0xC0000000 – 0xDFFFFFFF (512MB)
与物理内存 1:1 映射,VA = PA + 0x40000000kmalloc 所在
保护间隙 (VMALLOC_OFFSET)
0xE0000000 – 0xE07FFFFF (8MB)
lowmem 与 vmalloc 之间的越界保护带
vmalloc 区
0xE0800000 – 0xFEFFFFFF (≈488MB)
非连续物理页的虚拟连续映射;ioremap 外设也在这里
fixmap / pkmap / 向量表
0xFF000000 – 0xFFFFFFFF (≈16MB)
固定映射、early console、高向量表 0xFFFF0000
实际边界可用 cat /proc/iomem 和内核启动日志中的 Virtual kernel memory layout 段核对确认。
真机核对:抓一份你板子的 dmesg | grep -A 20 "Virtual kernel memory layout",对照表 5.3 看编译配置下真实边界(不同 defconfig 的 vmalloc 大小会变)。
5.3.1 vmalloc 与 ioremap 为什么共用一段虚拟地址
看上去两者用途截然不同:vmalloc 是给内核模块 / 大缓冲申请虚拟连续的 RAM,ioremap 是把外设寄存器物理地址映射成虚拟地址供内核读写。然而在 ARM32 Linux 里,它们共用同一段 VMALLOC_START – VMALLOC_END 区域。

第一性原理分析

把事情拆到最底层,内核对"非线性映射"的需求只有两类:
  1. 给一段 非连续物理页 拼一个虚拟连续区(vmalloc)。
  1. 给一段 物理地址不在 RAM 中的设备 (MMIO) 建一段虚拟映射(ioremap)。
两个需求完全共享同一套机制:
问题
解决方法
从哪里找一块没人用过的虚拟地址
vmap_area 红黑树 / 位图。vmalloc 和 ioremap 都开同一份树。
怎么在页表里建映射?
都调 vmap_page_range() / ioremap_page_range(),最终落到 __vmalloc_node_range() 同一条路径。
怎么记账、怎么释放?
同一链表 vmlist/proc/vmallocinfo 里你会同时看到两类条目。
既然下面的虚拟地址分配器 + 页表操作 + 元数据管理全部是同一套代码,就没有理由在最稀缺的 ARM32 内核虚拟空间里给它们各开一个切分好的子区。

三个设计动机

1. 虚拟地址资源太帊。 ARM32 3G/1G 下,内核只有 1GB,扣掉 512MB lowmem 后只剩不到 500MB 可用于非线性映射。如果还硬拆成 "vmalloc 区 / ioremap 区" 两块固定预留,任一边爆一次 → 全有 OOM 风险。共池动态占用,互相貢惠
2. 两者的核心操作本质一样。 都是"拿一段商定的虚拟地址 + 建 PTE + 指向某些物理地址",分别只是:
vmalloc
ioremap
物理页来源
buddy 新分 4KB 页
设备寄存器的固定物理地址
PTE 属性
Normal Cacheable
Device-nGnRE / Strongly-ordered
物理页连续性
可以不连续
必须连续(寄存器本来就连续)
骨架都是 alloc_vmap_area() + 页表设 PTE,差别只在 flag。Linus 没理由写两套。
3. 给 ARM 的 early ioremap 留路。 内核启动早期(buddy 还没准备好)需要映射 UART / GIC 调试。既然后期 ioremap 走 vmalloc 区,早期的 early_ioremap 走另一块固定的 fixmap 区,两者就能平滑过渡——这也是为什么 fixmap 在 0xFF000000 顶部单独留到的一个原因。

实战启示

  • cat /proc/vmallocinfo 会看到两种类型:
    • vmallocvmapmodule:真正占用 RAM 页的项。
    • ioremap:映射 MMIO,不占用物理 RAM,只吃虚拟地址和页表项。
  • 算 vmalloc 区剩余时务必把 ioremap 的占用算进去;外设多的 SoC (如 IMX6ULL 调满 LCDIF + CSI + GPU) 可能吃掉十几 MB 虚拟地址。
  • 在 ARM64 上这个约束消失:arm64 把 VMALLOC_START/END 开成数十 TB 并且 ioremap 另开独立区段,因为地址空间不再是稀缺资源,独立管理更方便调试和安全隔离。

5.4 运行时主要分配器

分配器
粒度
典型用途
Buddy System
页 (4KB) 的 2^n
最底层物理页分配
Slab / SLUB
对象级
task_structinode 等高频小对象
kmalloc
对象级
内核常规小块分配 (背后走 slab)
vmalloc
页级
大块非连续内存 (模块加载、大缓冲)
Page Cache
页级
文件 I/O 缓存、mmap 支撑

5.5 用户进程虚拟地址布局 (单进程 3GB)

每个进程都有独立的页表,MMU 将同一虚拟地址翻译到不同物理页,进程间天然隔离;内核部分 (0xC0000000+) 在所有进程页表中共享同一份映射,系统调用时无需切换页表。

5.6 为什么 0x80000000 – 0x80080000 (512KB) 留空

  • 给 BootROM / SPL 返回时的临时数据留余量,避免内核启动早期踩到。
  • 避免 DMA (SDHC、USB 等) 传输覆盖内核镜像头部。
  • 对齐 1MB / 2MB section (ARM 一级页表的最小映射粒度),方便 MMU 早期页表构建。

5.7 ARM32 → ARM64 内存模型迁移对照表

IMX6ULL 是 ARMv7 (32-bit),而 i.MX8 / 高通 / 骁龙 / 树莓派 4B+ 都是 ARMv8 (64-bit aarch64)。两者的内存模型在结构上有根本性差异,以下对照表可作为跨架构排错 / 面试 / 驱动调优的参考。
维度
ARM32 (ARMv7 / IMX6ULL)
ARM64 (ARMv8 / aarch64)
实战启示
虚拟地址宽度
32-bit,共 4GB
48-bit (默认) / 52-bit (ARMv8.2+),每半最大 256TB
ARM64 虚拟空间海量,根本不用抓襟见胘 1GB
内核 / 用户切分
3G/1G (默认) 或 2G/2G,VMSPLIT_* 可选
物理上拆成两段:低半 = 用户空间 0x0–TTBR0_TOP,高半 = 内核空间 TTBR1_BASE–...;中间是不可访问的 "canonical hole"
ARM64 内核/用户不再争线性地址空间,还用不完
页表寄存器
TTBR0 / TTBR1 共存,但内核映射多走 TTBR1;切空间要 flush TLB
TTBR0_EL1 = 用户页表、TTBR1_EL1 = 内核页表,硬件天然分离
ARM64 系统调用不需切 TTBR,上下文切换更便宜
页表级数
2 级 (section 1MB + page 4KB);LPAE 下 3 级
默认 4KB 页 + 4 级 (PGD/PUD/PMD/PTE),支持 16KB / 64KB 页
ARM64 可通过 64KB 页压减页表级数,限流分析页表开销
线性映射 (direct map)
lowmem:PAGE_OFFSET 向上映射只等于物理内存大小,上限 ~760MB
内核半边内有专用 linear map region(如 5.4+ 的 PAGE_OFFSET0xFFFF000000000000 附近),可容纳 TB 级物理内存
ARM64 整个 RAM 永远线性映射,所以下面的 HIGHMEM 不存在
HIGHMEM / kmap()
必要时开启(物理内存 > lowmem 上限)
彻底取消kmap_local 变成普通 API 没有映射作用
迁移代码时别在 arm64 代码路径里再假设 HIGHMEM 路径成立
vmalloc / ioremap
lowmem 上方一小段 (~488MB),与 ioremap 共享
独立的 VMALLOC_START–VMALLOC_END 区 (数十 TB),ioremap 单独区段
ARM64 上大块 vmalloc / 大内存模块 不再担心地址空间费
内核镜像位置
物理 PHYS_OFFSET + TEXT_OFFSET 固定 (如 0x80008000)
允许 KASLR 随机偏移 kimage_voffset;物理地址也可浮动
ARM64 驱动中不能硬编码内核虚拟地址
Zone 划分
DMANORMAL • (可选) HIGHMEM
DMA (通常 ≤ 4GB 给旧外设) + DMA32 (≤ 4GB) + NORMAL
ARM64 DMA 约束靠 DMA/DMA32,而不再靠物理地址低端
ASID 宽度
8-bit (256 个),溢出需全局刷 TLB
16-bit (可选 8-bit),溢出概率低
ARM64 上上下文切换成本更低
异常向量表
低向量 0x00000000 或高向量 0xFFFF0000(SCTLR.V)
VBAR_EL1 寄存器自由指定,每个异常级别独立
ARM64 没有"高/低向量"概念,都用 VBAR
特权保护
仅 U/S 两级;无 PAN(内核可直接读用户指针)
EL0/EL1/EL2/EL3 四级;PAN (ARMv8.1+) 禁止内核误触用户页;PAC/BTI (8.3/8.5+)
ARM64 驱动 copy_from_user 必须走正规 API,不能直接解引用
密切相关指令
dsb/isb/dmb • cp15 寄存器操作 cache/TLB
同名指令 + 专用的 TLBI / IC / DC 指令类;支持 inner/outer shareable 精细化
ARM64 cache 维护 更细粒度,多核性能更好
用户栈 / mmap 生长
栈下长、mmap 从中间向下长,受 3GB 限
用户半边成百 TB,mmap 布局更激进随机化(ASLR)
ARM64 用户态内存滥用暴露更难发现,已备好 monitoring
两句话总结
  1. ARM32 的全部"巧思"(lowmem/HIGHMEM/VMSPLIT/高向量)都是在 4GB 虚拟空间太窄的前提下抠出来的。
  1. ARM64 用 256TB 半边空间 + 硬件双 TTBR + 永久线性映射 一次性把这些巧思全部拿掉,设计回归简单。
 
上一篇
Data Structure and Algorithm
下一篇
用面试拷问嵌入式技术栈

Comments
Loading...