2016-08-19

Патчинг ядра для корректной работы Android при загрузке с SD-карты

Недавно решил помочь обладателям девайса LG L80 Dual (D380), на котором очень часто "отмирает" eMMC чип. При этом девайс переходит в 9008 режим. В данном режиме PBL на большинстве Qualcomm девайсов пробует произвести загрузку со второго канала SDCC. Конечно же Android ядро загрузится и со второго канала SDCC, но вот остальные части этой ОС совсем не приспособлены адекватно работать в такой ситуации.

Сразу отмечу, что переход девайса в режим 9008 не во всех случаях означает, что PBL будет пытаться произвести загрузку с SD-карты. Данное утверждение верно лишь тогда, когда переход в 9008 связан именно с проблемами инициализации eMMC устройства (подключено к первому каналу SDCC).

Самое простое решение озвученной проблемы - это патчинг исходников ядра. Данные патчи я уже давно разработал для своего девайса, т.к. eMMC чипы помирают и у владельцев Highscreen Boost 2 SE (я уже и лично с этим сталкивался).
Вот ссылки на патчи:
1) mmc: Swap msm_sdcc.1 <-> msm_sdcc.2 devices via kernel param
2) base: Fix initialization msm_sdcc.X devices when use swap_sdcc

Если эти патчи присутствуют в ядре и в командной строке ядра указан параметр "androidboot.swap_sdcc=1", то при обращении ОС к устройству с именем "/dev/block/platform/msm_sdcc.1/mmcblk0" будет задействован не eMMC чип, а SD-карта. Т.е. данные патчи позволяют перенаправить вызовы на самом низком уровне, что избавляет от необходимости изменения кучи разных настроек в прошивке для корректной работы ОС с SD-карты.

Но для LG L80 Dual (D380) никто так и не выложил исходников ядра. Поэтому в данном случае нужно патчить стоковое ядро. И я расскажу как это сделать без использования дизассемблера. Буду использовать только следующий набор утилит:
1) AndImgTool - распаковщик образов
2) dtbToolCM
3) dtc
4) WinHEX - виндовый hex-редактор
5) Notepad++ - функциональный тектовый редактор
6) zimg-packer.py - скрипт для пересоздания zImage

Работу с первыми тремя утилитами я уже описывал в этом блоге. Поэтому заострять внимание на них я не стану.

Приступим. Сначала нужно распаковать образ boot.img, для чего я использую виндовую утилиту AndImgTool. В результате мы получим на выходе следующие, интересующие нас, файлы:
1) kernel.img - оригинальный образ ядра;
2) zImage - упакованный образ ядра;
3) dtb.img - QCDT образ дерева устройств.

Начнем патчинг с файла kernel.img. Т.к. целевой девайс основан на SoC msm8210, то для начала нужно взглянуть на исходники ядра для этого чипа. Вначале стоит начать со структуры msm8610_auxdata_lookup:
static struct of_dev_auxdata msm8610_auxdata_lookup[] __initdata = {
  OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
      "msm_sdcc.1", NULL),
  OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
      "msm_sdcc.2", NULL),
  OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF9824900, \
      "msm_sdcc.1", NULL),
  OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF98A4900, \
      "msm_sdcc.2", NULL),
  {}
};
В данной структуре следует поменять местами физические адреса устройств: 0xF98240000xF98A4000, 0xF98249000xF98A4900. При наличии исходников сделать это просто. Но и без них это тоже довольно просто. Главное найти это место в файле kernel.img, который представляет оригинальный образ Android-ядра. Для поиска этого места нужно в WinHEX вызвать диалог "Find Hex Value" и указать для поиска следующую последовательность байт "004982F9" (так выглядит 32-битное число 0xF9824900 в виде массива байт). Результат поиска таков:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00D17C40   02 00 00 00 98 45 B2 C0  00 40 82 F9 A8 45 B2 C0
00D17C50   00 00 00 00 98 45 B2 C0  00 40 8A F9 B4 45 B2 C0
00D17C60   00 00 00 00 C0 45 B2 C0  00 49 82 F9 A8 45 B2 C0
00D17C70   00 00 00 00 C0 45 B2 C0  00 49 8A F9 B4 45 B2 C0
00D17C80   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
После изменения физических адресов этот участок файла примет такой вид:
00D17C40   02 00 00 00 98 45 B2 C0  00 40 8A F9 A8 45 B2 C0
00D17C50   00 00 00 00 98 45 B2 C0  00 40 82 F9 B4 45 B2 C0
00D17C60   00 00 00 00 C0 45 B2 C0  00 49 8A F9 A8 45 B2 C0
00D17C70   00 00 00 00 C0 45 B2 C0  00 49 82 F9 B4 45 B2 C0
00D17C80   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
Из структуры, приведённой выше, понятно, что сразу после физического адреса находится адрес строки, содержащей название канала SDCC. Поэтому делаем вывод о том, что строка "msm_sdcc.1" расположена по адресу 0xC0B245A8, а строка "msm_sdcc.2" - по адресу 0xC0B245B4.

Далее взглянем на содержимое структуры msm_clocks_8610:
static struct clk_lookup msm_clocks_8610[] = {
  CLK_LOOKUP("xo",  cxo_otg_clk.c, "f9a55000.usb"),
  CLK_LOOKUP("xo",  cxo_lpass_pil_clk.c, "fe200000.qcom,lpass"),
  CLK_LOOKUP("xo",  cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
  ...
  CLK_LOOKUP("iface_clk", gcc_prng_ahb_clk.c, "f9bff000.qcom,msm-rng"),
  CLK_LOOKUP("iface_clk",       gcc_sdcc1_ahb_clk.c, "msm_sdcc.1"),
  CLK_LOOKUP("core_clk",       gcc_sdcc1_apps_clk.c, "msm_sdcc.1"),
  CLK_LOOKUP("iface_clk",       gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"),
  CLK_LOOKUP("core_clk",       gcc_sdcc2_apps_clk.c, "msm_sdcc.2"),
  CLK_LOOKUP("sleep_clk", gcc_usb2a_phy_sleep_clk.c, "f9a55000.usb"),
  CLK_LOOKUP("iface_clk",      gcc_usb_hs_ahb_clk.c, "f9a55000.usb"),
  CLK_LOOKUP("core_clk",    gcc_usb_hs_system_clk.c, "f9a55000.usb"),
  ...
  CLK_LOOKUP("mem_clk",      gmem_gfx3d_clk.c, "fd8c4034.qcom,gdsc"),
};
В этой структуре следует поменять местами названия "msm_sdcc.1" ⇄ "msm_sdcc.2". Для начала это место нужно найти. Для этого достаточно устроить поиск HEX-последовательности "A845B2C0", которая означает адрес строки "msm_sdcc.1". Результат поиска может быть не один, но нас интересует такой участок файла kernel.img, в котором рядышком находятся адреса 0xC0B245A8, 0xC0B245A8, 0xC0B245B4, 0xC0B245B4 (см. структуру msm_clocks_8610). И такое место в файле действительно есть и оно выглядит так:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00D42B70   B4 55 B2 C0 F8 55 B2 C0  F8 CC D4 C0 00 00 00 00
00D42B80   00 00 00 00 A8 45 B2 C0  F8 55 B2 C0 24 C2 D4 C0
00D42B90   00 00 00 00 00 00 00 00  A8 45 B2 C0 F8 40 B6 C0
00D42BA0   B0 C1 D4 C0 00 00 00 00  00 00 00 00 B4 45 B2 C0
00D42BB0   F8 55 B2 C0 0C C3 D4 C0  00 00 00 00 00 00 00 00
00D42BC0   B4 45 B2 C0 F8 40 B6 C0  98 C2 D4 C0 00 00 00 00
00D42BD0   00 00 00 00 C0 51 B2 C0  CC 55 B2 C0 3C C1 D4 C0
Сразу можно заметить, что за первым адресом 0xC0B245A8 следует ещё один такой же, а затем следуют два адреса 0xC0B245B4. Второго похожего места в файле kernel.img просто нету.
Для патчинга структуры msm_clocks_8610 достаточно поменять местами эти самые адреса. После чего получаем такой листинг:
00D42B70   B4 55 B2 C0 F8 55 B2 C0  F8 CC D4 C0 00 00 00 00
00D42B80   00 00 00 00 B4 45 B2 C0  F8 55 B2 C0 24 C2 D4 C0
00D42B90   00 00 00 00 00 00 00 00  B4 45 B2 C0 F8 40 B6 C0
00D42BA0   B0 C1 D4 C0 00 00 00 00  00 00 00 00 A8 45 B2 C0
00D42BB0   F8 55 B2 C0 0C C3 D4 C0  00 00 00 00 00 00 00 00
00D42BC0   A8 45 B2 C0 F8 40 B6 C0  98 C2 D4 C0 00 00 00 00
00D42BD0   00 00 00 00 C0 51 B2 C0  CC 55 B2 C0 3C C1 D4 C0
На этом патчинг файла kernel.img закончен. Изменения следует сохранить.

Теперь приступим к патчингу QCDT. Сначала нужно распаковать файл dtb.img. Для этого нужно воспользоваться утилитами android-image-tools (сам процесс распаковки уже описывался в этой блоге). После распаковки я получил два файла "dt_00.dts" и "dt_01.dts". Дальнейшие изменения я буду вносить в оба файла, т.к. скорее всего они все могут быть задействованы ядром (видимо у LG L80 две ревизии мат. плат).

Файлы "dt_XX.dts" следует изменять в обычном тектовом редакторе Notepad++.

На первом этапе патчинга DeviceTree нужно пропатчить все места, где есть упоминание строки "sdcc".

После запуска поска строки "sdcc" находим следующее место:
mas-sdcc-1 {
  cell-id = <0x4e>;
  label = "mas-sdcc-1";
  qcom,masterp = <0x0>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,mas-hw-id = <0x21>;
};

mas-sdcc-2 {
  cell-id = <0x51>;
  label = "mas-sdcc-2";
  qcom,masterp = <0x2>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,mas-hw-id = <0x23>;
};
В этом месте (как и во всех остальных) нужно поменять местами содержимое данных объектов. В итоге получаем такое содержимое:
mas-sdcc-1 {
  cell-id = <0x51>;
  label = "mas-sdcc-1";
  qcom,masterp = <0x2>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,mas-hw-id = <0x23>;
};

mas-sdcc-2 {
  cell-id = <0x4e>;
  label = "mas-sdcc-2";
  qcom,masterp = <0x0>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,mas-hw-id = <0x21>;
};
Дальнейший поиск строки "sdcc" указал на следующее место:
slv-sdcc-1 {
  cell-id = <0x25e>;
  label = "slv-sdcc-1";
  qcom,slavep = <0x0>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,slv-hw-id = <0x1f>;
};

slv-sdcc-2 {
  cell-id = <0x260>;
  label = "slv-sdcc-2";
  qcom,slavep = <0x2>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,slv-hw-id = <0x21>;
};
В этом месте (как и во всех остальных) нужно поменять местами содержимое данных объектов. В итоге получаем такое содержимое:
slv-sdcc-1 {
  cell-id = <0x260>;
  label = "slv-sdcc-1";
  qcom,slavep = <0x2>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,slv-hw-id = <0x21>;
};

slv-sdcc-2 {
  cell-id = <0x25e>;
  label = "slv-sdcc-2";
  qcom,slavep = <0x0>;
  qcom,tier = <0x2>;
  qcom,buswidth = <0x8>;
  qcom,slv-hw-id = <0x1f>;
};
Затем поиск строки "sdcc" выводит на следующее место:
qcom,sdcc@f9824000 {
  cell-index = <0x1>;
  compatible = "qcom,msm-sdcc";
  reg = <0xf9824000 0x100="" 0x7000="" 0x800="" 0xf9804000="" 0xf9824800="">;
  ...
  qcom,nonremovable;
  qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
  status = "disabled";
};

qcom,sdcc@f98a4000 {
  cell-index = <0x2>;
  compatible = "qcom,msm-sdcc";
  reg = <0xf98a4000 0x100="" 0x7000="" 0x800="" 0xf9884000="" 0xf98a4800="">;
  ...
  qcom,xpc;
  qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
  qcom,current-limit = <0x320>;
  status = "disabled";
};
Здесь тоже следует помемять местами объекты. Но при этом объект "qcom,sdcc@f9824000", который отвечает за eMMC, лучшее вообще удалить, т.к. в рассматриваемом случае eMMC чип полностью нерабочий. Так же стоит выставить параметр "qcom,nonremovable", что укажет на то, что SD-карта будет неизвлекаемой. В результате получим это:
qcom,sdcc@f98a4000 {
  cell-index = <0x1>;
  compatible = "qcom,msm-sdcc";
  reg = <0xf98a4000 0x100="" 0x7000="" 0x800="" 0xf9884000="" 0xf98a4800="">;
  ...
  qcom,xpc;
  qcom,nonremovable;
  qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
  qcom,current-limit = <0x320>;
  status = "disabled";
};
На этом поиск строки "sdcc" заканчивается. Теперь нужно устроить поиск строки "sdhci".
После запуска поска строки "sdhci" находим следующее место:
aliases {
  sdhc1 = "/soc/sdhci@f9824900";
  sdhc2 = "/soc/sdhci@f98a4900";
  lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video";
  lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video";
};
В этом месте тоже исключаем поддержку eMMC чипа. В итоге получаем:
aliases {
  sdhc1 = "/soc/sdhci@f98a4900";
  lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video";
  lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video";
};
Дальнейший поиск строки "sdhci" указал на следующее место:
sdhci@f9824900 {
  compatible = "qcom,sdhci-msm";
  reg = <0xf9824900 0x11c="" 0x800="" 0xf9824000="">;
  reg-names = "hc_mem", "core_mem";
  interrupts = <0x0 0x0="" 0x7b="" 0x8a="">;
  ...
  qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
  qcom,nonremovable;
};

sdhci@f98a4900 {
  compatible = "qcom,sdhci-msm";
  reg = <0xf98a4900 0x11c="" 0x800="" 0xf98a4000="">;
  reg-names = "hc_mem", "core_mem";
  interrupts = <0x0 0x1="" 0x2="">;
  ...
  cd-gpios = <0x20 0x1="" 0x2a="">;
  linux,phandle = <0x2c>;
  phandle = <0x2c>;
};
Здесь тоже следует помемять местами объекты. Но при этом объект "sdhci@f9824900", который отвечает за eMMC, лучшее вообще удалить, т.к. в рассматриваемом случае eMMC чип полностью нерабочий. Так же стоит выставить параметр "qcom,nonremovable", что укажет на то, что SD-карта будет неизвлекаемой. В результате получим это:
sdhci@f98a4900 {
  compatible = "qcom,sdhci-msm";
  reg = <0xf98a4900 0x11c="" 0x800="" 0xf98a4000="">;
  reg-names = "hc_mem", "core_mem";
  interrupts = <0x0 0x1="" 0x2="">;
  ...
  cd-gpios = <0x20 0x1="" 0x2a="">;
  qcom,nonremovable;
  linux,phandle = <0x2c>;
  phandle = <0x2c>;
};
На этом редактирование DeviceTree можно закончить. Теперь нужно отредактированные dts-файлы преобразовать в QCDT-образ при помощи утилит android-image-tools (сам процесс уже описывался в этой блоге).

В результате патчинга мы получили обновлённые файлы kernel.img и dtb.img. Т.к. утилита AndImgTool не умеет преобразовывать файл kernel.img в файл zImage, то нужно предварительно воспользоваться специальным python-скриптом zimg-packer.py. Данный скрипт поддерживат только те zImage, которые используют GZIP алгоритм. После отработки этого скрипта должен появиться файл zImage_new, который нужно переименовать в zImage. После этого при помощи утилиты AndImgTool нужно создать новый файл boot.img, который будет содержать патченные kernel.img и dtb.img.

Полученный zImage допускается использовать и для создания TWRP, который будет запускаться с SD-карты.

Патченные boot.img и twrp.img для LG L80 Dual можно скачать отсюда: Google-Disk (20 MB)

6 комментариев:

  1. Очень познавательная статья, спасибо!

    ОтветитьУдалить
  2. Что то мало комментариев. Получилось ли у кого восстановить данный смартфон?
    Сейчас они мрут как мухи от дихлофоса. Ко мне в неделю по 2-3 раза уже начали обращаться.

    ОтветитьУдалить
  3. Подскажите пожалуйста , заливать boot.img через DiskImageRev2.exe на флешку? Я так пробую делать в итоге опять розовый экран.

    ОтветитьУдалить
  4. К сожалению тоже должен подтвердить эту печальную статистику. Очень неудачную установили память в этот смартфон. Тоже очень часто стали обращаться.

    ОтветитьУдалить
  5. Этот комментарий был удален автором.

    ОтветитьУдалить
  6. а кастомный бут можно так патчить? cyanogen / RRemix к примеру..

    ОтветитьУдалить