But one thing that's clear: to use them, you need to compile your own kernel. None of them come with a kernel that enables /sys/class/gpio, i2c or any of the other methods you might need to read and set bits. So the first step in any plug GPIO adventure involves building a kernel and installing it.
No problem, right? I've built a kazillion Linux kernels on x86 machines, and I've cross-compiled for the Arduino and other platforms. How hard can it be to build an arm kernel and install it on a plug?
As it turns out, quite hard. Building it is one thing; getting a bootable kernel installed onto the plug's flash is quite another. There are dozens of howtos scattered all over the net, each of them saying "Well, the standard instructions don't work, so here's how to really do it", and of course they're all different. And none of them worked for me. So here's Yet Another Tutorial on Building and installing a new kernel for a SheevaPlug. I'll be cross-compiling on a Linux box. You can also build a kernel directly on the plug (apparently it takes about an hour), but that would be a different tutorial.
Flashing a kernel that doesn't boot will leave your plug "bricked", unusable. Therefore, before you even try building your own kernel, you should make sure you can un-brick it.
This is harder than it sounds. In theory you can get ready-to-go installs of Debian, Ubuntu and Android, and you can use scripts like ESIA, the SheevaPlug Installer or the GuruPlug Installer to flash them to the plug.
In practice, none of those scripts worked for me, every plug is different, and some, like GuruPlugs, may be hopeless. I still haven't managed to unbrick my GuruPlug, let alone install a homebuilt kernel on it.
So find a setup that will unbrick your plug before you bother building a new kernel for it. Be prepared to spend a while downloading several different setups and trying different methods. And cross your fingers. If you find, like me with the GuruPlug, that you have a plug that you just can't unbrick, you can tell yourself that it probably would have happened eventually anyway.
If one of the above scripts work for you, rejoice! and skip ahead to Building a New Kernel.
Otherwise, don't give up. None of them worked for me either.
Go download a standard Debian kernel, initrd and rootfs and proceed
to the Install from USB on a Bricked Plug
section at the end of this article.
Once you're sure you can unbrick your plug, you're ready to build a kernel.
You can use a mainline Linux kernel -- whatever the current version is
at Kernel.org. Or you can use
Marvell's
"Orion" kernel. I used a mainline kernel, 2.6.35.2.
Download and unpack it.
First you need a cross compiler. There are at least three around,
none of them available in distro repositories.
I used the one from
CodeSourcery.
By default, CodeSourcery installs to a directory under your homedir,
~/CodeSourcery. You will need to specify this location when you build
the kernel. Using ~ doesn't work -- some of the kernel build scripts
break in confusing ways -- so either move the directory
somewhere close to where you're building your kernel (e.g. ../CodeSourcery)
or reference it by full pathname, /home/yourname/CodeSourcery.
Reference it how? This is something on which most howtos are rather vague.
You need a CROSS_COMPILE= argument that will be prepended to the name of
the various compiler binaries. In other words, if you've installed to
../CodeSourcery, use
You'll also need another binary: UBoot's mkimage program.
On Ubuntu you can get this with
Do not forget the
The root filesystem on the Ionics plug I'm using for this is Ubifs.
Ubifs is not enabled in the default kirkwood install, so you need it.
First enable UBI, under drivers->MTD, then
enable UBIFS, under Filesystems->Misc filesytems
Assuming this all worked, you have a kernel!
If your plug is up, running and unbricked, installing it may be fairly easy.
In theory, if you copy (using the network or USB) your kernel and
modules over to a running plug, you should be able to flash it like this:
In practice, that won't work and your plug won't boot.
Go figure. But at least you can unpack the modules:
Now go ahead and try that kernel you just flashed. When your plug
refuses to boot with "Bad magic number", proceed to the next section.
But wait! Since I wrote that, there are some new instructions:
lots of good info in this thread on
Installing
sheeva-with-linux kernels in Debian.
By the way,
Sheeva Uboot Tools
looks really useful for finding out about your uboot environment
from within a running plug.
This procedure assumes you have a serial/JTAG connection to the plug,
so you can talk to uBoot.
Before proceeding, power the plug, connect to it
and hit a key in time to stop in the uboot prompt.
Now type:
Check whether mainlineLinux is set to yes. If it's
not, you'll probably want
Now copy the kernel and modules to a fat- or vfat-formatted USB stick:
You may need an initrd as well.
Plug the USB stick into the plug.
Now comes the tricky part, because it varies between models.
On some plugs, like the Ionics PlugComputer Plus, you can type:
Great -- What if that doesn't work?
Or if you don't have recover2 defined at all?
Unfortunately, every plug seems to have a different set of boot commands.
Go look at that printenv output you saved earlier.
Anything that starts with recover or boot is
potentially relevant. Here's what those look like on the Ionics Plus:
Note that when you
If you get rid of all those separate variables, here's what the Ionics
is doing, and something very like this sequence may work for you:
Note that real_bootcmd is
setenv bootargs $(bootargs_console) $(mtdpartitions) $(bootargs_root); nand read.e 0x00800000 0x00100000 0x00400000; bootm 0x00800000
Some likely tripping points here:
In recover2, the kernel boots with
So when you're booting your real kernel, not just unbricking, you might
want to skip run recover2 and instead do something like:
The difference is you're skipping the setenv bootargs step which
would set root=/dev/ram0.
But wait! If your kernel doesn't need an initrd, you can skip that
part too. So this is really all you need, assuming your bootcmd
is already set right:
I suspect that nand write.e could be shortened, but I haven't found
documentation on what the numbers mean yet.
My guruplug seems to have broken USB -- it won't recognize a USB
stick. You can tell by running
In the absence of USB, uBoot can get a kernel using tftp.
Set up tftp on your server (hint: on Ubuntu, the tftp package has
broken dependencies; use
Now it gets a little confusing: I got these instructions from
somewhere but I'm not convinced they're correct. The addresses don't seem
to agree with the addresses used by recover4 in the previous section.
but the uBoot doc are a bit sparse (
Uboot at DULG,
U-Boot
Command Line Interface
and a PDF
U-Boot Reference Manual).
Anyway, at the uBoot prompt, type something like:
It might also help, instead of reset, to try
Someone else reports luck with the simpler:
If you have your new kernel booting, rejoice! You can worry about what
to do about all those proprietary drivers, like the network and wi-fi
chips that aren't in the Debian kernel, later.
Right. There's some trick to getting the "libertas" driver working.
I don't know what the trick is. Please tell me
if you know!
Ha! Are you kidding?
I have no idea. At this point, I'm happy to have a kernel that boots
at all. I think I'll stick to Arduinos for controlling hardware
lines, and use plug computers only for higher-level tasks.
Building a New Kernel
Download the kernel
Get a cross-compiler
CROSS_COMPILE=../CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi-
and when make looks for gcc, it will look for
../CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi-gcc.
apt-get install uboot-mkimage
... or you can try
building
mkimage from source (I haven't tried that myself).
Configure the kernel and enable ubi
make ARCH=arm kirkwood_defconfig
This creates a default .config file with the architecture, etc. set up
correctly. Then, if you need to change anything -- and presumably you do,
since otherwise why are you putting yourself through this? -- run
make ARCH=arm menuconfig
ARCH=arm
here! If you build a lot of
kernels it's easy to forget and just type make menuconfig
.
If you do that, menuconfig will trash all that nice kirkwood configuration
and default back to whatever architecture you're using to build (probably
x86 and you'll end up with a kernel that doesn't boot.
Build the kernel
make -j4 ARCH=arm CROSS_COMPILE=../CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi- uImage
make -j4 ARCH=arm CROSS_COMPILE=../CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi- modules
make -j4 ARCH=arm CROSS_COMPILE=../CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi- INSTALL_MOD_PATH=modules_install modules_install
cd modules_install; tar czvf ../modules.tar.gz . ; cd ..
Install on a working plug
nandwrite -p /dev/mtd0 /boot/uImage
(This should perhaps be /dev/mtd1 on some systems.
Use cat /proc/mtd
to see which mtd device has what.)
cd /
tar xvf /wherever/modules.tar.gz
Install or unbrick from uBoot using a USB stick
printenv
and copy everything it just printed to a file on your local machine.
You may want this later.
setenv mainlineLinux yes
for anything except the original kernel that came with the plug.
You should only have to set that once.
cp arch/arm/boot/uImage modules.tar.gz /stick
run recover2
and it will find the USB stick, load the kernel and initrd into flash,
reboot from them, then reload the root filesystem. You can hit Enter
to stop the reloading of the root filesystem, if all you need is the kernel.
bootargs_root=ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs
mtdpartitions=mtdparts=orion_nand:0x400000@0x100000(uImage),0x1fb00000@0x500000(rootfs)
real_bootcmd=setenv bootargs $(bootargs_console) $(mtdpartitions) $(bootargs_root); nand read.e 0x00800000 0x00100000 0x00400000; bootm 0x00800000
bootargs_console=console=ttyS0,115200
recover1=setenv mainlineLinux yes; setenv arcNumber 2097; setenv bootcmd run recover2; saveenv; reset
recover2=run recover3; setenv bootcmd $(real_bootcmd); saveenv; setenv bootargs $(bootargs_console) $(mtdpartitions) root=/dev/ram0 rw ramdisk=0x01100000,8M install_type=nand; bootm 0x00800000 0x01100000
recover3=run recover4; nand erase clean 0x00100000 0x00400000; nand write.e 0x00800000 0x00100000 0x00400000
recover4=usb start; fatload usb 0 0x00800000 uImage; fatload usb 0 0x01100000 initrd
arcNumber=2097
run_diag=no
bootargs=console=ttyS0,115200 mtdparts=orion_nand:0x400000@0x100000(uImage),0x1fb00000@0x500000(rootfs) ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs
ethaddr=00:26:DB:00:03:EA
filesize=1CAE6D
bootcmd=setenv bootargs $(bootargs_console) $(mtdpartitions) $(bootargs_root); nand read.e 0x00800000 0x00100000 0x00400000; bootm 0x00800000
run recover2
on this plug, what you're
really doing is running recover3 first. recover3 runs recover4,
which loads the kernel and initrd from USB. Then recover3 writes
those values to flash and passes control back to recover2, which sets
up the boot command and actually boots. Whew!
(recover4)
usb start
fatload usb 0 0x00800000 uImage
fatload usb 0 0x01100000 initrd
(recover3)
nand erase clean 0x00100000 0x00400000
nand write.e 0x00800000 0x00100000 0x00400000
(recover2)
setenv bootcmd $(real_bootcmd)
saveenv
setenv bootargs $(bootargs_console) $(mtdpartitions) root=/dev/ram0 rw ramdisk=0x01100000,8M install_type=nand
bootm 0x00800000 0x01100000
root=/dev/ram0 ramdisk=0x01100000,8M
.
But bootargs_root has
ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs
.
This is because recover2 is trying to do a one-time boot from
to the initrd it's just read in from USB (I think), while on reboot
you'll want the kernel to find the ubifs root filesystem.
When you're trying to boot your own kernel, without an initrd,
you'll probably want the ubi option, not the ramdisk option.
run recover3
setenv bootcmd $(real_bootcmd)
saveenv
bootm 0x00800000 0x01100000
usb start
fatload usb 0 0x00800000 uImage
nand erase clean 0x00100000 0x00400000
nand write.e 0x00800000 0x00100000 0x00400000
bootm 0x00800000 0x01100000
... or use tftp from uBoot
usb start
and checking
whether it sees a USB storage device.
apt-get install xinetd tftpd
then follow these
tftp
instructions; or tftpd-hpa or atftpd might work).
setenv ipaddr 192.168.1.something
setenv serverip 192.168.1.somethingelse
tftp 0x6400000 uImage
nand erase 0x0 0x100000
nand write.e 0x6400000 0x0 0x100000
reset
bootm 0x6400000
.
tftpboot 0x100000 uImage
bootm 0x100000
Any of these work? Woohoo! You're done!
But my wi-fi doesn't work any more!
Wait, what about those GPIO lines?