PlomWiki: Zur Start-Seite Suche Letzte Änderungen (Feed) Letzte Kommentare (Feed)
Impressum Datenschutz-Erklärung

SheevaPlugInstallation

Ansicht Bearbeiten Anzeige-Titel setzen Versions-Geschichte Seiten-Passwort setzen AutoLink-Anzeige ein-/ausschalten

Notizen zur Installation meines Sheeva-Plugs.

Notizen zu meinem Sheeva-Plug.

Serielle Verbindung zu meinem SheevaPlug

So ein SheevaPlug ist ja erstmal ein weißer Kasten, der nur über das Blinken zweier kleine Lämpchen sichtbar mit der Außenwelt kommuniziert. Diese Bandbreite würde für manchen Morse-Code-Oldtimer vielleicht reichen, ich hätte aber gerne den Luxus einer Schnittstelle, die es mir erlaubt, direkt im Buchstaben-Textformat mit ihm zu kommunizieren. Zum Beispiel wäre es doch nett, wenn ich ihn mit einem Kabel an meinen Laptop anschließen und dann im Laptop ein Terminal-Fenster in ihn hinein öffnen könnte. Der Weg hierzu ist die "serielle" Verbindung.

Ich verbinde meinen Laptop (auf dem ein Debian GNU/Linux läuft) via USB-Kabel mit dem SheevaPlug. Meines Laptop-Debians Kernel erkennt diese neue Verbindung und leitet sie intern auf etwas weiter, das er "ttyUSB0" nennt:

$ dmesg                        # Log der letzten Kernel-Nachrichten ausgeben 

(...)

[237247.840070] usb 5-1: new full speed USB device using uhci_hcd and address 40 
[237248.051109] usb 5-1: New USB device found, idVendor=9e88, idProduct=9e8f 
[237248.051118] usb 5-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 
[237248.051124] usb 5-1: Product: SheevaPlug JTAGKey FT2232D B 
[237248.051129] usb 5-1: Manufacturer: FTDI 
[237248.051134] usb 5-1: SerialNumber: FTSJA9Q3 
[237248.051332] usb 5-1: configuration #1 chosen from 1 choice 
[237248.060243] usb 5-1: Ignoring serial port reserved for JTAG 
[237248.065218] ftdi_sio 5-1:1.1: FTDI USB Serial Device converter detected 
[237248.065270] usb 5-1: Detected FT2232C 
[237248.065275] usb 5-1: Number of endpoints 2 
[237248.065280] usb 5-1: Endpoint 1 MaxPacketSize 64 
[237248.065285] usb 5-1: Endpoint 2 MaxPacketSize 64 
[237248.065289] usb 5-1: Setting MaxPacketSize 64 
[237248.066200] usb 5-1: FTDI USB Serial Device converter now attached to ttyUSB0 

Auf dieses "ttyUSB0" kann ich im Dateisystem meines Debians zugreifen: es liegt unter /dev/ttyUSB0. Ich setze das Programm "screen" darauf an:

$ screen /dev/ttyUSB0 115200   # "115200" ist die erwartbare Baudrate der Verbindung 

Aus irgendwelchen Gründen erzeugt "screen" nicht sofort eine zufriedenstellende Anzeige: Ich muss ein-zwei mal Enter drücken. Dann aber sollte sich meine Konsole dank "screen" in einen Terminal zu dem verwandeln, was der SheevaPlug an seiner seriellen Schnittstelle bereithält. Der Bootloader des SheevaPlug etwa, "U-Boot", wird mit der Voreinstellung ausgeliefert, eine serielle Schnittstelle auf sich offenzuhalten und mit einer Baudrate von 115200 zu beliefern (daher kommt die Zahl im obigen Befehl).

Achtung: das USB-Kabel rutscht sehr leicht aus dem SheevaPlug raus. Wenn's nicht funktioniert also besser mal nachgucken, ob's richtig drinne steckt.

U-Boot-Bootloader updaten

Hierfür konnte ich beinahe genau den Anweisungen in Martin Michlmayers Gebrauchsanweisung unter http://www.cyrius.com/debian/kirkwood/sheevaplug/uboot-upgrade.html folgen. Meine geringfügigen Abweichungen, allein durch Fehleranfälligkeit meinerseits, erfordern aber dennoch nochmal einen Nachvollzug (aus dem Gedächtnis rekonstruiert, ohne nochmaliges Durchprobieren, also nicht absolut verlässlich):

Ich brauchte erstmal ein Binary für den neuen Bootloader. Ich wähnte mich zuerst besonders schlau und dachte mir, ich fände ein aktuelleres als das von Michlmayer verlinkte "U-Boot 3.4.27+pingtoo binary"; aber das, was ich nach endlosem Umhersuchen in den PlugComputer-Foren stolz mein eigen nannte, stellte sich im Nachhinein als eben dasselbe heraus. Der nächste Fehler, den ich machte, war, es im versuchten Befolgen von Michlmayers Anweisungen in Unachtsamkeit in "u-boot.bin" statt "uboot.bin" zu benennen, was später einige Verwirrung beim buchstabengetreuen Kopieren von Befehlen stiften würde; das hab ich in diesem Nachvollzug aber bereits auskorrigiert.

Ich entschied mich unter den beiden von Michlmayer angebotenen Methoden für Installation via USB-Stick. Ein FAT-formatierter USB-Stick wurde verwandt, und ich glaubte mich in Besitz eines eben solchen, kopierte "uboot.bin" drauf, rammte den USB-Stick danach in Sheevas Ritze und gab im U-Boot-Prompt korrekt Folgendes ein:

Marvell>> usb start 
Marvell>> fatload usb 0:1 0x0800000 uboot.bin 

Die erste Zeile zeitigte den gewünschten Erfolg, der USB-Stick wurde erkannt. Die zweite Zeile -- grob lesbar als "fatload (lade von einem FAT-Dateisystem ...) usb 0:1 (... am USB-Interface soundso ...) 0x0800000 (... auf Arbeitsspeicheradresse soundso ...) uboot.bin (... diese Datei)" -- jedoch spuckte beim ersten Durchgang irgendeine Fehlermeldung aus. Nach viel Umhergooglen schälte sich der Verdacht heraus, das könnte an der FAT-Partition auf dem USB-Stick liegen. Offenbar hat U-Boot Probleme mit den Partitionen, wie sie zumindest mein Debian bastelt, wenn ich "cfdisk" und "mkfs.vfat" anwende.

Aus irgendwelchen Gründen lies sich das Problem durch erhebliche Verkleinerung der Partition auf dem USB-Stick und Verwendung von FAT16 anstatt einer neueren FAT-Version lösen; für auch alle weiteren Datei-Transfers in den kommenden Abschnitten reicht locker eine Partition von zweihundert Megabyte Größe. Nach ein bisschen Rumprobieren und Rebooten des Bootloaders (der sich weigerte, einen einmal entfernten (und via "usb stop" hoffentlich sauber unmounteten?) USB-Stick im selben Boot nochmal neu zu erkennen) klappte es: uboot.bin wurde sichtlich auf Adresse 0x0800000 im Arbeitsspiecher geschrieben.

Der nächste Schritt scheint kritisch, insoweit er den Anfangsbereich des internen NAND-Flash-Speichers des SheevaPlugs überschreibt. Hier muss der Bootloader liegen. Insofern kann man sich vermutlich leicht in Teufels Küche bringen, wenn man hier etwas falsch macht, z.B. kein brauchbares Bootloader-Image reinschreibt:

Marvell>> nand erase 0x0 0xa0000 
Marvell>> nand write 0x0800000 0x0 0xa0000 

Ich nehme das mal auseinander: "nand erase (lösche den Bereich ...) 0x0 (... der hier startet ...) 0xa0000 ( ...mit dieser Länge)"; "nand write (Bereich beschreiben: ...) 0x0800000 (... das, was an dieser Arbeitsspeicheradresse liegt, soll ...) 0x0 (... beginnend dieser Stelle in den NAND-Speicher geschrieben werden, und zwar ...) 0xa0000 (... zu dieser Länge)". Danach dann ...

Marvell>> reset 

... und der U-Boot-Bootloader sollte in einer geupdateten Version neustarten.

U-Boot einen Kernel zum Booten geben

Als Nächstes habe ich ausprobiert, von U-Boot aus einen neuen Kernel zu booten. Wohlgemerkt, nur einen neuen Kernel; was danach an Dateisystem hätte liegen können, hatte ich zu dem Zeitpunkt schon bis ins Unlesbare korrumpiert, so dass der Kernel erstmal ins Nichts würde booten müssen.

http://www.plugcomputer.org/plugwiki/index.php/Installing_Debian_To_Flash gibt einige brauchbare Hinweise und Beispiele zum Schreiben eines Kernels in den NAND-Flash des SheevaPlugs, setzt aber die Verwendung des "Trivial File Transfer Protocol" voraus, mit dem mich vertraut zu machen (oder für das gar einen Server auf meinem Debian-Laptop einzurichten?) ich zu faul bin. Stattdessen setze ich auf die bereits beim Updaten des U-Boot-Bootloaders bewährte Methode, im Bootloader Dateien über einen FAT-USB-Stick einzulesen (nähere Details siehe oben "U-Boot-Bootloader updaten").

Ich ziehe also ein SheevaPlug-geeignetes Kernel-"uImage" auf meinen USB-Stick. Ein "uImage" ist in diesem Fall ein Linux-Kernel-Image, dem noch einige Zusatz-Daten rangepappt sind, die es U-Boot-freundlicher machen; "uboot-mkimage" wäre das Tool der Wahl, wenn man sich selber ein Linux-Kernel-Image solcherart u-Boot-kompatibel verpacken möchte. Ich nehme für den Anfang mal "sheeva-2.6.35.3-uImage", zu finden hier: http://sheeva.with-linux.com/sheeva/ Also rauf auf den USB-Stick damit und den USB-Stick in Sheevas Loch gestoßen! Danach, im U-Boot-Prompt, wird der USB-Stick initialisiert:

Marvell>> usb start 
(Re)start USB... 
USB:   scanning bus for devices... 2 USB Device(s) found 
Waiting for storage device(s) to settle before scanning... 
1 Storage Device(s) found 

(Warum zwei USB-Devices? Na weil ich gleichzeitig via USB-Kabel mit dem SheevaPlug verbunden bin, um via "screen" von meinem Debian-Laptop ein Terminal-Fenster hinein zu haben.) Schauen wir uns nun auf dem USB-Stick mal um:

Marvell>> fatls usb 0:1 / 
  2795912   sheeva-2.6.35.3-uimage 
 
1 file(s), 0 dir(s) 

Vorgelesen: "fatls (liste Segmente in einem FAT-Dateisystem auf ...) usb 0:1 (... das sich an diesem USB-Interface befinden ...) / (... und zwar im Root-Verzeichnis)" Hier sehe ich die Datei, die ich als Nächstes in den Arbeitsspeicher einlesen will:

Marvell>> fatload usb 0:1 0x2000000 sheeva-2.6.35.3-uimage 
reading sheeva-2.6.35.3-uimage 
..................................................................... 
..................................................................... 
..................................................................... 
.................................................................. 
 
2795912 bytes read 

Ich habe das uImage jetzt in den Arbeitsspeicher an Adresse 0x2000000 eingelesen. (Die Adresse habe ich aus http://www.plugcomputer.org/plugwiki/index.php/Installing_Debian_To_Flash übernommen, k.A., was der Toleranzbereich für eine andere Position wäre.) Bevor ich jetzt U-Boot anweise, das an dieser Stelle gelegene Image zu booten, überprüfe ich nochmal, ob an der Adresse jetzt tatsächlich das liegt, was da liegen soll, und zwar mittels "iminfo":

Marvell>> iminfo 0x2000000 
 
## Checking Image at 02000000 ... 
   Image Name:   Linux-2.6.35.3 
   Created:      2010-08-21   0:22:34 UTC 
   Image Type:   ARM Linux Kernel Image (uncompressed) 
   Data Size:    2795848 Bytes =  2.7 MB 
   Load Address: 00008000 
   Entry Point:  00008000 
   Verifying Checksum ... OK 

"iminfo" ist ein U-Boot-Programm, das sich an eben den Zusatz-Kopfdaten abzuarbeiten scheint, die "uboot-mkimage" einem Image aufbürdet, wenn es in ein uImage umwandelt. Sieht ja soweit alles ok aus.

(Nachtrag: Alle Welt sagt, man solle bei Verwendung von etwas Anderem als dem mitgelieferten Kernel auch noch Folgendes setzen (ohne dass ich auch nur halbwegs verstehen würde, warum):

Marvell>> setenv mainlineLinux yes 
Marvell>> setenv arcNumber 2097 

Das hab ich hier an der Stelle wohl ursprünglich aufzuführen vergessen, weil ich diese Umgebungsvariable bei früheren Versuchen schon gesetzt und gespeichert hatte, so dass ich sie nicht mehr neu setzen brauchte. Ob der Kernel auch ohne booten würde, bin ich jetzt auszuprobieren zu faul.)

Also booten wir mal! Das geht ganz einfach, indem ich das Programm "bootm" auf besagte Arbeitsspeicheradresse jage. "bootm" scheint nochmal "iminfo" aufzurufen und schiebt danach den Kernel-Code durch den Prozessor:

Marvell>> bootm 0x2000000 
## Booting image at 02000000 ... 
   Image Name:   Linux-2.6.35.3 
   Created:      2010-08-21   0:22:34 UTC 
   Image Type:   ARM Linux Kernel Image (uncompressed) 
   Data Size:    2795848 Bytes =  2.7 MB 
   Load Address: 00008000 
   Entry Point:  00008000 
   Verifying Checksum ... OK 
OK 
 
Starting kernel ... 
 
Uncompressing Linux... done, booting the kernel. 
Linux version 2.6.35.3 (kelly@speedy) (gcc version 4.4.3 (Sourcery G++ Lite er) ) #1 PREEMPT Fri Aug 20 18:22:29 MDT 2010 

(...)

Es folgen jetzt die fünf Milliarden Bildschirme eines Bootvorganges voller kryptischer Meldungen über initialisierte Speicher, Treiber usw. Gegen Ende kommt dann die erwartbare Todesmeldung, weil der Kernel leider nichts Root-Dateisystem-Brauchbares vorfindet:

No filesystem could mount root, tried:  ext3 ext2 ext4 cramfs vfat msdos jfs 
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(31,1) 

Aber hey, bis hierher ist ja schonmal ganz schön weit ;-)

Lust, den Kernel permanent zu schreiben? Dann mal rasch im Schnelldurchlauf:

Marvell>> usb start 
Marvell>> fatload usb 0:1 0x2000000 sheeva-2.6.35.3-uimage 
Marvell>> nand erase 0x100000 0x400000 
Marvell>> nand write 0x2000000 0x100000 0x400000 
Marvell>> setenv mainlineLinux yes 
Marvell>> setenv arcNumber 2097 
Marvell>> saveenv 

Die Adresse 0x100000 ist die Default-Position des Kernels, und die Länge 0x400000 der Default-Raum für ihn. Wie man diese Defaults evtl. anpassen könnte, erzähle ich vielleicht im übernächsten Abschnitt. Dasselbe gilt für die Umgebungsvariable "bootargs", deren Anpassung eventuell auch für vollen Boot-Erfolg noch notwendig sein könnte; alles im übernächsten Abschnitt näher erörtert. Um bis dahin zu gelangen, sollte es erstmal reichen, obige Zeilen zu setzen, selbst wenn der Kernel jetzt noch nicht zufriedenstellend bootet.

Ein minimales Debian installieren, Schritt 1: das Dateisystem-Image bauen

Gut, ein Kernel ist also drauf auf dem SheevaPlug-internen NAND-Flash-Speicher, aber er hat noch nichts zum Reinbooten. Da helf ich natürlich gerne nach. Weitere wertvolle Hilfen gaben mir dabei folgende Tutorials / Infosammlungen, von denen sich aber keines eins zu eins hier abgebildet findet:

Zuerst baue ich auf meinem Laptop-Debian ein kleines Debian-Dateisystem mit dem Programm "debootstrap":

# debootstrap --foreign --arch=armel --variant=minbase --include=netbase,iproute,ifupdown,dhcp-client stable debian-stable/ 
I: Retrieving Release 
I: Retrieving Packages 
I: Validating Packages 
I: Resolving dependencies of required packages... 
I: Resolving dependencies of base packages... 
I: Found additional base dependencies: debian-archive-keyring dhcp3-client dhcp3-common gnupg gpgv libbz2-1.0 libreadline5 libusb-0.1-4 net-tools readline-common  
I: Checking component main on text-decoration: none;" href="http://ftp.us.debian.org/debian...">http://ftp.us.debian.org/debian.../] 
I: Retrieving libacl1 
I: Validating libacl1 
I: Retrieving apt 

(...)

I: Extracting mount... 
I: Extracting util-linux... 
I: Extracting zlib1g... 

Dieser Befehl schreibt in das Verzeichnis debian-stable/ die vollständige Hierarchie eines minimalen Debian-Systems. Der Parameter "--variant=minbase" trifft diese Minimal-Auswahl; wenn debootstrap mit dem obigen Durchgang fertig ist, wird das halbfertige System unkomprimiert ungefähr 100 MiB groß sein. Der Paramter "--arch=armel" gibt an, dass dabei die Programm-Varianten für die Armel-Prozessor-Architektur gezogen werden sollen, denn die enthaltenen Binaries sollen ja nicht auf meinem Laptop, sondern auf dem SheevaPlug laufen, der eben dieser Prozessor-Architektur folgt. Der Parameter "--foreign" gibt an, dass ich das System nicht hier vor Ort auf diesem Laptop zum Laufen bringen möchte, sondern auf einem anderen Gerät und deshalb die Finalisierung der System-Einrichtung in einer zweiten Stufe später/dort vorgenommen werden wird. Der Parameter "--include=netbase,iproute,ifupdown,dhcp-client" fügt der Minimal-Auswahl ein paar zusätzliche Pakete hinzu, die es mir im fertigen System erlauben werden, eine Internet-Verbindung herzustellen, so dass mit apt-get weitere Debian-Pakete reingeholt werden können.

Jetzt habe ich, im Verzeichnis debian-stable/, einen Debian-Verzeichnisbaum vor mir liegen, aber wie krieg ich den auf den SheevaPlug? So mächtig der U-Boot-Bootloader auch ist, er kann nicht einfach einen solchen Verzeichnisbaum in seinen rohen Flash-Speicher schreiben. Ich muss das Ganze verpacken in das Byte für Byte in den Speicher zu schreibende Image eines Dateisystems, das mit den Eigenheiten des NAND-Flash-Speichers im SheevaPlug kompatibel ist.

Nun habe ich sehr lange herumexperimentiert und dabei viel Müll produziert, der, in den Flash-Speicher geschrieben, mal gar nicht und mal nur fehlerproduzierend / Boot-Sequenz-abbrechend vom Kernel erkannt wurde. Der rohe Flash-Speicher ist ja etwas Anderes als die Festplatten, für die die PC-üblichen Dateisysteme-ausgelegt sind; dementsprechend erfordert er auch eigensinnige Dateisysteme. Zuerst probierte ich, mittels "mkfs.jffs2" aus dem Debian-Package "mtd-utils" (MTD, Memory Technology Devices, ist ein Linux-Projekt, um die Flash-Speicher-Welt zu erschließen), das Erzeugen eines JFFS2-Images und war schon stolz, als der Kernel immerhin beim Booten die Namen einiger Dateien mit Fehlermeldungen durchratterte, die offenbar aus meinem debian-stable/-Verzeichnis stammten; er ächzte und meckerte aber nur über sie und weigerte sich, irgendwas davon als bootfähiges Root-Verzeichnis anzuerkennen.

Mein zweiter Versuch galt UBIFS, das das neuere und bessere und hippere JFFS2 sein soll. Gleichzeitig ist es aber auch irgendwie komplizierter; im Gegensatz zu JFFS2 handelt man sich damit nämlich nicht nur einfach ein "mkfs.ubifs" ein, sondern eine ganze Kette von Tools, die übereinandergestülpt werden müssen. Denn eigentlich ist UBIFS nur ein Aufsatz auf UBI (Unsorted Block Devices), einer Schichtung über dem rohen Flash-Speicher, die schonmal ein bisschen was von dem Bodendreck wegabstrahiert, und UBIFS ist dann erst die nächste Abstraktionsstufe. Man muss über den rohen Flash-Speicher ein UBI-Device layern, in diesem UBI-Device dann einzelne Volumes anlegen, und in diesen Volumes kann man dann ein UBIFS-Dateisystem laufen lassen. Puh! Nach viel Rumprobieren und Umwegen hab ich dann aber doch eine grobe Vereinfachung gefunden, die wie folgt aussieht:

Zuerst würge ich das debian-stable/-Verzeichnis in das Image eines UBIFS-Dateisystems:

# mkfs.ubifs -v -r debian-stable/ -m 2048 -e 129024 -c 4096 -x zlib -o ubifs.img 
mkfs.ubifs 
        root:         debian-stable/ 
        min_io_size:  2048 
        leb_size:     129024 
        max_leb_cnt:  4096 
        output:       ubifs.img 
        jrn_size:     8388608 
        reserved:     0 
        compr:        zlib 
        keyhash:      r5 
        fanout:       8 
        orph_lebs:    1 
        super lebs:   1 
        master lebs:  2 
        log_lebs:     5 
        lpt_lebs:     2 
        orph_lebs:    1 
        main_lebs:    543 
        gc lebs:      1 
        index lebs:   9 
        leb_cnt:      554 
        UUID:         7C78CF08-339A-4570-84DE-DED2AAA0AD89 
Success! 

Wie man an den ganzen seltsamen Parametern zum Befehl und an der beim Image-Erzeugen ausgepuckten Liste von Werten sieht, kann man nicht mal eben so ein UBIFS-Image erzeugen, sondern muss diverse Größen beilegen, die der zu beschreibenden Speichergeometrie eigen sind. Was die Parameter-Angaben im Einzelnen bedeuten, lässt sich durch "mkfs.ubifs --help" rausfinden; ich habe sie 1:1 von http://www.digriz.org.uk/kirkwood übernommen, der für ziemlich genau die gleiche Situation / das gleiche Gerät baute.

Die Erledigung des ganzen restlichen Überbaus lässt sich mit dem Programm "ubinize" abkürzen. Dieses soll ein Image erzeugen, das man einfach so ohne weiteres Rumbasteln in den rohen Flash-Speicher schreiben kann, um ein von modernen Linux-Kernels bootbares UBI/UBIFS-Dateisystem zu erhalten. Nun wäre es natürlich zu einfach, wenn dafür einfach ein "ubinize" auf das ubifs.img reichen würde; man muss auch hier die mehr oder weniger gleichen Parameter zur Speichergeometrie eingeben und außerdem auch noch eine Konfigurationsdatei anlegen, die das ganze UBI-Organisations-Zeugs zwischen Device und Volumes und Dateisystemen usw. beschreibt. Diese Datei "ubi.cfg" sah in meinem Fall so aus:

$ cat ubi.cfg 
[ubifs] 
mode=ubi 
image=ubifs.img 
vol_id=0 
vol_size=160MiB 
vol_type=dynamic 
vol_name=rootfs 
vol_flags=autoresize 

Es wird also aus dem "image=ubifs.img" ein "[ubifs]"-Volume gebaut mit dem Namen "rootfs". Wie man vor allem an der etwas beliebigen Angabe "vol_size=160MiB" sieht, hab ich auch diese Zeilen irgendwo weggeklaut, ich weiß nur nicht mehr, woher. Wenn ich es richtig verstanden habe, ist diese Größen-Angabe aufgrund des "vol_flags=autoresize" einigermaßen egal und sollte wohl nur ungefähr groß genug sein, damit das ubifs.img reinpasst; aber ich lasse mich gerne eines Besseren belehren. Das "vol_type=dynamic" besagt, dass das Dateisystem nicht nur lesbar (das wäre "static"), sondern auch beschreibbar sein möge. Was genau "mode=ubi" entscheidet und ob es überhaupt notwendig ist, hab ich keine Ahnung :-)

Mit dieser Datei in der Hand kann ich jetzt "ubinize" anwerfen:

# ubinize -v -m 2048 -p 128KiB -s 512 ubi.cfg -o ubi.img 
ubinize: LEB size:      129024 
ubinize: PEB size:      131072 
ubinize: min. I/O size: 2048 
ubinize: sub-page size: 512 
ubinize: VID offset:    512 
ubinize: data offset:   2048 
ubinize: loaded the ini-file "ubi.cfg" 
ubinize: count of sections: 1 
 
ubinize: parsing section "ubifs" 
ubinize: mode=ubi, keep parsing 
ubinize: volume type: dynamic 
ubinize: volume ID: 0 
ubinize: volume size: 167772160 bytes 
ubinize: volume name: rootfs 
ubinize: volume alignment: 1 
ubinize: autoresize flags found 
ubinize: adding volume 0 
ubinize: writing volume 0 
ubinize: image file: ubifs.img 
 
ubinize: writing layout volume 
ubinize: done 

Heraus kommt eine Datei "ubi.img", die ich jetzt theoretisch so 1:1 in den NAND-Speicher schreiben können müsste. Sie enthält die 100 MiB debian-stable/ auf unter 70 MiB runterkomrpimiert.

Ein minimales Debian installieren, Schritt 2: Image in den NAND-Speicher schreiben, booten

Das fertige "ubi.img" hab ich mir dann auf meinen FAT16-USB-Stick kopiert, und rein ging's wieder in den U-Boot-Bootloader-Prompt vom SheevaPlug ...

Marvell>> usb start 
(Re)start USB... 
USB:   scanning bus for devices... 2 USB Device(s) found 
Waiting for storage device(s) to settle before scanning... 
1 Storage Device(s) found 
Marvell>> fatls usb 0:1 / 
 72876032   ubi.img 
 
1 file(s), 0 dir(s) 

Alles da, prima. Also rein in den ArbeitsSpeicher damit ...

Marvell>> fatload usb 0:1 0x800000 ubi.img 
reading ubi.img 
......................... 
..................................................................... 
..................................................................... 

(...)

72876032 bytes read 

... und dann den ganzen internen NAND-Speicher nach dem Kernel leer machen, tabula rasa über allem, was vorher da war an Dateisystemen, damit das Image Platz hat, sich frei zu entfalten:

Marvell>> nand erase 0x500000 0x1fb00000 
 
NAND erase: device 0 offset 0x500000, size 0x1fb00000 
Skipping bad block at  0x09660000                                           
Skipping bad block at  0x09760000                                           
Skipping bad block at  0x0fba0000                                           
Skipping bad block at  0x110c0000                                           
Skipping bad block at  0x12420000                                           
Skipping bad block at  0x126e0000                                           
Skipping bad block at  0x16920000                                           
Skipping bad block at  0x1a1c0000                                           
Skipping bad block at  0x1aca0000                                           
Skipping bad block at  0x1bdc0000                                           
Skipping bad block at  0x1c900000                                     
Erasing at 0x1ffe0000 -- 100%25 complete. 
OK 

Zwei Anmerkungen zu diesem Schritt: Erstens ist die "size" von 0x1fb00000 genau die Größe, die nach 0x500000 (wo der für den Kernel frei gehaltene Speicher-Bereich endet) noch insgesamt in den 512 MiB des NAND-Speichers verbleibt. Zweitens fallen die "bad blocks" auf, die hier zu sehen sind. Bei meinen vorherigen Lösch- und Schreib-Vorgängen im NAND-Speicher in Sachen Bootloader- und Kernel-Update stieß ich nicht auf Bad Blocks; die liegen nämlich weiter hinten. Sie sind keinen großen Alarm wert; einige Bad Blocks kann man ab Werk erwarten. Ich sollte sie allerdings im Hinterkopf behalten, wenn ich ein sich breiter über den Speicher ausbreitendes Image schreibe:

Marvell>> nand write.e 0x800000 0x500000 0x4580000 
 
NAND write: device 0 offset 0x500000, size 0x4580000 
 
Writing data at 0x4a7f800 -- 100%25 complete. 
 72876032 bytes written: OK 

Erklärung: Ich habe "nand write.e" genommen statt "nand write", weil ersteres schlechte Blöcke einfach überspringt, anstatt schockiert vor ihnen anzuhalten und abzubrechen. (In diesem konkreten Fall ist das Image zu klein, um beim Schreiben auf einen Bad Block zu stoßen, aber nur für den Fall ... lieber immer diesen Befehl benutzen.) Geschrieben wird das im Arbeitsspeicher an Adresse 0x800000 liegende auf den Bereich ab Adresse 0x500000, und zwar zur Länge 0x4580000. Wo kommt die her? Sie ist die Länge des Images in Bytes dargestellt im Hexadezimalsystem; im Dezimalsystem war's noch die Ziffernfolge 72876032. Fürs rasche Umrechnen zwischen beiden Darstellungsweisen gibt es sicher Millionen Dienstleister, zum Beispiel diese Webseite, die ich verwendet habe: http://www.statman.info/conversions/hexadecimal.html

So, und jetzt? Reicht das, damit der Kernel booten kann? Nein, natürlich nicht. Denn woher soll er wissen, dass das Dateisystem für ihn an 0x500000 beginnt? Ich muss ihn aufklären. Das tu ich mit der Umgebungsvariable "bootargs", die dem Kernel Boot-Parameter übergibt:

Marvell>> setenv bootargs=console=ttyS0,115200 mtdparts=orion_nand:0x100000@0x000000 
(u-boot),0x400000@0x100000(uImage),0x1fb00000@0x500000(root) ubi.mtd=2 root=ubi0:roo 
tfs rootfstype=ubifs 
Marvell>> saveenv 
Saving Environment to NAND... 
Erasing Nand...Writing to Nand... done 

"console=ttyS0,115200" ist eigentlich nicht Boot-relevant, sagt dem Kernel aber, dass er an die serielle Schnittstelle des SheevaPlug mit 115200 Baud seine Ausgabe lenken soll, was es mir erlaubt, via USB-Kabel und "screen /dev/ttyUSB0 115200" in den Kernel-Bootvorgang reinzugucken, also schonmal extrem praktisch ist.

"mtdparts=" (MTD Part[ition]s) kündigt nichts Geringeres an als eine ganze Partitionstabelle: Der interne Flash-Speicher wird vom Kernel an der Bezeichnung "orion_nand" erkannt (bzw. bei Problemen stattdessen mal "nand_mtd" ausprobieren; was hier greift, scheint abhängig vom Kernel in der selben Logik zu stecken wie der Wechsel zu "mainlineLinux yes" und "arcNumber 2097", den ich ja auch schon nicht verstanden habe), es folgen Festlegungen der Positionen und Größen seiner Partitionen (nach dem Muster "Größe"@"Position"), wobei jede Partition auch noch in Klammern einen Namen hinterhergeworfen bekommt.

"ubi.mtd=2" kennzeichnet die zweite dieser MTD-Partitionen als UBI. "root=ubi0:rootfs" benennt innerhalb dieser UBI-Partition das erste (nullte) Volume mit dem Namen "rootfs" als die Unter-Partition, die dem Kernel als Root-Partition zu übergeben ist. "rootfstype" sagt dem Kernel, dass er seine Root-Partition als Dateisystem UBIFS booten soll.

Gucken wir mal in den Kernel, wie er bootet, nachdem ich mit "reset" jetzt den SheevaPlug neu starte:

Starting kernel ... 
 
Uncompressing Linux... done, booting the kernel. 
Linux version 2.6.35.3 (kelly@speedy) (gcc version 4.4.3 (Sourcery G++ Lite er) ) #1 PREEMPT Fri Aug 20 18:22:29 MDT 2010 

Bis hierhin alles ok. Ein bisschen später dann:

NAND device: Manufacturer ID: 0xad, Chip ID: 0xdc (Hynix NAND 512MiB 3,3V 8-bit) 
Scanning device for bad blocks 
Bad eraseblock 1203 at 0x000009660000 
Bad eraseblock 1211 at 0x000009760000 
Bad eraseblock 2013 at 0x00000fba0000 
Bad eraseblock 2182 at 0x0000110c0000 
Bad eraseblock 2337 at 0x000012420000 
Bad eraseblock 2359 at 0x0000126e0000 
Bad eraseblock 2889 at 0x000016920000 
Bad eraseblock 3342 at 0x00001a1c0000 
Bad eraseblock 3429 at 0x00001aca0000 
Bad eraseblock 3566 at 0x00001bdc0000 
Bad eraseblock 3656 at 0x00001c900000 

Ok, die selben bösen Blöcke wie erwartet. Unmittelbar darauf:

Creating 3 MTD partitions on "orion_nand": 
0x000000000000-0x000000100000 : "u-boot" 
0x000000100000-0x000000500000 : "uImage" 
0x000000500000-0x000020000000 : "root" 

Hier sehen wir die Partitionstabelle, die wir als Boot-Argument mit übergeben haben.

Unmittelbar darauf treten wir offenbar in die schöne UBI-Welt ein:

UBI: attaching mtd2 to ubi0 
UBI: physical eraseblock size:   131072 bytes (128 KiB) 
UBI: logical eraseblock size:    129024 bytes 
UBI: smallest flash I/O unit:    2048 
UBI: sub-page size:              512 
UBI: VID header offset:          512 (aligned 512) 
UBI: data offset:                2048 
UBI: volume 0 ("rootfs") re-sized from 1301 to 4001 LEBs 
UBI: attached mtd2 to ubi0 
UBI: MTD device name:            "root" 
UBI: MTD device size:            507 MiB 
UBI: number of good PEBs:        4045 
UBI: number of bad PEBs:         11 
UBI: max. allowed volumes:       128 
UBI: wear-leveling threshold:    4096 
UBI: number of internal volumes: 1 
UBI: number of user volumes:     1 
UBI: available PEBs:             0 
UBI: total number of reserved PEBs: 4045 
UBI: number of PEBs reserved for bad PEB handling: 40 
UBI: max/mean erase counter: 1/0 
UBI: image sequence number: 0 
UBI: background thread "ubi_bgt0d" started, PID 462 

Die erste hervorgehobene Stelle, wenn man mal nachrechnet (also jeweils mit der Eraseblock-Größe multipliziert), versichert mir, dass tatsächlich die in der "ubi.cfg" gesetzten "160 MiB" für das Volumen jetzt auf ein mögliches Maximum hochgeschraubt wurden; "auto-resize" at work! Die zweite hervorgehobene Stelle gibt mir einen Wert für die größe des UBI-Devices aus, der gut Sinn ergibt, wenn man von den 512 MiB des Flash-Speichers 1 MiB für den Bootloader und 4 MiB für den Kernel abzieht. Die dritte hervorgehobene Stelle zählt nochmal die bösen Blöcke auf; hier sollte ich drauf achten, dass diese nicht durch irgendwelche Probleme unverhältnismäßig schnell mehr werden.

Der vierte hervorgehobene Bereich macht mich auf ein nicht zu verleugnendes Problem meines Hauruck-Verfahrens "schreibe einfach mal fremderorts generierte Images in den hiesigen Flash-Speicher" aufmerksam. Das UBI-System versucht nämlich bei jedem "erase block" zu zählen, wie oft er gelöscht wurde; die erwartbare Lösch-Zahl, bevor ein solcher Bereich den Geist aufgibt, ist schließlich endlich! "max/mean erase counter" gibt Auskunft über den höchsten im Einzelfall auftretenden und den mittleren allgemeinen Schonmal-gelöscht-worden-sein-Wert; dass der hier so niedrig ist, zeigt, dass etwaiges Wissen der Blöcke "wie oft wurde ich schon gelöscht?" durch mein Brute-Force-Verfahren überschrieben wurde. Solches Wissen könnte UBI benutzen, um noch nicht so oft gelöschte Blöcke zur Schonung schon oft gelöschter Blöcke bei neuen Löschvorgängen vorzuziehen ("wear leveling"). Nun ist der Speicher meines SheevaPlugs aber vorher nur sehr geringfügig strapaziert worden, insofern ist die so verursachte Verfälschung momentan noch verschmerzbar. Näheres siehe hier: http://www.linux-mtd.infradead.org/doc/ubi.html#L_format Gucken wir jetzt erstmal weiter, zum Ende des Kernel-Monologs:

UBIFS: mounted UBI device 0, volume 0, name "rootfs" 
UBIFS: file system size:   514805760 bytes (502740 KiB, 490 MiB, 3990 LEBs) 
UBIFS: journal size:       9033728 bytes (8822 KiB, 8 MiB, 71 LEBs) 
UBIFS: media format:       w4/r0 (latest is w4/r0) 
UBIFS: default compressor: zlib 
UBIFS: reserved for root:  0 bytes (0 KiB) 
VFS: Mounted root (ubifs filesystem) on device 0:13. 
Freeing init memory: 140K 
INIT: version 2.86 booting 
INIT: No inittab file found 
 
Enter runlevel:  

Die ersten beiden hervorgehobenen Zeile spucken schon wieder neue interessante Größenangaben aus. Eben hatten wir doch noch 507 MiB, wieso gibt's jetzt nur noch 490 MiB? Die 490 MiB sind "3990 LEBs", also "logical erase blocks"; diese sind wegen UBI-Metadaten stets ein bisschen kleiner als die "physical erase blocks" im Speicher. Rundet man die LEBs (je 126 KiB) auf PEBs (je 128 KiB) auf, kommen wir schon auf 498 MiB. Das ebenfalls Metadaten-reiche "Journal" von UBIFS verschlingt, wie die zweite hervorgehobene Zeile zeigt, auch nochmal 8 MiB. Fehlt noch ein MiB; der dürfte locker (wenn auch nicht ganz genau; irgendwer hat hier irgendwo abgerundet) in den elf "bad blocks" aufgehen.

Die dritte hervorgehobene Zeile kündet vom Erfolg meines Vorhabens: Yay, in dem Image, was ich in den Speicher geschrieben habe, findet der Kernel tatsächlich irgendeine Verzeichniswurzel, in die er hineinbooten kann! Die verbleibenden hervorgehobenen Zeilen dämpfen die Freude dann wieder etwas: Der Kernel möchte gerne an den "init"-Prozess abgeben. Der braucht aber eine Datei /etc/inittab, um sich zu orientieren; ohne die weiß er nicht viel zu tun. Im "Enter runlevel: "-Prompt könnte ich ein bestimmtes "Runlevel", also den Index eines Katalogs an weiteren aufzurufenden Schritten spezifieren; aber ohne inittab gibt es keine solchen Kataloge. Offenkundig ist das System doch noch nicht ganz aus sich selbst heraus bootbar; aber keine Sorge, es läuft alles nach Plan.

Ein minimales Debian installieren, Schritt 3: Dateisystem in eine funktionstüchtige Form bringen

Ich verweise zurück auf den "--foreign"-Parameter, den ich zu Anfang dem "debootstrap"-Befehl übergab: Der weist debootstrap an, den Bau des Systems in zwei Stufen zu unterteilen. Der erste davon kann egal wo stattfinden und baut ein System, das auch schon ans Ziel geflasht werden kann; dort braucht es aber einen Kernel, der reinbootet und an einen Benutzer übergibt, der notwendige Schritte ausführen kann, um das System fertig zu bekommen. Genaugenommen muss vor Ort in dem Dateisystem dann nämlich "debootstrap --second-stage" aufgerufen werden.

Aber ich kann ja nichts weiter eintippen als ein nicht vorhandenes Runlevel! Wie soll ich da irgendwas im System machen, geschweige denn "debootstrap --second-stage" aufrufen? Ganz einfach: Ich weise den Kernel an, als Init eine Shell zu öffnen, anstatt nach einer Inittab zu suchen, die noch gar nicht da ist. Mit dem Boot-Parameter "init=[Programmpfad]" kann ich den Kernel anweisen, seinen Init-Prozess ein Programm im Verzeichnisbaum aufzurufen. Also erweitere ich im U-Boot-Prompt die "bootargs"-Umgebungsvariable:

Marvell>> setenv bootargs=console=ttyS0,115200 mtdparts=orion_nand:0x100000@0x000000 
(u-boot),0x400000@0x100000(uImage),0x1fb00000@0x500000(root) ubi.mtd=2 root=ubi0:roo 
tfs rootfstype=ubifs init=/bin/bash 
Marvell>> saveenv 
Marvell>> reset 

Beim nächsten Boot sieht alles schon freundlicher aus:

VFS: Mounted root (ubifs filesystem) on device 0:13. 
Freeing init memory: 140K 
I have no name!@(none):/#  

Um zu testen, ob ich wirklich Befehle ausführen kann und das von debootstrap gebaute Dateisystem vor mir habe, geb ich mal "ls" ein:

# ls 
bin   debootstrap  etc   lib  proc  sbin     sys  usr 
boot  dev          home  mnt  root  selinux  tmp  var 

Prima! Alles da, wo es sein sollte. Dann machen wir uns mal an die Arbeit. Das debootstrap-Programm liegt intuitiverweise im Verzeichnis /debootstrap/:

# /debootstrap/debootstrap --second-stage 
I: Installing core packages... 
I: Unpacking required packages... 
I: Unpacking libacl1... 
I: Unpacking libattr1... 

(...)

I: Base system installed successfully. 

Er entpackt und konfiguriert fleißig bis zur abschließenden Erfolgsmeldung. Und damit ist theoretisch das Debian fertig eingerichtet. Sogar eine inittab ist jetzt unter /etc/inittab vorhanden.

Aber halt! Eine kleine Sachen muss jetzt noch angepasst werden, um weiter Freude an der Installation zu haben. Ich teile dem System mit, dass init beim Initialisieren ein TTY an der seriellen Schnittstelle aufmachen soll, mit der Baudrate 115200. Genau für sowas ist die inittab da:

# echo 'T0:2345:respawn:/sbin/getty -L ttyS0 115200 linux' >> /etc/inittab 
# mknod -m 660 /dev/ttyS0 c 4 64 
# sync 

In der ersten Zeile weise ich an, dass für die "runlevel" / Boot-Stufen 2,3,4,5 das Programm /sbin/getty ein TTY am Device ttyS0 mit der erwünschten Baudrate eröffnen soll. Das Device ttyS0 allerdings existiert gar nicht ohne Weiteres; ich muss es erst anlegen. Das mache ich in der zweiten Zeile. Und in der dritten Zeile stelle ich sicher, dass diese Veränderungen auch wirklich vom Kernel in den permanenten Speicher und nicht nur in einen Cache geschrieben wurden.

Nun wieder von vorne, in den Boot-Loader. Dort nehme ich das "init=/bin/bash" wieder aus der "bootargs"-Umgebungsvariable raus:

Marvell>> setenv bootargs=console=ttyS0,115200 mtdparts=orion_nand:0x100000@0x000000 
(u-boot),0x400000@0x100000(uImage),0x1fb00000@0x500000(root) ubi.mtd=2 root=ubi0:roo 
tfs rootfstype=ubifs 
Marvell>> saveenv 

Dann wird nochmal neu gebootet, und siehe da:

INIT: version 2.86 booting 

(...)

INIT: Entering runlevel: 2 
 
Debian GNU/Linux 5.0 plom-pad ttyS0 
 
plom-pad login: 

Init scheint alles vorzufinden, was es sucht, und ist außerdem so nett, mir einen Login-Prompt an den ttyS0 zu legen.

plom-pad login: root 
 
Linux plom-pad 2.6.35.3 #1 PREEMPT Fri Aug 20 18:22:29 MDT 2010 armv5tel 
 
The programs included with the Debian GNU/Linux system are free software; 
the exact distribution terms for each program are described in the 
individual files in /usr/share/doc/*/copyright. 
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent 
permitted by applicable law. 
plom-pad:~#  

Wunderbar! Ich kann mich als "root" einloggen. Da ich noch kein Passwort gesetzt habe, geht das sogar ohne Passwortabfrage. Evtl. nicht der sicherste dauerhafte Zustand, demnächst sollte ein Passwort gesetzt werden.

Eine andere Irritation ist der Hostname "plom-pad"; den hat debootstrap offenbar direkt vom System übernommen, auf dem es ausgeführt wurde, meinem Debian-bespielten Thinkpad. Ich korrigiere das mal rasch:

# echo "plom-plug" > /etc/hostname 

Jedenfalls hab ich jetzt ein System, das läuft und das ausgebaut werden kann. Apropos Ausbau, ich eröffne meiner Installation mal den Zugang zum Debian-Paket-Management:

# echo "deb ftp://ftp.de.debian.org/debian/ stable main" > /etc/apt/sources.list 

Die sources.list enthält in der aufgezeigten Form die Schlüssel zu den Debian-Paket-Repositories. Wie man an der URL sehen kann, liegen die im großen weiten Internet. Also brauche ich auch darauf Zugriff. Jetzt sollte sich auszahlen, dass ich vorhin beim debootstrap als "--include=" diverse Internet-Pakete angab. Ich stecke also das Ethernet-Kabel von meinem Router in den Sheeva-Plug und aktiviere den DHCP-Client, der diese Netzverbindung erkennen und das System dementsprechend anpassen sollte:

# dhclient 
Internet Systems Consortium DHCP Client V3.1.1 
Copyright 2004-2008 Internet Systems Consortium. 
All rights reserved. 
For info, please visit text-decoration: none;" href="http://www.isc.org/sw/dhcp/">http://www.isc.org/sw/dhcp//] 
 
Listening on LPF/eth0/00:50:43:01:69:d3 
Sending on   LPF/eth0/00:50:43:01:69:d3 
Sending on   Socket/fallback 
eth0: link up, 100 Mb/s, full duplex, flow control disabled 
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3 
DHCPOFFER from 192.168.0.1 
DHCPREQUEST on eth0 to 255.255.255.255 port 67 
DHCPACK from 192.168.0.1 
bound to 192.168.0.4 -- renewal in 115431 seconds. 

Nun weise ich das Debian-Paket-Management "apt" an, aus den in der sources.list verzeichneten Quellen sich einen Index verfügbarer Debian-Pakete zu ziehen:

# apt-get update 
Get:1 ftp://ftp.de.debian.org stable Release.gpg [1033B] 
Get:2 ftp://ftp.de.debian.org stable Release [73.8kB] 
Get:3 ftp://ftp.de.debian.org stable/main Packages [6675kB] 
Fetched 6750kB in 14s (469kB/s)                                                 
Reading package lists... Done 

Und um zu sehen, ob alles gut läuft, zieh ich gleich mal zur Probe ein solches Paket:

# apt-get install joe  
Reading package lists... Done 
Building dependency tree... Done 
The following NEW packages will be installed: 
  joe 
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. 
Need to get 382kB of archives. 
After this operation, 1180kB of additional disk space will be used. 
Get:1 ftp://ftp.de.debian.org stable/main joe 3.5-2 [382kB] 
Fetched 382kB in 1s (245kB/s) 
debconf: delaying package configuration, since apt-utils is not installed 
Selecting previously deselected package joe. 
(Reading database ... 6043 files and directories currently installed.) 
Unpacking joe (from .../archives/joe_3.5-2_armel.deb) ... 
Setting up joe (3.5-2) ... 

Nachträge

Um künftig von der seriellen Verbindung unabhängig zu sein, zuerst dafür sorgen, dass sich der SheevaPlug nach jedem Neustart selbständig mit dhclient ins Netz einwählt, also einen Aufruf "dhclient" in die /etc/rc.local schreiben; und danach mit "apt-get --no-install-recommends install ssh" SSH installieren.

Um einem im SheevaPlug erkannten USB-Stick einen Geräteknoten unter /dev/ zuzuweisen, noch mit "apt-get install udev" udev installieren. Mit "mkdir /mnt/usbstick" ein Verzeichnis anlegen und in die etc/rc.local den folgenden Befehl schreiben, um bei jedem Neustart den USB-Stick darauf zu mounten: "mount /dev/sda1 /mnt/usbstick -o noatime"

Halte die Systemzeit mit ntpd aktuell: "apt-get --no-install-recommends install ntp".

Kommentare

Keine Kommentare zu dieser Seite.

Schreibe deinen eigenen Kommentar

Kommentar-Schreiben derzeit nicht möglich: Kein Captcha gesetzt.

PlomWiki-Engine lizensiert unter der AGPLv3. Quellcode verfügbar auf GitHub.