[Initially written on , mostly rewritten on ]
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 the boot process. This page is not expected to be of interest to inexperienced users.
The main information site about these computers was the PlugWiki
at http://plugcomputer.org/plugwiki/index.php/Main_Page
,
which is now dead
(but Archive.org
remembers parts of it); there is some
information here
by Debian
and here
by ArchLinux which might still be useful (see
also this
page).
Note that an important difference between the DreamPlug and the GuruPlug is that the GuruPlug has an internal 512MB NAND flash memory for persistent storage whereas the DreamPlug has a smaller amount (2MB) of NOR memory to store the bootloader, and an internal 4GB SD card that is accessed through the USB bus (and can be changed if necessary by opening the plug). There are other differences, but this is the main reason why the boot and upgrade process is not the same in both plugs. (See here for a general introduction about NAND and NOR.)
Connect the JTAG adapter to the DreamPlug using the UART interface on both sides and do something like this (on the computer to which the JTAG adapter is connected by USB):
sudo modprobe ftdi-sio sudo chown $USER /dev/ttyUSB0 screen /dev/ttyUSB0 115200
Look here
for example (I'm pointing at the 4.2.21 kernel because that's the
most recent longterm stable kernel as of 2016-09-17, but of course,
another version may be more appropriate), and look for
the kirkwood
kernels or configs.
There used to be a number of patches that were in various ways useful, but now this seems to be irrelevant, and the DreamPlug will work fine with a vanilla kernel.
However, these recent kernels require a recent U-Boot.
Indeed, if booting from Marvell U-Boot or Debian U-Boot version
<2011.12-3, then 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).
Leaving CONFIG_ARM_PATCH_PHYS_VIRT
unset is a bad idea,
and incompatible with device trees, so the
best solution is to use a recent U-Boot. See
below on how to upgrade U-Boot.
Initially, the support for
every ARM system had to be coded separately in the
Linux kernel. Each system would
receive a
number, the machine identifier, and the bootloader
would pass this identifier to the kernel so as to activate the proper
bit of code. The machine identifiers of the GuruPlug and the
DreamPlug are 2659=0xa63
and 3550=0xdde
respectively. However, the DreamPlug appeared at a time when this
whole mechanism was being phased out, which led to complications:
while the GuruPlug was supported under identifier 0xa63
if the kernel was compiled with CONFIG_MACH_GURUPLUG
, on
the other hand, support for the DreamPlug under the machine
identifier 0xdde
, and
the CONFIG_MACH_DREAMPLUG
config variable, never made its
way into the mainline kernel. (The patches adding this
are 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,
but as I said, they were never merged.) To work around this, the
U-Boot provided by Marvell would pass an incorrect machine identifier
on the DreamPlug (pretending it was a GuruPlug), a quick and dirty
workaround which mostly worked, but required extra patches on the
kernel (to account for the differences between the two plugs,
especially with regards to flash memory devices). But this is all
obsolete anyway.
If using a recent U-Boot, the correct way to pass hardware
information to the kernel is now not to use the obsolete machine
identifier mechanism, but
a device tree blob. All the
necessary support is in the kernel: compilation should create a file
called arch/arm/boot/dts/kirkwood-dreamplug.dtb
(or arch/arm/boot/dts/kirkwood-guruplug-server-plus.dtb
for the GuruPlug), which should be passed to the kernel by U-Boot in a
way similar to the passing of the initial ramdisk
(see below). So just remember to place this
file somewhere U-Boot can find it.
(There is an optional hack,
called CONFIG_ARM_APPENDED_DTB
, which lets
you append the blob to the kernel zImage
instead
of passing it separately: it is simpler if you want to avoid changing
the boot config, but since U-Boot has to be replaced anyway for the
reasons explained above concerning the L2 cache, there is little
reason to use this hack on the DreamPlug.)
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.19-18+deb8u1_armel.deb /opt/arm-linux-gnueabi dpkg-deb -x libc6-dev_2.19-18+deb8u1_armel.deb /opt/arm-linux-gnueabi dpkg-deb -x linux-libc-dev_3.16.7-ckt11-1+deb8u5_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 # Similarly, need to move the contents of # /opt/arm-linux-gnueabi/include/arm-linux-gnueabi # to its parent and ditto for /opt/arm-linux-gnueabi/lib/arm-linux-gnueabi # (and replace them by symlinks .). cd /tmp ; apt-get source binutils/jessie cd binutils-2.25 ./debian/rules patch mkdir /tmp/binutils-build cd /tmp/binutils-build /tmp/binutils-2.25/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.8/jessie cd gcc-4.8-4.8.4 DEB_CROSS_NO_BIARCH=yes ./debian/rules patch mkdir /tmp/gcc-build cd /tmp/gcc-build /tmp/gcc-4.8-4.8.4/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:
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-4.4.21-dreamplug_0custom.20160917_armel.deb
and linux-headers-4.4.21-dreamplug_0custom.20160917_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-4.4.21-dreamplug -d
/boot/vmlinuz-4.4.21-dreamplug uImage
(where
the mkimage
program is from the package
named uboot-mkimage
or u-boot-tools
). If
using an initial ramdisk, convert it to a uInitrd
file
loadable by U-Boot with something like: mkimage -A arm -O linux
-T ramdisk -C none -a 0x01100000 -n Initramfs-4.4.21-dreamplug -d
/boot/initrd.img-4.4.21-dreamplug uInitrd
(note that, unlike
the kernel image and initial ramdisk, the device tree blob does not
have to be converted using mkimage
).
Beware
of this
bug in Debian kernel-package
version 12.036+nmu2
, however.
Learn how to use U-Boot. I suggest instructions such as the following:
ext2load usb 0:2 0x0c00000 /boot/kirkwood-dreamplug.dtb ext2load usb 0:2 0x6400000 /boot/uImage ext2load usb 0:2 0x1100000 /boot/uInitrd setenv bootargs console=ttyS0,115200 root=/dev/sda2 rootdelay=10 ro bootm 0x6400000 0x1100000 0x0c00000
assuming the second partition of the internal SD card
(usb 0:2
) holds an ext2 or ext3 filesystem where the
three files named above are, respectively, the device tree blob, the
kernel uImage
and the uInitrd
for the
initial ramdisk.
Here's what the entire U-Boot environment might look like:
baudrate=115200 bootcmd=${x_bootcmd_usb}; ${x_bootcmd_fdt}; ${x_bootcmd_kernel}; ${x_bootcmd_initrd}; setenv bootargs ${x_bootargs} ${x_bootargs_root}; bootm 0x6400000 0x1100000 0x0c00000; bootdelay=3 eth1addr=something ethact=egiga0 ethaddr=something stderr=serial stdin=serial stdout=serial x_bootargs=console=ttyS0,115200 x_bootargs_root=root=/dev/sda2 rootdelay=10 ro x_bootcmd_fdt=ext2load usb 0:2 0x0c00000 /boot/kirkwood-dreamplug.dtb x_bootcmd_initrd=ext2load usb 0:2 0x1100000 /boot/uInitrd x_bootcmd_kernel=ext2load usb 0:2 0x6400000 /boot/uImage x_bootcmd_usb=usb start
The DreamPlug comes from a version of U-Boot compiled by Marvell which, as explained above, is incapable of booting a recent kernel, doesn't know about device tree blobs, and doesn't even pass the correct (obsolete) machine identifier for the DreamPlug. The best thing to do is throw it away. Sadly, upgrading U-Boot isn't so simple.
One way to do this is from Linux on the device itself, provided one has a working kernel which correctly supports the NOR flash used on the DreamPlug (but this may run into a chicken-and-egg problem). If the kernel says something like this at boot on the DreamPlug:
[ 1.186000] m25p80 spi0.0: mx25l1606e (2048 Kbytes) [ 1.190915] 3 ofpart partitions found on MTD device spi0.0 [ 1.196449] Creating 3 MTD partitions on "spi0.0": [ 1.201890] 0x000000000000-0x000000080000 : "u-boot" [ 1.208287] 0x000000100000-0x000000110000 : "u-boot env" [ 1.215203] 0x000000180000-0x000000190000 : "dtb"
and there is a /dev/mtd0
device, then U-Boot can be
flashed with simply flashcp /usr/lib/u-boot/dreamplug/u-boot.kwb
/dev/mtd0
(assuming the new U-Boot raw binary is
in /usr/lib/u-boot/dreamplug/u-boot.kwb
, which is where
Debian puts it).
I think the equivalent command for the GuruPlug would
be flash_erase /dev/mtd0 0 4 ; nandwrite /dev/mtd0
/usr/lib/u-boot/dreamplug/u-boot.kwb
but I didn't check and my
GuruPlugs are all retired now.
While I'm at it, the U-Boot environment is
in /dev/mtd1
on the DreamPlug and
in /dev/mtd0
at offset 0xc0000
on the
GuruPlug. It is a 4kB block consisting of 4092 bytes of data
(null-terminated strings of the
form name=value
) preceded by a
4-byte checksum which is just the CRC32 of the 4092 next
bytes. Given such an environment, use flashcp uboot-env.bin
/dev/mtd1
on the DreamPlug, or flash_erase /dev/mtd0
0xc0000 1 ; nandwrite -s 0xc0000 /dev/mtd0 uboot-env.bin
on the
GuruPlug (again, this is untested).
U-Boot can be upgraded from U-Boot itself.
First, load it into memory, say at offset 0x0900000
with something like ext2load usb 0:2 0x0900000
/boot/u-boot.kwb
(or tftpboot 0x0900000
/boot/u-boot.kwb
if using TFTP, or whatever you
use to load a file into U-Boot).
Then flash it using the following lines for the DreamPlug (to write to NOR):
sf probe 0 sf erase 0x0 0x100000 sf write 0x0900000 0x0 size
where the last argument on the third line is the size of the file (which is printed by the load command), or at least some upper bound on that size.
For the GuruPlug:
nand erase 0x0 0x80000 nand write 0x0900000 0x0 0x80000
where 0x80000
at the end of both lines is an upper
bound on the size of the file which is a multiple
of 0x20000
(the block size of the NAND).
If the device is bricked, OpenOCD should be able to write to its RAM and restart a good U-Boot, which can then reflash itself (or do whatever U-Boot can do).
First connect the JTAG adapter as explained
in the manual
(page 6): connect the JTAG interface on the adapter and
on the plug, and connect the adapter to some computer
through USB. The adapter appears as USB
identifier 9e88:9e8f
. Make sure you have the proper
permissions on the device
(/dev/bus/usb/xxx/yyy
where the bus
and device numbers xxx and yyy are shown
by lsusb
).
Note that the JTAG interface is incredibly fickle. If things don't work at the first try, try to disconnect it and reconnect it (at both ends!). There seem to be many things that can go wrong, and I haven't been able to identify them all, but a badly wired JTAG connexion is definitely one.
Also, use a version of OpenOCD between 0.5.0 and 0.7.0 inclusive. Versions 0.8.0 and 0.9.0 can't seem to connect to the interface. See this bug-report for details.
Once ready, run openocd -f
/usr/share/openocd/scripts/board/sheevaplug.cfg
and telnet to
localhost port 4444 to get to the OpenOCD console. Here
is what the output should look like when everything is properly
connected and OpenOCD is able to control the plug's
processor:
Open On-Chip Debugger 0.7.0 (2016-09-16-10:42) Licensed under GNU GPL v2 For bug reports, read http://openocd.sourceforge.net/doc/doxygen/bugs.html Info : only one transport option; autoselect 'jtag' adapter speed: 2000 kHz trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst adapter_nsrst_delay: 200 jtag_ntrst_delay: 200 dcc downloads are enabled Warn : use 'feroceon.cpu' as target identifier, not '0' sheevaplug_load_uboot Info : clock speed 2000 kHz Info : JTAG tap: feroceon.cpu tap/device found: 0x20a023d3 (mfg: 0x1e9, part: 0x0a02, ver: 0x2) Info : Embedded ICE version 0 Info : feroceon.cpu: hardware has 1 breakpoint/watchpoint unit
On the other hand, if you get messages like Error: JTAG scan
chain interrogation failed: all zeroes
and Error:
feroceon.cpu: IR capture error; saw 0x00 not 0x01
and Error: unexpected Feroceon EICE version signature
then things are not all right. But in this case you should
still be able to use the reset
command, and it should
still work, so if the problem is not from a badly
wired JTAG connexion, you can try to reset
the processor until OpenOCD announces that it found
a feroceon.cpu tap/device
as shown above.
There are definitely a number of factors which can cause
OpenOCD to succeed or fail to control the plug's
processor, and I have been unable to identify them all: a badly
wired JTAG is one (it will fail repeatedly, no matter how
often reset
is run), but sometimes the first connexion
fails, and after a reset
it works again. (I noticed that
when the plug is sitting idle at the U-Boot prompt,
OpenOCD seems to control it at the first try, whereas if
it is running Linux, at least one reset
is necessary. I
really don't understand what it going on.)
Anyway, assuming we have a working or workable JTAG
connexion, inside the OpenOCD console, you can
run cd /usr/lib/u-boot/dreamplug
(replacing by a
directory where uboot.elf
resides) and then reset ;
init ; sheevaplug_load_uboot
: upon success,
OpenOCD should display something like this:
JTAG tap: feroceon.cpu tap/device found: 0x20a023d3 (mfg: 0x1e9, part: 0x0a02, ver: 0x2) target state: halted target halted in ARM state due to debug-request, current mode: Supervisor cpsr: 0x000000d3 pc: 0xffff0000 MMU: disabled, D-Cache: disabled, I-Cache: disabled 233373 bytes written at address 0x00600000 downloaded 233373 bytes in 1.664493s (136.921 KiB/s) verified 233373 bytes in 0.850263s (268.039 KiB/s)
and the plug will be running the new U-Boot (just for once, but it can be used to flash itself, or flash a different U-Boot, as explained above).
I don't think the ELF U-Boot image
loaded into memory by OpenOCD can be used directly as
such — but if you have no other way of getting the U-Boot image inside
memory before flashing it, you can also use OpenOCD to do
this, e.g., reset ; init ; sheevaplug_init ; load_image
uboot.elf ; verify_image uboot.elf ; load_image u-boot.kwb 0x0900000
bin ; resume 0x0600000
(and then, flash U-Boot from inside
itself assuming it resides at offset 0x0900000
).
If it doesn't work, double check the JTAG connection.
If it still doesn't work, perhaps try adding a
small sleep
command between reset
and init
, or lowering the JTAG interface
speed with adapter_khz 250
(I'm not sure this is useful,
though). I don't think cold rebooting the plug itself is useful,
though: reset
should work just as well. But I can't
claim to understand what's really going on: there's a lot of black
magic, and some people have reported failing to unbrick a plug after
dozens or hundreds of attempts. Good luck!
[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. 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.