Random notes on the DreamPlug

[Written ]

This page contains some random notes, mostly for myself but which might be useful to others, on the Marvell DreamPlug (and incidentally some of its older cousins the GuruPlug and SheevaPlug) and on getting some things to work with it mainly around Debian GNU/Linux. The main information site about them is the PlugWiki.

Table of contents

How to get a useful kernel

There are some useful kernel patches over here (I'm pointing at the ones for 3.2.9 because that's the most recent stable kernel as I'm writing — actually, no, 3.2.10 was released yesterday — but of course one should probably take the most recent ones). The ones called mvsdio-1-2.6.30.patch and mvsdio-2.patch seem to be about giving the SD cards some delay to respond, I'm not sure how important that is; sheeva-3-leds.patch is only of concern for the SheevaPlug. The Wifi driver is linux-3.0-SDIO-micro-AP.patch: this is necessary to use the Wifi chipset as an access point (aka master mode), and only works for certain plug versions anyway (those that have an SD8688, see below): support for managed mode (Wifi client) is already in stock kernels; besides, this driver seems like it's disastrously bad code (e.g., it has a comment suggesting it doesn't implement multicast), so I'm not sure it's a good idea to apply this patch, but if one wants Wifi in master mode it's a requirement. The linux-3.0.4-LIBERTAS-WEXT_PRIV.patch is just a workaround for a trivial config problem, and 0002-Driver-for-Marvell-Libertas-8688-SDIO-micro-AP-suppo-3.2.patch is required to compile the Wifi driver for ≥3.2 kernels. As for 0003-Initial-defconfig.patch, it's just an initial config file, hence not needed if one takes a config file from somwhere else. E.g., this one, which is pretty sane.

But the most important patch is not there at all, it is here (note that .boot_params near the end needs to be replaced by .atag_offset just as in the neighboring file guruplug-setup.c) and here, adding support for the DreamPlug, as opposed to the GuruPlug. The point is that kernel support for this kind of ARM systems is done through machine identifiers, each one of which must be defined by a setup file in the kernel sources: the stock kernels have support for the GuruPlug (machine identifier 2659=0xa63), but not the very similar DreamPlug (machine identifier 3550=0xdde) and this patch adds it. This patch is not, and never will be, included in standard kernels, because machine identifiers are deprecated in favor of something known as flattened device trees; but unfortunately, these don't seem to work at the moment (or at least I was unable to get them to work), and even if/when they do they'll require a new U-Boot, which is a mess, so applying the patch creating the DreamPlug identifier and associated dreamplug-setup.c is much simpler. (And I personally wish kernel developers would wait for the new mechanism to work and be well supported before deciding not to support the old one any more.) Of course, you need to activate CONFIG_MACH_DREAMPLUG for the patch to be useful (note that this is not incompatible with CONFIG_MACH_GURUPLUG or any other: it is fortunately quite possible to support more than one system on the same kernel).

Now, you will ask, if this patch is necessary for correct DreamPlug support, why is it not included in the directory I mentioned above, which seems be be essentially the Marvell patches? Good question: the answer seems to be that the Marvell U-Boot passes a fake identifier to the kernel (probably that of the GuruPlug, but I'm not completely sure), and kernel support for the GuruPlug will mostly work for the DreamPlug, with one major exception: the DreamPlug has no internal NAND storage (instead it has a NOR for U-boot and internal SD card for root filesystem), so if you have a kernel compiled with CONFIG_MTD_NAND_ORION set and the kernel thinks you're on a GuruPlug, it will freeze. The proper way to fix this is, as I suggest, to configure a kernel properly supporting the DreamPlug; the easy but stupid way, which Marvell chose, is to pretend you have a GuruPlug and configure it to avoid touching the NAND. To make sure the Marvell U-Boot passes the correct machine identifier for the DreamPlug, use setenv machid 0xdde (and possibly saveenv to make this permanent); some web pages around suggest setenv mainlineLinux yes and setenv arcNumber, but that's for an older version of U-Boot than that found on the DreamPlug (2011.06-02334-g8f495d9-dirty). If using the Debian version of U-Boot (see below), the machine identifier will be correct from start.

✱ Another tricky point: the kernel needs to be configured with CONFIG_ARM_PATCH_PHYS_VIRT not set, and CONFIG_PHYS_OFFSET set to 0x0 (or else it will hang after Uncompressing Linux... done, booting the kernel.). Apparently this is due to L2 cache needing to be disabled for the kernel decompressor to work (I have no idea how things got so seriously fscked up that having L2 cache enabled — something which ought to be completely transparent — can break things, nor how that is related to the two options I just mentioned, but I don't really want to know). This may be fixed on newer versions of the kernel or U-Boot, I don't know.

How to (cross-)build the kernel

Here are some potentially useful command lines (which, of course, should be adapted intelligently) to create a cross-compiler for arm(el) on an x86/x86_64 Debian system and install it under /opt/arm-linux-gnueabi-tools (and /opt/arm-linux-gnueabi for the system libraries):

mkdir /opt/arm-linux-gnueabi
mkdir /opt/arm-linux-gnueabi-tools
dpkg-deb -x libc6_2.13-27_armel.deb /opt/arm-linux-gnueabi
dpkg-deb -x libc6-dev_2.13-27_armel.deb /opt/arm-linux-gnueabi
dpkg-deb -x linux-libc-dev_3.2.9-1_armel.deb /opt/arm-linux-gnueabi
(cd /opt/arm-linux-gnueabi/usr ; tar cf - *) | (cd /opt/arm-linux-gnueabi ; tar xf -)
rm -rf /opt/arm-linux-gnueabi/usr
ln -s . /opt/arm-linux-gnueabi/usr
cd /tmp ; apt-get source binutils
cd binutils-2.22
./debian/rules patch
mkdir /tmp/binutils-build
cd /tmp/binutils-build
/tmp/binutils-2.22/configure --target=arm-linux-gnueabi --prefix=/opt/arm-linux-gnueabi-tools --enable-shared --enable-plugins --with-sysroot=/opt/arm-linux-gnueabi
make -j4 && make install
cd /tmp ; apt-get source gcc-4.6
cd gcc-4.6-4.6.3
DEB_CROSS_NO_BIARCH=yes ./debian/rules patch
mkdir /tmp/gcc-build
cd /tmp/gcc-build
/tmp/gcc-4.6-4.6.3/src/configure --target=arm-linux-gnueabi --prefix=/opt/arm-linux-gnueabi-tools --with-sysroot=/opt/arm-linux-gnueabi --enable-languages=c
make -j4 && make install

After this, something like the following might be used to compile the kernel, from a directory where it has been extracted and patched as according to the previous section:

PATH=/opt/arm-linux-gnueabi-tools/bin:$PATH
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabi-
export PATH ARCH CROSS_COMPILE
make menuconfig
make -j4 uImage

To create a proper Debian package instead, use something like this:

PATH=/opt/arm-linux-gnueabi-tools/bin:$PATH
export PATH
day="`date +%Y%m%d`"
make-kpkg --arch armel --cross-compile arm-linux-gnueabi- --revision=0custom.${day} --append-to-version=-dreamplug --config menuconfig configure
CONCURRENCY_LEVEL="4" make-kpkg --arch armel --cross-compile arm-linux-gnueabi- --revision=0custom.${day} --append-to-version=-dreamplug --rootcmd fakeroot kernel_image kernel_headers

This will create packages with names such as linux-image-3.2.9-dreamplug_0custom.20120313_armel.deb and linux-headers-3.2.9-dreamplug_0custom.20120313_armel.deb which can be installed with dpkg -i; after they have been installed, the uImage file can be created on the target using something like this: mkimage -A arm -O linux -T kernel -C none -a 0x00008000 -e 0x00008000 -n Linux-3.2.9-dreamplug -d /boot/vmlinuz-3.2.9-dreamplug uImage (where the mkimage program is from the package named uboot-mkimage or u-boot-tools).

Beware of this bug in Debian kernel-package version 12.036+nmu2, however.

How to use or install a different U-Boot

The DreamPlug comes from a version of U-Boot compiled by Marvell which, as explained above, passes an incorrect machine identifier to the kernel, and which cannot read ext2 formatted partitions. Instructions on installing a different U-Boot are here: they are ridiculously complicated because OpenOCD is, unfortunately, not directly capable of flashing the NOR nonvolatile memory used to store U-Boot on the DreamPlug (as opposed to the GuruPlug, which has both U-Boot and root filesystem on NAND). So basically one has to first load the new U-Boot to RAM, boot it there, and use U-Boot itself to reflash itself

To load the new U-Boot to RAM, connect the JTAG adapter as explained in the manual (page 6), and try to control it using OpenOCD. Since many people have reported that the timing is very difficult (the OpenOCD commands must be run immediately after the DreamPlug is switched on, otherwise it is not able to halt the processor for some reason), a better solution is probably as follows: run openocd -f /usr/share/openocd/scripts/board/sheevaplug.cfg with no other argument (from a directory containing the uboot.elf to be flashed), make sure the DreamPlug is running and connected to JTAG and ready to be reset (e.g., waiting at the U-Boot prompt), then connect to the OpenOCD console on localhost port 4444, and type reset ; init ; sheevaplug_load_uboot (since reset resets the processor immediately, the remainder of the command should have no timing issues); if this is still not sufficient, try lowering the JTAG interface speed with adapter_khz 250 and/or inserting an extra init and/or inserting a small sleep between reset and init; also double-check whether the JTAG connector is correctly wired in, because I've found it to be somewhat capricious. But at least the trick of using reset should avoid having to plug and unplug the Plug a hundred times before it works.

Once the new U-Boot has booted, it can be made to flash itself with something like (as explained on the previously mentioned page):

setenv ipaddr 192.168.1.2
setenv serverip 192.168.1.1
tftpboot 0x6400000 u-boot.kwb
sf probe 0
sf erase 0x0 0x100000
sf write 0x6400000 0x0 0x2fd74

where 192.168.1.2 is some available IP, and 192.168.1.1 is a TFTP server serving the file u-boot.kwb, and assuming the latter has size 0x2fd74 (the correct size will be printed after the tftpboot command). The U-Boot being flashed at this point does not have to be identical to the one doing the flashing, i.e., one can restore the original Marvell U-Boot (found here) using Debian's U-Boot.

Remember the warnings given above: the Debian U-Boot will pass the correct machine identifier (3550=0xdde) to the kernel, so the latter has to be able to handle it.

The mess with the Wifi

[Written ]

There are two different Marvell Wifi chipsets found on these GuruPlugs and DreamPlugs: the SD8688 (SDIO identifier 0x02df:0x9104) and the SD8787 (SDIO identifier 0x02df:0x9119). My GuruPlugs have the former while my DreamPlugs have the latter; however, the distribution may not be along the GuruPlug/DreamPlug line in all cases: I don't know. Support for them in Linux is very different. The (older) SD8688 is supported in Wifi client mode using the libertas driver in stock kernels or a driver from Marvell called sd8xxx (whose source code was released); and it is also supported in master mode using the uap8xxx driver from Marvell (whose source code was also released and which essentially forms the linux-3.0-SDIO-micro-AP.patch patch mentioned above). The newer SD8787 is not the same chip, and apparently needs different drivers: it is also supported in client mode on stock kernels, the driver being then called mwifiex; however, this driver does not support master mode (=access point mode).

The kernel distributed with the DreamPlug does support Wifi master mode, but at the price of a proprietary driver, which is useless not so much for the philosophical reason that it is proprietary, but for the very practical reason that it supports only one kernel version (2.6.33.7-dirty, there's some irony in the word dirty), and that kernel is completely obsolete and contains known security vulnerabilities. Do not use it! So, basically, if you have a newer DreamPlug, you can't use it as a Wifi access point. Yeah, I really despise Marvell for that.