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

查询设置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

Format All+Download

会擦除所有分区,所有NVRAM LID都会用code中的默认值重新生成。全擦后只有触发备份过,binregion中才会有备份内容,

否则binregion是空的(Flash默认的全0 or 全f)。

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就丢失了


NVRAM数据结构

MTK平台采用LDI(Logical Data Item)和LDI Table哈希表的数据结构来管理NV Item列表。

NVRAM LID table就是由一个个具体NVRAM LID的结构构成。

包括9项参数:LID,total_records,size, default_value,category,attr, fileprefix,fileverno,description。
其中 description,目前对于Smart Phone平台来说都是NULL,没有使用,是Feature Phone遗留下来的, 说明这个item的功能的

LID Entry
nvram_Itable_entry_struct {
   uint16 LID,
   uint16 total_records,
   unit32 size
   uint8  const * default_value,
   uint32 category,
   uint16  attr,
   char fileprefix[4+1],
   char fileverno[3+1],
   char * description
}

LID文件 组成
记录1
校验和1
记录2
校验和2

kal_bool nvram_external_read_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 *buffer, kal_uint32 buffer_size);

kal_bool nvram_external_write_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 *buffer, kal_uint32 buffer_size);

kal_bool nvram_external_reset_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint16 rec_amount);

PC META工具可以读写备份NV相关文件

vendor\mediatek\proprietary\external\nvram

https://github.com/hyperion70/HSPA_MOLY.WR8.W1449.MD.WG.MP.V16/blob/master/service/nvram/src/nvram_main.c


修改modem的对应的nvram的值分两种方式:

1.在code里修改默认值,这样修改的好处就是即使恢复出厂设置,或者升级软件,这个修改也是生效的;
修改默认值分两种case:

[1]这个feature是定义在除NVRAM_EF_SBP_MODEM_CONFIG_LID/NVRAM_EF_SBP_MODEM_DATA_CONFIG_LID的其他NVRAM里的:
以要disable bit 1: slotFormat4 and bit 4: supportEDPDCHPowerInterpolation in “r7_cap2”为例:
查看文档可以看到这个是定义在NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID里,要修改default值的话,首先贵司要了解NVRAM的一些基本内容,比如
nvram的dafault值在哪里定义的,首先搜这个LID的total这个参数,以NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID为例,就是搜索
NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_TOTAL,搜到包含这个NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_TOTAL的.c文件,而不是.h文件,贵司可以看
到这个是对NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID的元素的基本定义,其中第四个参数就是default值。

举例,NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID的元素定义就在nvram_data_items.c里,内容如下:
{
NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID,
NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_TOTAL,
NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_SIZE,
NVRAM_NORMAL(NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_DEFAULT),
NVRAM_CATEGORY_USER,
NVRAM_ATTR_AVERAGE,
#ifdef __UMTS_FDD_MODE__
“UM04”,
#endif
#ifdef __UMTS_TDD128_MODE__
“UM05”,
#endif
VER(NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID)
},
可以看到第4个参数是:NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_DEFAULT
点进这个default值看一下,贵司可以看到注释里有:r7_cap2:
/* r7_cap2: USIME_R7_Cap2
* bit 8~7: not used
* bit 6: supportofPSHandoverToGAN
* bit 5: supportofTxDivOnNonMIMOChannel
* bit 4: supportEDPDCHPowerInterpolation
* bit 3: supportForTwoDRXSchemesInPCH
* bit 2: supportForEDPCCHPowerBoosting
* bit 1: slotFormat4 */
0x00+ /* supportofPSHandoverToGAN: 0x20 supported, 0x00 not supported */
0x00+ /* supportofTxDivOnNonMIMOChannel: 0x10 supported, 0x00 not supported */
0x00+ /* supportEDPDCHPowerInterpolation: 0x08 supported, 0x00 not supported */
0x04+ /* supportForTwoDRXSchemesInPCH: 0x04 supported, 0x00 not supported */
0x02+ /* supportForEDPCCHPowerBoosting: 0x02 supported, 0x00 not supported */
0x01 /* slotFormat4: 0x01 supported, 0x00 not supported */
#endif /* __UMTS_R7__ */

比如说,我现在要disable bit 1,只要修改为:
/* r7_cap2: USIME_R7_Cap2
* bit 8~7: not used
* bit 6: supportofPSHandoverToGAN
* bit 5: supportofTxDivOnNonMIMOChannel
* bit 4: supportEDPDCHPowerInterpolation
* bit 3: supportForTwoDRXSchemesInPCH
* bit 2: supportForEDPCCHPowerBoosting
* bit 1: slotFormat4 */
0x00+ /* supportofPSHandoverToGAN: 0x20 supported, 0x00 not supported */
0x00+ /* supportofTxDivOnNonMIMOChannel: 0x10 supported, 0x00 not supported */
0x00+ /* supportEDPDCHPowerInterpolation: 0x08 supported, 0x00 not supported */
0x04+ /* supportForTwoDRXSchemesInPCH: 0x04 supported, 0x00 not supported */
0x02+ /* supportForEDPCCHPowerBoosting: 0x02 supported, 0x00 not supported */
#endif /* __UMTS_R7__ */
就可以了。

最后,修改完之后,要生效的话,需要升级这个nvram lid的verno,只需要找到这个nvram verno,然后加1就可以了。
以NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID为例,他的verno值就是NVRAM_EF_UMTS_USIME_RRC_DYNAMIC_CAP_LID_VERNO,如果它原来是018的话
,将其修改为019就可以了,或者贵司不想修改verno值的话,那total format之后升级,也可以达到更新nvram的目的。

[2]这个feature是定义在除NVRAM_EF_SBP_MODEM_CONFIG_LID/NVRAM_EF_SBP_MODEM_DATA_CONFIG_LID;
以Disable “MM_RETRY_ABNORMAL_LAI_TIMER” and Enable “PLMN_SEARCH_AFTER_LU_ABNORMAL”为例:
disable “MM_RETRY_ABNORMAL_LAI_TIMER”对应的是SBP_MM_DISABLE_RETRY_ABNORMAL_LAI,
Enable “PLMN_SEARCH_AFTER_LU_ABNORMAL”对应的是SBP_MM_PERFORM_PLMN_SEARCH_AFTER_LU_ABNORMAL,关于sbp的enum值可参见
sbp_md_feature_enum。

像这种修改SBP NVRAM的feature也是一样,先找到default值在哪里,以NVRAM_EF_SBP_MODEM_CONFIG_LID为例,他的defaut值就是
:NVRAM_EF_SBP_MODEM_CONFIG_DEFAULT,通过sbp_md_feature_enum我们可以知道SBP_MM_DISABLE_RETRY_ABNORMAL_LAI是第1个字节第4个bit(从1
数起),SBP_MM_PERFORM_PLMN_SEARCH_AFTER_LU_ABNORMAL对应第4个字节的第7个bit,要enable的话,只需要将这个bit改为1,要disable的话,只需
改为0。

举例,disable SBP_MM_DISABLE_RETRY_ABNORMAL_LAI的话,只需要找到NVRAM_EF_SBP_MODEM_CONFIG_DEFAULT,看到他的第1个字节第4个bit是这样
定义的:
#if defined(__MM_DISABLE_RETRY_ABNORMAL_LAI__)
0x08 |
#endif
之所以还有宏控制,是因为要兼容以前的版本,要disable的话,只需要将这三行全部注释掉:
//#if defined(__MM_DISABLE_RETRY_ABNORMAL_LAI__)
// 0x08 |
//#endif
同时要enable SBP_MM_PERFORM_PLMN_SEARCH_AFTER_LU_ABNORMAL的话 :只需要将#if defined(__MM_PERFORM_PLMN_SEARCH_AFTER_LU_ABNORMAL__)
0x40 |
#endif
修改为 :
0x40 |
也就是将判断条件删掉。

最后,nvram的修改要生效的话,需要修改nvram verno值,也就是升级: NVRAM_EF_SBP_MODEM_CONFIG_LID_VERNO。

2.使用meta修改,这种方法是修改现有值,而不是默认值,这样修改的弊端是在恢复出厂设置和升级版本之后,会恢复默认值,而不是meta修改后的
值,关于meta修改哪一个字节,可以通过方法1介绍的找default值的方法找到这个nvram的结构体定义,从而知道这个feature具体修改哪一个字节或
者bit。


Modem NVRAM 四个分区

(1) nvdata:手机运行过程中,使用(读写)的 NVRAM (除了存在protect_f和protect_s中的少数NVRAM)都是该分区的nvram文件。

存储着普通NVRAM数据、IMEI、barcode、Calibration数据等。

对应的modem path是Z:\NVRAM,对应的AP path是/data/nvram/md/NVRAM,这是在AP CCCI有做映射,

对应的都是nvdata分区modem NVRAM。NVRAM目录下有CALIBRAT、NVD_DATA、NVD_CORE和NVD_IMEI四个目录]

(Smart Phone中IMPORTANT目录是空的,是Feature Phone遗留下来的),后续第三点会对这四个目录进行介绍。

(2) protect_1、protect_2 :一般也称为 protect_f 和 protect_s 分区,protect_s是对 protect_f 中部分NVRAM的复制。

主要存储着SIM Lock数据。具有NVRAM_CATEGORY_IMPORTANT_L4属性的NVRAM LID会存在protect_f分区,

如果还具有NVRAM_ATTR_MULTIPLE属性,会存一个B文件到protect_s中。正常情况下protect_f中的A文件和protect_s中的B文件是相同的,

因为写的时候都会去写两个nvram文件。

(3) nvram:一般也称为 binregion 分区,是一个备份分区,备份具有 NVRAM_CATEGORY_IMPORTANT 和 NVRAM_CATEGORY_CALIBRAT 属性的 NVRAM LID,

备份的也就是CALIBRAT和NVD_IMEI两个目录,具体有IMEI、 barcode、Calibration数据和4G RF driver参数这些NVRAM。

备份可以由META Tool触发 ,ATE校准完会自动触发备份,SN Writer tool写完号后也会自动触发备份。


升级方式对各个NVRAM分区的影响:

因为Format All+Download会擦除所有分区,所以修改任何NVRAM的默认值,然后 Format All+Download升级,均可生效。

因为Firmware Upgrade 只会擦除nvdata分区,所以修改普通NVRAM(NVRAM_CATEGORY_USER属性的NVRAM)的默认值,然后Firmware Upgrade升级,可以生效。

但是修改具有备份的NVRAM(IMEI、 barcode、Calibration数据和4G RF driver)或者存在protect_f/protect_s分区的SIM Lock的默认值,是不会生效的。

如修改备份的4G RF driver NVRAM LID的默认值,Firmware Upgrade升级不生效 :

这是因为备份的4G RF driver NVRAM LID具有NVRAM_CATEGORY_CALIBRAT属性,在binregion中有备份,当Firmware Upgrade升级擦除nvdata分区后,开机后会从binregion中还原回来。

针对这种情况,想要修改默认值生效,Firmware Upgrade升级也是需要将相应的NVRAM LID VERNO+1。

因为Download Only、OTA、SD upgrade 、Push modem不会擦除任何分区,所以如果有修改code中的default值,想升级后生效,那么需要将相应NVRAM LID的VERNO+1,

才会生效,这样将会利用code中的默认值重新生成新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升级前/data/nvram/md/NVRAM/NVD_DATA/目录中就有MT79_000这只文件。

升级后,当读写到这只NVRAM的时候,是按MT79_001去NVD_DATA目录找这只文件,发现没有就会用代码中的默认值重新生成新文件MT79_001。

[1]注意:并不是所有的NVRAM LID都可以通过VERNO+1的方式来升级:

(1)具有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的保护。

(2)校准NVRAM LID和Barcode(NVRAM_EF_BARCODE_NUM_LID)也不能通过VENRO+1的方式升级,否则校准参数/Barcode就丢失了,不过一般也不会有这类需求。

(3)部分RF NVRAM LID也不能通过VERNO+1的方式升级,目前明确的有CA组合和RF support band不行,如有修改必须全擦升级。


如何在AP端读写Modem NVRAM

AP端去读写Modem NVRAM,可以通过AP端向modem发送AT Command,而modem端需要客制化一条AT Command,同时在command handler中调用接口进行读写nvram。

步骤如下:

(1)AP端向Modem透传AT命令,AP端具体做法可以参考FAQ19648【如何发送AT命令】。

(2)Modem端对AT命令进行客制化,具体可以参考FAQ17997【[AT Command]Smart Phone如何客制化AT command】。

(3)command handler内部通过调用nvram_external_read_data/nvram_external_write_data来读写。

kal_bool nvram_external_read_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 *buffer, kal_uint32 buffer_size);

kal_bool nvram_external_write_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 *buffer, kal_uint32 buffer_size);

注意:从91Modem开始读写IMEI和SIM Lock只能使用nvram_external_secure_read_data/nvram_external_secure_write_data接口,不能使用nvram_external_read_data/nvram_external_write_data。

nvram_errno_enum nvram_external_secure_read_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 buffer, kal_uint32 buffer_size, void reserved_ptr);

nvram_errno_enum nvram_external_secure_write_data(nvram_lid_enum LID, kal_uint16 rec_index, kal_uint8 buffer, kal_uint32 buffer_size, void reserved_ptr);

高通手机重要分区

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映像了。

sprd nvmerge

nvmerge是用于处理nvitem分区升级的工具。可以将手机中的某些需要保留的nv项
(如校准参数)备份下来,保证升级后不会被重置。这里的nv项,指的是与modem相
关的一些参数,这些参数均保存在nvitem分区。

其中具体的要备份的items保存在nvmerge.cfg 文件中,这个文件也会被打包进升级
包,里面的内容可以根据需要进行增减,以达到备份某些特殊数据的目的,比如IMEI号
等。

此文件在Android 4.4平台上是存放在bootable/recovery/nvmerge/下面,

在Android5.1以后平台上是存放在device/sprd/XXX/下面,

XXX代表芯片系列,如scx35l, iwhale2,sharkl2等。

/tmp/nvmerge

/tmp/nvmerge.cfg

/dev/block/platform/sprd-sdhci.3/by-name/wfixnv1

/cache/wnvitem.bin

/cache/merged_wnvitem.bin

0x40000

write_emmc_image(“/cache/merged_wnvitem.bin”, “/dev/block/platform/sprd-sdhci.3/by-name/wfixnv1”);

write_emmc_image(“/cache/merged_wnvitem.bin”, “/dev/block/platform/sprd-sdhci.3/by-name/wfixnv2”);

如何区分 M.2 卡之间的差异

M.2 平台是多用途的平台,支持多种 M.2 卡。

M.2 卡可以在 M.2 平台内配置不同的硬件和功能。(例如:为了满足计算机的各个需求并为用户提供一系列配置选项。)

物理尺寸是最容易观察到的卡之间的差异。M.2 标准使用数字命名约定来标识卡的特定属性和功能。

M.2 卡可能包括:

Wi-Fi 和蓝牙卡的组合
使用 SATA 总线的 SanDisk SSD
也使用 PCIe 总线的 Intel SSD
可使用多达四个 PCIe 通道 (x4) 的 Samsung SSD


数字命名约定用于定义卡的大小和功能,方法如下图

前两位数字指定宽度(以毫米为单位)
随后的两(或三)位数字指定长度(以毫米为单位)
最后一个字母用于指定密钥、接口和总线(双插槽卡将使用两个字母)

可以看出,5G NR模块,只能用B Key类型

可用尺寸接口常见用途
A1630、2230、3030PCIE x2/USB 2.0/I2C/DisplayPort (DP) x4WiFi/蓝牙/WWAN
B2230、2242、2260、2280、3042、22110PCIe x2/USB 2.0/USB 3.0/音频/PCM/IUM/SSIC/I2CSATA/PCIe x2/SSD
E1630、2230、3030PCIe x2/USB 2.0/I2C/SDIO/UART/PCMWiFi/蓝牙/WWAN
M2230、2242、2260、2280、22110PCIe x4/SATAPCIe x4/SSD

主板上的 M.2 插槽

所有主板 M.2 插槽均为 22 毫米宽,即使是支持 30 毫米宽卡的插槽

两种类型的 M.2 SSD(SATA 和 PCI-E)

  1. 采用 SATA 总线的 SSD 倾向于使用 B-key 接口
  2. 采用 PCIe x2 总线的 SSD 可以使用 B-key 和 M-key 接口(以最大限度地提高兼容性)

提醒:采用 PCIe x4 总线的 SSD(快速的 SSD)使用 M-key 接口,因为它是唯一支持 PCIe x4 通道的接口。

M.2 SSD 之间的性能差异是什么?

一般而言:

  • 采用 SATA 总线的 M.2 SSD 在性能方面与采用 SATA 接口的 SSD 相似,但尺寸更小
  • 采用 PCIe 总线的 M.2 SSD 比采用 SATA 总线的 M.2 SSD 快
  • 采用 PCIe 总线的 M.2 M-key 接口的 SSD 比采用 PCIe 总线的 M.2 B-key 接口的 SSD 快

总而言之:不管是何种外形规格,采用 SATA 总线的 SSD 均能够以 SATA 速度运行。PCIe 总线比 SATA 总线快,而 M-key 接口的 SSD 比 B-key 接口的 SSD 快。

提醒:所有这些性能指标都与总线和总线接口相关。此外,还有其他一些此处未讨论的因素会决定单个 SSD 的性能。