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##牛客创作赏金赛#
全部评论

相关推荐

点赞 评论 收藏
分享
小米软件开发方向的,美团是测开。小米第一道大题,测试了很久,死活只能A18%,第二道直接print都能A18%,无语住了,第一道代码现在也不知道哪里有问题,求大佬解答!另外考完剩半小时直接进入美团,选择题做完没剩多少时间,大题第一题甚至还没咋看懂题目,准备直接print了,好家伙,防了一手,只能过自测的用例,运行起来通过率0%,哈哈哈哈太菜了太菜了。继续准备别的笔试了,各位佬也加油~from collections import Counterm,n=map(int,input().split())st1=map(str,input().split())st2=''.join(st1)result=[]for i in range(n):    count=0    b,c=map(int,input().split())    a_=st2[b-1:c]    counter = Counter(a_)  # 记录字典    for key,value in counter.items():        if value>2 and value%2!=0:            count+=(value-1)/2        if value%2==0:            count+=value/2    if count==(c-b+1)/2:        result.append('yes')    else:        result.append('no')for i in result:    print(i)
投递小米集团等公司9个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务