Forum: PC-Programmierung Wie funktioniert der Boot-Prozess bei Consumer-Appliances mit NAND-Flash?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Olli Z. (z80freak)


Lesenswert?

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...

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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.

von Olli Z. (z80freak)


Lesenswert?

Uff, das wären ja trübe Aussichten...

Jedenfalls komme ich ja in die Bootloader-CLI rein:
1
Realtek> help
2
3
Available commands :
4
5
.           . (repeat last command line)
6
;           ; (command separator)
7
+           + <repeat count> ;
8
compare     compare <address1> <address2> <size>
9
continue    continue
10
copy        copy [-f] <src> <dst> <size>
11
dump        dump [-m][-8|-16|-32] <address> [<size>]
12
erase       erase -e|-m| <address> <size>
13
factory     factory [-r|-w|-sha1] <address> <size> or factory -c
14
fill        fill [-8|-16|-32] <address> <size> <data>
15
fread       fread  tftp://<ipaddr>/<filename> <address>
16
fwrite      fwrite tftp://<ipaddr>/<filename> <address> <size>
17
go          go [?|.|<address> [<args>]]
18
help        help [<command>]
19
info        info [boot|board|cpu|sysctrl|memory|uart|all|pll]
20
keyset      keyset
21
load        load [-r]
22
                 ([tftp:][//<ipaddr>][/<filename>]) |
23
                 ([asc:] [//(tty0|tty1)])
24
lzma        lzma <src_address> <dst_address> <size>
25
ping        ping ipaddr [<datagramsize>]
26
port        port [-a] [-8|-16|-32] <address> [<value>]
27
reset       reset
28
saveenv     saveenv
29
setenv      setenv [<variable> [<value>]]
30
stty        stty [-tty<0|1>] [-b|-u|[-p][<baudrate>][n|o|e][7|8][1|2][hw|none]]
31
tar         tar <name> <src_address> <size>

Mit "setenv" kann man die aktuell geladenen Parameter anzeigen lassen:
1
Realtek> setenv
2
3
EthDrv              (R/W)  44
4
Gbit                (R/W)
5
bootrev             (RO)   ICIE.0402.1067
6
bootserport         (RO)   flash
7
ethaddr             (R/W)  00.11.22.33.44.55
8
flashsize           (RO)   0x100000000
9
gateway             (R/W)  192.168.0.254
10
ipaddr              (R/W)  192.168.0.9
11
linuxparameter      (R/W)  go 803bc000 rootfstype=squashfs root=31:02 mtdparts=
12
linuxstart          (R/W)
13
linuxstartdelay     (R/W)
14
memsize             (RO)   0x20000000
15
modetty0            (RO)   115200,n,8,1,hw
16
modetty1            (RO)   115200,n,8,1,hw
17
prompt              (R/W)  Realtek
18
regioncode          (RO)   0x0
19
shellstart          (R/W)
20
shellstartdelay     (R/W)
21
subnetmask          (R/W)  255.0.0.0
22
system_parameters_1 (RO)   param_addr=10000 tv_system=PAL usb_otg=off otg_gpio=
23
                           0
24
system_parameters_2 (RO)   boot_flash=nand nand_boot_size=3200000 factory_size=
25
                           400000
26
system_parameters_4 (R/W)  12V5V_GPIO=35,hion cr_pw=b100-1f

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.

von Rüdiger B. (rbruns)


Lesenswert?

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.)

von Olli Z. (z80freak)


Lesenswert?

Ok, sehr cryptisch, dann muss man wissen was xx:yy für den Kernel 
bedeutet.

von Horden (Gast)


Lesenswert?

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.

von Olli Z. (z80freak)


Lesenswert?

Ja, U-Boot habe ich auch im Visier. Irgendwie hängt das auch mit MTD 
zusammen:
https://www.digi.com/resources/documentation/digidocs/embedded/dey/2.4/cc6ul/yocto_r_nand-flash-layout

Komisch nur das in der setenv Ausgabe nichts für mtdparts angegeben ist
1
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:
1
mtdparts=orion_nand:896k(u-boot),128k(u-boot-env),-(root)

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.

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

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.

von Olli Z. (z80freak)


Angehängte Dateien:

Lesenswert?

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:
1
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
2
3
004D9310  49 4B 43 46 47 5F 53 54 1F 8B 08 00 D5 EF BB 52  IKCFG_ST.‹..Õï»R
4
004D9320  02 03 94 5C 6D 73 1B A9 B2 FE BE BF 62 CA 7B AB  ..”\ms.©²þ¾¿bÊ{«
5
004D9330  EE B9 55 9B C8 7A B5 B2 55 FE C0 30 48 62 35 2F  î¹U›Èzµ²UþÀ0Hb5/
6
...
7
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
8
9
004DC800  94 48 0B D4 7C 79 DA DA 1C FC 0F B7 8C 5F 13 7A  ”H.Ô|yÚÚ.ü.·Œ_.z
10
004DC810  E1 00 00 49 4B 43 46 47 5F 45 44 00 00 00 00 00  á..IKCFG_ED.....

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.

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

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:
1
fwrite tftp://192.168.0.10/a000_0000.bin 0xa0000000 0x1000000
2
fwrite tftp://192.168.0.10/a100_0000.bin 0xa1000000 0x1000000
3
...
4
fwrite tftp://192.168.0.10/bf00_0000.bin 0xbf000000 0x1000000

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.

: Bearbeitet durch User
von imonbln (Gast)


Lesenswert?

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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.