ARM64 Linux 6.10内核启动流程6-psci.c和ATF/U-Boot 的电源接口

§1.1 arch/arm64/kernel/psci.c:和固件的 CPU 电源接口

平台:QEMU virt + ARM64,-kernel Image。没有真 ATF,也没有 U-Boot。


0. 这条要明白什么

ARM64 上 CPU 的上电、下电、改 PC,不是内核去写 SoC 复位寄存器。内核只发 PSCI 请求;真正动电源的是更高异常级的固件。

本文件是客户这一侧的适配层:把 struct cpu_operations(SMP/热插拔用的虚表)翻成 PSCI 的 CPU_ON / CPU_OFF / AFFINITY_INFO。

清单写成「ATF/U-Boot」,是 BSP 口头习惯。virt 上对端是 QEMU。真板最常见是 ATF(BL31)。U-Boot 首先是加载 Image 的 bootloader,只有少数没 ATF 的板子才会顺带实现 PSCI。


1. 三个「psci」不要并成一份

文件 角色 管什么
本文件 arch/arm64/kernel/psci.c ARM64 的 cpu_ops 某一颗 CPU 的上电/下电
drivers/firmware/psci/psci.c 通用 PSCI 驱动 发 hvc/smc;还挂整机 SYSTEM_OFF / SYSTEM_RESET
arch/arm64/kernel/smp.c 启动/热插拔流程 何时调用上面这张表

调用方向永远是:

复制代码
smp.c  (时机)
  → 本文件 cpu_psci_ops.cpu_boot / cpu_die / cpu_kill
    → psci_ops.*     (drivers/firmware/psci/psci.c 填好的函数指针)
      → invoke_psci_fn → hvc/smc
        → 固件(virt = QEMU;真板 = ATF)

本文件 没有 hvc 指令,也 没有 poweroff。它只决定「这次要调 PSCI 的哪一个功能」。


2. 对端到底是谁

复制代码
真板典型:
  U-Boot / 厂商 bootloader     加载 Image,x0 = DTB     ← 启动协议
  ATF (BL31, EL3)              实现 PSCI,接 smc        ← 电源接口 ★ 本条
  Linux                        本文件 + firmware/psci.c  ← 客户

你现在的 virt:
  QEMU -kernel                 兼当 bootloader(填 x0)
  QEMU 自己模拟 PSCI            兼当 ATF(接 hvc)
  Linux                        客户侧完全一样

两件不同的事:

  1. 谁把内核加载进来 ------ bootloader(现在是 QEMU,以后可以是 U-Boot)。
  2. 谁给 CPU 上电/改入口 ------ PSCI 实现者(现在是 QEMU,真板是 ATF)。

加 U-Boot 只换第 1 件;加 ATF 才换第 2 件。本条关心第 2 件。

DT 里和本文件对上的是 cpu 节点的 enable-method = "psci"(cpu_ops.c 用这个名字找到 cpu_psci_ops)。psci { method = "hvc"; } 则是 firmware 那份驱动选 hvc 还是 smc,不是本文件读的。


3. 文件本体:一张表

全文就是给 cpu_psci_ops 填回调。.name = "psci" 必须和 DT enable-method 字符串一致。

复制代码
const struct cpu_operations cpu_psci_ops = {
    .name            = "psci",
    .cpu_init        = cpu_psci_cpu_init,      // 空实现,返回 0
    .cpu_prepare     = cpu_psci_cpu_prepare,   // 启动前确认固件有 cpu_on
    .cpu_boot        = cpu_psci_cpu_boot,      // ★ 上电:CPU_ON
    .cpu_can_disable = ...,                    // 这颗核能不能热拔
    .cpu_disable     = ...,                    // 拔之前再检查一次
    .cpu_die         = cpu_psci_cpu_die,       // ★ 本核请求下电:CPU_OFF
    .cpu_kill        = cpu_psci_cpu_kill,      // ★ 另一核确认已经关掉
};

没有 .cpu_postboot:从核起来后不必在本文件做额外同步。

谁在什么时候调这些指针(smp.c):

回调 谁调 何时
cpu_init smp_cpu_setup() setup_arch → smp_init_cpus,登记阶段
cpu_prepare smp_prepare_cpus() kernel_init_freeable,拉核之前
cpu_boot boot_secondary() ← __cpu_up smp_init,真正点亮从核
cpu_disable 热插拔下线路径 目标核还在跑,允许失败并中止
cpu_die 目标核自己 cpu_die() 点了不归路,必须成功
cpu_kill 另一颗核 arch_cpuhp_cleanup_dead_cpu 轮询固件:那颗核是不是真关了

4. 上电:cpu_psci_cpu_boot

这是第 1 个月必精读的一个函数。

复制代码
cpu_psci_cpu_boot(cpu)
  pa = __pa_symbol(secondary_entry)     物理地址:从核 MMU 关着
  psci_ops.cpu_on(cpu_logical_map(cpu), pa)

两个参数就是 PSCI CPU_ON 契约的全部:

  1. MPIDR (cpu_logical_map(cpu),来自 DT cpu 节点 reg),不是 Linux 逻辑 CPU 号。
  2. 入口物理地址 (secondary_entry)。固件把目标核的 PC 设到这里。

psci_ops.cpu_on 在 setup_arch → psci_dt_init() 里已经填好(virt 上是 psci_0_2_cpu_on)。本文件不关心 hvc 还是 smc。

返回 0 只表示固件接单。从核是否 online,仍由 smp.c 的 __cpu_up 等 cpu_running。细节见拉核笔记第 3~5 节。

cpu_prepare 只做一件事:没有 psci_ops.cpu_on 就拒绝拉这颗核(-ENODEV)。cpu_init 为空:PSCI 不需要按 CPU 再读一份私有数据(对比 spin-table 还要读 cpu-release-addr)。


5. 下电:看函数名,不深挖热插拔

第 1 个月知道「对称的下电也走固件」即可,不必顺着 cpuhp 状态机走。

三步,故意拆开:

复制代码
1. cpu_can_disable / cpu_disable   还在内核里,可以拒绝
     - 固件没实现 CPU_OFF → -EOPNOTSUPP
     - Trusted OS 驻留在这颗核上 → -EPERM(psci_tos_resident_on)
2. cpu_die                         目标核自己调,发 CPU_OFF,不再返回
3. cpu_kill                        另一颗核轮询 AFFINITY_INFO,确认 OFF

cpu_die 里的 power_state 字段,注释写明现有实现基本不用,填一个 POWER_DOWN 即可。

cpu_kill 最多等约 100ms,是因为 cpu_die 和 cpu_kill 并发:杀得太早会误判「还活着」。查不到 affinity_info 就当成功(没有手段确认,只能假设)。

virt 学习阶段很少做 echo 0 > /sys/devices/system/cpu/cpu1/online。知道下电同样是 PSCI、同样不写复位寄存器,这条就够。


6. 整机电源不在本文件

poweroff / reboot 挂在 drivers/firmware/psci/psci.c 的 psci_0_2_set_functions():

  • pm_power_off = psci_sys_poweroff → SYSTEM_OFF
  • restart handler → SYSTEM_RESET

和本文件的 CPU_ON/CPU_OFF 是同一份契约、同一次 hvc 通道,但是:

本文件 firmware/psci.c
粒度 一颗 CPU 整台机器
调用者 smp.c 的 cpu_ops kernel_power_off / reboot
第 1 个月 要知道「不在这里」 第 3.1 再顺着走

7. 重点(合上文件能用的)

  1. 客户 vs 管家 :Linux 发请求;ATF(virt 上是 QEMU)管 CPU 电源。从核起不来,先查 enable-method、PSCI 节点、固件,不是某个外设 probe。
  2. 本文件只做翻译 :cpu_boot → CPU_ON,cpu_die → CPU_OFF,cpu_kill → AFFINITY_INFO。发指令的是另一份 psci.c。
  3. ATF ≠ U-Boot:U-Boot 管加载;ATF 管 PSCI。virt 上两件都由 QEMU 做。
  4. 核级 ≠ 整机 :开关某一颗核走本文件;关机/重启走 firmware 的 SYSTEM_*。
  5. MPIDR + 物理入口 :CPU_ON 只认硬件编号和 secondary_entry 的物理地址。

相关推荐
syagain_zsx2 小时前
spdlog实战:日志分级、滚动与性能调优
linux·c++·日志库·二次开发日志库
GeW15 小时前
新质生产力路线图Linux、数据库、国产化替代一次讲透
linux
aningx17 小时前
cachyos 从 GRUB 切换到 rEFInd
linux
第404号工位18 小时前
40块钱二手瘦客户端,如何跑满家庭物联网中枢?(拒绝Docker爆盘实录)
linux·架构·node.js
对象存储与RustFS18 小时前
网盘限速逼你开会员?用 RustFS 自己搭一个不限速的“私人网盘
服务器·后端·docker
骑着蜗牛撵大象32720 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
..Dauntless..20 小时前
【Linux】一切皆文件和缓冲区
linux·运维·服务器
裕晟资质规划20 小时前
三类资质变更报告时限差在哪?军工 15 日、集成印制 10 日
大数据·服务器·网络
QMCY_jason20 小时前
x86 ubuntu2204 无gui 安装 openeuler 2203 aarch64版本
linux·运维·服务器
重生之小比特20 小时前
【Linux系统】12 进程程序替换 自定义Shell 命令行解释器
linux·运维·服务器