remoteproc (4)记一次firmware加载失败, 完整的驱动加载流程
1.remoteproc 与 PIL 的关系
Linux 内核的 remoteproc 框架用于管理远程处理器,而 PIL 是其底层加载逻辑的具体实现之一。
- remoteproc 的作用:提供统一的 API 管理远程处理器的生命周期(加载、启动、停止、监控)。通过 sysfs 或 debugfs 向用户空间暴露控制接口。
- PIL 的作用:实际处理固件加载的细节(如解析二进制、配置内存映射)。可能是内核模块的一部分,或由厂商提供的闭源二进制工具实现
2.问题
以kernel 6.6 sm8150举例
error -22 initializing firmware qcom/sm8150/cdsp.mbn
3.probe代码分析
(1)从dtsi到对应的_of_match:https://elixir.bootlin.com/linux/v6.6/source/drivers/remoteproc/qcom_q6v5_pas.c#L1184
of_match到of_match_table:https://elixir.bootlin.com/linux/v6.6/source/drivers/remoteproc/qcom_q6v5_pas.c#L1210
(2)首先便是probe:https://elixir.bootlin.com/linux/v6.6/source/drivers/remoteproc/qcom_q6v5_adsp.c#L666
分析probe流程(qcom_q6v5_adsp.c)q6v5的意思是: 第六代 Hexagon DSP 的第五个版本
desc = of_device_get_match_data(&pdev->dev);获取of_match_table的资源,特定芯片的配置
of_property_read_string(pdev->dev.of_node, "firmware-name", &firmware_name);读取firmware-name的属性
rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, firmware_name, sizeof(*adsp)); 创建remoteproc实例,绑定操作集adsp_ops,指定要加载的firmware adsp_alloc_memory_region(adsp); // 分配共享内存区域 adsp_init_clock(adsp, desc->clk_ids); // 初始化时钟 qcom_rproc_pds_attach(..., desc->proxy_pd_names); // 附加电源域 adsp_init_reset(adsp); // 控制复位信号 adsp_init_mmio(adsp, pdev); // 映射寄存器空间
qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, desc->load_state, qcom_adsp_pil_handover); 下一节详解
qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); 分别是glink(通信),ssr(subsystem recovery协处理器崩溃后,自动重新加载固件并恢复服务),sysmon(远程检控官)子模块, 注册remoteproc:ret = rproc_add(rproc);
(3)qcom_q6v5_init
https://elixir.bootlin.com/linux/v6.6/source/drivers/remoteproc/qcom_q6v5.c#L246
qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, desc->load_state, qcom_adsp_pil_handover); 中断配置(wdog/fatal/ready/handover/stop-ack) handover:DSP 完成初始化后,将控制权从引导环境(如 TrustZone/启动加载程序)移交给主处理器(Linux 内核) q6v5->state = devm_qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit);获取 SMEM 中用于控制 DSP 停止的状态位。SMEM 是 Qualcomm 平台多处理器协作的“中枢神经”,承担数据共享、状态同步、崩溃恢复等关键任务。 q6v5->load_state = devm_kstrdup_const(&pdev->dev, load_state, GFP_KERNEL); q6v5->qmp = qmp_get(&pdev->dev); // 获取 QMP(QoS Messaging Protocol)句柄 q6v5->path = devm_of_icc_get(&pdev->dev, NULL);获取主 CPU 与 DSP 之间的总线路径(确保通信带宽)
4.start代码分析
https://elixir.bootlin.com/linux/v6.6/source/drivers/remoteproc/qcom_q6v5_pas.c#L246
直接到adsp_start,因为在probe里adsp_ops已经被注册了
PAS:PAS 是 Protected Authentication System(受保护认证系统)的缩写,它是 Qualcomm 安全架构的核心组件之一,用于安全地加载和验证协处理器(如 DSP、Modem)的固件
代码主要分为4部分
1.准备工作 2.电源/时钟配置 3.固件加载与验证 qcom_scm_pas_auth_and_reset(adsp->dtb_pas_id); qcom_mdt_pas_init(adsp->dev, adsp->firmware, rproc->firmware, adsp->pas_id, adsp->mem_phys, &adsp->pas_metadata); 主固件初始化 qcom_mdt_load_no_init(..., adsp->mem_region, adsp->mem_phys, adsp->mem_size, ...);加载固件到内存 qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size); 记录固件的物理地址和大小 4.启动DSP qcom_scm_pas_auth_and_reset(adsp->pas_id); qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); 等待并发送
qcom_mdt_pas_init中的qcom_scm_pas_init_image出现错误,这里报错
https://elixir.bootlin.com/linux/v6.6/source/drivers/soc/qcom/mdt_loader.c#L249
5. qcom_scm_pas_init_image分析
https://elixir.bootlin.com/linux/v6.6/source/drivers/firmware/qcom_scm.c#L498
里面执行了SCM调用
https://elixir.bootlin.com/linux/v6.6/source/drivers/firmware/qcom_scm-smc.c#L149
最终是__scm_smc_call的调用
__scm_smc_call
是 Qualcomm 实现的 Secure Monitor Call (SMC) 调用机制,主要用于:
- 与 TrustZone 安全世界(EL3)通信
- 访问受保护的硬件资源
- 执行特权安全操作
最后就是由于hypervisor的修改,导致了该问题
#Linux内核##Linux##牛客创作赏金赛#