高通modem启动过程

HLOS kernel通过PIL加载MBA(Modem Boot Authenticator)到DDR
TrustZone 按照 mba.mdt的签名信息 对 mba 进行验证
HLOS kernel对Hexagon modem DSP 复位
Modem PBL(Primary BootLoader,位于rom中) 开始启动
HLOS kernel 通过PIL (Peripheral image loader) 加载modem image到DDR
Modem PBL验证MBA, 然后跳转到MBA

在msm8909平台上,需要签名的文件如下:

 boot_images/build/ms/bin/8909/emmc/sbl1.mbn 
 boot_images/build/ms/bin/8909/emmc/unsigned/prog_emmc_firehose_8909_ddr.mbn 
 LINUX/android/out/target/product/msm8909/emmc_appsboot.mbn 
 modem_proc/build/ms/bin/8909.gen.prod/mba.mbn 
 modem_proc/build/ms/bin/8909.gen.prod/qdsp6sw.mbn 
 rpm_proc/build/ms/bin/8909/pm8909/rpm.mbn 
trustzone_images/build/ms/bin/MAZAANAA/tz.mbn 
 wcnss_proc/build/ms/bin/SCAQMAZ/reloc/wcnss.mbn 

Modem Primary Boot Loader 的功能:

Sets up Hexagon TCM, copies MBA from LPDDR3 into Hexagon TCM, and authenticates MBA in Hexago TCM

Modem Boot Authenticator 的功能:
Authenticates the modem image, xPU protects the DDR regions for modem, and memory dump


签名镜像格式
Qualcomm Technologies 固件镜像使用标准 ELF 格式

ELF 标头主要用于定位 ELF 镜像文件中的程序标头。程序标头包含 ELF 镜像文件中所有段的位置,特别是用于定位 ELF 文件中的哈希段,其中包含用于验证 ELF 镜像的验证信息。

哈希段(hash segment)按顺序包含以下信息:

(1) 哈希段标头(元数据)是一个 48 字节的字段(Field),其中包含有关哈希段其他部分大小的信息。这允许在哈希段内识别字段。
(2) QTI 和设备制造商元数据字段都是 128 字节的字段,其中包含有关镜像的信息,例如镜像的类型和设计用于运行镜像的硬件。 QTI 元数据仅在镜像由 QTI 和设备制造商双重签名时才存在。
(3) 哈希表字段包含 ELF 文件中每个段(Segment)的哈希值。第一个条目(Entry)始终是 ELF 标头和程序标头的哈希值。这是为了确保标头段中的重要信息也得到验证(包括 ELF 标头中包含的镜像入口点地址)。
hash table field

(4) QTI签名和证书链。数字签名是根据哈希段标头、镜像元数据和哈希表(hash table)计算的。可以使用叶证书(Leaf Certificate)中的公钥对其进行验证。证书链最终由一个根证书验证,该根证书根据硬件中保存的 QTI 特定值进行验证。仅当镜像由 QTI 和设备制造商双重签名时,才应存在 QTI 签名和证书链。

(5) 设备制造商签名和证书链。与 QTI 签名和证书链一样,数字签名是根据哈希段标头、图像元数据和哈希表计算的,它可以使用证书链叶节点中的公钥进行验证,该公钥可以最终通过根证书进行验证,根证书根据硬件中的 OEM 特定值进行验证

镜像元数据(Image Metadata)描述了镜像的意图(intent)。最重要的是,它描述了镜像的意图(以便在要加载 Qualcomm TEE 镜像时系统不会加载 WLAN 镜像)和用于执行镜像的硬件(以便系统不会在 Qualcomm® Snapdragon™ 845 设备上加载 Snapdragon 835 的 Modem 镜像)。

在旧版本的 QTI 安全启动架构中,此信息已被编码到证书链中叶证书的组织单位(OU,Organizational Unit)字段中。新版本的安全引导格式将此信息移动到独立的元数据字段中。这旨在通过降低解析或证书创建错误的可能性来提高安全性,这些错误可能导致镜像不具有设备制造商预期的属性。

元数据字段允许签名者指定有关镜像的信息,包括以下内容:

镜像的软件标识(SW_ID)。每种不同的镜像类型都有不同的软件标识。这是为了确保在引导过程中正确的时间点加载正确的镜像。
设备的硬件标识(HW_ID)。每个 QTI 芯片组都有不同的硬件标识。这是为了确保只有那些被设计为在该硬件上执行的镜像才能在该硬件上执行。
设备制造商标识(OEM_ID)。这是为了确保由一个设备制造商签名的软件不能在另一个制造商的设备上运行,即使它们共享一个公共根证书。
设备的调试功能。某些镜像允许签名者在设备上启用调试功能;但是,签名者只能在特定设备(必须在镜像签名时标识)上使用这些功能。
镜像是绑定到单个设备还是可以在所有设备上使用。出于开发和调试目的,可以通过指定设备的序列号并在镜像元数据字段中设置标志来将镜像绑定到单个设备。每个设备都有一个不同的序列号,该序列号在制造过程中被烧入芯片中。
镜像的防回滚版本号。如果启用,则防回滚系统将防止已知存在错误的镜像在设备上运行。该架构旨在防止设备在成功加载具有更大防回滚版本号的镜像时接受镜像。

如果镜像由 QTI 和设备制造商签署,则 QTI 和设备制造商都将提供镜像元数据字段。两个元数据字段中的所有条件都必须有效才能加载镜像。

镜像加载

所有镜像加载都遵循相同的通用过程。在本节中,我们将加载镜像的软件称为“加载程序”(Loader)。流程如下:

加载程序分配一个安全的内存区域来加载 ELF 标头,该内存将被映射(mapped)以防止它被设备中的其他执行环境篡改,加载程序将 ELF 标头从(不受信任的 untrusted)存储(storage)复制到此内存中。如果因 ELF 标头太大而无法放入此内存区域,则镜像将被拒绝。

加载程序分配一个安全的内存区域来加载程序头(Program Header),该内存将被映射以防止它被设备中的其他执行环境篡改,加载程序将程序标头从(不受信任的)存储复制到此内存中。如果因程序标头太大而无法放入此内存区域,则镜像将被拒绝。

加载程序分配一个安全的内存区域来加载哈希段(hash segment),该内存将被映射以防止它被设备中的其他执行环境篡改,加载程序将哈希段从(不受信任的)存储复制到此内存中。如果因哈希段太大而无法放入此内存区域,则镜像将被拒绝。

加载程序通过验证根证书、证书链、镜像元数据和哈希表(hash table)来验证哈希段(hash segment)。

加载程序通过计算(已加载的)ELF 头 和 程序头 的哈希值并将哈希值与哈希表中的第一个条目进行比较来验证它们。如果哈希值不匹配,则镜像将被拒绝。

The loader validates the (already loaded) ELF Header and Program Header by hashing them and
comparing the hash value with the first entry in the hash table. If the hashes do not match, the
image is rejected.

然后加载程序将尝试加载镜像中的其他每个 ELF 段。对于每个段,加载程序检查整个段是否可以加载到其已批准(列入白名单)的安全且适合该镜像的内存区域中,如果无法加载段到内存的白名单区域,则镜像将被拒绝。

The loader will then attempt to load each of the other ELF segments in the image. For each
segment, the loader checks that the entire segment can be loaded into an area of memory that
has been approved (whitelisted) by the loader as safe and appropriate for that image. If a
segment cannot be loaded into a whitelisted area of memory, the image is rejected.

加载程序通过计算每一个加载的 ELF 段的哈希值并将哈希值与哈希表中的相应条目进行比较来验证。如果任何计算的哈希值与哈希表中的值不同,则镜像将被拒绝。
The loader verifies each of the loaded ELF segments by hashing them and comparing the hash
value with the corresponding entry in the hash table. If any of the computed hash values differ
from the value in the hash table, the image is rejected.

如果合适,加载程序使用在(已经加载和验证的)ELF 标头中定义的入口点将执行传递给镜像。

If appropriate, the loader passes execution to the image using the entry point defined in the
(already loaded and validated) ELF Header.

此过程旨在确保加载程序永远不会意外覆盖包含从不受信任的存储中加载的镜像数据的内存中的重要数据(包括加载程序自己的代码和数据)。


Modem Image modification
Modem image patching

Modem binaries are unencrypted on disk
modem 二进制代码以未加密的形式保存在 文件系统中
This facilitates easy disassembly, and easy patching
这有助于 反汇编 和打补丁
Secboot prevents unsigned images from loading
但是,安全启动机制,阻止 载入未签名的映像。
Signature verification performed in secure world
签名验证 执行在 安全世界 (Trustzone)

Leverages a integer overflow to achieve an arbitrary write into the trustzone, and patches two bytes to neuter signature checking.

Prereqs: ability to compile your own kernel and flash it,
Modem internal hashes still need to be consistent

https://github.com/eti1/tzexec
https://github.com/eti1/pymdt
https://github.com/laginimaineb/unify_trustlet


mdt: contains headers and information (certificate chain and signature blob) used to verify *.bxx

———————-
A bug can bypass the MBA to inject Hexagon code
– Ability to read/write modem memory at any time from the Linux kernel
– Reported to Qualcomm, patch in development
————————-

missing sections with critical OTA vectors code (q6zip decompressed & relocated in runtime)

——————
hash table segment 也是一个elf segment,只是qualcomm为这个hash table segment设置了自定义的格式。

hash table segment 包含:
mbn header: hash table segment各个field的位置信息
QTI metadata: Qualcomm对当前image设置的一些信息
OEM metadata: OEM对当前image设置的一些信息
Hash Table: 该ELF文件的{ elf header + program header}的hash、其他elf segments的hash
QTI signature
QTI Cert Chain
OEM signature
OEM Cert Chain
0xFF padding


QTI signature/OEM signature所对应的原文范围都是 { mbn header, QTI metadata, OEM metadata, Hash Table }

Qualcomm Hexagon DSP

QDSP6 V4, 用于 S400 和 S600产品中,3G 4G换代的时代, 如8926, 8930, 8230, 8630, 8930AB, 8230AB, 8630AB, 8030AB, 8226, 8626
8064T, 8064M等
QDSP6 V5(V5A, V5H) 用于 Snapdragon 410/412/800/801 (如8974, 8274, 8674, 8074) 处理器中,2013-2015年产品
Hexagon 536 DSP, 用于 Snapdragon 205/208/210/212, 425/427/429/430/435/439 处理器中
V50 , 用于 Snapdragon 415/610/615/616/805
546 , 用于 Snapdragon 450/617/625/626/632
V56 , 用于 Snapdragon 650/652/653/808/810
642 , 用于 Snapdragon 630
QDSP6 V6/680, 用于 Snapdragon 820/821/636/660
682, 用于 Snapdragon 835
683 , 用于 Snapdragon 662/460
685 , 用于 Snapdragon 850/845/670/675/678/710/712
686 , 用于 Snapdragon 695/685/680/665/480/480+
688 , 用于 Snapdragon 730(G)/732G
690 , 用于 Snapdragon 855/855+/860/8c/8cx , Microsoft SQ1/SQ2
692 , 用于 Snapdragon 720G/690/7c
694 , 用于 Snapdragon 750G
696 , 用于 Snapdragon 765(G)/768G
698 , 用于 Snapdragon 865(SM8250)/865+/870
770 , 用于 Snapdragon 778G/778G+/780G/782G
780, 用于 Snapdragon 888((SM8350), 888+ ((SM8350P) ,
790 , 用于 Snapdragon 8 gen 1 ((SM8450), 8+ gen 1 ((SM8475)
Hexagon V73 , 用于 Snapdragon 8 gen 2 ((SM8550)

https://en.wikipedia.org/wiki/Qualcomm_Hexagon

https://github.com/n-o-o-n/idp_hexagon
Supports all Hexagon versions: V4, V5, V55, V60, V61, V62, V65, V66, V67, V67t, V68, V69, V71, V73

https://github.com/gsmk/hexagon

编译MSM8626 modem

参考:
Building Qualcomm modem from sources (msm8626)

Assembling a Qualcomm modem from source(msm8916)

Assembling a Qualcomm modem from source(msm8626)(原文是俄语)

https://projects.osmocom.org/projects/quectel-modems/wiki/Lenovo_A6000__Other_phones_with_leaked_qualcomm_sources

 

 

0.    首先在磁力链上下载

(1)

qcom_msm8x26_modem

magnet:?xt=urn:btih:61e8b8a520181ade801c251e5ec9352e33a7bb8c&dn=qcom_msm8x26_modem

(2)

qcom_leaked_sources.zip

magnet:?xt=urn:btih:ef3bf4fd7388657ff35c3dbf2b6367f924f8cdbc&dn=qcom_leaked_sources.zip

 

在gitlab也可以下载

https://gitlab.com/qcom-sources15

 

(3)

THE-CODE-8916.zip

http://www.mediafire.com/file/0t1znbr1sl2y6fm/THE-CODE.zip

http://www.mediafire.com/file/awo4z3i3j4qdz45/THE-CODE-8916.gz

 

1.  安装 Debian 或者 Ubuntu系统

我安装的是 Debian Buster  (Debian 10.1.3)  amd64

 

2.  安装必须的软件包

<pre>

apt install      p7zip-full    libxml-parser-perl      scons       lib32stdc++6

</pre>

p7zip-full  用来解压缩 下载的源代码

libxml-parser-perl  用来分析xml配置文件, debian系统 安装基本系统时就默认装好了

libc6-i386    lib32stdc++6  是因为  qcom 的 dsp编译程序实际是 32位的,必须装上32位的支持包

 

3.  安装HEXAGON Tools 到 特定的目录

HEXAGON_ROOT=$HOME/Qualcomm/HEXAGON_Tools
7za x -y -o$HEXAGON_ROOT $HOME/Downloads/hexagon_tools_6.4.06.a.7z

假设 将 hexagon_tools_的压缩包,放在了 $HOME/Downloads 目录下, 如果放在其他目录,对上面的命令自行修改

4. 解压缩 msm8626 modem的源代码

ROOTDIR=$HOME/qcom/msm8626
mkdir -p $ROOTDIR
cd $ROOTDIR
7za x -y -0$ROOTDIR     $HOME/Downloads/qcom_msm8626_modem_proc.7z
mv   qcom_msm8626_modem_proc   modem_proc
cd $ROOTDIR/modem_proc

因为压缩包没有保持 UNIX文件属性,sh脚本没有执行权限,需要修复

find . -name '*.sh' -exec chmod -f 775 {} \;
find . -name '*.mk' -exec chmod -f 775 {} \;
find . -name '*.py' -exec chmod -f 775 {} \;
find . -name '*.pl' -exec chmod -f 775 {} \;
find . -name '*.lcs' -exec chmod -f 775 {} \;
find . -name '*.api' -exec chmod -f 775 {} \;
find . -name '*.xml' -exec chmod -f 775 {} \;
find . -name '*.scons' -exec chmod -f 775 {} \;
find . -name 'scons' -exec chmod -f 775 {} \;
find . -name 'SConscript' -exec chmod -f 775 {} \;
find . -name 'SConstruct' -exec chmod -f 775 {} \;
find . -name 'Makefile' -exec chmod -f 775 {} \;
find . -name 'makefile' -exec chmod -f 775 {} \;
find . -name 'qaic' -exec chmod -f 775 {} \;
find . -name 'doxygen' -exec chmod -f 775 {} \;
find . -name 'qdsp6-image-build' -exec chmod -f 775 {} \;
find . -name 'SleepSynth' -exec chmod -f 775 {} \;
find . -name 'crypto_cbc' -exec chmod -f 775 {} \;
find . -name 'crypto_ccm' -exec chmod -f 775 {} \;

5. 开始编译

cd $ROOTDIR/modem_proc/build/ms/
./build.sh 8626.gen.prod BUILD_ID=AAAAANAZ -k

编译成功,提示

Elfparser Merge: Copying 4376 bytes from ELF file /home/softsim/qcom/msm8626/modem_proc/build/ms/M8x26AAAAANAZQ00772_DEVCFG.elf section ".8626_PLATFORM_CDP_DEVCFG_DATA" at offset 0x1e1a0 into merged_elf_8626_PLATFORM_MTP_MSM_DEVCFG_DATA.elf at offset 0x1b931a0                
Install file: "/home/softsim/qcom/msm8626/modem_proc/core/bsp/devcfg_img/build/devcfg_img/qdsp6/AAAAANAZ/M8x26AAAAANAZQ00772.elf" as "M8x26AAAAANAZQ00772.elf"                                                                                                                      
=== Generating  devcfg_img/qdsp6/AAAAANAZ/M8x26AAAAANAZQ00772_reloc.elf
=== Generating  devcfg_img/qdsp6/AAAAANAZ/M8x26AAAAANAZQ00772.mbn
=== Generating  devcfg_img/qdsp6/AAAAANAZ/M8x26AAAAANAZQ00772_relocflags.elf
Install file: "/home/softsim/qcom/msm8626/modem_proc/core/bsp/devcfg_img/build/M8x26AAAAANAZQ00772.mbn" as "bin/AAAAANAZ/qdsp6sw.mbn"
=== Generating  devcfg_img/qdsp6/AAAAANAZ/M8x26AAAAANAZQ00772.mbn
Install file: "/home/softsim/qcom/msm8626/modem_proc/core/bsp/devcfg_img/build/reloc/M8x26AAAAANAZQ00772.mbn" as "bin/AAAAANAZ/reloc/qdsp6sw.mbn"                                                                                                                                   
scons: done building targets.

==============================================================================
   SCons build summary
==============================================================================
** Build time...
 Build start  : Sat Jan 27 19:06:15 2024
 Build end    : Sat Jan 27 19:19:38 2024
 Elapsed time : 0:13:24
#-------------------------------------------------------------------------------
# BUILD END: AAAAANAZ
#-------------------------------------------------------------------------------
Build AAAAANAZ: Start Time: Sat Jan 27 19:06:15 2024,  End Time: Sat Jan 27 19:19:40 2024
Build AAAAANAZ: Delta Time: 13 minutes, 24 seconds
#-------------------------------------------------------------------------------
Overall Start Time: Sat Jan 27 19:06:15 2024,  Overall End Time: Sat Jan 27 19:19:40 2024
Overall Delta Time: 13 minutes, 24 seconds
#-------------------------------------------------------------------------------

可以看到,生成了 bin/AAAAANAZ/qdsp6sw.mbn 这个 modem文件

6. 清除编译的结果

./build.sh 8626.gen.prod BUILD_ID=AAAAANAZ --clean

遇到错误,修正后,重新编译前,可以先清楚。

7. mba.mbn 和 qdsp6sw.mbn 都没有做数字签名
准确地说,是用的高通给的测试签名, 可以在 firmware/image/modem.mdt 文件里看到 “General Use Test Key (for testing only)” 字符串
说明就是测试签名

编译骁龙400 modem固件

2013年上市, ARM Cortex-A7 , 28nm LP 工艺生产

8226, 8626, 8228, 8628
红米手机1S, 酷派8702, 中兴Q505T, 联想s810t,天语touch2

准备工作

apt install p7zip-full
apt install lib32z1 lib32ncurses5
apt install scons
apt install libxml-parser-perl

qualcomm subsystem

https://android.googlesource.com/kernel/msm/+/refs/tags/android-13.0.0_r0.46/drivers/soc/qcom/subsystem_restart.c

static const char * const restart_levels[] = {
	[RESET_SOC] = "SYSTEM",
	[RESET_SUBSYS_COUPLED] = "RELATED",
};

restart level (0 – panic, 1 – related, 2 – independent, etc.)

static int subsystem_shutdown(struct subsys_device *dev, void *data)
static int subsystem_powerup(struct subsys_device *dev, void *data)
static int subsys_start(struct subsys_device *subsys)
static void subsys_stop(struct subsys_device *subsys)
int subsystem_set_fwname(const char *name, const char *fw_name)
int subsystem_restart_dev(struct subsys_device *dev)
int subsystem_restart(const char *name)

restart_level的设置为related, 就只重启该子系统,而不是重启整个系统

init.svc.vendor.ssr_setup
init.svc_debug_pid.vendor.ssr_setup
persist.vendor.ssr.enable_ramdumps
persist.vendor.ssr.restart_level

/vendor/bin/ssr_setup (binary exe)
vendor/qcom/proprietary/ss-restart/ssr_setup/ssr_setup.c

[persist.vendor.ssr.restart_level]: [ALL_ENABLE] 应该设置为 modem

The Peripheral Image Loader (PIL) is a framework that allows services to load and reload firmware images for each subsystem of the chipset into DDR memory. Upon loading, TrustZone’s services are invoked to authenticate the content of the image file using a hash segment. Once the image is authenticated, the processor of that subsystem is taken out of reset, i.e., allowed to execute instructions.
外设映像加载器 (PIL) 是一个框架,它允许服务将芯片组每个子系统的固件映像加载和重新加载到 DDR 内存中。 加载后,将调用 TrustZone 的服务以使用哈希段来认证映像文件的内容。 一旦认证通过,该子系统的处理器就会脱离复位状态,也就是允许执行指令。

Firmware images are written in ELF (Executable and Linkable Format), which consists of a metadata portion and a program portion.
The program portion is further split up into multiple files. This splitting is done for several reasons:
1. If authentication information is found to be invalid, we do not need to load the entire image.
2. It is advantageous in low memory conditions since the amount of memory used at any given instance, when loading one segment at a time, is smaller than loading the whole image as one file.
3. Allows more flexibility for future changes to the loading procedure. The firmware image filenames all begin with a base name that matches the name of the peripheral, e.g., “modem”.

固件映像以 ELF(可执行和可链接格式)编写,由元数据部分和程序部分组成。
程序部分进一步分为多个文件。 进行这种拆分有几个原因:
1.如果发现认证信息无效,我们不需要加载整个映像。
2. 这在低内存条件下是有利的,因为在任何给定实例中,每次加载一个片段时使用的内存量小于将整个映像作为一个文件加载。
3. 为未来加载程序的更改提供更大的灵活性。 固件映像文件名均以与外围设备名称匹配的基本名称开头,例如“modem”。
modem.b01, modem.b02, ….

The metadata file has an extension of .mdt . This comprises the ELF header, program headers, hash, and signature.
The program segment files are named with numbered extensions in the form of .bXX, where XX is the index of the sengment beginning from 00, i.e., .b01, .b02, .b03, etc. Each of these files is referred to as a segment or a “blob”.Firmware files are expected to be placed under /etc/firmware. The PIL loads each segment of the ELF image from the firmware directory using generaic kernel method request_firmware().
This places the segment into the relevant memory hole. Once loaded, TrustZone is invoked to authenticate the segment. After all the firmware segments have been successfully loaded and authenticated, PIL resets the subsystem and initiate its boot sequence.

元数据文件的扩展名为 .mdt 。 这包括 ELF 标头、程序标头、哈希值和签名。

程序段文件以 .bXX 的形式以编号扩展名命名,其中 XX 是从 00 开始的段索引,即 .b01、.b02、.b03 等。
这些文件中的每一个都称为一个 段或“blob”。
固件文件应放置在 /etc/firmware (/vendor/firmware_mnt) 下。

PIL 使用通用内核方法 request_firmware() 从固件目录加载 ELF 映像的每个片段。
这会将段放入相关的内存孔中。 加载后,将调用 TrustZone 来认证该段。
所有固件段均已成功加载并经过认证后,PIL 会重置子系统并启动其引导序列。

PIL is implemented as a kernel driver at the following location: kernel root/drivers/soc/qcom/peripheral-loader.c
Generic PIL drivers: kernel root/drivers/soc/qcom/subsys-pil-tz.c
Drivers implement power up, shutdown, error handling, and RAM dump of the subsystem. Interfaces are encapsulated and registered to the SSR driver.

PIL被实现为一个内核中的驱动,位置在 kernel root/drivers/soc/qcom/peripheral-loader.c
通用的PIL驱动是:kernel root/drivers/soc/qcom/subsys-pil-tz.c
该驱动实现了子系统的power up, shutdown,err handing和RAM dump功能。接口被封装并注册到 SSR 驱动程序。

(SSR subsystem restart子系统重启)

PS: 在kernel/msm-5.4/drivers/remoteproc/qcom_q6v5_mss.c中会调用request_firmware()对modem.bxx进行加载

Device tree configurations
Device tree configs for subsystems are at the following locations:
      /arch/arm64/boot/dts/qcom/.dtsi

子系统的设备树配置位于: kernel root/arch/arm64/boot/dts/qcom/sdm chipset.dtsi

PS: 在该dtsi文件中可以看到相应的compatible = “qcom,pil-tz-generic”的节点


Support for Modem Self-Authentication (MSA)
PIL loading of Modem Boot Authenticator (MBA)
PIL loading of Primary Modem Image (PMI)

Confidential and Proprietary – Qualcomm Technologies, Inc. | MAY CONTAIN U.S. AND INTERNATIONAL EXPORT CONTROLLED INFORMATIONPAGE 12 80-NC839-21 A Apr 2013
Reporting Failure Reasons
1 Subsystem should report failure reason to host driver
2 Subsystem software failure reason report should use a preallocated
shared memory location
3 Subsystem failure report should include information about source of
failure
Filename
Line number
Program counter of instruction that generated the exceptio

AP应该支持的功能
1. 关闭子系统 Shutting down the subsystem
AP shall implement recommended subsystem shutdown procedures for the subsystem
AP should support both QXDM and AT power-down command
2. Powering up the subsystem
AP shall implement recommended subsystem power-up procedures for the subsystem
3. Processing the failure reasons
AP host driver on MSM shall record subsystem failure reasons in Kernel log
AP host driver shall erase subsystem failure reason from shared memory before restarting the system
4.

Configuration Options
AP shall provide a runtime configurable option to select between Full Chip Restart or Independent Subsystem Restart handling
AP shall provide a runtime configurable option to enable RAM dump collections for the peripheral subsystem
AP shall provide configuration parameters for the maximum allowed number of restarts within a configurable time window, after which a full chip restart will occur


enable Subsystem Restart (SSR)
3 > restart_level
Phase-3 (Independent Restart)

Trigger Modem restart using the QXDM command: send_data 75 37 03 00 (致命错误) send_data 75 37 03 00 02(软件异常) 75 37 03 00 01( 看门狗复位)
Trigger WCNSS restart using the QXDM command: send_data 75 37 03 32
Trigger ADSP restart using the QXDM command: send_data 75 37 03 48

在qxdm command窗口发送send_data 75 37 03 48 00 (重启音频dsp)


在 Android手机的 属性中 开启/关闭 永久性的 SSR功能(SSR Persistent Flag Enable/Disable )

启用Independent SSR
setprop persist.sys.ssr.restart.level 3

关闭 Independent SSR
setprop persist.sys.ssr.restart_level 1

[persist.vendor.ssr.restart_level]: [ALL_ENABLE]


/system/build.prop
persist.vendor.ssr.enable_ramdumps=1
/vendor/build.prop
# enable all system restart_level to relative
persist.vendor.ssr.restart_level=ALL_ENABLE


SUBSYS_MODEM=”mss”
SUBSYS_MODEM_ESOC0=”esoc0″

/sys/devices/platform/soc/4080000.remoteproc-mss/remoteproc/remoteproc2

cat /sys/class/remoteproc/remoteproc2/name
4080000.remoteproc-mss

persist.vendor.sys.ssr.restart_level=modem,adsp
setprop persist.vendor.ssr.restart_level mss
setprop persist.vendor.ssr.restart_level “ALL_DISABLE”

setprop persist.vendor.ssr.enable_ramdumps 0
setprop persist.vendor.ssr.restart_level ALL_ENABLE

29 02 00 重启整个手机系统
29 01 00 offline
29 04 00 online

/sys/module/subsystem_restart/parameters

write /sys/module/subsystem_restart/parameters/enable_ramdumps 0

on property:persist.vendor.sys.ssr.restart_level=*
start vendor.ssr_setup
service vendor.ssr_setup /vendor/bin/ssr_setup

service vendor.ssr_diag /vendor/bin/ssr_diag

persist.vendor.ssr.restart_level=MODEM

persist.vendor.sys.ssr.restart_level=modem,adsp
persist.vendor.sys.ssr.restart_level=modem,adsp,slpi

persist.sys.ssr.restart_level=venus,AR6320,slpi,modem,adsp


send_data 75 37 03 00 会被转成实际的命令
4B 25 03 00 Debug/Simulate Crash Request
4b 25 03 00 Debug/Simulate Crash Response

mtkclient

mtk 天玑920之前的soc都能 解锁,刷机

git clone https://github.com/bkerler/mtkclient
cd mtkclient

apt install python3-fuse
python3 -m venv /home/my/pymtk
source /home/my/pymtk/bin/activate

python3 -m pip install -r requirements.txt 
python3 -m pip install  .
cp mtk  ~/pymtk/bin/
cp mtk_gui ~/pymtk/bin/
cp stage2  ~/pymtk/bin/

sudo cp mtkclient/Setup/Linux/*.rules /etc/udev/rules.d

sudo usermod -a -G plugdev $USER
sudo usermod -a -G dialout $USER

50-android.rules 必须要用 mtkclient 带的, 用debian发行版配置的,可能 找不到设备

usb_class - [LIB]: Couldn't get device configuration

显示gpt分区信息 (use –preloader for brom)

python mtk printgpt

备份所有的分区(除了userdata) 到 out 目录

python mtk rl   --skip userdata    out  

备份所有的分区 到 flash.bin 单个文件 (use –preloader for brom)

python mtk rf flash.bin

从 flash.bin 恢复到手机 (currently only works in da mode)

python mtk wf flash.bin

从 out 目录恢复所有分区文件到手机

python mtk wl out

解锁bootloader

mtk da seccfg unlock

mtk vsim 分析

属性

ro.vendor.mtk_external_sim_support 必须为 1

ro.vendor.mtk_external_sim_only_slots 含义 vsimOnlySlots

ro.vendor.mtk_persist_vsim_disabled

persist.vendor.radio.external.sim 永久vsim (1= persist enabled, 0=disable persist vsim)

vendor.gsm.external.sim.enabled vsim是否启用

vendor.gsm.external.sim.inserted vsim是否插入 (1=loccalsim insert, 2=remote sim inserted)

vendor.gsm.modem.vsim.capability 设置为 2,2 表示都支持热插拔

相关 AT命令
vendor/mediatek/proprietary/hardware/ril/platformlib/common/libmtkrilutils/libmtkrilutils.c

"+ERSAIND",
"AT+ERSA",
"+ERSAAUTH",
"AT+ERSIMATO",

请求

 
const char* mtkRequestToString(int request) {
     .....

   // MTK-START: SIM
    case RIL_REQUEST_SIM_GET_ATR: return "SIM_GET_ATR";
    case RIL_REQUEST_SIM_GET_ICCID: return "RIL_REQUEST_SIM_GET_ICCID";
    case RIL_REQUEST_SET_SIM_POWER: return "RIL_REQUEST_SET_SIM_POWER";

    // ESIM -START
    case RIL_REQUEST_GET_SLOT_STATUS: return "RIL_REQUEST_GET_SLOT_STATUS";
    case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
        return "RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
    case RIL_UNSOL_SIM_SLOT_STATUS_CHANGED: return "RIL_UNSOL_SIM_SLOT_STATUS_CHANGED";
    // ESIM - END

...
        case RIL_UNSOL_VIRTUAL_SIM_ON: return "RIL_UNSOL_VIRTUAL_SIM_ON";
        case RIL_UNSOL_VIRTUAL_SIM_OFF: return "RIL_UNSOL_VIRTUAL_SIM_OFF";
...
        // External SIM [START]
        case RIL_REQUEST_VSIM_NOTIFICATION: return "RIL_REQUEST_VSIM_NOTIFICATION";
        case RIL_REQUEST_VSIM_OPERATION: return "RIL_REQUEST_VSIM_OPERATION";
        case RIL_UNSOL_VSIM_OPERATION_INDICATION: return "RIL_UNSOL_VSIM_OPERATION_INDICATION";
        // External SIM [END]

上面这些代码都是默认启用

ril_service.cpp

...
struct RadioImpl : public VENDOR_V3_20::IRadio {
...
    // External SIM [Start]
    Return sendVsimNotification(int32_t serial, uint32_t transactionId,
            uint32_t eventId, uint32_t simType);

    Return sendVsimOperation(int32_t serial, uint32_t transactionId,
            uint32_t eventId, int32_t result, int32_t dataLength, const hidl_vec& data);
    // External SIM [End]
....


// External SIM [START]
bool dispatchVsimEvent(int serial, int slotId, int request,
        uint32_t transactionId, uint32_t eventId, uint32_t simType) {
    RequestInfo *pRI = android::addRequestToList(serial, slotId, request);
    if (pRI == NULL) {
        return false;
    }

    RIL_VsimEvent args;
    args.transaction_id = transactionId;
    args.eventId = eventId;
    args.sim_type = simType;

    s_vendorFunctions->onRequest(request, &args, sizeof(args), pRI, pRI->socket_id);

    return true;
}

bool dispatchVsimOperationEvent(int serial, int slotId, int request,
        uint32_t transactionId, uint32_t eventId, int32_t result,
        int32_t dataLength, const hidl_vec& data) {

    RLOGD("dispatchVsimOperationEvent: enter id=%d", eventId);

    RequestInfo *pRI = android::addRequestToList(serial, slotId, request);
    if (pRI == NULL) {
        RLOGD("dispatchVsimOperationEvent: pRI is NULL.");
        return false;
    }

    RIL_VsimOperationEvent args;

    memset (&args, 0, sizeof(args));

    // Transcation id
    args.transaction_id = transactionId;
    // Event id
    args.eventId = eventId;
    // Result
    args.result = result;
    // Data length
    args.data_length = dataLength;

    // Data array
    const uint8_t *uData = data.data();
    args.data= (char  *) calloc(1, (sizeof(char) * args.data_length * 2) + 1);
    memset(args.data, 0, ((sizeof(char) * args.data_length * 2) + 1));
    for (int i = 0; i < args.data_length; i++) {
        sprintf((args.data + (i*2)), "%02X", uData[i]);
    }

    //RLOGD("dispatchVsimOperationEvent: id=%d, data=%s", args.eventId, args.data);

    s_vendorFunctions->onRequest(request, &args, sizeof(args), pRI, pRI->socket_id);

    free(args.data);

    return true;
}

Return RadioImpl::sendVsimNotification(int32_t serial, uint32_t transactionId,
        uint32_t eventId, uint32_t simType) {
    RLOGD("sendVsimNotification: serial %d", serial);
    dispatchVsimEvent(serial, mSlotId, RIL_REQUEST_VSIM_NOTIFICATION, transactionId, eventId, simType);
    return Void();
}

Return RadioImpl::sendVsimOperation(int32_t serial, uint32_t transactionId,
        uint32_t eventId, int32_t result, int32_t dataLength, const hidl_vec& data) {
    RLOGD("sendVsimOperation: serial %d", serial);
    dispatchVsimOperationEvent(serial, mSlotId, RIL_REQUEST_VSIM_OPERATION,
            transactionId, eventId, result, dataLength, data);
    return Void();
}

int radio::vsimNotificationResponse(int slotId, int responseType, int serial, RIL_Errno e,
        void *response, size_t responseLen) {

    RLOGD("vsimNotificationResponse: serial %d, error: %d", serial, e);

    if (radioService[slotId]->mRadioResponse != NULL) {
        RadioResponseInfo responseInfo = {};
        populateResponseInfo(responseInfo, serial, responseType, e);
        VsimEvent params = {};
        if (response == NULL || responseLen != sizeof(RIL_VsimEvent)) {
            RLOGE("vsimNotificationResponse: Invalid response");
            if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
        } else {
            RIL_VsimEvent *p_cur = ((RIL_VsimEvent *) response);
            params.transactionId = p_cur->transaction_id;
            params.eventId = p_cur->eventId;
            params.simType = p_cur->sim_type;
        }

        Return retStatus = radioService[slotId]->mRadioResponseMtk->
                vsimNotificationResponse(responseInfo, params);
        radioService[slotId]->checkReturnStatus(retStatus, RADIO_MTK);

    } else {
        RLOGE("vsimNotificationResponse: radioService[%d]->mRadioResponse == NULL", slotId);
    }

    return 0;
}

int radio::vsimOperationResponse(int slotId, int responseType, int serial, RIL_Errno e,
        void *response, size_t responseLen) {

    RLOGD("vsimOperationResponse: serial %d", serial);

    if (radioService[slotId]->mRadioResponseMtk != NULL) {
        RadioResponseInfo responseInfo = {};
        populateResponseInfo(responseInfo, serial, responseType, e);
        Return retStatus
                = radioService[slotId]->mRadioResponseMtk->vsimOperationResponse(responseInfo);
        radioService[slotId]->checkReturnStatus(retStatus, RADIO_MTK);
    } else {
        RLOGE("vsimOperationResponse: radioService[%d]->mRadioResponseMtk == NULL", slotId);
    }

    return 0;
}

int radio::onVsimEventIndication(int slotId,
        int indicationType, int token, RIL_Errno e, void *response, size_t responselen) {

    RLOGD("onVsimEventIndication: indicationType %d", indicationType);

    if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndicationMtk != NULL) {
        if (response == NULL || responselen == 0) {
            RLOGE("onVsimEventIndication: invalid response");
            return 0;
        }

        VsimOperationEvent event = {};
        RIL_VsimOperationEvent *response_data = (RIL_VsimOperationEvent *)response;
        event.transactionId = response_data->transaction_id;
        event.eventId = response_data->eventId;
        event.result = response_data->result;
        event.dataLength = response_data->data_length;
        event.data = convertCharPtrToHidlString(response_data->data);

        //RLOGD("onVsimEventIndication: id=%d, data_length=%d, data=%s", event.eventId, response_data->data_length, response_data->data);

        Return retStatus = radioService[slotId]->mRadioIndicationMtk->onVsimEventIndication(
                convertIntToRadioIndicationType(indicationType), event);
        radioService[slotId]->checkReturnStatus(retStatus, RADIO_MTK);
    } else {
        RLOGE("onVsimEventIndication: radioService[%d]->mRadioIndicationMtk == NULL", slotId);
    }

    return 0;
}
// External SIM [END]

定义
IRadioIndication.hal



     /*
     * [SIM] Virtual SIM On Indication
     * @param info Response info struct containing response type, serial no. and error
     * @param result Indication Data
     *
     * Valid errors returned:
     *   RadioError:NONE
     *   RadioError:INVALID_ARGUMENTS
     *   RadioError:GENERIC_FAILURE
     */
     oneway onVirtualSimOn(RadioIndicationType type, int32_t simInserted);

     /*
     * [SIM] Virtual SIM Off Indication
     * @param info Response info struct containing response type, serial no. and error
     * @param result Indication Data
     *
     * Valid errors returned:
     *   RadioError:NONE
     *   RadioError:INVALID_ARGUMENTS
     *   RadioError:GENERIC_FAILURE
     */
     oneway onVirtualSimOff(RadioIndicationType type, int32_t simInserted);


.....
    // External SIM [Start]
    /*
     * Send raw data to external sim manager.
     *
     * @param type Type of radio indication
     * @param event vsim data payload
     */
    oneway onVsimEventIndication(RadioIndicationType type, VsimOperationEvent event);
    // Exteranl SIM [End]

MTKRil.java


    // External SIM [Start]
    protected RegistrantList mVsimIndicationRegistrants = new RegistrantList();
    public void registerForVsimIndication(Handler h, int what, Object obj) {
        Registrant r = new Registrant(h, what, obj);
        if (RILJ_LOGD)  mtkRiljLog("registerForVsimIndication called...");
        mVsimIndicationRegistrants.add(r);
    }

    public void unregisterForVsimIndication(Handler h) {
        if (RILJ_LOGD)  mtkRiljLog("unregisterForVsimIndication called...");
        mVsimIndicationRegistrants.remove(h);
    }

    public boolean sendVsimNotification(
            int transactionId, int eventId, int simType, Message message) {
        boolean result = true;

        vendor.mediatek.hardware.radio.V3_0.IRadio radioProxy = getRadioProxy(message);
        if (radioProxy != null) {
            RILRequest rr = obtainRequest(RIL_REQUEST_VSIM_NOTIFICATION, message,
                    mRILDefaultWorkSource);

            if (RILJ_LOGD) {
                mtkRiljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                        + ", eventId: " +  eventId
                        + ", simTpye: " + simType);
            }

            try {
                radioProxy.sendVsimNotification(rr.mSerial, transactionId, eventId, simType);
            } catch (RemoteException e) {
                handleRadioProxyExceptionForRR(rr, "sendVsimNotification", e);
                result = false;
            }
        } else {
            result = false;
        }

        return result;
    }

    public boolean sendVsimOperation(int transactionId, int eventId, int message,
            int dataLength, byte[] data, Message response) {
        boolean result = true;

        vendor.mediatek.hardware.radio.V3_0.IRadio radioProxy = getRadioProxy(response);
        if (radioProxy != null) {
            RILRequest rr = obtainRequest(RIL_REQUEST_VSIM_OPERATION, response,
                    mRILDefaultWorkSource);

            if (RILJ_LOGD) {
                mtkRiljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            }

            if (RILJ_LOGV) {
                mtkRiljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                        + ", eventId: " + eventId
                        + ", length: " + dataLength);
            }

            ArrayList arrList = new ArrayList<>();
            for (int i = 0; i < data.length; i++) {
                arrList.add(data[i]);
            }

            try {
                radioProxy.sendVsimOperation(rr.mSerial, transactionId, eventId,
                        message, dataLength, arrList);
            } catch (RemoteException e) {
                handleRadioProxyExceptionForRR(rr, "sendVsimOperation", e);
                result = false;
            }
        } else {
            result = false;
        }

        return result;
    }
    // External SIM [End]

mediatek/proprietary/hardware/ril/fusion/mtk-ril/mdcomm/sim/RmcSimUrcEntryHandler.cpp
---需要补代码---

    // External SIM [Start]
#ifdef MTK_EXTERNAL_SIM_SUPPORT
    if (RfxRilUtils::isExternalSimSupport()) {
        RFX_HANDLER_CREATE(mVsimUrcHandler, RmcVsimUrcHandler, (slot_id, channel_id));
    } else {
        mVsimUrcHandler = NULL;
    }
#else
    mVsimUrcHandler = NULL;
#endif
    // External SIM [End]

mediatek/proprietary/hardware/ril/gsm/mtk-ril/ril_sim.c

// External SIM [Start]
#ifdef MTK_EXTERNAL_SIM_SUPPORT
extern void requestSwitchExternalSim(RIL_SOCKET_ID rid);
extern void onVsimEventDetected(const char *s, RIL_SOCKET_ID rid);
extern void onVsimAuthEventDetected(const char *s, RIL_SOCKET_ID rid);
extern void requestVsimNotification(void *data, size_t datalen, RIL_Token t);
extern void requestVsimOperation(void *data, size_t datalen, RIL_Token t);
extern int isVsimEnabledByRid(int rid);
#endif
// External SIM [End]
...

extern int rilSimMain(int request, void *data, size_t datalen, RIL_Token t)
{
switch (request)
{
...
// Exteranl SIM [Start]
#ifdef MTK_EXTERNAL_SIM_SUPPORT
case RIL_REQUEST_VSIM_NOTIFICATION:
requestVsimNotification(data, datalen, t);
break;
case RIL_REQUEST_VSIM_OPERATION:
requestVsimOperation(data, datalen, t);
break;
#endif
// External SIM [End]

}
.....

extern int rilSimUnsolicited(const char *s, const char *sms_pdu, RILChannelCtx* p_channel)
{

...

    // External SIM [Start]
#ifdef MTK_EXTERNAL_SIM_SUPPORT
    } else if (strStartsWith(s, "+EIND: 32")) {
        // Add for slow SIM card error handling.
        // Backgournd: get sim status try 30 times with 0.2 second sleep duration.
        // If the card can't get non-busy status within 6 second, then Java layer always can't
        // get correct sim status and broadcast them.
        // Solution: we need a sim status changed unsolicited message to trigger java layer once
        // modem complete SIM card initialization.
        // FIXME: Actually, we need an event represent the SIM card complete initialization.
        // Modem suggest AP use phonebook start to initialization event as a SIM card
        // initialization done. Might change to other exactly URC in the further.
        // Reference CR: ALPS02408560
        if (isVsimEnabledByRid(rid) && isRecoverySimState[rid] == 1) {
            isRecoverySimState[rid] = 0;
            setSimStatusChanged(rid);
        }
        return 0;
    } else if (strStartsWith(s, "+ERSAIND:")) {
        onVsimEventDetected(s, rid);
    } else if (strStartsWith(s, "+ERSAAUTH:") || strStartsWith(s, "+ERSIMAUTH:")) {
        onVsimAuthEventDetected(s, rid);
#endif
    // External SIM [End]

}

mediatek/proprietary/hardware/ril/rilproxy/libril/ril.cpp

// M: ril proxy
bool cacheUrc(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id){
....
switch(unsolResponse) {
...
        // External SIM [Start]
        case RIL_UNSOL_VSIM_OPERATION_INDICATION:
        // External SIM [End]
....

TF卡引脚定义

管脚名称类型作用
1DAT2I/O/PP数据线(bit 2)
2CD/DAT3I/O/PP卡检测/数据线(bit 3)
3CMDPP命令响应
4VDDS提供电压(也就是电源脚) 2.7V-3.6V
5CLKI时钟
6VSSS提供电压地 (也就是接地)
7DAT0I/O/PP数据线(bit 0)
8DAT1I/O/PP数据线(bit 1)
TF卡管脚定义

CLK

D2

D3

CMD

SDC

D1

D0

Qualcomm msm8916 wifi dongle

骁龙410 系统芯片于2013年12月9日宣布,它是高通的第一款64位芯片上移动系统,具有多模4G LTE,蓝牙,Wi-Fi无线,NFC,GPS,GLONASS和北斗功能,并包含了Adreno 306 GPU。它支持1080p的屏幕和1300万像素摄像头

0. 购买
淘宝,抖音
带sim卡槽

1. 安装 开源edl 工具

sudo apt install adb fastboot         python3-dev python3-pip liblzma-dev git
sudo apt purge modemmanager

python3 -m venv /home/my/pyedl
source /home/my/pyedl/bin/activate

git clone https://github.com/bkerler/edl.git
cd edl
git submodule update --init --recursive

sudo cp Drivers/51-edl.rules /etc/udev/rules.d
sudo cp Drivers/50-android.rules /etc/udev/rules.d

pip3 install -r requirements.txt

python3 setup.py build
python3 setup.py install

2. 备份分区信息

adb reboot edl

进入9008 edl紧急下载模式

edlclient会自动检测芯片,加载合适的loader

edl printgpt | tee /dev/shm/stock-003v2.gpt.txt


Qualcomm Sahara / Firehose Client V3.62 (c) B.Kerler 2018-2023.
main - Trying with no loader given ...
main - Waiting for the device
main - Device detected :)
sahara - Protocol version: 2, Version supported: 1
main - Mode detected: sahara
sahara - 
Version 0x2
------------------------
HWID:              0x007050e100000000 (MSM_ID:0x007050e1,OEM_ID:0x0000,MODEL_ID:0x0000)
CPU detected:      "MSM8916"
PK_HASH:           0xcc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f
Serial:            0x07d0afcb

sahara - Possibly unfused device detected, so any loader should be fine...
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/qualcomm/factory/msm8916/007050e100000000_8ecf3eaa03f772e2_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/qualcomm/factory/msm8916/007050e100000000_394a2e47cf830150_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/longcheer/007050e100000000_3022817d373fd7f9_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/cyanogen/007050e100000000_d36c6073c9c2cb1c_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/cyanogen/007050e100000000_4e3eefa63a67eb7a_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/cyanogen/007050e100000000_4614048173062ae4_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/lenovo_motorola/007050e100000000_99c8c13e374c34d8_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/xiaomi/007050e100000000_278448179ac756a1_fhprg_peek.bin
sahara - Possible loader available: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/xiaomi/007050e100000000_50838757eab7c632_fhprg_peek_wt88047.bin
sahara - Trying loader: /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/qualcomm/factory/msm8916/007050e100000000_8ecf3eaa03f772e2_fhprg_peek.bin
sahara - Protocol version: 2, Version supported: 1
sahara - Uploading loader /home/my/pyedl/lib/python3.11/site-packages/edlclient-3.62-py3.11.egg/edlclient/../Loaders/qualcomm/factory/msm8916/007050e100000000_8ecf3eaa03f772e2_fhprg_peek.bin ...
sahara - 32-Bit mode detected.
sahara - Firehose mode detected, uploading...
sahara - Loader successfully uploaded.
main - Trying to connect to firehose loader ...
firehose_client
firehose_client - [LIB]: No --memory option set, we assume "eMMC" as default ..., if it fails, try using "--memory" with "UFS","NAND" or "spinor" instead !
firehose
firehose - [LIB]: !DEBUG! rsp.data: 'bytearray(b'')'
firehose - TargetName=MSM8916
firehose - MemoryName=eMMC
firehose - Version=1
firehose - Trying to read first storage sector...
firehose - Running configure...
firehose_client - Supported functions:
-----------------

Parsing Lun 0:

GPT Table:
-------------
modem:               Offset 0x0000000004000000, Length 0x0000000004000000, Flags 0x1000000000000000, UUID f202334c-7239-17e5-2f23-93572e235c8f, Type EFI_BASIC_DATA, Active False
sbl1:                Offset 0x0000000008000000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 240d65b6-cae4-4f02-7575-e5c7852d2cc1, Type 0xdea0ba2c, Active False
sbl1bak:             Offset 0x0000000008080000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 5cd3b64c-2081-bd72-306c-d8a497592506, Type EFI_BASIC_DATA, Active False
aboot:               Offset 0x0000000008100000, Length 0x0000000000100000, Flags 0x0000000000000000, UUID cdc87237-a238-aa25-ee11-0ac6094db845, Type 0x400ffdcd, Active False
abootbak:            Offset 0x0000000008200000, Length 0x0000000000100000, Flags 0x0000000000000000, UUID 13d52bce-8c77-a4e2-4235-5a47803b956f, Type EFI_BASIC_DATA, Active False
rpm:                 Offset 0x0000000008300000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 4d5f212f-7dde-08d3-bd6b-c3f4faeb9b46, Type 0x98df793, Active False
rpmbak:              Offset 0x0000000008380000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 1d3926af-b3c2-4f7c-77ea-b80cede253f4, Type EFI_BASIC_DATA, Active False
tz:                  Offset 0x0000000008400000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 2c6f74be-4f53-e91d-a493-ccdf6b083658, Type 0xa053aa7f, Active False
tzbak:               Offset 0x0000000008480000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID ad543db5-3b76-23ed-e7f0-0bfc76a20867, Type EFI_BASIC_DATA, Active False
hyp:                 Offset 0x0000000008500000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID 59a6cc8e-96fb-3cc0-6bf3-5298e5a15505, Type 0xe1a6a689, Active False
hypbak:              Offset 0x0000000008580000, Length 0x0000000000080000, Flags 0x0000000000000000, UUID e53d6b40-0bac-0eb5-837e-5f9acd1894bc, Type EFI_BASIC_DATA, Active False
pad:                 Offset 0x0000000008600000, Length 0x0000000000100000, Flags 0x0000000000000000, UUID 10c9a0a4-5b33-fcf6-06bd-5d61b1541231, Type EFI_BASIC_DATA, Active False
modemst1:            Offset 0x0000000008700000, Length 0x0000000000180000, Flags 0x0000000000000000, UUID 59d29285-5cfa-deed-0146-e1ff8e5ae01a, Type 0xebbeadaf, Active False
modemst2:            Offset 0x0000000008880000, Length 0x0000000000180000, Flags 0x0000000000000000, UUID 42088f49-e2e5-4cbe-4d35-a5054776d23a, Type 0xa288b1f, Active False
misc:                Offset 0x0000000008a00000, Length 0x0000000000100000, Flags 0x0000000000000000, UUID 50988588-e26b-8be4-093d-6427a25cee72, Type 0x20117f86, Active False
fsc:                 Offset 0x0000000008b00000, Length 0x0000000000000400, Flags 0x0000000000000000, UUID 189f41d8-77f9-194b-38db-c80357bbf011, Type 0x57b90a16, Active False
ssd:                 Offset 0x0000000008b00400, Length 0x0000000000002000, Flags 0x0000000000000000, UUID 996dc2d2-1e53-4ceb-f5a5-e355011c0cfb, Type 0x2c86e742, Active False
splash:              Offset 0x0000000008b02400, Length 0x0000000000a00000, Flags 0x0000000000000000, UUID 7af86780-46c2-7c2d-8346-2e2749611479, Type 0x20117f86, Active False
DDR:                 Offset 0x000000000c000000, Length 0x0000000000008000, Flags 0x1000000000000000, UUID 50956725-b15d-198a-fb4f-889cb5a5ecde, Type 0x20a0c19c, Active False
fsg:                 Offset 0x000000000c008000, Length 0x0000000000180000, Flags 0x1000000000000000, UUID 486ca85e-96fe-765a-cdec-2137b4fd7a97, Type 0x638ff8e2, Active False
sec:                 Offset 0x000000000c188000, Length 0x0000000000004000, Flags 0x1000000000000000, UUID f3904d7f-7282-9d53-0a34-74a55c59f9ef, Type 0x303e6ac3, Active False
boot:                Offset 0x000000000c18c000, Length 0x0000000001000000, Flags 0x1000000000000000, UUID ee8de777-10d5-9d19-2a57-18e0f3c3de69, Type 0x20117f86, Active False
system:              Offset 0x000000000d18c000, Length 0x0000000032000000, Flags 0x1000000000000000, UUID 809a5a3f-fb5c-4d6f-d655-d561d2c33140, Type EFI_BASIC_DATA, Active False
persist:             Offset 0x000000003f18c000, Length 0x0000000002000000, Flags 0x1000000000000000, UUID 7b8ddecb-a286-77e1-d30c-8439e1ea51fe, Type EFI_BASIC_DATA, Active False
cache:               Offset 0x000000004118c000, Length 0x0000000008000000, Flags 0x1000000000000000, UUID 4da06896-4ab8-392b-992e-ac032a5824d2, Type EFI_BASIC_DATA, Active False
recovery:            Offset 0x000000004918c000, Length 0x0000000001000000, Flags 0x1000000000000000, UUID bce9c7c5-0a2f-19f3-9275-16209bc53829, Type 0x20117f86, Active False
userdata:            Offset 0x000000004a18c000, Length 0x0000000097e6fe00, Flags 0x1000000000000000, UUID 598664a9-7976-e692-dd1c-010c5d49b568, Type EFI_BASIC_DATA, Active False

Total disk size:0x00000000e2000000, sectors:0x0000000000710000

备份整个emmmc (dump whole flash for device with emmc)

edl rf   /dev/shm/stock-001v2.bin
Qualcomm Sahara / Firehose Client V3.62 (c) B.Kerler 2018-2023.
main - Trying with no loader given ...
main - Waiting for the device
main - Device detected :)
main - Mode detected: firehose
main - Trying to connect to firehose loader ...
firehose - Nop succeeded.
firehose - No supported functions detected, configuring qc generic commands
firehose - 
firehose_client
firehose_client - [LIB]: No --memory option set, we assume "UFS" as default ..., if it fails, try using "--memory" with "UFS","NAND" or "spinor" instead !
firehose - TargetName=MSM8916
firehose - MemoryName=eMMC
firehose - Version=1
firehose - Trying to read first storage sector...
firehose - Running configure...
firehose_client - Supported functions:
-----------------
configure,program,firmwarewrite,patch,setbootablestoragedrive,ufs,emmc,power,benchmark,read,getstorageinfo,getcrc16digest,getsha256digest,erase,peek,poke,nop,xml
Dumping flash with sector count 7405568 as /dev/shm/stock-001v2.bin.
firehose - 
Reading from physical partition 0, sector 0, sectors 7405568
...

3. 从备份中恢复

edl wf    stock-001v2.bin

4. 生成xml分区信息

edl rl stock-UFI003 --genxml

会在 stock-UFI003 目录下生成 rawprogram0.xml 和 各个分区的img

如果要跳过userdata分区( to dump all partitions to directory dumps for device with emmc and skipping userdata partition, write rawprogram0.xml), 用下面的命令

edl rl   dump_dir   --skip=userdata --genxml

dump单独的boot_a分区,保存为boot.img (dump the partition “boot_a” to the filename boot.img for device with emmc)

edl r boot_a boot.img 

dump多个分区

edl r boot_a,boot_b     boot_a.img,boot_b.img

单独写入一个分区

edl   w   boot_a    boot.img

5. 模拟qfil线刷

edl qfil rawprogram0.xml patch0.xml image_dir

感谢:
https://github.com/bkerler/edl
https://github.com/u0d7i/uz801