Friday, January 24, 2014

USBBooting Belkin F7D7301 (incomplete)

Warning: Please note that this is something I started documenting almost a year back, and then lost interest to make it complete for properly publishing to be usable for most. Whatever I documented are still applicable, and I have a working USB boot. But I do not plan to make the missing pieces of links complete on this topic. Publishing this as it is, in hope that it could be usable for somebody who is thinking in the same terms.

Warning: Not for non-tech users.

I have published earlier how to apply some custom patches and get OpenWrt working for Belkin F7D7301. I found a lot of interest from folks in updating the firmware, and due to the specific custom headers required for these TRX files, I believe it is a good idea to not update these TRXs often, if you are a guy experimenting with different kernels and versions. If you dont touch the trx, and only worry about the raw linux for the updates, you also dont need most of the custom patches addressing the trx stuff, and as long as you have got the networking drivers compiled, you got your router alive. F7D7301 on the heart is anyway a mips, so if you handle the drivers, you open the world for a lot of experimentation via USB and NFS or NBD roots.

Another big advantage for the folks who like to live on the edge are, in the case of a wrong kernel or firware update, going back is as simple as taking your usb drive out and changing the boot file.

Please note that most of the concepts mentioned here can be applied to other embedded devices or routers as well.

We have three options for getting the usb boot working.

a) Use a modified CFE to allow usbfs boot.
     Advantage: Uses least amount of flash/mtd, so if you are going to use USB loading, you dont depend on anything else in the flash other than CFE. You could still leave your stock firmware in flash or even have Tomoto/DDWrt in flash.
      Disadvantage: You mess up CFE, you brick the router really bad.

b) Use a second level boot loader using methods like kexec. Load this second level boot loader from CFE.  This options stands exactly between a and b. Uses and depends a bit more on flash than a custom CFE, but a lot less than a full OpenWrt Image on flash. You could still have stock firmware in flash, but might need some customizations.

c) Have a regular root squashfs image in flash and use a custom /etc/preinit script, to do the kexec loading.
     Advantage: Least risky for a regular user. Even in case of usb boot failure, you could boot the regular openwrt image from flash and business will be as before any usb booting.
    Disadvantage: You still depend on your squashfs image. So a flash corruption in this area still could break the USB boot. Also you read a full linux image from flash and mounts a full squashfs image from flash. Which means a lot of dependability on the flash, even though eventually you are keeping your experimental boot images in USB drive. But this is probably OK for most users.

I have tried all the above 3 methods, but found that option c is the least risky one and will be the most transparent one for any openwrt user.

The custom preinit script in squashfs.

If you are familiar with the openwrt boot process, you know the importance of /etc/preinit. This is the first script runs after mounting squashfs. I moved the /etc/preinit from squashfs to /etc/preinit.00, and put a custom init script in that place, so that I can do the USB booting or Network booting ealiest possible stage.  Here are the steps done by custom preinit.

1. Loads the required modules for USB and basic Networking.
2. mount tmpfs for all the work.
3. Initialize IP and try getting a boot script from an http server on LAN side.
4. If that succeds execute that init script (will get to the details of this init below)
5. If no network boot script try mounting USB read only using vfat or ext4.
6. See whether  USB has the boot init script at location /boot/
7. If it finds the boot script in USB, copy that over to tmpfs
8. unmount USB and execute that boot script (will get to details f this in below)
9. If no boot script was found from network or usb, then go proceed with squashfs preinit.
10. boot the openwrt in regular way via squshfs+jffs2.

Please note that you cannot modify the custom preinit script, since it is in squashfs filesystem, which is read only. But since you are loading your main boot script from either network or from USB you could modify them and experiment all you want without breaking the router.

Boot scripts for USB boot and Network boot

Note that you are not just looking to have your root file system in USB or Network, you also want your kernel to be in USB or Network.That way you dont depend on the version of kernel you have in your MTD/Flash of the router. The boot script is the one achiving this, and below are the functions.

1. Mount your USB or Network block device.
2. use kexec to load your new custom kernel from USB or network mount. (details of the customizations needed in kernel are explained below).
3. umount your USB or network device.
4. execute the custom kernel.

Now your custom kernel will take care of other stuff.

The custom Kernel Image for USB boot and Network boot

The custome Kernel Image does use a bigger initramfs. So the image is bigger. But it doesnt steal much memory, once you mount your root file system from USB, since the switch_root function does a good job of cleaning the initramfs from memory. Below are the functions of custom initramfs.

1. Initiramfs has a static busybox for doing all the required functions it need to do.
2. Load All the required modules for mouting your rootfs. For network root it will be the network drivers and nbd or nfs drivers. For USB root it will be your usb drivers. Also you need file system driversto be loaded. It could be vfat ext4 etc. You may also want loop driver.
3. Mount USB or NBD block device. If you keep the root files in a loopimage, mount that as well.
4. Now since the root is mounted, SwitchRoot to your root, and thereby cleaning up the memory used by initiramfs.

The customizations of preinit in USB root image.

I added some customizations in the USB/NBD root image preinit, so that I wont corrupt the jffs2 in flash. Also I chose to not use an overlay, since the USB root image is writable. So I commented out the boot hooks for jffs2 check and mount.

Your options.

1. You can just not use any of it and do something more useful :)
2. You can just flash the trx I compiled and have a regular openwrt image.
3. Following step 2, copy the usb-kernel and usb-root I compiled to your USB and have a USB boot.
4. Follow step 2 and 3. And then compile your own usb-kernel and usb-root.

Enough talk... Show me the example of USB Boot.

 1. Follow the steps here and flash the trx image.
 2. Format your USB drive to ext4/ext3/ext2 or vfat.
 3. Create a directory boot in your USB drive.
 4. Download and copy the below files to /boot/ of USB drive.
       a) Customized Kernel elf
       b) Cutomized root loop image
       c) USB boot script
 5. Unmount your USB drive from computer, and attach it to your F7D7301 router.
 6. You should be able to boot to USB loop image when you power cycle the router.