作者归档:softsim

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

MTK NVRAM的读写

vendor/mediatek/proprietary/external/nvram

Android8.0以前,是通过AIDL的方式去获取一个名为【NvRAMAgent】的服务,然后通过该服务调用相关接口进行读写,获取NVRAM服务的方式如下:

IBinder binder = ServiceManager.getService("NvRAMAgent");
NvRAMAgent agent = NvRAMAgent.Stub.asInterface(binder);

读写方式如下:

fid = getProductInfoLid(); //这里获取SN的lid
byte[] ret = agent.readFile(fid); //通过lid去读取产品SN
agent.writeFile(fid, ret); //向NVRAM中写入SN

Android8.0以前,向NVRAM中读写SN或者MAC地址等信息时,一般是通过LID的方式,LID的值是在native中定义的。可以通过一个native函数获取到SN或者MAC地址对应的LID号,然后再进行读写。
LID号可以简单理解为SN或者MAC地址在NVRAM中的地址。

Android9.0 NVRAM的读写方式

到了Android8.0之后(这里以Android9.0系统为例),NVRAM的读写方式已经变更,不再通过AIDL去获取NVRAM服务,然后进行读写。而是通过HIDL的方式去获取服务来进行读写,方法如下:

import vendor.mediatek.harware.nvram.V1_0.INvram;

INvram agent = INvram.getService();

读写方法、

import com.android.internal.util.HexDump;
//读取:
String buffer = agent.readFileByName(path,size);
//size读取多长的数据,处理数据要注意直接通过字符串getByte获取的长度
//会是size的两倍+1(size是有效大小,根据Nvram存储的数据结构决定)
//+1是因为字符串带有结束符

//写入:
byte[] buff = HexDump.hexStringToByteArray(buffer.subString(0,buffer.length() - 1);
ArrayList dataArray = new ArrayList(size);

//修改buff数组中某一个字节的数据,然后重新写入到Nvram:
buff[index] = 0xff;
//將修改后的字节数组存到Array中
for(int index = 0; index < size; index ++) {
    dataArray.add(index,new Byte(buff[index]))
}
//最后需要:
agent.writeFileByNamevec(path,size,dataArray);

可以看到,Android8.0前后,NVRAM读写的不同点如下:
1.获取NVRAM服务的方式不一样,一个是AIDL,一个是HIDL;
2.NVRAM服务的接口不一致,一个是通过LID号的方式来进行读写,一个是通过节点的方式来进行读写;

这里需要注意的是,调用NvRam的新读写方法,这里要在Android.mk中新增依赖库,否则会编译不通过:

LOCAL_STATIC_JAVA_LIBRARIES += \
vendor.mediatek.hardware.nvram-V1.0-java-static

上层如何使用NVRAM接口

Android9.0 NVRAM的接口在上一节已经大概介绍,细心的朋友已经注意到,新的NVRAM接口是通过节点方式来读写的,那么这个节点在哪里呢?节点路径如下:

/vendor/nvdata/APCFG/APRDEB/WIFI  //wifi mac 地址
/VENDOR/nvdata/APCFG/APRDEB/BT_Addr //蓝牙 mac地址
/VENDOR/nvdata/APCFG/APRDEB/PRODUCT_INFO //SN号

调用如上接口,传入以上路径,就可以读写SN号和MAC地址。

但是,这里要说一个比较坑的事。那就是,如果通过这种方法去读写SN号,那么系统重启之后,SN号并没有更新成我们所烧录的那个SN号。
这是因为在MTK Android9.0中,有两种方式读写SN号:客户应用自己写或者通过MTK烧录工具烧写。专门提供给应用层读写的,路径就是如上提供的,但这个SN号读写之后并不会被系统所识别,也就是说不会被写入到序列号相关的属性中,如ro.serialno。另外一种通过烧录工具写入的,写入的地方为/dev/pro_info,重启后,这个可以被系统识别。

那如果即想要实现上层读写SN号,又能够被系统识别,那能否做到呢?答案当然是YES。
实现上层读写SN,系统也能识别,要想系统能够识别,则PRODUCT_INFO就不能出现,可以通过如下的宏来进行配置:

MTK_PRODUCT_INFO_SUPPORT = yes
则表示支持MTK的sn号烧录工具进行读写,然后该SN号可以在LK的时候被读出来,最后在init.cpp中赋值给ro.serial属性;
但这种配置无法支持应用对PRODUCT_INFO进行读写。


MTK_PRODUCT_INFO_SUPPORT = no
这种则表示支持应用对PRODUCT_INFO进行读写,但不支持MTK的sn号烧录工具读

https://www.cnblogs.com/guanxinjing/p/16869583.html
https://blog.csdn.net/u012932409/article/details/110393582
https://blog.csdn.net/qq_15347925/article/details/121682385
https://blog.csdn.net/u012932409/article/details/113172535

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

查询设置android的launcher

默认桌面/启动器,我喜欢开源的 Lawnchair

查询

cmd shortcut get-default-launcher

Launcher: ComponentInfo{com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher}

设置

cmd package set-home-activity app.lawnchair/app.lawnchair.LawnchairLauncher

 cmd package resolve-activity -c android.intent.category.HOME

setRoleHolderAsUser
addRoleHolderAsUser

联发科手机重要分区

nvcfg​分区

作用: This partition stores variable configs of NVDATA and NVRAM.​

nvdata​分区

作用: This partition stores variable data of secure and identifying infos for your device. Like IMEI, WiFi MAC, Bluetooth MAC, calibration data and others.​

ext4 文件系统

nvram​分区

作用: This partition stores persistent data of secure and identifying infos for your device. Like IMEI, WiFi MAC, Bluetooth MAC, calibration data and others.​

nvram称为binregion分区,是一个备份分区。 没有文件系统, 格式是自定义的。

persist​分区

作用: This partition stores persistent data for factory reset protection. Like google account and miaccount/micloud

proinfo​分区

作用: This partitions stores persistent data of default structure for NVRAM/RADIO/MODEM/BASEBAND

protect1 (或者叫 protect_f)​ 分区

作用: This partition stores variable data of SIM/RADIO/MODEM/BASEBAND settings and infos.​

protect2 (or protect_s)

Description: This partition stores variable data of SIM/RADIO/MODEM/BASEBAND settings and infos.​

Sim Lock 相关数据在 protect中
从WR8.W1244.DNR.WG.MP开始的Smart Phone版本都将SIM Lock存到protect_f和protect_s中了

挂载 于 /mnt/vendor/protect_f 和 /mnt/vendor/protect_s, 也都是ext4 文件系统

frp分区

flashinfo分区

otp 分区

seccfg


/dev/block/by-name/nvdata 挂载到 /mnt/vendor/nvdata 目录,文件系统是 ext4

APCFG/APRDEB/BT_Addr
        WIFI

md/NVRAM/
     BACKUP
     CALIBRAT
     NVD_DATA
     NVD_IMEI

Firmware Upgrade(固件更新) Factory Reset(恢复出厂设置)

只会擦除nvdata分区, 开机时AP 会将 binregion(nvram分区) 中备份的(1) CALIBRAT和 (2) NVD_IMEI两个目录还原到nvdata分区
具体的NVRAM LID 是IMEI、 barcode、Calibration数据和4G RF driver参数。 (3) 其他NVRAM LID恢复为code中的默认值。

protect_f和protect_s分区不会被擦除,所以SIM Lock保持不变。

如果binregion是空的,那么IMEI、 barcode、Calibration数据和4G RF driver参数也会用code中的默认值重新生成。


Download Only、OTA、SD upgrade 、Push modem

不会擦除任何分区。这种升级方式下,如果有修改code中的default值,那么需要将相应NVRAM LID的VERNO+1,才会生效,即将会利用code中的默认值重新生成新文件。
注意:这里所说的所有分区和任何分区均指Modem NVRAM的四个分区:nvdata分区、 protect_f分区、protect_s分区和binregion(nvram)分区。


具有NVRAM_CATEGORY_USER属性的NVRAM,会存到/mnt/vendor/nvdata/md/NVRAM/NVD_DATA 目录。
里面存的是普通NVRAM, 2G RF driver NVRAM,3G RF driver NVRAM。
(从92Modem开始有将部分4G RF driver NVRAM从CALIBRAT属性修改为USER属性,也就是从CALIBRAT目录移到了NVD_DATA目录)

具有NVRAM_CATEGORY_INTERNAL属性的NVRAM,会存到/mnt/vendor/nvdata/md/NVRAM/NVD_CORE/目录。
里面只存了NVRAM_EF_SYS_LID这只NVRAM,由于具有NVRAM_ATTR_MULTIPLE属性,该目录下有A,B两只文件。

具有NVRAM_CATEGORY_CALIBRAT属性的NVRAM,会存到/mnt/vendor/nvdata/md/NVRAM/CALIBRAT/目录。
里面存了barcode,calibration NVRAM,部分4G RF driver NVRAM。

具有NVRAM_CATEGORY_IMPORTANT属性的NVRAM,会存到/mnt/vendor/nvdata/md/NVRAM//NVD_IMEI/目录。
80/90/91Modem里面只存了NVRAM_EF_IMEI_IMEISV_LID(IMEI)这只NVRAM。
92Modem添加了NVRAM_EF_NVRAM_LOCK_LID(IMEI Lock)这只NVRAM。
93Modem新加入了5只NVRAM(C2K相关的3只NVRAM)。

具有NVRAM_CATEGORY_IMPORTANT_L4属性的NVRAM,会存到/mnt/vendor/protect_f/md/目录,如果该NVRAM具有NVRAM_ATTR_MULTIPLE属性,会有相应的B文件存在/mnt/vendor/protect_s/md/目录。

里面存了NVRAM_EF_SML_LID(SIM Lock), NVRAM_EF_SIM_LOCK_LID(也是SIM Lock,默认不会用到) ,NVRAM_EF_IMPT_COUNTER_LID(这只NVRAM可以不用care,没实用,从92Modem开始移到了NVD_DATA)。 93Modem新加入了3只NVRAM。

在flash上存储的NVRAM 对应的文件名是由文件名前缀,中间连接符和文件名后缀构成。文件名后缀就是NVRAM LID对应的VERNO。那么代码中将对应的NVRAM LID VERNO+1后,去flash上面读写这只文件的时候,就会发现不存在,那么就会用code中的默认值重新生成新文件

假设修改的是NVRAM_EF_IMS_PROFILE_LID这只NVRAM的默认值,它的文件名前缀MT79,升级前的NVRAM_EF_IMS_PROFILE_LID_VERNO号是000,修改默认值后将NVRAM_EF_IMS_PROFILE_LID_VERNO修改为001,那么Download Only升级前 /mnt/vendor/nvdata/md/NVRAM/NVD_DATA/目录中就有MT79_000这只文件。升级后,当读写到这只NVRAM的时候,是按MT79_001去NVD_DATA目录找这只文件,发现没有就会用代码中的默认值重新生成新文件MT79_001。

具有NVRAM_CATEGORY_IMPORTANT或NVRAM_CATEGORY_IMPORTANT_L4属性的NVRAM LID不能通过VERNO+1方式升级,否则就会有modem assert,如NVRAM_EF_IMEI_IMEISV_LID(IMEI)和NVRAM_EF_SML_LID(SIM Lock),这是我们的design,是对IMEI和SIM Lock的保护。

校准NVRAM LID和Barcode(NVRAM_EF_BARCODE_NUM_LID)也不能通过VENRO+1的方式升级,否则校准参数/Barcode就丢失了

高通手机重要分区

persist分区:帐号信息, DRM(数字版权管理)相关文件,传感器注册表,对我们的wifi,蓝牙,mac地址来说必不可少。

请注意,恢复出厂设置并不能清空persist分区,另外线刷包不包含persist分区,一旦出问题我们需要动手修复。

modem&radio基带分区:控制手机通讯功能的分区,此分区一旦损坏,通讯相关功能大概率会寄寄,具体表现为不读卡,丢失imei等

fsg

fsc

modemst1

modemst2

这4个分区必须备份

FRP(factory reset protect)

dsp

bluetooth

modem

persistsec

选择备份

misc分区

recovery用这个分区来保存一些关于升级的信息,应对升级过程中的设备掉电重启的状况。

bootloader启动的时候,会读取这个分区里面的信息,以决定系统是否进Recovery System 或 Main System。

fastboot –disable-verity –disable-verification flash vbmeta vbmeta.img

FRP 从字面意思可知,是用于对恢复出厂设置的保护。比如手机丢了,一般情况下小偷都会恢复出厂设置来绕过锁屏密码,这样就能使用了。但若是登录了 google 账号,此时在开机引导处还是要输入密码或者 google 账号才能进入,这就是使用了 FRP。若是从设置里触发恢复出厂设置,不会触发 FRP,可能 google 在设计时认为能进入到设置中操作,此时桌面已经解锁了,那大概率认为是机主所为,所以不会触发。

LabelPurpose of this partition
ModemPartition for modem
FscCookie partition to store Modem File System’s cookies.
SsdPartition for ssd diag module. stores the encrypted RSA keys
Sbl1Partition for secondary boot loader
Sbl1bakBack up Partition for secondary boot loader
RpmPartition for rpm image
RpmbakBack up Partition for rpm image
TzPartition for tz image
TzbakBack up Partition for tz image
HypPartition for hypervisor image
HypbakBack up Partition for hypervisor image
DspPartition for adsp dymanic loaders image
Modemst1Copy of Modem File System (Encrypted)
Modemst2Copy of Modem File System (Encrypted)
DDRPartition for DDR.
FsgGolden copy or backup of Modem File System (Encrypted). Also used to pre-populate the file system.
SecSec.dat contains fuse settings, mainly for secure boot and oem setting
SplashThe splash screen is displayed during the apps bootloader (also called the LK). The display driver in LK will read the splash image data from a separate eMMC partition named as ’splash’
AbootPartition for apps boot loader
AbootbakBack up Partition for apps boot loader
BootThis is the boot partition of your android device,It includes the android kernel and the ramdisk.
RecoveryThis is specially designed for backup. The recovery partition can be considered as an alternative boot partition
DevinfoDevice information including:iis_unlocked, is_tampered, is_verified, charger_screen_enabled, display_panel, bootloader_version, radio_version All these attirbutes are set based on some specific conditions and written on devinfo partition,.
SystemThis partition contains the entire Android OS, other than the kernel and the ramdisk. This includes the Android GUI and all the system applications that come pre-installed on the device
CacheThis is the partition where Android stores frequently accessed data and app components
PersistPartition entry for persist image. which contains data which shouldn’t be changed after the device shipped, for example: calibration data of chips(wifi, bt, camera, etc.), certificates and other security related files.
MiscThis partition contains miscellaneous system settings in form of on/off switches. These settings may include CID (Carrier or Region ID), USB configuration and certain hardware settings etc
KeystorePartition for keystore service.
ConfigPartition needed during display panel initialization. More info at Display_panel_configuration_in_Device_Tree
Oem“It is meant for storing OEM specific info. Customer in this case can decide whether he wants to keep this partition or not typically reserved partitions are kept for future use
LimitsPartition to store LMh params on 8976 target. LMh (Limits management) driver in SBL writes the LMh HW trimmed data into separate partition and uses the same data for later reboots
MotaBackup partition for M ota upgrade
DevcfgPartition needed by TZ for M upgrades.
DipPartition needed for SafeSwitch, feature (FR26255) designed to allow OEMs and carriers to address new smartphone theft bill issues.
MdtpPartition needed for SafeSwitch, feature (FR26255) designed to allow OEMs and carriers to address new smartphone theft bill issues.
UserdataPartition for userdata image
CmnlibVerified boot feature introduced in M needLK to load cmnlib corresponding partitions
KeymasterVerified boot feature introduced in M needs LK to load keymaster from corresponding partitions
SyscfgSyscfg is internal testing for Vmin and CPR characterization
McfgAll MBNs place holder in flash. Specific MBN would be loaded by mcfg image based on the SIM/Carrier.
MsadpUsed for modem debug policy
ApdpUsed for persisting the debug policy. “Debug policy” is used to better support development and debug on secure/fuse-blown devices One instance of the debug policy will be signed for the AP
DpoThis partition will store a policy override

在5G参数和IMEI写入Modemst1分区后将Modemst1分区的所有数据
复 制 到 F S G 分 区 进 行 保 存 ;

每 次 开 机 后 检 查Modemst1分区读写是否正常:

  1. 当Modemst1分区读写异常时,清空Modemst1分区;
  2. 当Modemst1分区为空时,将FSG分区复制至Modemst1分区进行恢复;
  3. 当Modemst1分区读写正常时,完成后续正常开机流程。

优点在于,充分利用了FSG分区的存储空间,作为Modemst1分区的影子存在,提高分区利用率的同时,做到物联网设备实现完全恢复出
厂设置。巧妙绕开了以往Modemst1分区在出厂后不能再reset的问题,避免因5G参数异常而必须回厂维修的缺陷。

NV的工作分区被高通平台定义为modemst1或modemst2,工作中modemst1 分区或modemst2分区等价

5G参数在出厂时一直保存在modemst1分区中,路由器 在运行过程中不停的和modemst1分区进行读写交互。

存储NV参数初始化值的分区是FSG分区,高通平台会制作一个包含NV参数初始化值的image文件,将这个image文件下载到FSG分区中

高通平台在启动过程中将FSG分区copy到modemst1分区的判断依 据是,判断当前NV工作modemst1分区是否为空,如果是空的,则将FSG分区中image 文件内的NV参数初始化值更新到modemst1分区中。

一般5G设备在空片第一次下载软件后,因为modemst1分区为空,所以FSG分区中的NV参数初始化值会写入modemst1分区中。然后在生产环节会向 modemst1分区写入校准参数、IEMI等5G重要参数,导致modemst1分区以后不能擦 除。因为modemst1分区不能擦除,自然不会再出现为空的状态,FSG分区的数据也 不会再有机会写入modemst1分区中。modemst1分区不能擦除的原因不是该分区不能 读写操作,而是擦除了该分区数据后,会失去出厂时写入的校准参数、IEMI等5G重 要参数。这些5G重要参数,不回厂维修的话,在物联网设备上没有任何备份。

首先对空 片进行软件下载和软系统安装,高通平台的工作机制在modemst1分区为空时,会将 FSG分区存有NV参数初始化值全部复制至modemst1分区中。

然后对modemst1分区进行各种出厂设置和参数写入,例如依次进行:单板写号、校准综测参数写入、单板电流参数写入、单板功能写入、整机电流写入、耦合测试参 数写
入、整机功能写入以及IMEI写入等操作

最后将上述操作完毕的modemst1分区复制至FSG分区,完成物联网设备5G参数的保存工作。

物联网设备在正常使用过程中不操作fsg分区 ,fsg分区参数始终是设备出厂时的初始状态。

物联网设备上网、注册5G网络时,根据需要读取modemst1分区的参数,运行状 态会被写入本设备的modemst1分区中,因此运行过程中不停地和modemst1分区进行 数据交互。

频繁的数据交互,自然大大提高了5G参数的出错几率。当读写modemst1分区出 现异常,读写异常影响到设备注册网络和modem正常运行,此时需要将modemst1分 区恢复到出厂时的状态。

用户只需将出现异常的物联网设备重新 开机两次即可解决,

第一次是清空Modemst1分区,第二次是完全Reset Modemst1分 区(也就是将fsg复制到moemst1)

每次开机后,物联网设备都检查modemst1分区的读写功能,此时 有三种可能,一是为空,二是正常,三是异常。


1)当Modemst1分区读写异常时,清空Modemst1分区;

2)当Modemst1分区为空时,将FSG分区复制至Modemst1分区进行恢复;
3)当Modemst1分区读写正常时,完成后续正常开机流程。

fastboot flash fsg fsg.mbn

fastboot erase modemst1

fastboot erase modemst2

高通平台EFS的制作

用来制作EFS的机器称为A,用来验证EFS的机器称为B。
1.
在modem_proc/core/storage/efs/inc/fs_config_i.h中增加以下三行:
#ifndef FEATURE_EFS_ENABLE_FACTORY_IMAGE_SECURITY_HOLE
#define FEATURE_EFS_ENABLE_FACTORY_IMAGE_SECURITY_HOLE
#endif
2.对于没有使能secure boot的device,在modem_proc/core/storage/fs_tar/src/fs_tar.c中增加一行:
#define FEATURE_FS_TAR_ALLOW_DUMMY_KEY
使能了secure boot的device,不需要定义上面的宏。
3.清除modem后重新编译,编译完成后进入common/build目录执行脚本python update_common_info.py,将新生成的common/build/bin/asic/NON-HLOS.bin文件通过fastboot烧进手机的modem分区。
4.重启手机,打开QPST Software Download软件,切换到Restore页面,USB连接手机,将QCN文件(该QCN文件配置了若干NV项)烧入手机,手机会自动重启。
5.重启就绪后,连接USB线至手机,确保QPST Software Download软件打开,将modem_proc/core/storage/tools/efsreadimage.pl文件放到C:\Users\yuntaohe\Desktop\EFS目录下,打开windows上的cmd窗口,进入C:\Users\yuntaohe\Desktop\EFS目录下,执行perl efsreadimage.pl -z,在当前目录下会生成新文件fs_image.tar.gz。
6.将fs_image.tar.gz上传到ubuntu的modem_proc/core/storage/tools/qdst/目录下,在该目录下执行python QDSTMBN.py fs_image.tar.gz,生成fs_image.tar.gz.mbn
7.将fs_image.tar.gz.mbn和modem_proc/build/ms/bin/8909.gen.prod/efs_image_meta.bin复制到modem_proc/core/bsp/efs_image_header/tools目录下,在该目录下执行python efs_image_create.py efs_image_meta.bin fs_image.tar.gz.mbn,生成fs_image.tar.gz.mbn.img。
至此,EFS制作完成,EFS的验证过程如下:
1.
对于没有使能secure boot的device,在modem_proc/core/storage/fs_tar/src/fs_tar.c中增加一行:
#define FEATURE_FS_TAR_ALLOW_DUMMY_KEY
使能了secure boot的device,不需要定义上面的宏。
2.清除modem后重新编译,编译完成后进入common/build目录执行脚本python update_common_info.py,重新生成common/build/bin/asic/NON-HLOS.bin文件。
3.生成全0的二进制文件:在linux下执行dd if=/dev/zero of=zero.bin bs=<modem_st1 size> count=1,modem_st1 size参看rawprogram0_unspare.xml文件:
  <program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”zero.bin” label=”modemst1″ num_partition_sectors=”3072″ physical_partition_number=”0″size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0x8680000″ start_sector=”275456″ />
本例中,modem_st1 size = 1536 * 1024 = 1572864。
4.将步骤2生成的NON-HLOS.bin和步骤3生成的zero.bin以及生成EFS的fs_image.tar.gz.mbn.img放入刷机包中。
5.修改刷机包中的rawprogram0_unspare.xml文件:
-<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”” label=”modemst1″ num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0x8680000″ start_sector=”275456″ />
-<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”” label=”modemst2″ num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0x8800000″ start_sector=”278528″ />
-<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”” label=”fsg” num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0xc008000″ start_sector=”393280″ />
+<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”zero.bin” label=”modemst1″ num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0x8680000″ start_sector=”275456″ />
+<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”zero.bin” label=”modemst2″ num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0x8800000″ start_sector=”278528″ />
+<program SECTOR_SIZE_IN_BYTES=”512″ file_sector_offset=”0″ filename=”fs_image.tar.gz.mbn.img” label=”fsg” num_partition_sectors=”3072″ physical_partition_number=”0″ size_in_KB=”1536.0″ sparse=”false” start_byte_hex=”0xc008000″ start_sector=”393280″ />
6.用QFIL刷机后,USB连接PC,用QXDM查看若干NV是否生效。

注:若B机器使能了secure boot,那么生成的EFS文件fs_image.tar.gz.mbn.img需要经过签名后放入刷机包。


在大多数比较新的高通设备上有两个分区,分别叫做xbl和abl。 xbl 作为高通平台上的UEFI固件,内含基于EFI的程序和驱动(如fastboot)。对于LA平台,如果开机时Fastboot模式未被触发,xbl将会立即加载abl。 abl 只在高通LA平台上存在,其源代码可以在CodeAurora上被找到,它内含一个名叫LinuxLoader.efi的EFI程序,用来加载并启动位于boot分区里的安卓Linux内核。 不幸的是,在进入市场的大多数零售设备上,xbl与abl均需经过厂商签名,我们不能对其作任何修改。因此,用设备自带的bl是不可能直接启动Windows的。 我们所采取的途径是将我们的UEFI固件伪装成一个Linux内核。通过在映像的开头添加一段代表Linux内核的代码,并在尾部附加设备树(device tree),abl就能很高兴地加载UEFI映像了。