LG V35 刷 LineageOS(上):備份與升級 Android 10
手邊有一支 LG V35(LM-V350AWM,AT&T 硬體),之前交叉刷成美版開放版的 Pie 20f。這次想刷 LineageOS 22.2(Android 15)當 App 測試機,但 LineageOS 官方安裝說明要求先裝「最新的原廠 Android 10」,而 AT&T 的 Android 10 只透過 OTA 推送,沒有 KDZ 可以用 LGUP 刷。
最後是用開源的 edl 工具進高通 EDL(9008)模式,先把手機獨有的分區完整備份,再把網友從手機讀出來的 AT&T 30e 原廠分區寫進 b 槽。這篇是上集,寫到 Android 10 開機成功,刷 LineageOS 留到下集。
這是我在「一支手機」上的實作紀錄,不是保證成功的教學。刷寫手機底層分區可能讓手機開不了機(變磚)、遺失 IMEI 或 Wi-Fi 校準資料,也拿不到原廠保固。請自行承擔風險,動手前先完整備份,並確認自己的狀況跟下面的「適用範圍」相符。
適用範圍
| 項目 | 本文的環境 |
|---|---|
| 手機型號 | LG V35 ThinQ LM-V350AWM(AT&T 硬體) |
| 開始時的韌體 | 美版開放版 Pie V350ULM20f(ARB00) |
| 之前刷過的韌體 | AT&T 原廠 → 剛買到時刷成美版開放版 Oreo V350ULM10h(V350ULM10h_00_NAO_US_OP_1130.kdz)→ 後來因為 Roon 的 App 不支援 Android 8,再刷成 Pie 20f |
| Bootloader | 已解鎖(本文不含解鎖步驟) |
| 電腦 | macOS 27.0.1,Apple Silicon(arm64) |
| 工具 | bkerler/edl commit 1cda1a6(2026-09-19)、Python 3.14.6、libusb 1.0.30 |
| 日期 | 2026-10-19 |
沒測試過、不保證適用:
- 其他 V35 型號(LM-V350N 韓版、LM-V350ULM、LM-V350EAW…):分區表可能不同,已知韓版和美版的分區大小不一樣
- 從 AT&T 原廠韌體、Oreo、韓版韌體開始
- Windows/Linux 電腦、其他版本的 edl
- 還沒解鎖的手機
各步驟的驗證狀態:
| 步驟 | 狀態 |
|---|---|
| 工具準備 | ✅ 已在真機驗證 |
| 進入 EDL | ✅(成功率看手感,我試了很多次) |
| 讀分區表 | ✅ |
| 完整備份(做法) | ✅;整理後的 read_lun 指令 🧪 |
| 驗證備份腳本 | ✅(已用實際備份執行) |
| 寫入前自我檢查腳本 | ✅(已用實際分區表和 30e 映像執行) |
| 寫入 b 槽(做法) | ✅;整理後的 write_lun 指令 🧪 |
| Android 10 開機 | ✅ |
| LineageOS | ⏳ 下集 |
✅ 是在上面的環境實際執行並成功;🧪 是指令語法和參數已經用假的 edl 乾跑測試過,跟實際執行時一致,但這份整理後的指令本身還沒接真機跑過;⏳ 是還沒做。
目標與限制
- 目標:LineageOS 22.2,保留 Hi-Fi Quad DAC
- 用途:App 測試機,只用 Wi-Fi,沒有 SIM
- 工具只用開源、看得到原始碼的版本。之前下載過假的「修改版 LGUP」,結果帳號被盜,這次不想再賭
- 每一步寫入前先完整備份
- 不覆蓋手機獨有的分區:persist、ftm、modemst1/2、fsg、fsc 等
手機現況
2026-10-19 用 adb 讀到的:
| 項目 | 值 |
|---|---|
| ro.boot.vendor.lge.model.name | LM-V350AWM |
| ro.vendor.lge.swversion | V350AWM20f |
| ro.vendor.lge.factoryversion | LMV350AT-00-V20f-LAO-COM-FEB-18-2020-ARB00+0 |
| 對應的 KDZ | V350ULM20f_00_NAO_US_OP_0218.kdz |
| ro.boot.verifiedbootstate | orange(已解鎖) |
| ro.boot.slot_suffix | _a |
| root | 無 |
| 電池(用 charge counter 估) | 約 2,350/3,300 mAh,約 71% |
Bootloader 是 2024 年照 Nemo 社區的教學,用 EDL 加工程版 abl 解鎖的,當時用的 firehose 檔還留在 NAS 上。
工具準備
# 開源 EDL 工具(GPL-3.0)
git clone --depth 1 https://github.com/bkerler/edl.git # commit 1cda1a6
python3 -m venv venv && ./venv/bin/pip install -r edl/requirements.txt
# 只抓需要的 LG SDM845 firehose
curl -L -o edl/Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin \
https://raw.githubusercontent.com/bkerler/Loaders/main/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin
- 安裝前先看過 edl 的相依套件和網路呼叫:只有附屬工具會連網,主程式不會
- firehose 核對了兩件事:git blob 雜湊和 GitHub 上一致(
2ed52868…);SHA-256 是82808c5e…1da13,跟 2024 年解鎖時用的prog_ufs_firehose_Sdm845_lge.elf完全相同
為什麼不能直接用 LGUP
| 檔案 | 跟手機相不相符 | 格式 | LGUP 能不能刷 |
|---|---|---|---|
| V350AWM30e.zip(Android 10) | ✅ 同硬體 | 網友從手機讀出的分區映像包 | ❌ 只能用 EDL 一個一個寫 |
| V350ULM20f KDZ(Pie) | ✅ 手機原本的韌體 | LG 官方 KDZ | ✅ 救機用 |
| V350N 30a/30d KDZ(Android 10) | ⚠️ 韓版 | KDZ | 要用修改版 LGUP 的 PARTITION DL |
有同型號的官方 KDZ 時,用 LGUP 的 Upgrade 模式就等於官方更新,風險低。AWM 沒有 Android 10 的 KDZ,所以只能走 EDL。
另外要注意,KDZ 本身就包含 persist、ftm、mpt 這些映像(飞羽 ROM 從韓版 KDZ 抽出的檔案裡就看得到),PARTITION DL 如果全選,會用 KDZ 裡的內容蓋掉手機原本的。不在 KDZ 裡的只有 modemst1/2、fsg。
研究過但沒採用的兩條路:
- 飞羽 ROM(Nemo 社區的韓版 30a/30c 卡刷包):讀了它的 update-binary,發現會用 dd 覆蓋 persist、ftm、mpt、misc、persdata 這些手機獨有的分區,不用
- 韓版 V350N 30d KDZ:要用修改版 LGUP,而且有人回報刷到美版後 Wi-Fi、網路壞掉,不用
下載的韌體
AT&T V350AWM30e(Android 10)分區包
- 來源:XDA 上 jacob019 分享的「V350AWM30e ATT_US stock & rooted partitions」
- 3,698,834,161 bytes,SHA-256
cb962c92…f876,unzip -t沒有錯誤 - 裡面沒有任何 Windows 執行檔:
partitions/是原廠分區,modified/是改過的 boot、dtbo、vendor(Magisk、TWRP、關 verity),另外還有sources/、notes.txt、load.sh - vendor 指紋
lge/judyp_lao_com/judyp:10/QKQ1.191222.002,安全性更新 2020-04-01
讀完 notes 和討論串記下的重點:
- 作者只寫 b 槽,a 槽留著當退路
OP.bin(650 MB)比手機的 OP 分區(450 MB)大,不能刷persist.bin是作者手機的校準資料,絕對不能刷- 有人回報 modified 的 boot 讓手機變磚,所以只用原廠映像
- ARB(防降級)值從映像字串讀不到;間接證據是 XDA 有人從 30e 刷回 Pie 20f 成功
LineageOS 官方檔案
全部核對過官方的 SHA-256:
- lineage-22.2-20261015-nightly-judyp-signed.zip
- boot.img(Lineage Recovery)、dtbo.img、vbmeta.img
- MindTheGapps-15.0.0-arm64-20260915_032013.zip
- copy-partitions-20220613-signed.zip(SignApk 簽章)
救機用
NAS 上留著兩個 KDZ:V350ULM20f(Pie 20f,跟手機現在一樣)和 V350ULM10i(Oreo)。
進入 EDL(9008)
這是花最多時間的一步。失敗的嘗試:
| 嘗試 | 結果 | USB(vid:pid) |
|---|---|---|
adb reboot edl | 進 LG Download 模式,LG 把它導向 LAF(無效) | 1004:633A |
| 按住電源+音量下,黑屏時「放開」再連按音量上 | Download 模式,試了 2 次(無效) | 1004:633A |
| 關機,按住音量上+音量下再插 USB | 關機充電畫面(無效) | 1004:6300 |
| 同上,時機稍早 | LG 的 IMEI 條碼資訊畫面,無害,按電源離開(無效) | 1004:633E → 斷線 |
成功的按法(14:00:23 偵測到 05C6:9008):
- 手機接著 Mac,從 Download 模式或開機狀態開始都可以
- 按住電源+音量下,全程不要放
- 螢幕一黑,另一隻手指快速連按音量上
- 電腦偵測到 9008 之後,才放開所有按鍵
一開始我照英文教學,理解成「黑屏時放開電源+音量下」,所以一直進到 Download 模式。後來看 Nemo 社區的中文原文,上面寫得很清楚:「切記同時不要鬆開音量減和電源鍵」。
成功那次的 USB 變化:
13:59:55 1004:633A Download 模式
14:00:04 斷線 → 1004:633E
14:00:21 斷線(黑屏)
14:00:23 05C6:9008 EDL ✔
兩隻手按著按鍵很難同時盯螢幕,所以寫了一個小迴圈,偵測到 9008 就用 Mac 的語音念出來:
# 偵測到 Qualcomm 9008 時用語音通知,就不用盯著螢幕
while :; do
ioreg -p IOUSB -l -w0 | grep -q '"idProduct" = 36872' && { say "E D L"; break; }
sleep 0.3
done
36872 就是 0x9008(1478 是 0x05C6)。Mac 靜音的時候聽不到 say,後來另外用 osascript 跳出視窗:
osascript -e 'display alert "偵測到 EDL!" message "立刻放開所有按鍵,不要再碰手機。" giving up after 15'
另外兩個坑:
- 電源+音量下按滿約 10 秒會強制重開,在 EDL 裡也一樣,所以偵測到 9008 就要立刻全部放開
- 手機在 EDL 裡進入錯誤狀態後,只能重新進一次 EDL(按電源+音量下重開)
讀分區表(只讀)
python edl.py printgpt --memory=ufs --loader=Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin
- CPU 是 SDM845,PK_HASH 開頭
1030cd12ce708f29,跟 loader 檔名一致 - firehose 支援 14 個指令:program、read、nop、patch、configure、setbootablestoragedrive、erase、power、firmwarewrite、getstorageinfo、benchmark、emmc、ufs、fixgpt
- UFS 是東芝 THGAF8G9T43BAIRB0300,7 個 LUN,block 4096
- OP_a 大小 0x1c200000(450 MB),比 30e 的 OP.bin(650 MB)小,證實 OP 不能刷
各 LUN 的完整分區清單(照分區表順序):
- LUN0:mpt、drm、sns、ssd、persist、misc、ftm、power、encrypt、eksst、rct、fota、srtc、pstore、els、carrier、persdata、oem_a、oem_b、vendor_a、vendor_b、system_a、system_b、OP_a、OP_b、userdata、grow
- LUN1:xbl_a、xbl_config_a
- LUN2:xbl_b、xbl_config_b
- LUN3:ALIGN_TO_128K_1、cdt、ddr
- LUN4:aop_a、tz_a、hyp_a、modem_a、mdtpsecapp_a、mdtp_a、abl_a、dsp_a、keymaster_a、akmu_a、boot_a、cmnlib_a、cmnlib64_a、devcfg_a、qupfw_a、laf_a、vbmeta_a、dtbo_a、storsec_a、raw_resources_a、sid_a、aop_b、tz_b、hyp_b、modem_b、mdtpsecapp_b、mdtp_b、abl_b、dsp_b、keymaster_b、akmu_b、boot_b、cmnlib_b、cmnlib64_b、devcfg_b、qupfw_b、laf_b、vbmeta_b、dtbo_b、storsec_b、raw_resources_b、sid_b、sec、devinfo、dip、apdp、msadp、spunvm、limits、toolsfv、logfs、sti、logdump
- LUN5:ALIGN_TO_128K_2、modemst1、modemst2、fsg、fsc(IMEI 和網路設定)
- LUN6:frp
完整備份(只讀)
本來打算用 rl 一次讀完整機,跳過 userdata,用 --genxml 產生之後可以整機寫回的 rawprogram xml:
python edl.py rl ~/LG-V35-backup/dumps \
--memory=ufs --skip=userdata --genxml \
--loader=Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin
結果一口氣讀失敗,前後進了 6 次 EDL 才讀完:
| 第幾次進 EDL | 做法 | 結果 |
|---|---|---|
| 1(14:00) | rl 整機讀 | 讀了約 187 MB,到 els 出現 USBError(32, 'Pipe error'),手機進入「Sahara error state」(無效) |
| 2(14:11) | 偵測到後隔約 1 分鐘才開始讀 | 手機已經自己重開回 Android,可能等太久,或電源鍵按太久(無效) |
| 3(14:15) | 每個分區各呼叫一次 edl r | modemst1/2、fsg、fsc、devinfo、sec、abl_a/b 成功;讀 laf_a 時手機重開,推測是聽到提示後電源鍵還按著超過 10 秒 |
| 4(14:21) | 同上 | laf_a/b、boot_a 成功;boot_b 讀到一半 Pipe error |
| 5(14:25) | 同上 | cdt、ddr、frp、carrier、persdata 成功;els 又失敗(Input/Output Error) |
| 6(14:37) | 同一個 LUN 的分區用一次 edl r 讀完(逗號分隔清單) | 32 個分區一次成功;接著讀 b 槽的韌體也成功,讀到 modem_a 時又 Pipe error(有效) |
每讀一個分區就重新呼叫一次 edl,等於每次都重新連線,在這台手機上很容易觸發 USB Overflow/Pipe error。bkerler/edl 的 issue #752 也有人回報「第二次連線就 overflow」。改成一個 LUN 呼叫一次之後穩定很多。
整理後的備份指令
下面是把上面的做法整理成可以直接跑的版本。語法和參數已經用假的 edl 乾跑測試過,但這份指令本身還沒接真機跑過(🧪),下次進 EDL 時會實際跑一次確認。
在 edl 的資料夾裡執行,手機要已經在 EDL(9008)。edl r 可以用逗號一次指定多個分區和對應的檔名,所以先寫一個小函式把清單接起來:
cd edl # bkerler/edl 的資料夾
PY=../venv/bin/python # 安裝 edl 相依套件的 venv
LOADER=Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin
OUT=~/LG-V35-backup/dumps # 備份存放位置
# 讀取同一個 LUN 的多個分區(只呼叫 edl 一次),已經存在的檔案會跳過
read_lun() {
local lun=$1; shift
local names=() files=() p
mkdir -p "$OUT/lun$lun"
for p in "$@"; do
[ -s "$OUT/lun$lun/$p.bin" ] && continue
names+=("$p"); files+=("$OUT/lun$lun/$p.bin")
done
[ ${#names[@]} -eq 0 ] && return 0
(IFS=,; "$PY" edl.py r "${names[*]}" "${files[*]}" --memory=ufs --lun="$lun" --loader="$LOADER")
}
# 1. 手機獨有:IMEI/網路設定
read_lun 5 modemst1 modemst2 fsg fsc
# 2. 手機獨有:校準、出廠、解鎖狀態(els 讀不到,不列)
read_lun 0 mpt drm sns ssd persist misc ftm power encrypt eksst rct fota srtc pstore carrier persdata
read_lun 3 cdt ddr
read_lun 6 frp
read_lun 4 devinfo sec apdp msadp spunvm limits toolsfv dip logfs sti
# 3. 開機鏈(a/b 兩槽)
read_lun 1 xbl_a xbl_config_a
read_lun 2 xbl_b xbl_config_b
read_lun 4 abl_a abl_b laf_a laf_b boot_a boot_b vbmeta_a vbmeta_b dtbo_a dtbo_b
# 4. 韌體(a/b 兩槽)
read_lun 4 tz_a aop_a hyp_a keymaster_a cmnlib_a cmnlib64_a devcfg_a qupfw_a storsec_a \
sid_a akmu_a raw_resources_a mdtpsecapp_a mdtp_a dsp_a
read_lun 4 tz_b aop_b hyp_b keymaster_b cmnlib_b cmnlib64_b devcfg_b qupfw_b storsec_b \
sid_b akmu_b raw_resources_b mdtpsecapp_b mdtp_b dsp_b
幾個要注意的地方:
- 一次 EDL 裡呼叫 edl 的次數還是越少越好,我實測到第 5 次呼叫時又出現 Pipe error。中斷後重新進 EDL,再跑同樣的指令就好,已經讀好的會自動跳過
- 讀到一半中斷的檔案大小會不對,重跑前要先刪掉(或像下一節那樣比對大小)
els(LUN0,16 MB)兩次都讀到 3~4 MB 就失敗,判定讀不到,跳過。讀到一半的檔案另外留著,不列入雜湊清單
驗證備份
拿 printgpt 的輸出當基準,檢查每個分區有沒有備份到、大小對不對、內容是不是全空:
# 先把分區表存成檔案(手機在 EDL 時執行一次即可)
cd edl
../venv/bin/python edl.py printgpt --memory=ufs \
--loader=Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin > ~/LG-V35-backup/printgpt.log
#!/usr/bin/env python3
# verify_backup.py <printgpt.log> <dumps 資料夾>
import os, re, sys
log, dumps = sys.argv[1], sys.argv[2]
lun, parts = None, []
for line in open(log):
m = re.match(r'Parsing Lun (\d+)', line)
if m:
lun = int(m.group(1)); continue
m = re.match(r'(\S+):\s+Offset 0x[0-9a-f]+, Length 0x([0-9a-f]+)', line)
if m and lun is not None:
parts.append((lun, m.group(1), int(m.group(2), 16)))
missing, wrong_size, empty = [], [], []
for lun, name, length in parts:
if name == 'userdata':
continue
path = os.path.join(dumps, f'lun{lun}', f'{name}.bin')
if not os.path.exists(path):
missing.append(f'lun{lun}/{name}'); continue
size = os.path.getsize(path)
if size != length:
wrong_size.append(f'lun{lun}/{name} ({size} != {length})'); continue
data = open(path, 'rb').read()
if not any(data[i:i+4096].strip(b'\x00') and data[i:i+4096].strip(b'\xff')
for i in range(0, len(data), 4096)):
empty.append(f'lun{lun}/{name}')
print('missing:', missing)
print('wrong size:', wrong_size)
print('all zero/FF:', empty)
python3 verify_backup.py ~/LG-V35-backup/printgpt.log ~/LG-V35-backup/dumps
# 最後產生雜湊清單,之後可用 shasum -c 檢查備份有沒有損壞
cd ~/LG-V35-backup/dumps && find . -name '*.bin' -print0 | sort -z | xargs -0 shasum -a 256 > ../SHA256SUMS.txt
- 「wrong size」通常是讀到一半中斷,刪掉那個檔再重讀
- 「all zero/FF」不一定是錯,有些分區本來就是空的,但要回頭看讀取紀錄有沒有錯誤
備份結果
77 個分區,加上 2 個分區表檔(gpt_main0、gpt_backup0),約 1 GB。SHA-256 清單涵蓋這 79 個檔案(讀到一半的 els 不列入),連同完整分區表和 rawprogram xml 一起存在 Mac 上:
| 類別 | 分區 | 狀態 |
|---|---|---|
| IMEI/網路設定(4) | modemst1、modemst2、fsg、fsc | ✅(fsc 本來就是空的) |
| 校準/出廠(15) | persist、ftm、mpt、cdt、ddr、carrier、persdata、power、drm、sns、ssd、rct、srtc、fota、pstore | ✅ |
| 解鎖狀態(2) | devinfo、sec | ✅ |
| 開機鏈(a/b 兩槽,14) | xbl、xbl_config、abl、laf、boot、vbmeta、dtbo | ✅ |
| 韌體(a/b 兩槽,30) | tz、aop、hyp、keymaster、cmnlib、cmnlib64、devcfg、qupfw、storsec、sid、akmu、raw_resources、mdtp、mdtpsecapp、dsp | ✅ |
| 其他小分區(12) | frp、apdp、msadp、spunvm、limits、toolsfv、dip、logfs、sti、misc、encrypt、eksst | ✅ |
全空(全 0 或全 FF)的有 12 個:misc、eksst、cdt、fsc、mdtpsecapp_a、mdtpsecapp_b、mdtp_a、mdtp_b、dip、spunvm、toolsfv、sti。大小正確、讀取也沒有錯誤,判定本來就是空的。
沒備份的有:
- system、vendor、OP、oem、modem 的 a/b 兩槽,共約 12 GB,NAS 上的 Pie 20f KDZ 裡都有
- logdump(系統紀錄)、grow、ALIGN_TO_128K_*(對齊用的空分區)
- 讀不到的 els
手機獨有、下載不到的分區都已經備份到了。
把 30e 寫進 b 槽
準備:
- 從 30e 包只解出
partitions/,排除OP.bin(比分區大)和persist.bin(原作者手機的校準資料) - 不用
modified/,只用原廠映像 - 26 個映像依 GPT 對到
_b分區,大小全部跟分區長度相同 - 依 LUN 分資料夾:LUN4(21 個)、LUN2(xbl_b、xbl_config_b)、LUN0(oem_b、system_b、vendor_b)
準備檔案,把 30e 的映像改名成 _b 分區名,依 LUN 分資料夾:
ZIP=~/Downloads/V350AWM30e.zip
STAGE=~/LG-V35-work/30e_slot_b
mkdir -p "$STAGE" && cd "$STAGE"
# 只解出原廠分區,排除 OP 與 persist
unzip -q "$ZIP" 'V350AWM30e/partitions/*' \
-x 'V350AWM30e/partitions/OP.bin' 'V350AWM30e/partitions/persist.bin'
P=V350AWM30e/partitions
mkdir -p lun0 lun2 lun4
for n in oem system vendor; do mv "$P/$n.bin" "lun0/${n}_b.bin"; done
for n in xbl xbl_config; do mv "$P/$n.bin" "lun2/${n}_b.bin"; done
for n in abl akmu aop boot cmnlib cmnlib64 devcfg dsp dtbo hyp keymaster laf \
mdtp mdtpsecapp modem qupfw raw_resources sid storsec tz vbmeta; do
mv "$P/$n.bin" "lun4/${n}_b.bin"
done
寫入前一定要先做自我檢查:拿你自己手機的分區表比對每個映像,任何一個不符就不要寫,代表你的手機跟本文的環境不同。
#!/usr/bin/env python3
# precheck_30e.py <printgpt.log> <staging folder>
# Before writing: every staged image must map to an existing partition on THIS phone,
# sit on the expected LUN, and be EXACTLY the partition's size. Any mismatch -> do not write.
import os, re, sys
log, stage = sys.argv[1], sys.argv[2]
NEVER = {'persist', 'ftm', 'mpt', 'misc', 'persdata', 'modemst1', 'modemst2', 'fsg', 'fsc',
'devinfo', 'frp', 'OP_a', 'OP_b'}
lun, gpt = None, {}
for line in open(log):
m = re.match(r'Parsing Lun (\d+)', line)
if m:
lun = int(m.group(1)); continue
m = re.match(r'(\S+):\s+Offset 0x[0-9a-f]+, Length 0x([0-9a-f]+)', line)
if m and lun is not None:
gpt[m.group(1)] = (lun, int(m.group(2), 16))
problems, count = [], 0
for d in sorted(os.listdir(stage)):
m = re.fullmatch(r'lun(\d+)', d)
if not m:
continue
folder_lun = int(m.group(1))
for f in sorted(os.listdir(os.path.join(stage, d))):
if not f.endswith('.bin'):
continue
count += 1
name = f[:-4]
size = os.path.getsize(os.path.join(stage, d, f))
if name in NEVER:
problems.append(f'{d}/{f}: {name} must never be written')
elif not name.endswith('_b'):
problems.append(f'{d}/{f}: not a slot-b partition')
elif name not in gpt:
problems.append(f'{d}/{f}: partition {name} does not exist on this phone')
elif gpt[name][0] != folder_lun:
problems.append(f'{d}/{f}: {name} is on lun{gpt[name][0]}, not lun{folder_lun}')
elif size != gpt[name][1]:
problems.append(f'{d}/{f}: image {size} bytes != partition {gpt[name][1]} bytes')
print(f'checked {count} images')
if problems:
print('DO NOT WRITE:')
for p in problems:
print(' ' + p)
sys.exit(1)
print('OK: every image matches a slot-b partition of exactly the same size')
python3 precheck_30e.py ~/LG-V35-backup/printgpt.log ~/LG-V35-work/30e_slot_b
我的手機跑出來的結果:
checked 26 images
OK: every image matches a slot-b partition of exactly the same size
也故意用錯的輸入測過:放了 persist、放錯 LUN、大小不符,都會列出問題並回傳錯誤碼 1。
寫入一樣是一個 LUN 呼叫一次 edl,全部在同一次 EDL 裡做完。這段也是整理後的版本(🧪):
cd edl
PY=../venv/bin/python
LOADER=Loaders/LG/0008b0e100310000_1030cd12ce708f29_fhprg.bin
STAGE=~/LG-V35-work/30e_slot_b
# 把某個 LUN 資料夾裡的所有 <分區名>.bin 一次寫入
write_lun() {
local lun=$1 names=() files=() f
for f in "$STAGE/lun$lun"/*.bin; do
names+=("$(basename "$f" .bin)"); files+=("$f")
done
(IFS=,; "$PY" edl.py w "${names[*]}" "${files[*]}" --memory=ufs --lun="$lun" --loader="$LOADER")
}
python3 ../precheck_30e.py ~/LG-V35-backup/printgpt.log "$STAGE" && \
write_lun 4 && write_lun 2 && write_lun 0 && \
"$PY" edl.py setactiveslot b --loader="$LOADER"
- 自我檢查沒通過,就完全不會寫入
- 用
&&串起來,任何一步失敗就停下,不會在寫入不完整時切換槽位 - 寫入前 edl 會檢查映像是不是比分區大,太大就不寫
| 步驟 | 花的時間 | 結果 |
|---|---|---|
| LUN4 的 21 個分區 | 22 秒 | ✅ |
| LUN2 | 1 秒 | ✅ |
| LUN0(含 4.4 GB 的 system_b) | 3 分 7 秒,約 30 MB/s | ✅ |
| setactiveslot b | 13 秒 | ✅ |
寫完核對 log:每個寫入的起始磁區都等於 GPT 上 _b 分區的起點(例如 abl_b 155126、system_b 1754886、xbl_b 6),a 槽沒有被寫到。這次進 EDL 也是試了很多次才成功。
第一次開機進 Android 10
長按電源+音量下重開,這次不按音量上。LG 標誌停了約 3 分鐘(Pie 留下的資料在做第一次開機的升級),15:03:49 adb 上線。沒有清資料,Pie 的 userdata 直接升級成功。
| 檢查 | 結果 |
|---|---|
| Android 版本/槽位 | 10/_b |
| 軟體版本 | V350AWM30e |
| 出廠字串 | LMV350AT-00-V30e-LAO-COM-JUL-28-2020-ARB00+0 |
| 防降級(ARB) | 00,跟 Pie 20f 相同,之後可以退回 Pie |
| 安全性更新 | 2020-04-01 |
| Bootloader | 仍是解鎖狀態(orange、flash.locked=0) |
| 基頻 | MPSS.AT.4.0.c2.9-00088-SDM845_GEN_PACK |
| Wi-Fi | 已連線、VALIDATED,ping 正常 |
| IMEI | 讀得到 15 位數 |
| Hi-Fi Quad DAC | persist.lge.hifiquaddac=true、hifi_dac=ON |
30e 的 ARB 值在網路上查不到,這次直接從手機讀到是 ARB00。


刷成 Android 10 之後,滑動 UI 感覺比 Android 9 還順暢。
下集要做的事
- 移除手機上的 Google 帳號,避免觸發出廠重設保護
- 用 EDL 把 Lineage Recovery(官方的 boot.img)寫進 boot_a、boot_b,不需要 root
- 在 Lineage Recovery 裡 sideload copy-partitions,把 b 槽複製到 a 槽(會蓋掉 a 槽的 Pie)
- Format data,sideload LineageOS 22.2,再 sideload MindTheGapps
- 檢查 DAC 面板、Wi-Fi,以及要測的 App 裝不裝得上
- 下次進 EDL 時,用整理後的
read_lun實際跑一次(例如重讀一個小分區比對 SHA-256),把 🧪 改成 ✅
這次最花時間的兩件事都不是寫入本身:一個是進 EDL 時按鍵全程不能放,另一個是 edl 每呼叫一次就重連,分區要照 LUN 一次讀完才穩。
參考資料
官方:
- LineageOS Wiki:LG V35 ThinQ(judyp)
- LineageOS 安裝說明(Android 10 韌體要求、root、copy-partitions)
- LineageOS 下載(judyp)
- LineageOS GApps 說明
- MindTheGapps 15.0.0 arm64
- LineageOS 裝置原始碼(Quad DAC 支援):android_device_lge_judyp、android_device_lge_sdm845-common、android_hardware_lge(dac_panel)
工具:
- bkerler/edl(開源 Qualcomm EDL 工具,GPL-3.0)
- bkerler/Loaders(LG SDM845 firehose)
- edl issue #752(重新連線觸發 USB Overflow)
- edl issue #103(LG G6 的 USB Overflow)
教學與討論:
- kaneorotar:LG V35 Tinkering Instructions(EDL、解鎖、TWRP)
- XDA:V350ULM Oreo 10i/ULM Pie 20f/AWM30e final Q 下載整理
- XDA:V350AWM30e ATT_US stock & rooted partitions(30e 分區包的原始出處)
- XDA:V350AWM30e android 10 stuck in fastboot(從 30e 退回 Pie 20f 的案例)
- XDA:Official LineageOS for the LG V35 ThinQ
- Nemo 社區:LG V35 解鎖 BL 教程(EDL 按法:電源+音量下不放、黑屏連按音量上)
- Nemo 社區:LG V35 9008 救磚教程
- Nemo 社區:飞羽 ROM 30a(研究過但沒採用)
韌體資料庫: