Temat: Reanimacja trupa
Dzięki uprzejmości jednego z użytkowników (dziękuję jeszcze raz!) stałem się posiadaczem uszkodzonego routera sygnowanego przez pewnie mało Wam znaną firmę Cell C. Jeżeli o niej nie słyszeliście to już śpieszę że wyjaśnieniami, że jest to operator z Republiki Południowej Afryki, więc dość egzotyczny w naszych warunkach. Sprzęt został oznaczony metką "device not booting" i tak też było po podłączeniu go do prądu. Zanim przejdę do sposobu reanimacji, trzeba kronikarsko poinformować Was czym ten sprzęt jest - ogólnie jest to router z wbudowanym modemem LTE kategorii 6 (czyli nasze LTE-A) o dość mocnej konfiguracji - procesor IPQ4019 (4 rdzenie po 716MHz), 16MB+128MB pamięci flash, 256MB pamięci RAM, wifi 2.4 (300Mbps) oraz 5GHz (AC do 867Mbps), dwa gigabitowe porty RJ45, port USB 2.0 i kilkanaście różnych diod LED. Modem obsługuje pasma B1/B3/B7/B20/B38, umożliwia pobieranie z prędkością do 300Mbps i ma agregację dwóch pasm LTE. Urządzenie oficjalnie nosi nazwę Cell C RTL30VW LTE-A Home Router choć w internecie można znaleźć informację że jest to brandowany ASKEY RTL0030VW i nie cieszy się zbyt dobrą opinią.
Pierwsze uruchomienie - bootloop. Rozebrałem urządzenie, w środku można było znaleźć konsolę szeregową (piny co 2mm, nie 2.54!); podłączenie konwertera szeregowego dało taką informację (zbędne linie zostały usunięte):
[ 0.888987] m25p80 spi0.0: found w25q128, expected n25q128a11
[ 0.894416] m25p80 spi0.0: w25q128 (16384 Kbytes)
[ 0.899164] 9 ofpart partitions found on MTD device spi0.0
[ 0.904558] Creating 9 MTD partitions on "spi0.0":
[ 0.909357] 0x000000000000-0x000000040000 : "0:SBL1"
[ 0.915563] 0x000000040000-0x000000060000 : "0:MIBIB"
[ 0.920805] 0x000000060000-0x0000000c0000 : "0:QSEE"
[ 0.925932] 0x0000000c0000-0x0000000d0000 : "0:CDT"
[ 0.930841] 0x0000000d0000-0x0000000e0000 : "0:DDRPARAMS"
[ 0.936278] 0x0000000e0000-0x0000000f0000 : "0:APPSBLENV"
[ 0.941769] 0x0000000f0000-0x000000170000 : "0:APPSBL"
[ 0.946964] 0x000000170000-0x000000180000 : "0:ART"
[ 0.951967] 0x000000180000-0x000000190000 : "0:BOOTCONFIG"
[ 0.996252] nand: device found, Manufacturer ID: 0xef, Chip ID: 0xaa
[ 1.001599] nand: Winbond W25N01GV 128MiB 3.3V
[ 1.006002] nand: 128MiB, SLC, page size: 2048, OOB size: 64
[ 1.011680] Scanning device for bad blocks
[ 1.076407] Bad eraseblock 32 at 0x000000400000
[ 1.101273] Bad eraseblock 44 at 0x000000580000
[ 1.554340] mt29f spi0.1: ecc error, page=18432
[ 1.558791] mt29f spi0.1: ecc error, page=18433
[ 1.563296] mt29f spi0.1: ecc error, page=18496
[ 1.567828] mt29f spi0.1: ecc error, page=18497
[ 2.107100] Bad eraseblock 577 at 0x000004820000
[ 2.115360] Bad eraseblock 580 at 0x000004880000
[ 2.249283] Bad eraseblock 651 at 0x000005160000
[ 2.496043] Bad eraseblock 783 at 0x0000061e0000
[ 2.594904] Bad eraseblock 835 at 0x000006860000
[ 2.621635] Bad eraseblock 848 at 0x000006a00000
[ 2.750562] Bad eraseblock 915 at 0x000007260000
[ 2.851197] Bad eraseblock 968 at 0x000007900000
[ 2.931519] Bad eraseblock 1010 at 0x000007e40000
[ 2.959288] 5 ofpart partitions found on MTD device spi0.1
[ 2.963737] Creating 5 MTD partitions on "spi0.1":
[ 2.968518] 0x000000000000-0x000000400000 : "kernel"
[ 2.974508] 0x000000400000-0x000002400000 : "rootfs"
[ 2.979442] mtd: device 10 (rootfs) set to be root filesystem
[ 2.984174] 0x000002400000-0x000002800000 : "kernel_1"
[ 2.990287] 0x000002800000-0x000004800000 : "rootfs_1"
[ 2.995399] 0x000004800000-0x000008000000 : "ubifs"
[ 3.029973] UBI: attaching mtd13 to ubi0
[ 5.796460] UBI: scanning is finished
[ 5.869586] UBI warning: print_rsvd_warning: cannot reserve enough PEBs for bad PEB handling, reserved 2, need 11
[ 5.880287] UBI: attached mtd13 (name "ubifs", size 56 MiB) to ubi0
[ 5.885523] UBI: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
[ 5.892320] UBI: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
[ 5.898993] UBI: VID header offset: 2048 (aligned 2048), data offset: 4096
[ 5.905832] UBI: good PEBs: 439, bad PEBs: 9, corrupted PEBs: 0
[ 5.911750] UBI: user volume: 5, internal volumes: 1, max. volumes count: 128
[ 5.918870] UBI: max/mean erase counter: 416/8, WL threshold: 4096, image sequence number: 294980133
[ 5.927971] UBI: available PEBs: 0, total reserved PEBs: 439, PEBs reserved for bad PEB handling: 2
[ 5.937034] UBI: background thread "ubi_bgt0d" started, PID 71
[ 5.958002] List of all partitions:
[ 5.960532] 1f00 256 mtdblock0 (driver?)
[ 5.965501] 1f01 128 mtdblock1 (driver?)
[ 5.970535] 1f02 384 mtdblock2 (driver?)
[ 5.975569] 1f03 64 mtdblock3 (driver?)
[ 5.980605] 1f04 64 mtdblock4 (driver?)
[ 5.985639] 1f05 64 mtdblock5 (driver?)
[ 5.990675] 1f06 512 mtdblock6 (driver?)
[ 5.995708] 1f07 64 mtdblock7 (driver?)
[ 6.000742] 1f08 64 mtdblock8 (driver?)
[ 6.005778] 1f09 4096 mtdblock9 (driver?)
[ 6.010813] 1f0a 32768 mtdblock10 (driver?)
[ 6.015933] 1f0b 4096 mtdblock11 (driver?)
[ 6.021057] 1f0c 32768 mtdblock12 (driver?)
[ 6.026176] 1f0d 57344 mtdblock13 (driver?)
[ 6.031300] 1f0e 16492 mtdblock14 (driver?)
[ 6.036419] 1f0f 16492 mtdblock15 (driver?)
[ 6.041543] 1f10 16492 mtdblock16 (driver?)
[ 6.046663] 1f11 2108 mtdblock17 (driver?)
[ 6.051787] 1f12 2108 mtdblock18 (driver?)
[ 6.056900] No filesystem could mount root, tried: squashfs
[ 6.062550] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(31,10)
[ 6.070969] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.14.43 #1
[ 6.076985] [<c021e460>] (unwind_backtrace) from [<c021b540>] (show_stack+0x10/0x14)
[ 6.084693] [<c021b540>] (show_stack) from [<c03c300c>] (dump_stack+0x88/0xcc)
[ 6.091897] [<c03c300c>] (dump_stack) from [<c022ebb4>] (panic+0x84/0x1e0)
[ 6.098754] [<c022ebb4>] (panic) from [<c07a0028>] (mount_block_root+0x238/0x274)
[ 6.106218] [<c07a0028>] (mount_block_root) from [<c07a0150>] (prepare_namespace+0x88/0x1c4)
[ 6.114638] [<c07a0150>] (prepare_namespace) from [<c079fc98>] (kernel_init_freeable+0x180/0x1d0)
[ 6.123493] [<c079fc98>] (kernel_init_freeable) from [<c020cb88>] (kernel_init+0x8/0xfc)
[ 6.131565] [<c020cb88>] (kernel_init) from [<c0208d18>] (ret_from_fork+0x14/0x3c)
....
Powód rebootowania urządzenia? Uszkodzona pamięć NAND. Pech chciał że uszkodzony blok pojawił się akurat na początku partycji z systemem plików, więc kernel nie może znaleźć sygnatury i po prostu wywalał się. W pierwszym odruchu chciałem urządzenie rozebrać i pozyskać z niego części. Po kilku dniach refleksji jednak wyciągnąłem je ponownie z pudła bo jest ono zbyt dobre sprzętowo żeby je tak potraktować.
Co więc można zrobić? Pierwsze skojarzenie - wymienić pamięć. Niestety potrzeba mi było pamięci SPI NAND o pojemności 128MB a takiej nie miałem w swoich zasobach. Co więc zrobić?
Urządzenie ma dwie pamięci - jedną o pojemności 16MB która zawiera partycje systemowe IPQ (coś co przedstawia się jako m25p80 spi0.0) oraz drugą która zawiera podwójny kernel, system plików i partycję z UBIFS. Ta pierwsza nie jest w całości wykorzystana, po partycjach systemowych jest trochę wolnego miejsca, więc pomyślałem że tam można schować system...
Pierwszy krok - znaleźć obraz który uruchamia się w pamięci.
Niestety tak różowo nie było, bo żaden obraz nie chciał się uruchomić (przy próbie uruchomienia wywalał się z rejestrami CPU i następował natychmiastowy restart). Po kilku wieczorach testów odkryłem, że problemem nie jest obraz (ostatecznie okazało się że zwykły initramfs z ap-dk01.1-c1 uruchamia się poprawnie), tylko problemem jest sposób jego uruchomienia. Jeżeli próbowałem go uruchomić bezpośrednio z bootlodera po restarcie to wykładał się z rejestrami. Jeżeli zaś pozwoliłem uruchomić mu oryginalny kernel który i tak się wywalał to przy następnym uruchomieniu i przerwaniu uboota udawało się wczytać obraz do pamięci i go uruchomić.
Drugi krok - napisane odpowiedniego DTSa
Tutaj wykorzystałem opis platformy ap-dk01.1-c1 z OpenWrt, dodałem tylko swój podział pierwszej pamięci flash:
[ 0.595351] m25p80 spi0.0: w25q128 (16384 Kbytes)
[ 0.595450] 12 fixed-partitions partitions found on MTD device spi0.0
[ 0.599381] Creating 12 MTD partitions on "spi0.0":
[ 0.605832] 0x000000000000-0x000000040000 : "SBL1"
[ 0.611213] 0x000000040000-0x000000060000 : "MIBIB"
[ 0.616047] 0x000000060000-0x0000000c0000 : "QSEE"
[ 0.620734] 0x0000000c0000-0x0000000d0000 : "CDT"
[ 0.625656] 0x0000000d0000-0x0000000e0000 : "DDRPARAMS"
[ 0.630368] 0x0000000e0000-0x0000000f0000 : "APPSBLENV"
[ 0.635461] 0x0000000f0000-0x000000170000 : "APPSBL"
[ 0.640618] 0x000000170000-0x000000180000 : "ART"
[ 0.645854] 0x000000180000-0x000000190000 : "BOOTCONFIG"
[ 0.650437] 0x000000190000-0x000000490000 : "kernel"
[ 0.655840] 0x000000490000-0x000001000000 : "rootfs"
[ 0.660727] mtd: device 10 (rootfs) set to be root filesystem
[ 0.665340] 1 squashfs-split partitions found on MTD device rootfs
[ 0.670750] 0x000000860000-0x000001000000 : "rootfs_data"
[ 0.677570] 0x000000190000-0x000001000000 : "firmware"
Zadziałało perfekcyjnie, zrobił podział pamięci. Więc tam można było schować system, dawało mi ok 8MB wolnego flash. Da się żyć.
Trzeci krok - wgranie OpenWrt
Jeżeli obraz uruchomił się w pamięci to wgranie było proste - po prostu przesłałem gotowy obraz do /tmp, zrobiłem sysupgrade -n /tmp/a; reboot. I co? Nie uruchamiał się, zrzut rejestrów pamięci. Oczywiście wcześniej zmodyfikowałem uboota tak że nie uruchamiał systemu poleceniem bootipq tylko mój kernel - przez wczytanie go do pamięci i jego uruchomienie.
Czwarty krok - dlaczego to się nie chce uruchamiać?
I tu znów straciłem kilka wieczorów. Co jest w tym że mój się nie chce uruchomić a wystartowaniu oryginalnego (który się wywalał) i mojego - działa? Wracamy do oryginalnego podziału pamięci. Osoby rozeznane z platformę IPQ40xx zauważają zapewne że oprócz standardowych partycji (SBL1 do ART) znajduje się tam jeszcze jedna partycja o nazwie BOOTCONFIG. Zawiera ona dane binarne, zaś oryginalne polecenie (to bootipq) najpierw wczytuje tą partycję do pamięci, później wczytuje kernel do pamięci i go uruchamia. Postanowiłem zrobić to samo - najpierw wczytałem tą partycję, później swój kernel, uruchomiłem go i ... działa! Wystarczyła więc modyfikacja zmiennej bootcmd, reboot i wszystko zadziało tak jak trzeba - wczytał mój system. Stałem się więc posiadaczem unikalnego w skali światowej modelu RTL30VW - unikalnego, bo pozbawionego drugiej pamięci flash. Oczywiście nie było to tak gładko jak napisałem - najpierw okazało się ze że dałem za mało miejsca na kernel, potem dałbym sobie rękę uciąć że excel (a właściwie libreoffice calc) z premedytacją źle przeliczał offsety na hexach i robi mi inne zakresy partycji, ale ostatnie doszedłem do działającego systemu.
Piąty krok - szczegóły.
Modem - nie działał, nie widział go. USB - nie działał, nie widział go. Ledy - kilka znalezionych, reszta nie działała. Okazało się ze prawie wszystko jest obsługiwane przez dodatkowe gpio zbudowane na rejestrze przesuwnym 74HC595. Na szczęście w OpenWrt jest urządzenie z podobnym rozwiązaniem, więc przez analogię poprawiłem swój DTS i nawet zaczyna to wyglądać sensownie.
Led jest aż nadto - w sumie 5 systemowych, 4 od poziomu sygnału, dodatkowa czerwona led od power oraz możliwość włączenia dodatkowego koloru na wszystkich LED od sygnału. Nie działało za to parę innych rzeczy - np. USB (tak, okazało się że to też jest sterowane przez GPIO i trzeba włączyć, tak samo jak slot minipcie). Modem okazał się zwykłym Qualcommem obsługiwany przez QMI, więc z jego obsługą nie ma problemu - mała zmiana w sterowniku i działa. Tak samo jak jego porty szeregowe.
Co dalej?
Miałem przerobiony system po swojemu, działający. Ale strasznie swędzą palce żeby jednak zrobić coś z tą dodatkową pamięcią - przecież 128MB nie może tak leżeć odłogiem. Pomysł miałem dość dziki - zrobić z niej jedną wielką partycję, przeformatować całość jako UBIFS (z wadliwymi blokami sobie poradzi, będą oznaczone ale system plików rozmieści wszystko tak jak trzeba) i wykorzystać to jako... overlay do działającego systemu.
Pomysł dobry tylko znów przez kilka wieczorów nie mogłem dojść dlaczego nie wykrywa mi w ogóle tej pamięci. Cały sekret tkwił w odpowiednim zmodyfikowaniu DTSa - a konkretnie podaniu które GPIO służą do obsługi m.in linii SS (Slave Select, Chip Select jeżeli tak ktoś woli). Po kilkunastu (!) próbach i timeoutach otrzymałem w końcu to:
[ 1.344589] nand: device found, Manufacturer ID: 0xef, Chip ID: 0xaa
[ 1.344630] nand: Winbond W25N01GV 1G 3.3V 8-bit
[ 1.350020] nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
[ 1.354696] Scanning device for bad blocks
[ 1.380155] Bad eraseblock 32 at 0x000000400000
[ 1.384988] Bad eraseblock 44 at 0x000000580000
[ 1.472182] mt29f spi0.1: ecc error, page=18432
[ 1.472448] mt29f spi0.1: ecc error, page=18496
[ 1.577087] Bad eraseblock 577 at 0x000004820000
[ 1.578174] Bad eraseblock 580 at 0x000004880000
[ 1.605813] Bad eraseblock 651 at 0x000005160000
[ 1.652496] Bad eraseblock 783 at 0x0000061e0000
[ 1.670851] Bad eraseblock 835 at 0x000006860000
[ 1.675460] Bad eraseblock 848 at 0x000006a00000
[ 1.699159] Bad eraseblock 915 at 0x000007260000
[ 1.717982] Bad eraseblock 968 at 0x000007900000
[ 1.732767] Bad eraseblock 1010 at 0x000007e40000
[ 1.737465] 5 fixed-partitions partitions found on MTD device spi0.1
[ 1.737492] Creating 5 MTD partitions on "spi0.1":
[ 1.742888] 0x000000000000-0x000000400000 : "kernel"
[ 1.748233] 0x000000400000-0x000002400000 : "rootfs"
[ 1.753345] 0x000002400000-0x000002800000 : "kernel_1"
[ 1.758186] 0x000002800000-0x000004800000 : "rootfs_1"
[ 1.763286] 0x000004800000-0x000008000000 : "ubifs"
Dzięki temu mam też dostęp do dodatkowej, częściowo uszkodzonej pamięci. Nie zdecydowałem się jeszcze na przerobienie jej na cały /overlay (taki miałem pierwotny pomysł), ale mam swobodny dostęp do tych partycji i mogą się one przydać chociaż by do trzymania danych.
Co mi to dało?
Wiedzę. Sprzęt który miałem spisać na straty postanowiłem reanimować programowo - bo w tym przypadku akurat było to możliwe. Kilka ładnych wieczorów dało możliwość zapoznania się z platformą IPQ, poznania sposobów uruchomienia obrazów oraz przećwiczenia kilku różnych pomysłów które od dawna chciałem sprawdzić a nie miałem na czym. Oczywiście były też wieczory w których serdecznie pozdrawiałem do kilku pokoleń wstecz rodziny twórców tego urządzenia, ale bardziej wynikało to z braku mojego doświadczenia z taką platformą niż że złośliwości autorów.