4. 平台目录结构解析:plat文件夹详解、关键文件的作用
好,咱们今天来聊聊ATF移植里最核心的一块——平台目录结构。说白了,你拿到一块新板子,第一件事就是搞清楚plat文件夹里到底放了些什么。我刚开始接触ATF时,面对那一堆文件夹和文件,说实话有点懵。但摸清楚规律后,你会发现,其实套路很固定。
4.1 plat文件夹的整体布局
ATF的plat目录,位于源码根目录下的plat/。它按厂商和平台分了两级:
- 第一级:厂商名,比如
arm/、mediatek/、rockchip/、allwinner/等。 - 第二级:具体平台名,比如
arm/fvp/、rockchip/rk3399/、mediatek/mt8183/。
每个平台目录下,通常包含这些核心文件:
plat/<vendor>/<platform>/
├── platform.mk # 编译控制文件
├── plat_def.h # 平台宏定义
├── plat_common.c # 通用平台函数
├── plat_setup.c # 初始化流程
├── plat_pm.c # 电源管理
├── plat_topology.c # 核心拓扑
├── include/ # 头文件目录
│ └── plat_private.h # 私有头文件
└── ...
嗯,这里要注意:不是所有文件都必须有。有些平台会把功能合并到一两个文件里。我个人习惯是,先看 platform.mk 和 plat_def.h,这两个文件基本能告诉你这个平台是怎么组织的。
4.2 platform.mk:编译的“总指挥”
platform.mk 是Makefile的入口。ATF的构建系统会先加载通用的 Makefile,然后根据你指定的 PLAT 变量,找到对应的 platform.mk 并包含进来。
它的核心作用有3个:
- 指定源文件:告诉编译器要编译哪些 .c 文件。
- 设置编译选项:比如开启哪些特性、链接哪些库。
- 定义平台常量:比如内存基址、中断号等。
来看一个典型的例子(以ARM FVP为例,简化版):
# platform.mk (简化)
PLAT_INCLUDES := -Iinclude/plat/arm/common/ \
-Iinclude/plat/arm/common/${ARCH}/
PLAT_BL_COMMON_SOURCES := drivers/arm/pl011/pl011_console.S \
plat/arm/common/arm_common.c
BL31_SOURCES += plat/arm/fvp/fvp_bl31_setup.c \
plat/arm/fvp/fvp_pm.c \
plat/arm/fvp/fvp_topology.c
$(eval $(call add_define,PLAT_ARM_TRUSTED_SRAM_SIZE))
$(eval $(call add_define,PLAT_ARM_DRAM2_BASE))
你看,它通过 PLAT_INCLUDES 指定头文件路径,通过 BL31_SOURCES 列出BL31阶段需要的源文件。最后两行用 add_define 把宏定义传给编译器。
platform.mk 里加上对应的 SOURCES 变量。我曾经漏加过一次,结果编译出来的镜像死活不执行新代码,排查了半天才发现是文件没被编译进去。
4.3 plat_def.h:平台特性的“身份证”
plat_def.h 是平台相关的宏定义头文件。它定义了该平台独有的常量、内存布局、硬件特性等。ATF的通用代码会通过条件编译(#ifdef)来适配不同平台。
常见的定义包括:
| 宏名称 | 作用 | 示例值 |
|---|---|---|
PLATFORM_CORE_COUNT |
核心数量 | 4 |
PLATFORM_CLUSTER_COUNT |
簇数量 | 1 |
PLATFORM_MAX_CPUS_PER_CLUSTER |
每簇最大核心数 | 4 |
PLAT_ARM_TRUSTED_SRAM_BASE |
Trusted SRAM基址 | 0x04000000 |
PLAT_ARM_TRUSTED_SRAM_SIZE |
Trusted SRAM大小 | 0x00040000 |
PLAT_ARM_DRAM2_BASE |
第二块DRAM基址 | 0x88000000 |
举个例子,假设你要移植到一块4核Cortex-A55的板子上,内存布局和FVP不同,你需要在 plat_def.h 里这样写:
#ifndef PLAT_DEF_H
#define PLAT_DEF_H
#define PLATFORM_CORE_COUNT 4
#define PLATFORM_CLUSTER_COUNT 1
#define PLATFORM_MAX_CPUS_PER_CLUSTER 4
#define PLAT_ARM_TRUSTED_SRAM_BASE 0x00100000
#define PLAT_ARM_TRUSTED_SRAM_SIZE 0x00020000
#define PLAT_ARM_DRAM2_BASE 0x80000000
#define PLAT_ARM_DRAM2_SIZE 0x80000000
/* 其他平台特有定义 */
#define PLAT_MAX_PWR_LVL 2
#define PLAT_MAX_RET_STATE 1
#define PLAT_MAX_OFF_STATE 2
#endif /* PLAT_DEF_H */
PLAT_ARM_TRUSTED_SRAM_SIZE 定义小了,结果BL31加载时直接溢出,系统起不来。调试了整整一天才发现是内存大小不对。所以,一定要对照芯片手册确认SRAM和DRAM的地址范围,别想当然。
4.4 其他关键文件速览
除了上面两个核心文件,还有几个文件你迟早会打交道:
- plat_common.c:放一些通用函数,比如
plat_get_my_entrypoint()、plat_is_my_cpu_primary()。这些函数在多个阶段都可能被调用。 - plat_setup.c:初始化入口。BL31启动时会先执行这里的
bl31_plat_arch_setup()和bl31_platform_setup()。你想想看,这相当于板子的“开机自检”流程。 - plat_pm.c:电源管理。CPU上电、下电、挂起、唤醒都在这。这部分最容易出问题,因为硬件时序要求很严格。
- plat_topology.c:描述核心拓扑。告诉ATF系统有多少个簇、每个簇有多少个核心、哪些核心是同一个电源域。
嗯,这里我多说一句:plat_topology.c 很容易被忽略。我见过有人移植时没改这个文件,结果系统以为只有1个核心,多核调度直接崩了。所以,核心数量、簇结构一定要和硬件一致。
4.5 知识体系结构图
为了让你更直观地理解这些文件之间的关系,我画了一张图:
从这张图你可以看到,platform.mk 是顶层入口,它决定了编译哪些文件;plat_def.h 是底层支撑,为所有源文件提供平台相关的宏定义。而 plat_setup.c、plat_pm.c、plat_topology.c 等则是具体功能的实现。
4.6 移植时的小建议
最后,给你几个实操建议:
- 先复制再修改:找一个和你硬件最接近的参考平台,把整个目录复制一份,然后逐步修改。别从零开始写,太容易出错了。
- 从platform.mk入手:先看参考平台的
platform.mk,搞清楚它引用了哪些文件,然后对照你的硬件做调整。 - 验证plat_def.h:编译通过后,先检查
plat_def.h里的宏定义是否被正确传递。可以在代码里加个#error来验证。 - 分阶段测试:先让BL31能跑起来,再逐步添加电源管理、多核支持等功能。别想一口吃成胖子。
嗯,关于平台目录结构,今天就聊这么多。记住,platform.mk 和 plat_def.h 是移植的“任督二脉”,打通了它们,后面的路就好走了。