Custom bootloader
Android vs Linux dual boot
For XO-4 OLPC has created a graphical boot menu that selects between Android and Linux, further technical detail can be found in Android#Boot_menu. This can be adapted for general use.
Note: compatibility with 12.1.0 and later releases
The bootloader examples below were written before a redesign of the boot file layout. See relevant discussion here. The effect of the change is that the files "vmlinuz" and "initrd.img" have been replaced with "actos.zip" and "actrd.zip" respectively.
The examples below that mention "vmlinuz" and "initrd.img" may be made to work if the "olpc-dev-kernel" command is used after install but before changing the olpc.fth file.
olpc.fth during boot
On an unsecured XO, Open Firmware boots by executing a Forth script named /boot/olpc.fth. The search order is USB, then SD, then internal storage. If OFW doesn't find /boot/olpc.fth on any of those devices, it will try to network boot from a USB ethernet adapter or wireless LAN using DHCP and TFTP.
Firmware security tells how Open Firmware boots a secured XO.
The olpc.fth script that is present on OLPC system installations tries to boot Linux from the same device where olpc.fth was found, using command line arguments appropriate for that device. (Some versions of it do additional things, like automatically updating the firmware if a newer firmware image file is present.)
If you want to try a new operating system build, you can put it and a suitable olpc.fth on a removable device like a USB flash drive or SD card. If the device is inserted, the XO will try to boot from them; you can revert to the "normal" XO operating system on internal storage by removing the device and rebooting.
You can make custom olpc.fth scripts to boot any way you want. For example, you could have an olpc.fth on a USB flash drive that boots Linux from an SD card. You could make an olpc.fth that boots a different operating system with arbitrary command line arguments. Or you could make an olpc.fth that does pretty much anything without needing an operating system, because olpc.fth can contain an arbitrary Forth language program that can call any of the thousands of functions that Open Firmware provides.
Some example olpc.fth scripts are shown below.
Boot Parameters
To boot Linux, you need to specify
- The device and file that contains the OS kernel
- The Linux command line arguments
- Optionally, a ramdisk image to use as an initial root filesystem. (Linux can boot without a ramdisk, but it's tricky to set that up, especially on a USB flash drive.)
There are two different ways to specify the OS image file and the command line - either on the same line with the OFW "boot" command or in configuration variables. These two command sequences do the same thing:
boot u:\boot\vmlinuz ro root=sda1
or
" u:\boot\vmlinuz" to boot-device " ro root=sda1" to boot-file boot
OS Image names ("boot-device" configuration variable)
The "boot-device" configuration variable contains the name of the device and file from which to load the OS kernel image (unless overridden on the "boot" line). The form is "device-specifier:pathname". The most common "device-specifier" values for the XO are:
u - USB 2.0 mass storage device (e.g. USB flash drive) disk - USB 1.1 or 2.0 mass storage device (USB 1.1 storage devices are supported, but not recommended) sd - SD card nand - NAND FLASH with JFFS2 filesystem n - an even shorter alias for "nand"
On USB and SD devices, the supported filesystems are FAT (any variant) and ext2. ext3 also works if the journal is empty (ext3 is not a great choice for a removable device). When specifying the pathname to Open Firmware, you must use "\" instead of "/". (OFW uses "/" to separate device tree name components. Using "/" in the pathname would confuse OFW's device-specifier parser.) For FAT filesystems, the pathname is case-insensitive and 8.3 names must be used. (OFW doesn't support long FAT filenames because of patent issues.) For ext2/3 filesystems, the pathname is case-sensitive and names can be long.
The OS image file (e.g. u:\boot\vmlinuz) can be in either Linux "bzImage" format or ELF format. For bzImage format, OFW places the data at memory address 0x100000 (1 MiB). For ELF format, OFW places the various ELF program sections at the addresses specified in the ELF header. (The image file can also be in Forth source code format, signified by the first line starting with a " \ " comment. That is how OFW initially boots olpc.fth .)
If the OS image file is zlib-compressed, OFW will automatically uncompress it. OFW recognizes the compressed format by the image contents, not by the pathname.
Example values for the OS image name:
sd:\boot\vmlinuz disk:\boot\vmlinuz nand:\boot\vmlinuz disk:\bzImage
OLPC system installations put OS images in the /boot directory (\boot in the OFW pathname representation), but that is just a convention, not a requirement. The olpc.fth file, however, must be in /boot if you wish to auto-boot, because the pathname "\boot\olpc.fth" is in OFW's default value for boot-device . olpc.fth typically then either replaces the value of boot-device with the name of an OS image file, or else supplies the image name on the "boot ..." line, thus "chaining" from olpc.fth to an OS.
Linux command line arguments (boot-file value)
The "boot-file" configuration variable contains the command line arguments to pass to the OS (unless overridden on the "boot" line). (The name "boot-file" is historical.) For booting Linux, the most important command line argument is "root=", which specifies the root filesystem device. When booting from NAND, you also have to specify "rootfstype=jffs2", because Linux can't auto-detect the JFFS2 filesystem type. The other "standard" OLPC command line arguments are not strictly necessary.
Some common Linux command line arguments for XO are:
- ro - mount read only (it is remounted read-write during the boot process)
- root=<device> - location of the root filesystem
- rootfstype=jffs2 - needed only when the root filesystem is on the NAND FLASH
- rootfstype=ext3 - conventionally supplied for EXT3 filesystems, but not strictly necessary, as EXT3 can be auto-detected
- rootdelay=<seconds> - optional adds in a delay before booting (useful for slow-starting devices)
- console=<device>[,arguments] - sets up the console display. You can have multiple console= arguments.
- fbcon=font:SUN12x22 - chooses a font that's a good size for the OLPC display resolution
If you just want the normal Linux text console on the OLPC screen, you don't really need and "console=<device>" arguments, because screen console is the default. A lot of olpc.fth scripts say "console=ttyS0,115200 console=tty0" to get console messages on both the screen and on the serial debug port (which you can only get to by opening the machine and connecting a special adapter).
Some useful values for the "root=" device are:
- mmcblk0p1 - SD card
- sda1 - USB flash drive
- mtd0 - internal NAND (also specify rootfstype=jffs2)
- LABEL=OLPCRoot - instead of specifying a specific device, you can specify a label value and Linux will search for a disk that has that label on it
A complete list of linux kernel options may be found at Linux Kernel in a Nutshell (in PDF format).
Ramdisk
Linux can boot without a ramdisk, locating the initial root filesystem on a disk device, but it is now common practice to use a ramdisk. The ramdisk image contains a minimal root filesystem that Linux uses to get started, doing things like probing for USB devices with udev, before switching over to the real root filesystem on disk.
OFW doesn't get the ramdisk image name from the "boot ..." line. To boot with a ramdisk, you must specify the device and pathname of the ramdisk image file via the "ramdisk" configuration variable, as in:
" u:\boot\initrd" to ramdisk
or
" sd:\boot\initrd" to ramdisk
If the value of "ramdisk" is not empty, OFW will load the specified file into memory after it loads the Linux OS image, telling Linux the memory location of the ramdisk via Linux "zero page" startup-info data structure. The ramdisk loading mechanism only applies to Linux. If OFW loads an operating system image that doesn't look like Linux, OFW won't try to load a ramdisk. Only Linux has a defined mechanism for passing the ramdisk location to the OS, so OFW would have no way to tell another OS about the ramdisk.
The device:pathname format is the same as for the OS kernel image file. As with the kernel image, if the ramdisk image is zlib-compressed, OFW will automatically decompress it.
Simple /boot/olpc.fth script
Here is a simple olpc.fth script that doesn't do anything fancy; it just boots Linux from a USB disk with a ramdisk.
\ OLPC boot script unfreeze " u:\boot\initrd" to ramdisk boot u:\boot\vmlinuz root=sda1
In many cases, that simple script is all you really need. See the next section for an explanation of "unfreeze".
In fact, if you just want to try something, and you don't want to auto-boot, you don't have to make the script at all. You can just type the commands at the "ok" prompt. At the ok prompt, you don't have to type the comment line ("\ OLPC boot script"), but olpc.fth must be begin with a comment so OFW will recognize that it contains Forth source code instead of a binary image format. Only the first two characters "\ " (backslash space) are required for recognition, so if you're really lazy, you don't need much of a comment line.
\ Menu bootscript for OLPC Place in /boot as olpc.fth cr ." 1 to boot from SD" cr ." 2 to boot from USB" cr ." 3 to boot from internal NAND" cr ." 4 to boot alternate image from NAND" cr cr key case [char] 1 of \ SD boot info " ro root=mmcblk0p1 rootdelay=1 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " sd:\boot\vmlinuz" to boot-device " sd:\boot\olpcrd.img" to ramdisk endof [char] 2 of \ USB boot info " ro root=sda1 rootdelay=1 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " disk:\boot\vmlinuz" to boot-device " disk:\boot\olpcrd.img" to ramdisk endof [char] 4 of \ Alternate boot image info " ro root=mtd0 rootfstype=jffs2 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " nand:\boot-alt\vmlinuz" to boot-device " nand:\boot-alt\olpcrd.img" to ramdisk endof ( default ) \ Default sugar boot image info " ro root=mtd0 rootfstype=jffs2 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " nand:\boot\vmlinuz" to boot-device " nand:\boot\olpcrd.img" to ramdisk endcase unfreeze boot
The "unfreeze" line near the end releases the display so that OS output can be seen. Open Firmware "freezes" the screen (by using the DCON chip to hold the last graphical image) just before starting the OS. This hides Linux's scrolling text messages to make the boot sequence prettier. The OLPC Linux/Sugar startup code "unfreezes" the screen when it is ready to display its own startup animation. If your OS doesn't know how to tell the DCON to unfreeze the screen, you need to say "unfreeze" in olpc.fth, otherwise your OS's output won't be seen. You can also use "unfreeze" if you want to see the Linux startup text with the normal OLPC OS.
overclock
You can also overclock your OLPC XO-1 with the olpc.fth file. In order to accomplish this you need to set your timings before it loads the operating system. You must add some numbers to the forth stack and when write these registers using wmrsr. Shown below is a specific overclock frequency and that can be changed by altering the middle number to what you find below in the overclock table.
7de009e 5dd 4c000014 wrmsr
You then add in any specific booting information you want (see above).
XO-1 Overclock Settings in MHz; default is 433/166 (4d9) |
|||||
CPU | Bus Speed | ||||
133 | 166 | 200 | 216 | 233 | |
333 | 3d3 | 4d3 | 5d3 | 653 | 6d3 |
366 | 3d5 | 4d5 | 5d5 | 655 | 6d5 |
400 | 3d7 | 4d7 | 5d7 | 657 | 6d7 |
433 | 3d9 | 4d9 | 5d9 | 659 | 6d9 |
466 | 3db | 4db | 5db | 65b | 6db |
500 | 3dd | 4dd | 5dd | 65d | 6dd |
533 | 3df | 4df | 5df | 65f | 6df |
566 | 3e1 | 4e1 | 5e1 | 661 | 6e1 |
Sample Uber Bootscript
\ Menu bootscript for OLPC with overclocking. Place in /boot as olpc.fth \ select overclock level cr ." 1 to set the clock speed as normal" cr ." 2 to under clock the machine to 333 MHZ memory 133" cr ." 3 to overclock it to 500 memory 200" cr ." 4 to overclock extreme to 533 233 WARNING MIGHT BE UNSTABLE" cr cr key case [char] 2 of \ underclock 7de009e 3d3 4c000014 wrmsr ." underclock" endof [char] 3 of \ over clock normal 7de009e 5dd 4c000014 wrmsr ." overclock normal" endof [char] 4 of \ overclock EXTREME 7de009e 6df 4c000014 wrmsr ." overclock EXTREME" endof \ Default - keep the normal setting endcase \ select boot location cr ." 1 to boot from SD" cr ." 2 to boot from USB" cr ." 3 to boot from internal nand" cr ." 4 to boot alternate image from Nand" cr cr key case [char] 1 of \ SD boot info " ro root=mmcblk0p1 rootdelay=1 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " sd:\boot\vmlinuz" to boot-device " sd:\boot\olpcrd.img" to ramdisk endof [char] 2 of \ USB boot info " ro root=sda1 rootdelay=1 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " disk:\boot\vmlinuz" to boot-device " disk:\boot\olpcrd.img" to ramdisk endof [char] 4 of \ Alternate boot image info " ro root=mtd0 rootfstype=jffs2 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " nand:\boot-alt\vmlinuz" to boot-device " nand:\boot-alt\olpcrd.img" to ramdisk endof ( default ) \ Default sugar boot image info " ro root=mtd0 rootfstype=jffs2 console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22" to boot-file " nand:\boot\vmlinuz" to boot-device " nand:\boot\olpcrd.img" to ramdisk endcase unfreeze boot
A single bootloader for both XO-1 and XO-1.5
With the availability of XO-1.5 and the hardware similarities between XO-1 and XO-1.5, yet with different kernels required, we need a bootloader that can identify the hardware and boot the appropriate version.
Other than the kernel and its associated initrd.img, the XO-1 and XO-1.5 software is similar enough to allow a common build for both machines. This is primarily useful in the case where the system is booted from an external device like a plug-in SD card or a USB FLASH drive. The internal storage (raw NAND FLASH on XO-1, micro-SD on XO-1.5) requires different filesystems (JFFS or UBIFS on XO-1, ext2/3/4 on XO-1.5), so a combined image is not worthwhile.
Mitch Bradley, the Open Firmware developer, made the following script that detects the XO-version and boots the appropriate initrd/vmlinuz.
In this example the olpc.fth script goes into the "/boot" folder while the XO-1 initrd.img and vmlinuz into the "/boot10" folder. Respectively, the XO-1.5 initrd.img and vmlinuz go into the "/boot15" folder.
The script also checks the firmware version and compares it with a preset minimum (in this case q2e45 and q3a54), displaying a message if the firmware is out of date. The olpc.fth in official OLPC builds automatically updates the firmware if a newer firmware version is present in the filesystem; this script only displays a message based on a value in the script.
The script lets you boot from the internal NAND/SDcard by pressing the "O" game key during power up. If an SD card or USB FLASH drive containing this script is inserted, you can still boot the internal OS without having to remove the external media.
Finally, the script defines the PD macro to pass specific kernel arguments depending on the boot media; different arguments can be passed for USB, SD card or internal NAND.
olpc.fth
\ OLPC boot script visible \ Returns a number identifying the XO version - 2 for XO-1, 3 for XO-1.5 : xo-version ( -- n ) fw-version$ drop 1+ c@ [char] 0 - ; \ To pass hardware specific arguments we define the xo-1? and xo-1.5? flags \ Returns true if the machine is XO-1 : xo-1? ( -- flag ) xo-version 2 = ; \ Returns true if the machine is XO-1.5 : xo-1.5? ( -- flag ) xo-version 3 = ; \ Overclocking the XO-1 is an example of the above. \ Overclocking may affect reliability and longevity of the XO-1 \ Increases power consumption and decreases battery life per charge. \ If you still want overclock your XO-1 to 500Mhz CPU, 200MHz bus speed \ remove the back-slash ( "\" ) from the beginning of the next 3 lines only \ xo-1? if \ 7de009e 5dd 4c000014 wrmsr \ then \ End overclock section \ We set hardware-specific requirements based on the xo-version parameter. \ in this case the desired OFW version : desired-ofw-version$ ( -- adr len ) xo-version case 2 of " q2e45 " endof \ XO-1 3 of " q3a56 " endof \ XO-1.5 ( default ) ." UNKNOWN XO VERSION" cr " q0000 " rot endcase ; \ Given a string like " q3a54 " (note the trailing space) \ or " q3a54b", return a numerical value that can be used \ to compare OFW versions. : $ofw-version-num ( adr len -- n ) drop h# fffc6 - (fw-version) ; \ Return the numerical value representing the currently-installed OFW version. : this-ofw-version-num ( -- n ) rom-pa (fw-version) ; \ Complain if the firmware is out of date : check-ofw ( -- ) desired-ofw-version$ $ofw-version-num this-ofw-version-num u> if ." You need to update the firmware to " desired-ofw-version$ type cr then ; \ Sets the DN macro to expand to the device name from which this script \ was booted. That's useful for subsequently booting the kernel from \ the same device. \ Also sets the PN macro depending on the XO version : set-path-macros ( -- ) \ "O" game button forces boot from internal FLASH button-o game-key? if " \boot" pn-buf place " int:" dn-buf place exit then \ Set DN to the device that was used to boot this script " /chosen" find-package if ( phandle ) " bootpath" rot get-package-property 0= if ( propval$ ) get-encoded-string ( bootpath$ ) [char] \ left-parse-string 2nip ( dn$ ) dn-buf place ( ) then then \ Set PN according to the XO version xo-version case 2 of " \boot10" endof 3 of " \boot15" endof ( default ) " \" rot endcase ( adr len ) pn-buf place ; \ We check if we are booting from USB or SDcard to specify device-specific parameters : dn-contains? ( $ -- flag ) " ${DN}" expand$ sindex 0>= ; : usb? ( -- flag ) " /usb" dn-contains? ; : sd? ( -- flag ) " /sd" dn-contains? ; : slot1? ( -- flag ) " /disk@1" dn-contains? ; : olpc-fth-boot-me ( -- ) set-path-macros \ We can pass boot-device-specific kernel arguments here with the PD macro \ PDEV1 tells Puppy Linux's init script where to find Puppy-specific files. \ If PDEV1 is omitted, Puppy searches for the files. If a USB FLASH drive \ takes a long time to wake up after a USB reset, Puppy sometimes misses it, \ so it's better to avoid the search by telling Puppy where to find the files. usb? if " PDEV1=sda1" else sd? if slot1? if " PDEV1=mmcblk0p1" \ External SD card else " PDEV1=mmcblk1p1" \ Internal SD card then else " " \ Internal raw NAND then then " PD" $set-macro check-ofw " console=ttyS0,115200 console=tty0 fbcon=font:SUN12x22 ${PD}" expand$ to boot-file \ Uncomment the next 2 lines to see the command line \ ." cmdline is " boot-file type cr \ d# 4000 ms " ${DN}${PN}\vmlinuz" expand$ to boot-device " ${DN}${PN}\initrd.img" expand$ to ramdisk boot ; olpc-fth-boot-me
An alternate version supplied by Sascha Silbe to James Cameron uses the model description rather than relying on the firmware version string. This allows custom firmware to be used with version strings that are uncommon. One /boot/ directory is used, with different file names for the kernel and initrd.
Model detection:
: is-xo1? " model" " /" find-package drop get-package-property 2drop c@ [char] D < ; : is-xo1.5? " model" " /" find-package drop get-package-property 2drop c@ [char] D = ;
Decision making:
: olpc-fth-boot-me set-path-macros ?ofw-reflash " ${ROOTDEV}" expand$ to boot-file is-xo1? if button-o game-key? if " ${DN}\vmlinuz-xo1.old" expand$ to boot-device " ${DN}\initrd-xo1.img.old" expand$ to ramdisk else " ${DN}\vmlinuz-xo1" expand$ to boot-device " ${DN}\initrd-xo1.img" expand$ to ramdisk then else is-xo1.5? if button-o game-key? if " ${DN}\vmlinuz-xo1.5.old" expand$ to boot-device " ${DN}\initrd-xo1.5.img.old" expand$ to ramdisk else " ${DN}\vmlinuz-xo1.5" expand$ to boot-device " ${DN}\initrd-xo1.5.img" expand$ to ramdisk then else ." Unrecognized / unsupport machine" halt then then \ disable pretty boot dcon-unfreeze boot ;