Aus meinem Problem mit dem Fantec MediaPlayer
(Beitrag "Fantec Mediaplayer 3DS4600 zeigt nur noch Startbildschirm") heraus merke ich das ich
den Boot-Prozess wohl noch nicht verstanden hab. Meine Suche im Internet
nach dem Board, Prozessor, Log-Meldungen ergab aber so viele Treffer auf
ähnliche Produkte das es sinnvoll erscheint diesen Prozess genau zu
verstehen.
Wie gehe ich hier vor um an die richtigen Informationen zu kommen? Ich
habe diese Appliance, Fantec 3DS4600 welche mit einem Realtek Board vom
Typ "RT-1186G15".
Eine Internetsuche ergab das der RT1186 ein Mikrocontroller ist welcher
in einer ganzen Reihe von Geräten verbaut wurde: "Realtek released the
next generation of its chipsets, the 1xx6 series 1186, in early October
2011. These ran at 750Mhz, supported HDMI 1.4, were capable of 3D
including 3D ISO, and were able to dual-boot into Android. Key 1186
players include the Mede8er X3D Series (MED1000X3D, MED800X3D,
MED600X3D), Xtreamer Prodigy 3D and HiMedia 900B"
Die Bootmeldungen lassen darauf schließen das mit einem Bootloader
gearbeitet wird:
1
REALTEK ROM Monitor, Revision ICIE.0402.1067.
2
Copyright (c) Realtek Semiconductor Corp. - All Rights Reserved.
3
4
For a list of available commands, type 'help'.
5
6
Compilation time /version= Feb 19 2014 10:14:08 /ICIE.0402.1067
7
MAC address = 00.11.22.33.44.55
8
Processor Company ID/options = 0x01 (MIPS Technologies, Inc.) / 0x00
9
Processor ID/revision = 0x93 / 0x85
10
Endianness = Little
11
Flash memory size = 4 GByte
12
SDRAM size = 512 MByte
13
First free SDRAM address = 0x800c4000
Dieser müsste ja irgendwie parametrierbar sein, oder es müsste doch
irgendwo Informationen darüber geben?
Von einem "normalen" Linux-Boot kenn ich das so was man im Bootloader
die Startparameter festlegt, also woher der Kernel zu laden ist, welches
Root-Filesystem zu laden ist, welche Startparameter dem Kernel
mitgegeben werden, etc.
Der Rescue-Mode der noch funktioniert zeigt das so an:
1
Rescue kernel in FLASH, total size = 0x1f52d0
2
seg.1 addr 0xa0a06fd3, size = 0x1f52d0
3
seg.2 addr 0x0, size = 0x0
4
copy rescue0: from a0a06fd3 to 81000000, len = 1f52d0
5
decompressing rescue: from 81000000 to 80100000: size = 1f52d0
6
lzma finished
7
decompressing rescue done
8
go 0x80100000
Da weis der bootloader also offenbar ganz genau wo der Kernel im Flash
liegt, kann den in den Hauptspeicher ab 0x8100 0000 laden, entpacken und
ausführen ("go ...").
Wo aber sind all diese Informationen hinterlegt? Vermutlich auch im
Flash...
Olli Z. schrieb:> Die Bootmeldungen lassen darauf schließen das mit einem Bootloader> gearbeitet wird:
Das liegt allerdings nicht an dem SOC, sondern an Linux.
> Dieser müsste ja irgendwie parametrierbar sein
Nein, das ist ein Fehlschluss. Bei PCs und Konsorten ist er es natürlich
über weite Teile, aber je kleiner und weniger variabel die Architektur
ist, desto weniger ist da typisch parametrierbar.
> oder es müsste doch> irgendwo Informationen darüber geben?
Auch das ist ein Fehlschluss. Es ist im Gegenteil sogar ziemlich typisch
für SOCs, dass gerade die Details des Bootloaders oft unter
NDA-Verschluss gehalten werden.
Jeden dieser Parameter kann man mit "setenv <NAME> <VALUE>" zur Laufzeit
ändern und mit "setenv" zurück ins Flash speichern. Wobei ich nicht
sicher bin ob diese Daten im 2GB NAND-Flash liegen oder in dem
zusätzlichen seriellen Flash (EEPROM). Aber man kann ganz klar
parametrieren.
Das hier sieht besonders interessant aus:
1
linuxparameter (R/W) go 803bc000 rootfstype=squashfs root=31:02
Mit "go" startet man ein im Speicher befindliches Programm, z.B. einen
geladenen Kernel, oder was auch immer an 0x0803 BC000 liegt.
Der Parameter "rootfstype=squashfs" ist wohl ein Standard-Parameter für
Linux-Kernel. Er zeigt an das das Root-Filesystem, welches der Kernel
nach dem Start benötigt im Squashfs Format vorliegt. Ein Squashfs ist
Read-Only, wird also vermutlich dann nur die Binaries und Libs
enthalten. Vermutlich wird das Linux nach dem Start dann über seine
Init-Skripte (fstab) eine Ramdisk erstellen und die dynamischen Teile
wie /log /tmp /var damit überlagern.
Der Parameter "root=" zeigt bei einem normalen Linux an wo das
root-Filesystem zu laden ist. Hier hätte ich eher sowas wie
"/dev/mtdblock2" erwartet, aber stattdessen steht dort "31:02". Was auch
immer das bedeutet für den Bootloader.
aus dem bootparam manual:
The more awkward and less portable numeric specification
of the above possible root devices in major/minor format
is also accepted. (For example, /dev/sda3 is major 8,
minor 3, so you could use 'root=0x803' as an alternative.)
Olli Z. schrieb:> Wo aber sind all diese Informationen hinterlegt? Vermutlich auch im> Flash...
Bei SoC funktioniert das meist mit mehrstufigen Bootloadern.
Der erste Teil ist in der Regel fix gebrannt bzw. direkt eine Struktur
im SoC, der kann nicht viel außer die zweite Stufe zu laden (und teils
verifizieren) welche den RAM initialisiert und dann eine dritte Stufe
bzw. den Kernel lädt. Gibt auch diverse Konzepte bei denen die Stufen
übersprungen werden können um Bootzeit zu sparen (in der Regel hat man
mehrere Schritte da der SoC allein noch nicht die Initialisierung für
den RAM kann, ergo nicht direkt den Kernel laden kann da zu wenig
Speicher auf dem SoC selbst vorliegt). Die erste Stufe schaut meist an
fixen Adressen in den unterstützen Speichern, rudimentäre Unterstützung
für eMMC/NAND ist da in der Regel eingebaut. Kann auch über
Pins/Fuses/EEPROM konfiguriert werden.
Der Bootloader auf deinem Gerät erinnert etwas an u-boot mit saveenv,
dabei werden die Einstellungen auf einem EEPROM bzw. einem definierten
Bereich im NAND/eMMC gesichert. Der Bootloader könnte z.B. bei 0x000
beginnen, bei 0x3FF enden und von 0x9FF bis 0x4FF liegen im Speicher die
Variablen. In diesen Variablen stehen teils Skripte zur Ermittlung des
Kernels usw., es ist meist auch problemlos möglich das Netzwerk zu
initialisieren und anschließend weitere Daten über das Netzwerk zu laden
(z.B. den Kernel über tftp), das ist gerade in der Entwicklung sehr
praktisch.
https://www.emcraft.com/som/vf6/using-u-boot-env
Tipp einmal printenv ein, dann müsstest du alle Variablen usw. sehen.
PS: Ich gehe davon aus, dass sich MIPS relativ ähnlich zu ARM
dahingehend verhält, habe bisher nur mit ARM gearbeitet.
linuxparameter (R/W) go 803bc000 rootfstype=squashfs root=31:02 mtdparts=
Hier müsste nach u-boot Logik eine ganze Reihe von Infoa stehen, ausser
das entspricht einfach dem im obigen Link genannten Default-Layout?
Erwartet wäre z.B. sowas hier:
Die Angabe vom root-FS "root=31:02" könnte bedeuten:
Major-Device 31 (block device) = ROM/Flash
Minor-Device 02 = /dev/mtd2
Starte ich das Rescue-Linux finde ich dort im /dev Verzeichnis z.B.
1
brw------- 1 0 0 31, 0 Jan 1 00:00 mtdblock0
2
brw------- 1 0 0 31, 1 Jan 1 00:00 mtdblock1
Dann wäre Minor "02" vermutlich /dev/mtdblock2
Das hier ist auch interessant, da wird was über diese MTDs erklärt:
https://bootlin.com/blog/managing-flash-storage-with-linux/
Interessant ist auch das die Adressen die man im ROM Monitor ansprechen
kann nicht bei 0x0000 0000 beginnen. Versucht man ein:
1
dump -m -8 0x00000000 0x1000
erhält man eine Fehlermeldung:
1
Error : No match in TLB for mapped address : Address = 0x00000000
TLB steht wohl für "Translation Lookaside Buffer" eine Technik um aus
virtuellen Adressen physikalische (z.B. für Page-basierte Speicher wie
das NAND) zu erzeugen.
Adressen werden im Bereich 0x8000 0000 bis 0xBFFF FFFF akzeptiert.
Darüber und darunter kommt der Adressfehler.
Ok, also ein MTD device (hier der 4GB große NAND Flash) hat keine
Partitionstabelle. Die gewünschten Partitionen (/dev/mtdblock0 usw.)
werden im compilierten Kernel abgelegt, oder eben im Aufrufparameter
"mtdparts". Ich vermute das der Grund für den leeren mtdparts oben eine
im Kernel eincompilierte Konfiguration ist.
Wenn man das Update-File "install.img" entpackt (z.B. 7zip unter Windows
oder tar unter Linux) dann erhält man den Ordner "package5" und darin
die Datei "vmlinux.develop.android.saturn.nand.bin". Dies ist der
Linux-Kernel. Dieser enthält per Definition die zum kompilieren
verwendeten Einstellungen welche im laufenden OS unter /proc/config.gz
bereitgestellt werden. Die Datei muss also Bestandteil des Kernel-Images
sein.
Ein Image welches mit "vmlinux" beginnt ist in der Regel unkomprimiert,
sonst hieße es per Konvention "vmlinuz". Ich müsste das config.gz also
im Image finden.
Ein GZIP hat das Magic "0x1F 0x8B", gefolgt von der Kompressionsmethode
"0x08" (für GZ). Eine Suche nach der Bytefolge "0x1F 0x8B 0x08" im
Kernel ergab genau einen Treffer an Offset-Adresse 0x4D9318:
Auf die Schnelle konnte ich nicht rausfinden wie man die komprimierte
Länge eines GZIP ermittelt, aber da hier offensichtlich IKCONFIG zur
Anwendung kam dürfte das GZIP wohl in den Schlüsselwörtern "IKCFG_ST"
(für START) und "KCFG_ED" (für END) eingebettet sein. Daher habe ich
einfach mal aus diesem Teil in eine Datei "config.gz" erstellt welche
sich dann problemlos zu einer "config" Datei entpacken ließ (müsste per
Konvention eigentlich ".config" heißen, aber das sollte hier keine Rolle
spielen).
Um sie besser lesen zu können habe ich alle kommentierten Parameter mal
entfernt und daraus "config.nocomments" gemacht.
Es finden sich zahlreiche Verwendungshinweise zum MTD, jedoch erkenne
ich keine Filesystem-Definition darin.
Parallel zum Reverse-Engineering des Kernels habe ich versucht über den
Realtek ROM Monitor mittels fwrite den Inhalt des Flash auf meinen
TFTP-Server (Solarwinds) zu übertragen um auch mal die Positionen der
Binary-Teile dort zu bestimmen.
Ich habe das Mapping (TLB) noch nicht gefunden aber erkannt das ich nur
bestimmte Adressbereiche nutzen kann. Der Bereich 0x8000 0000 - 0x8FFF
FFFF ist dem SDRAM zugewiesen, das lässt sich aus dem Kontext der
Verwendung ermitteln. Das Board hat 512MB SDRAM (0x2000 0000) müsste
also von 0x8000 0000 bis 0x9FFF FFFF gehen, wenn alles gemapped ist.
Das Flash hat eine größe von 4 GB (0x1 0000 0000) und ich vermute das es
ab der TLB Adresse 0xA000 0000 gemapped ist. Da sich oberhalb von 0xC000
0000 keine Adressen ansprechen lassen (evtl. auch nur hier im ROM
Monitor?) werden also nur 0xA000 0000 - 0xBFFF FFFF vom Flash genutzt,
also nur 512MB. Eine ziemliche Verschwendung, aber für das Filesystem,
zwei Kernel und bischen Firlefanz reichte es auch.
Diesen Bereich habe ich mir mit "fwrite" dann rüberkopiert. Aufgrund von
Dateigrößenbeschränkungen im "fwrite" des ROM Monitors musste ich das zu
32MB happen machen und diese später wieder zusammensetzen:
Interessanterweise kann ich den Bereich 0xb800 0000 - 0xb8ff ffff nicht
übertragen, da kommt es zu einem Timeout, sprich es wird nicht mehr
gesendet. Möglicherweise liegt hier ein defekter Block im Flash der auch
mein Problem verursacht?
Das System verwendet, soweit ich das ermittelt habe den MTD-Treiber zum
ansprechen des NAND Flash. Dieser besitzt keine Möglichkeit der
Fehlerkorrektur (BBT=Bad Block Table) und kann daher auftretende
Lese/Schreibfehler nicht selbstständig korrigieren. Hierzu wäre ein
höherwertiger Treiber wie UBI erforderlich.
Olli Z. schrieb:> Ein GZIP hat das Magic "0x1F 0x8B", gefolgt von der Kompressionsmethode> "0x08" (für GZ). Eine Suche nach der Bytefolge "0x1F 0x8B 0x08" im> Kernel ergab genau einen Treffer an Offset-Adresse 0x4D9318:
Für solche Analysen gibt es das Tool binwalk
(https://github.com/ReFirmLabs/binwalk), das ist eigentlich immer ein
guter Start um ein unbekanntes Binary zu untersuchen. Bei Bedarf kann
binwalk auch gleich gefundene Sachen extrahieren, wie die komprimierte
Konfiguration.