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个:

  1. 指定源文件:告诉编译器要编译哪些 .c 文件。
  2. 设置编译选项:比如开启哪些特性、链接哪些库。
  3. 定义平台常量:比如内存基址、中断号等。

来看一个典型的例子(以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 知识体系结构图

为了让你更直观地理解这些文件之间的关系,我画了一张图:

ATF平台目录结构核心逻辑 platform.mk 编译控制、源文件列表 plat_def.h 宏定义、内存布局、硬件特性 plat_setup.c 初始化流程 plat_pm.c 电源管理 plat_topology.c 核心拓扑 关键依赖关系 platform.mk 决定编译哪些文件 plat_def.h 为所有源文件提供平台宏定义

从这张图你可以看到,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 是移植的“任督二脉”,打通了它们,后面的路就好走了。

可可精品资料库