跟着源码和qemu模拟过程研究下KGSL模块的加载流程。
环境配置
真机xiaomi 14 pro设备树(pineapple 6.1.75)
qcom-kernel 源码:KERNEL.PLATFORM.4.0.r12-00400-kernel.0.xml
graphic-kernel 源码
模拟流程
获取目标模块依赖树以及设备树;
加载目标模块所有依赖模块,先满足目标模块能够insmod的条件;
通过源码或者IDA反编译分析目标模块以及依赖模块需要的硬件属性,添加设备树设置;
记录硬件属性的数据流
硬件属性分为必要/非必要,均可以存储到数据库中
设备树应该为dts格式,而非dtb反编译
设备树每一个节点 qcom 有官方说明 txt
注意设备树地址是否与virt本身冲突,冲突部分需要进行地址迁移,同时在 QEMU 侧注册相应 MMIO 地址区域
查看目标模块是否加载成功,进行插桩动态循环测试。
反馈信息:日志,跟踪执行路径作插桩(bpftrace)
为不兼容设备模拟qemu硬件设备环境,包括初始化内存,…
实在不行则patch
当模拟复杂,递归进行2模拟
设备树配置 在virt.dtb中添加vienna.dtb中的配置项(注意需要保留原virt项); 在virt.dtb中添加以下配置,以便qemu虚拟机能够在msm-kgsl.ko注册设备时正常返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 soc { # address-cells = <0x01>; # size-cells = <0x01>; ranges = <0x00 0x00 0x00 0xffffffff>; compatible = "simple-bus"; phandle = <0x29d>; qcom,kgsl-3d0@3d00000 { nvmem-cell-names = "speed_bin"; nvmem-cells = <0x3f3>; qcom,bus-table-ddr = <0x00 0x209a8e 0x2dc6c0 0x5caf6a 0x65ce03 0x7cb163 0xa3140c 0xbdf5c2 0xdbb3e5 0xfbc520>; qcom,bus-table-cnoc = <0x00 0x64>; interconnect-names = "gpu_icc_path"; interconnects = <0x53 0x11 0x41 0x200>; qcom,tzone-names = "gpuss-0", "gpuss-1", "gpuss-2", "gpuss-3", "gpuss-4", "gpuss-5", "gpuss-6", "gpuss-7"; qcom,gpu-qdss-stm = <0x161c0000 0x40000>; qcom,ubwc-mode = <0x04>; qcom,min-access-length = <0x20>; qcom,chipid = <0x43051401>; qcom,gpu-model = "Adreno740"; clock-names = "gcc_gpu_memnoc_gfx", "gcc_gpu_snoc_dvm_gfx", "gpu_cc_ahb", "apb_pclk"; clocks = <0x2c 0x25 0x2c 0x26 0x4e 0x00 0x3f 0x00>; interrupt-names = "kgsl_3d0_irq"; interrupts = <0x00 0x12c 0x04>; reg-names = "kgsl_3d0_reg_memory", "cx_dbgc", "rscc", "cx_misc", "qdss_gfx", "qdss_etr", "qdss_tmc"; reg = <0x3d00000 0x40000 0x3d61000 0x3000 0x3d50000 0x10000 0x3d9e000 0x2000 0x10900000 0x80000 0x10048000 0x8000 0x10b05000 0x1000>; status = "ok"; compatible = "qcom,adreno-gpu-gen7-6-0", "qcom,kgsl-3d0"; # cooling-cells = <0x02>; phandle = <0x92>; }; };
这样的话在进行modprobe时,内核才会真正去尝试去probe设备。当然,由于大部分设备都没有进行模拟,导致肯定会失败。
KGSL 模块依赖 构建了打印模块依赖树的脚本,输出模块依赖树如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 msm-kgsl ├── socinfo │ └── smem ├── llcc-qcom │ └── socinfo │ └── smem ├── qcom-dcvs │ └── dcvs-fp │ ├── qcom-rpmh │ │ ├── qcom-ipc-logging │ │ │ └── minidump │ │ │ ├── debug-symbol │ │ │ └── smem │ │ └── cmd-db │ └── cmd-db ├── governor-msm-adreno-tz │ └── qcom-scm ├── qcom-scm ├── cmd-db ├── msm-performance │ ├── qcom-pmu-lib │ │ ├── qcom-scmi-client │ │ └── qcom-llcc-pmu │ └── sched-walt │ └── socinfo │ └── smem ├── qcom-aoss │ └── qcom-ipc-logging │ └── minidump │ ├── debug-symbol │ └── smem ├── mem-buf-dev │ └── secure-buffer │ └── qcom-scm ├── qcom-iommu-util │ └── qcom-scm ├── clk-qcom │ ├── gdsc-regulator │ │ ├── proxy-consumer │ │ └── debug-regulator │ └── icc-clk ├── secure-buffer │ └── qcom-scm ├── mdt-loader │ └── qcom-scm ├── minidump │ ├── debug-symbol │ └── smem ├── qcom-va-minidump │ └── minidump │ ├── debug-symbol │ └── smem └── coresight
模块依赖项加载 这里目前只看加载日志是否报错以及是否会成功lsmod查看到装载,目标对象是叶子节点的模块。
模块设备模拟 cmd-db 该模块主要用于 rpmh 等模块查看 resource 的对应存放值的地址。 在 qemu/hw/misc/qcom-cmd-db.c 创建设备,在对应地址初始化内存,主要是设置 magic header 以及添加了最小的 rsc_header 以及 entry。可以使其加载成功。
smem 该模块用于各模块之间 share_memory 的共享数据。 在 qemu/hw/misc/qcom-smem.c 创建设备,在对应地址初始化内存
解决依赖后可直接加载的模块:
KGSL Init 内核模块启动时,会先调用 module_init 函数进行初始化,msm_kernel 也是如此。其初始化函数为 kgsl_3d_init。在直接 insmod 时(已经事先加载其他依赖模块),其会正常调用该函数并正常返回。 下面是添加日志函数后打印结果,并没有进入错误分支。
1 2 3 4 [ 19.256512][ T83] boot_kpi: M - DRIVER KGSL Init [ 19.261323][ T83] boot_kpi: M - DRIVER KGSL Ready [ 19.261681][ T83] boot_kpi: M - DRIVER KGSL MMU Init [ 19.262443][ T83] boot_kpi: M - DRIVER KGSL GMU Core Register
初始化后查看bind情况:
1 2 3 4 5 6 7 8 9 # platform driver register success ls /sys/bus/platform/devices/ | grep -i kgsl 3d00000.qcom,kgsl-3d0 # device tree is valid cat /proc/device-tree/soc/qcom,kgsl-3d0@00000/status ok # There is no device link file. ls /sys/bus/platform/drivers/kgsl-3d bind modules uevent unbind
但是发现/dev目录下并没有创建 /dev/kgsl-3d0,猜测没有执行 adreno_dev->probe函数,或者中途退出。
GPU Init arm-smmu 需要内核编译开启 CONFIG_ARM_SMMU_V3 相关配置。并在启动时加载 arm-smmu 模块 arm-smmu 模块加载会报错:gpu_qtb, anoc_1_qtb TBU failed probe. 说明在 probe 时 TBU 加载失败。 其问题代码函数调用链为:
1 2 3 4 5 arm_smmu_device_probe -> arm_smmu_impl_init -> qssmuv500_impl_init -> qsmmuv500_create -> device_for_each_child
将TBU相关内容先注释掉,继续执行,发现kgsl_smmu 卡在