|
MikroTik RouterBOARD 500![]()
Linux Hacking on the RB500![]()
The MikroTik Having seen that MikroTik also provided CF images containing a pre-installed Debian system, I took the plunge and bought one. For many people, these images are great: simply use the dd utility to write the image to a CF card (which you can access with an inexpensive CF reader for your PC), plug it into the RouterBOARD and away you go. But for the rest of us, who like to customise our systems to the max, the provided images just wouldn't do - I wanted to replace the supplied kernel almost right away after finding out that many of the cool features I was used to on my PC-based router/gateway weren't compiled in and couldn't even be added as modules. Stuff like SSH and even syslogd didn't work properly, because basic things like support for Unix domain sockets were lacking.
So, if you're like me and want to customise your RB-500's Debian installation,
you'll need a couple of things. First off, apart from the rather obvious items
such as the RouterBOARD itself and a suitable CF Card, you'll need a card reader.
I got an "internal" model which cost less that €20 and fits into
a 3.5" or 5.25" drive bay. The cable from this then leaves the case though
a spare PCI backplate and fits into an external USB slot. This sounds daft until you
realise you may need to dis/reconnect the thing now and again to refresh things. The CF
card and reader work through SCSI emulation, like USB thumb drives. When connected, the
kernel assigns the card a virtual SCSI block device, which you can then partition, format,
read from, write to and whatever.
See this guide to USB mass storage
The other thing you'll need is a Debian reference image from
MikroTik's RouterBOARD 500 page $ unzip rb500-1024.img.zip $ ls -l rb500-1024.img -rw-r--r-- 1 steve steve 1077608448 Mar 24 17:35 rb500-1024.img Hmmm... so dd-ing this onto my CF card isn't going to work - it's simply too big. Fortunately we can still access the data on the image by mounting the appropriate partition through a loopback device. In order to do this, we need to know a little about the geometry of the image. Something like fdisk will do the job fine: $ /sbin/fdisk -l rb500-1024.img Disk rb500-1024.img: 1077 MB, 1077608448 bytes 16 heads, 63 sectors/track, 2088 cylinders Units = cylinders of 1008 * 512 = 516096 bytes Device Boot Start End Blocks Id System rb500-1024.img1 1 8 4000+ 27 Unknown rb500-1024.img2 9 2088 1048320 83 Linux Ok, so we have two partitions: one of about 4Mb, which we'll come to later, and another which takes up the rest of the 1Gb. We can see that the second partition is of type 83 and therefore a regular Linux partition we can mount on our machine. The problem is, we're dealing with a regular file here, not a block device, so we need to figure our where in the image our partition starts. Thankfully, we can work this out pretty easily from the above data using this formula: Offset (in bytes) = Heads x Sectors x Cylinders x Block Size 16 x 63 x 8 x 512 = 4,128,768 bytes So we can mount the partition like this: # mkdir -p /mnt/cf # mount -o loop,offset=4128768 rb500-1024.img /mnt/cf # ls /mnt/cf bin cdrom etc home lib mnt proc sbin tmp var boot dev floppy initrd lost+found opt root sys usr vmlinux Cool! There's what we were after - the root filesystem of the CF image. If you just want to use the supplied kernel, but the image doesn't quite match your CF card's size, it's merely a matter of:
Your CF card can then be plugged into the RouterBoard. Set the boot device to CF and away you go! If, however, you want to use a custom kernel, or your CF card is a completely different size than (and therefore has different geometry to) the image, things are a little trickier. This can get pretty involved, depending on the level of customisation you want to achieve. If you're new to Linux, you'll probably want to enlist a knowledgable freind to help you out. Still up for it? Good - let's begin...
Unless you fancy compiling the kernel on the RouterBoard itself (which can be done
on a 1Gb CF card, although I wouldn't recommend it!) you'll need either another MIPS machine
or a cross-compiler to create mipsel-linux binaries, that is, binaries for the
little-endian MIPS architecture. This isn't too hard, especially if you follow
this HOWTO for Debian
As if that wasn't enough, you'll need to apply the patch from
MikroTik's RouterBOARD 500 page # cd /usr/src # unzip linux-2.4.29.patch.zip # patch -p0 < linux-2.4.29.patch I also needed to edit /usr/src/linux-2.4.29/Makefile, setting "CROSS_COMPILE" to "mipsel-linux" and reverting "kgcc" back to "gcc". I also changed the "tool-prefix" in /usr/src/linux-2.4.29/arch/mips/Makefile back to "mipsel-linux-". I then configured the kernel, using the default configuration provided by the patch as a reference: # cd /usr/src/linux-2.4.29 # cp config.mipsel .config # make menuconfig You may of course prefer to use a different method for configuring the kernel. The file config.mipsel provides the correct configuration options for things like the CPU and other built-in hardware. Surprisingly though, the via-rhine driver for the NICs was not compiled in, so I enabled this.
Another problem I came across was setting the kernel parameters. Without the correct settings,
you'll end up with a kernel panic when you try to boot as things like the location of the root
filesystem are not known to the kernel. I've yet to study the mechanism used by the images
supplied by MikroTik for setting these options, so my current solution is to make use of
CONFIG_CMDLINE to hard-code the required values into the new kernel.
[Update: the much more elegant solution using objcopy is documented on the
MikroTik Wiki #ifdef CONFIG_CMDLINE strncpy(command_line, CONFIG_CMDLINE, sizeof command_line); #else strncpy(command_line, arcs_cmdline, sizeof command_line); #endif This means we can copy the options specified in .config into the command line buffer at boot time. Probably not the most elegant solution, but it works. The options themselves are defined during configuration by enabling "Default bootloader kernel arguments" (under "General setup") and setting the default parameter string. In any case, the resulting .config should contain something like this: CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="root=/dev/cfa2 gpio=16375 console=ttyS0,115200 HZ=165000000 mem=32764K kmac=00:09:22:22:22:00" This line is taken from the dmesg of a MikroTik Debian image. You'll probably want to set kmac to the MAC address of the Korina NIC, as noted in the documentation supplied with your routerboard (or on a sticker on the case, or the board itself). With the configuration done, it's time to get compiling. If you're used to compiling on x86, note that there's no bzImage target - vmlinux is what you need. To save a few bytes, the resulting kernel can be stripped, as shown. If you configured some modules, don't forget to build them, too.
# make deps # make vmlinux # mipsel-linux-strip vmlinux # make modules # make modules_install If you're not working on a MIPS machine, you'll probably get a whole bunch of warnings if you attempt to install the modules. This isn't a problem, as long as the modules are installed to /lib/modules where we can get at them. Remember to copy the subdirectory under /lib/modules on your host machine to the same directory on the CF card. OK: so you've got your kernel, but what are you meant to do with it now? As you probably guessed, it's not just a matter of updating the bootloader - there isn't one, at least: not on the CF card, so this next part can take a hack or two to complete. This example is based on the 1Gb image featuring a 2.4.29 kernel, but it should apply to others. The whole process is detailed so that it should be possible to figure out the required steps for future releases. As we saw way back at the beginning, the CF image is split into two partitions. Whilst there's a vmlinux in the root of the filesystem on the second partition, this isn't the kernel which will be booted when we run off the CF card. The "live" kernel is on that mysterious first partition, which fdisk tells us is of an unknown type. The kernel image is simply written serially to the partition at a particular offset. So in order to replace the kernel, we first need to know where it is. First, lets see what we're meant to be looking for. Here's a dump of the first few bytes of the vmlinux in the root filesystem on the second partition. This is a standard ELF header. $ hexdump -vC /mnt/cf/vmlinux | head -n 3 00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 02 00 08 00 01 00 00 00 40 c0 3d 80 34 00 00 00 |........@.=.4...| 00000020 1c 51 26 00 01 10 00 50 34 00 20 00 03 00 28 00 |.Q&....P4. ...(.| Then let's have a look through the first partition of the image for ELF headers. The parameter to head is calculated by divinding the size of the partition in bytes by 16, the number of bytes hexdump prints per line. # hexdump -vC rb500-1024.img | head -n 258048 | grep ELF 00000200 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00007e00 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 001bfcd0 00 00 00 00 7f 45 4c 46 00 00 00 00 2f 75 73 72 |.....ELF..../usr| Well, our search pattern wasn't perfect, but it looks like we found two valid ELF headers. Let's take a closer look: $ hexdump -s 0x200 -vC rb500-1024.img | head -n 3 00000200 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000210 02 00 08 00 01 00 00 00 40 40 3c 80 34 00 00 00 |........@@<.4...| 00000220 48 e9 24 00 01 10 00 50 34 00 20 00 02 00 28 00 |H.$....P4. ...(.| $ hexdump -s 0x7e00 -vC rb500-1024.img | head -n 3 00007e00 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00007e10 02 00 08 00 01 00 00 00 40 c0 3d 80 34 00 00 00 |........@.=.4...| 00007e20 1c 51 26 00 01 10 00 50 34 00 20 00 03 00 28 00 |.Q&....P4. ...(.| Thankfully, they're both slightly different. The second one matches that of the vmlinux on the second partition, so this ought to be it! This can be confirmed by comparing some more bytes after the header, which is left as an exercise for the reader ;-) So we now know that the "live" kernel starts at 0x7e00 in this image, which translates to 32256 bytes in decimal, or 63 blocks of 512. We should therefore be able to overwrite the kernel on our CF card directly like so: # dd if=/usr/src/linux-2.4.29/vmlinux of=/dev/sda bs=512 seek=63 Incidentally, the number of blocks we need to skip is equal to the number of sectors per track on the device in question. This is also true for the 128Mb image, although it of course pays to check the offset before dd-ing your kernel into the wrong part of the CF card. For different-sized CF cards, you may need to experiment with this value. If you get things running on such a card, please let me know so I can amend this guide! Phew! Assuming you got this far without too much hair loss, now's the moment of truth. Stick the CF card into the RouterBOARD and, at startup, make sure the boot device is set to CF. If you have the immense good fortune that it works first time: congratulations - and you're welcome ;-) It took me a several attempts to get to this stage, so I came across a few different errors:
Another thing you may wish to consider when running Linux on your RouterBOARD is making use of the onboard NAND storage. This usually contains MikroTik's RouterOS, which you probably won't need any more. Indeed, if you're having trouble booting from CF, this may be the solution to your problems as it's also possible to install a custom kernel here and boot from NAND. The NAND also provides you with up to 64Mb of non-volatile storage, should you be short of space on your CF card. If your installation is small enough, you could install Linux directly to NAND and eliminate the need for a CF card entirely. To access the NAND storage, you'll need to create some device files in /dev, like this: # mknod /dev/mtd0 c 90 0 # mknod /dev/mtd1 c 90 2 # mknod /dev/mtdblocka b 31 0 # mknod /dev/mtdblockb b 31 1 The NAND chip, like the CF images, is split into two partitions - one of 4Mb and another filling the rest of the device. The filesystem used is yaffs, so your kernel will need to support this (the CF images' kernels do). Mounting is as simple as: # mkdir -p /mnt/nand1 # mount /dev/mtdblocka /mnt/nand1 In the first partition is a file named kernel which will be loaded if you set the RouterBOARD to boot from NAND. You can therefore simply copy you own image over this via a filesystem on the CF card. |
||||||||||||||||||||||||||||||