Bootloader ========== Kurz gesagt, ein Bootloader ist die erste Software die geladen und ausgeführt wird wenn ein Computer/Embedded System startet. Er ist dafür zuständig den Betriebssystem Kernel in den Arbeitsspeicher zu laden und auszuführen sowie einfache Hardwarekomponenten zu initialisieren die in so einem frühen Stadium benötigt werden. Bootsequenz des Raspberry Pi ---------------------------- Die Bootsequenz des RPi unterteilt sich in mehere Punkte, wobei das Laden des Bootloaders und des Kernel nur einen Teil einer strikten Kette von einzelnen Schritten darstellt. 1. Der Stage 1 Bootloader wird ausgeführt, er wird aus einem On-Chip ROM geladen. Zu seiner Aufgabe gehört es den Stage 2 Bootloader zu laden. 2. Der Stage 2 Bootloader (bootcode.bin) wird von der SD-Karte geladen in den L2 Cache geladen. Seine Aufgabe ist es den SDRAM zu initialisieren und den Stage 3 Bootloader in den RAM zu laden. 3. Im Stage 3 Loader (loader.bin) wird die GPU-Firmware im elf-Format gelesen (start.elf) und anschließend ausgeführt. 4. Hier würde nun nach dem Lesen der System Konfigurationsparameter und der Kernel Parameter versucht ein Kernel Image zu laden. Allerdings haben wir hier eine zusätzliche Schicht eingezogen, die anstatt dem Kernel Image ein U-Boot-Image lädt. 5. Der U-Boot-Bootloader kann im letzten Schritt verwendet werden um den Kernel zu starten. Folgendes Bild veranschaulicht den Bootvorgang nochmal: .. image:: images/pi-boot.png Vorteile eines konfigurierbaren Bootloaders ------------------------------------------- * Flexibilität * Bootargumente * Steuerbare Bootvorgänge * Booten von unterschiedlichen Medien. SD-Karte, USB-Stick, Netzwerk, etc. U-Boot - Universal Boot Loader ------------------------------ Der umfangreichste und flexibelste Bootloader im Embedded Bereich ist unbestritten U-Boot. Die quelloffene Software steht unter der GNU GPL und wird aktiv weiterentwickelt. Gepflegt wird das Projekt von der DENX Software Engineering GmbH und einer stetig wachsenden Community die sich um das Projekt bebildet hat. Es werden unterschiedliche Prozessoren und Architekturfamilien z. B. PowerPC, ARM, AVR32 und MIPS unterstützt. Der Bootloader kann schon vor dem Kompilieren durch eine breite Palette von Scripten flexibel konfiguriert werden, d. h. geht es leicht von der Hand spezielle Varianten für unterschiedliche Anwendungsfälle zu generieren. Auch zur Laufzeit lässt sich das Verhalten durch umfangreiche Kommandozeilenbefehle bzw. eine Shell (Hush vom BusyBox-Projekt) sowie persistent speicherbare Umgebungsvariablen beeinflussen. Die offizielle Dokumentation des U-Boot-Bootloaders findet sich unter: http://www.denx.de/wiki/U-Boot/Documentation U-Boot besorgen --------------- Der Master-Branch des U-Boot Repositories findet sich hier_. .. _hier: http://git.denx.de/u-boot.git/ Um eine lokale Kopie dieses Repositories zu erstellen, wird ``git`` benötigt. .. code:: bash git clone git://git.denx.de/u-boot.git Konfiguration ------------- Die Konfiguration von U-Boot gestaltet sich relativ einfach, da sowohl für den RPi1 als auch für den RPi2 bereits vorgefertigte Konfigurationen enthalten sind. Die Konfigurationsscripte befinden sich im Verzeichnis **configs/**, will man eine bestimmte Config finden verwendet man ``grep``: .. code:: bash cd configs ls | grep rpi # Ausgabe rpi_2_defconfig rpi_defconfig Beispielhaft der Inhalt der Datei **rpi_2_defconfig**: .. code:: bash CONFIG_ARM=y CONFIG_ARCH_BCM283X=y CONFIG_TARGET_RPI_2=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_SETEXPR is not set CONFIG_PHYS_TO_BUS=y Bevor man eine neue Konfiguration erstellt, sollte man den Projektordner von alten Konfigurationen befreien. .. code:: bash make mrproper Im nächsten Schritt konfigurieren wir U-Boot für den Kompiliervorgang: .. code:: bash export CROSS_COMPILE=arm-linux-gnueabi- make rpi_2_defconfig # Ausgabe HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o SHIPPED scripts/kconfig/zconf.tab.c SHIPPED scripts/kconfig/zconf.lex.c SHIPPED scripts/kconfig/zconf.hash.c HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf # # configuration written to .config # Wie beim Linux Kernel ist es nun möglich mit ``menuconfig``, ``xconfig`` und anderen grafischen Tools die Konfiguration zu verändern. Änderungen direkt in der ``.config`` sind zwar möglich aber nicht empfohlen, weil dadurch unerfüllte Abhängigkeiten oder Widersprüche entstehen können. Möchte man verschiede Konfigurationen erstellen und auch behalten, macht es Sinn, bei der Konfiguration einen Output Parameter mitzugeben. Abschließend kann U-Boot kompiliert werden: .. code:: bash make Ohne vorherigen export der Variablen und mit Angabe eines Output Pfades sieht das dann folgendermaßen aus. .. code:: bash cd /path/to/u-boot ARCH=arm CCPREFIX=path/to/toolchain/arm-linux-gnueabihf make ARCH=${ARCH} CROSS_COMPILE=${CCPREFIX} V=1 O=/home/user/build/150610_rpi2_u-boot rpi_2_defconfig make ARCH=${ARCH} CROSS_COMPILE=${CCPREFIX} V=1 O=/home/user/build/150610_rpi2_u-boot all Die erzeugte **u-boot.bin** wird anstelle von **kernel.img** auf der SD-Karte des RaspberryPi plaziert. Default ________ Die Stanndardkonfiguration läuft skriptbasiert ab, wobei typische Fälle durchprobiert werden. Diese Konfiguration sucht automatisch auch nach einem ``boot.scr.uimg``. Anpassungen im Betrieb ______________________ Wird die Plattform mit U-Boot gestartet, läuft zu Beginn ein Timer, der unterbrochen werden kann. Man landet dann auf einem Prompt der einige UNIX-artige Kommandos kennt. Anpassungen im laufenden Bootloader können zwar gespeichert werden, sind dann aber in einem unbekannten Datenformat unter ``uboot.env`` hinterlegt. Man kann die Datei zwar auslesen, Änderungen können aber zu Problemen führen. Beispielkonfiguration: .. code:: bash # uboot.env arch=arm baudrate=115200 board=rpi board_name=rpi boot_a_script=load ${devtype} ${devnum}:${bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}boot_extlinux=sysboot ${devtype} ${devnum}:${bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.confboot_prefixes=/ /boot/boot_script_dhcp=boot.scr.uimgboot_scripts=boot.scr.uimg boot.scrboot_targets=mmc0 usb0 pxe dhcp bootcmd=run distro_bootcmdbootcmd_dhcp=usb start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fibootcmd_mmc0=setenv devnum 0; run mmc_bootbootcmd_pxe=usb start; dhcp; if pxe get; then pxe boot; fibootcmd_usb0=setenv devnum 0; run usb_bootbootdelay=2cpu=arm1176dhcpuboot=usb start; dhcp u-boot.uimg; bootmdistro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; donefdt_addr_r=0x02000000fdtfile=bcm2835-rpi-b-rev2.dtbkernel_addr_r=0x01000000loadaddr=0x00200000mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fipxefile_addr_r=0x00100000ramdisk_addr_r=0x02100000scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; donescan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${bootpart} bootfstype; then run scan_dev_for_boot; fi; donescan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi; scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; donescriptaddr=0x00000000soc=bcm283xstderr=serial,vgastdin=serial,usbkbdstdout=serial,vgausb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fiusbethaddr=b8:27:eb:20:44:c4vendor=raspberrypi% Anpassungen als Script ______________________ Eleganter ist der Weg über ein Script, das vom Bootloader ausgeführt wird. .. code:: bash echo "Be sure, my script is running!" mmc dev 0 #setenv fdtfile bcm2835-rpi-b.dtb #fatload mmc 0:1 ${fdt_addr_r} ${fdtfile} setenv bootargs "dwc_otg.lpm_enable=0 earlyprintk console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait" echo "This will take about 2 minutes, take a coffee!" fatload mmc 0:1 ${kernel_addr_r} zImage #bootz ${kernel_addr_r} - ${fdt_addr_r} bootz ${kernel_addr_r} Das Skript muss mit dem U-Boot Tool ``mkimage`` mit folgenden Parametern zu einem ``uimg`` umgewandelt werden. .. code:: bash mkimage -A arm -O linux -T script -C none -n boot.scr -d boot.scr boot.scr.uimg Die erzeugte ``*.uimg`` Datei und am besten auch die ``*.scr`` Datei werden dann einfach ins selbe Verzeichnis gelegt wie die U-Boot Binärdatei.