Hardware¶
The CPU family functional manual is wonderfully detailed and available at http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf The processor itself is documented at http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6281_OpenSource.pdf Table 26, on page 53, details the pin multiplexing arrangement and is very useful!
Serial console is available on vias. With the power plug component-side-up and top-left, the block of four pins to the left of the power plug are, top-to-bottom, 3.3V, uart0 txd (GPIO 10), uart0 rxd (GPIO 11), and gnd.
I2C is available on pads. With the CPU component-side-up and the RAM chip top-left, the pad to the right of R259 (in the region marked Q12) is SCK (GPIO 9); the pad to the right of R261 (in Q13) is SDA (GPIO 8).
JTAG is available on test-pads. (Not tested; taken from http://wiki.openwrt.org/toh/cloudengines/pogo-v4 .) : TDI on TP19; TDO on TP20; TCK on TP18; TMS_CPU on TP21; RSTn on TP17; TMS_CORE on TP22.
To the right of the SD card socket is J17, which looks to be (not tested; taken from http://wiki.openwrt.org/toh/cloudengines/pogo-v4 and visual inspection), in left-to-right order: 3.3V, GND, GPIO 3 (miso), GPIO 7 (cs), GPIO 1 (mosi), GPIO 2 (sck); this appears to be an SPI breakout!
On the board’s back side, by the processor, LED7 is GPIO 23 and LED8 is GPIO 21, both through resistors. Note that GPIO 21 is by default assigned to SATA0_ACT, which indicates either presence or activity on the SATA link. It could be repurposed.
There is an un-populated button? near the power connector; I do not know what GPIO(s) are there, if any.
There appears to be a GPIO pin, but I am unsure which, on the top right pin of U11 (next to R219, and in fact connected to one of its pads, too).
Processor pin multiplexing configuration values (MPPs) can be investigated
from within uboot, by something like md.l f1010000 4
.
UART Booting¶
The kirkwood chips are very friendly and can be fed their initial boot image
over a UART. The requisite kwboot
tool is even packaged in Debian (in
u-boot-tools
). Use, for example:
kwboot -b /path/to/uboot.kwb -B 115200 -t /dev/ttyACM0 -p
Building UBoot¶
With an arm-none-eabi
cross-compiler (say, Debian’s gcc-arm-none-eabi
),
run:
make pogo_v4_defconfig
echo CONFIG_ENV_OFFSET=0x1C0000 >> .config
make oldconfig
CROSS_COMPILE=arm-none-eabi- make KCFLAGS=-fshort-enums
./tools/mkimage -T kwbimage \
-n ./board/cloudengines/pogo_v4/kwbimage.cfg \
-a 0x00600000 -e 0x00600000 -d u-boot.bin u-boot.kwb
Or you could grab my binary, if you trust me, at
pogo4-uboot.kwb
.
Landing uBoot without serial console¶
Taking over a from-factory device time! Do the pogoplug activation dance, but you can stop just after it finds and updates the device. Then run, on a host
curl -k "https://root:ceadmin@PogoplugMobile/sqdiag/HBPlug?action=command&command=dropbear%20start";
Then you want to run all of this, as taken wholesale from http://blog.qnology.com/2014/07/hacking-pogoplug-v4-series-4-and-mobile.html
#stop my.pogoplug.com service
killall hbwd
#download firmware utilities
cd /tmp
wget http://download.doozan.com/uboot/nanddump
wget http://download.doozan.com/uboot/nandwrite
wget http://download.doozan.com/uboot/flash_erase
wget http://download.doozan.com/uboot/fw_printenv
#make execuable
chmod +x flash_erase fw_printenv nanddump nandwrite
#printenv and setenv are normally symbolic links
cp fw_printenv fw_setenv
#remount '/' as read/write
#by default the Pogoplug OS (internal flash) is read only
#skip if running from Debian/ALARM
mount -o remount,rw /
#setup fw_env.config
echo "/dev/mtd0 0xc0000 0x20000 0x20000">/etc/fw_env.config
scp over your own uBoot image, and then write it:
/tmp/flash_erase /dev/mtd0 0 4
/tmp/nandwrite --pad /dev/mtd0 /tmp/uboot.kwb
Then run whatever commands to adjust the uboot environment.
Loading New Firmware from UBoot¶
Flash bootloader without nuking environment (at 0x1C0000
)
mw 0x800000 0xffff 0x1C0000
tftpboot 0x800000 u-boot.kwb
nand erase 0x0 0x1C0000
nand write.e 0x800000 0x0 0x1C0000
To nuke the environment, too, change the nand erase
above to nand
erase 0x0 0x200000
.
You can also substitute loadb 0x800000
(kermit) or loady 0x800000
(ymodem) to use the serial port. In ckermit on the host, then, use, for
example
set prefixing all
send /binary /protocol:kermit u-boot.kwb
Flash UBI-nized image
nand erase 0x200000 0x7e00000
tftpboot 0x800000 ...
nand write 0x800000 0x200000 ${filesize}
The Greatest UBoot Environment Settings¶
Here are what I consider to be the best uboot environment configuration
settings. If you’re pasting along, it may be useful to run
:%s!^setenv!/tmp/fw_setenv!g
for pasting into a root shell!
Basics. You’ll probably want to keep your ethaddr
as it was
setenv ethaddr 00:11:22:33:44:55
setenv baudrate 115200
setenv bootdelay 30
setenv arcNumber 3960
setenv machid f78
Netconsole ala http://forum.doozan.com/read.php?3,14,14 . To use this run
nc -u -p 6666 -s 192.168.77.1 192.168.77.100 6666
or socat STDIO
UDP4-DATAGRAM:192.168.77.100:6666,sourceport=6666
on your host:
setenv serverip 192.168.77.1
setenv ipaddr 192.168.77.100
setenv if_netconsole 'ping ${serverip}'
setenv start_netconsole 'setenv ncip ${serverip}; setenv stdin nc; setenv stdout nc; setenv stderr nc; version;'
setenv preboot 'run if_netconsole start_netconsole'
Kernel command-line arguments
setenv mtdids nand0=orion_nand
setenv mtdparts 'mtdparts=orion_nand:0x1c0000(uboot),0x40000(uboot_env),0x7e00000(ubi)'
setenv bootargs_console 'console=ttyS0,115200n8 panic=10'
Note
The choice of partition layout is consistent with OpenWRT as of commit c19f811c4d732f634ac18f33ae95e954ac9e8c81 (on 2 Jan 2018, “kirkwood: add pogoplug v4” by Alberto Bursi); earlier editions of this page had smaller uboot partitions and placed the environment elsewhere.
Boot commands and conditional operations. This wad of complexity attempts…
to boot the following file-system-based options:
A boot script
/boot.scr
of the 1st partition, formatted FAT, of any USB2 storage.A kernel in the root of the 1st partition, formatted ext4, of the sdcard.
Like above, but on a FAT partition
The above two options, but on any USB2 storage.
The above three options, but searching
/boot
instead of/
OpenWRT compatibility: a file “initramfs.bin” in the root of a FAT fs via USB.
In all file-system-based attempts, the kernel is expected to be named
uImage
. If that is found, the loader additionally searches foruInitrd
andpogo4.dtb
and installs them correctly. For FAT partition options, it is assumed that the 2nd partition is ext4 and contains the root filesystem.Note
The MMC is apparently insufficiently reliable to be a good source of boot scripts (or, rather, of large file reads? in any case, my attempt at making UBI-paving update devices doesn’t go well), so while the machinery could load scripts from MMC, it’s less of a headache to use USB.
Having exhausted file-system options, the loader attempts a “raw” UBI boot, using a UBI partition
root
and UBI volumes namedkernel
(mandatory),initrd
(optional),dtb
(optional), androotfs
(mandatory). If these are found, the system will boot withrootfs
as/
.
The centerpieces of this mess are the bootcmd_load_*
macros, which are
pointed to by loadercmd
(set in boot_try_*
). These expect to find
five variables set in the environment:
loadermedia
: source media (set inboot_if_*
)loaderpart
: partition on media (set inboot_common_pre
)loaderwhere
: memory address for resulting load (set inboot_cond*
)loaderlimit
: a size limit on the thing to load (set inboot_cond*
)loaderwhence
: source file or volume (set inboot_cond*
from other vars)
The boot_cond*
wrappers around this know to build loaderwhence
from:
loaderprefix
: the filesystem path prefix (/
or/boot
; set inbootcmd
)loaderkernel
,loaderinitramfs
,loaderdtb
: the names of components of the boot process (set inboot_common_fs
andboot_try_ubi
) * (if a DTB is loaded, themachid
environment variable is cleared)
setenv nc_disable 'setenv stdin serial; setenv stdout serial; setenv stderr serial' setenv bootcmd_do_bootm 'setenv bootargs ${bootargs_console} ${mtdids} ${mtdparts} ${loaderbootargs} ${xtrabootargs}; run nc_disable; bootm ${bootmargs}' setenv bootcmd_load_fat 'fatload $loadermedia $loaderpart $loaderwhere $loaderwhence' setenv bootcmd_load_ext 'ext4load $loadermedia $loaderpart $loaderwhere $loaderwhence' setenv bootcmd_load_ubi 'ubi read $loaderwhere $loaderwhence $loaderlimit' setenv boot_condload_fdt 'if loaderwhere=0x00700000; loaderwhence=${loaderprefix}${loaderdtb}; loaderlimit=0x100000; run ${loadercmd}; then fdt addr 0x00700000; fdt resize; fdt chosen; setenv machid; bootmargs="${bootmargs} 0x00700000"; fi;' setenv boot_condload_ird 'if loaderwhere=0x01100000; loaderwhence=${loaderprefix}${loaderinitramfs}; loaderlimit=0x300000; run ${loadercmd}; then bootmargs="${bootmargs} 0x01100000"; else bootmargs="${bootmargs} -"; fi;' setenv boot_condload 'if loaderwhere=0x00800000; loaderwhence=${loaderprefix}${loaderkernel}; loaderlimit=0x300000; run ${loadercmd}; then bootmargs="0x00800000"; run boot_condload_ird; run boot_condload_fdt; run bootcmd_do_bootm; fi' setenv boot_condscript 'if loaderwhere=0x00700000; loaderwhence=${loaderprefix}boot.scr; loaderlimit=0x100000; run ${loadercmd}; then source 0x700000; fi' setenv boot_common_pre 'loaderpart="0:1";' setenv boot_common_fs 'loaderkernel="uImage"; loaderinitramfs="uInitrd"; loaderdtb="pogo4.dtb"; loadercondcmd="boot_condload";' setenv boot_if_mmc 'if mmc rescan; mmc info; then loadermedia="mmc"; run ${loadercondcmd}; fi' setenv boot_if_usb 'if usb start; then loadermedia="usb"; run ${loadercondcmd}; fi' setenv boot_try_mmcE 'loadercmd="bootcmd_load_ext"; loaderbootargs="root=/dev/mmcblk0p1 rootfstype=ext4 rootwait"; run boot_if_mmc' setenv boot_try_mmcF 'loadercmd="bootcmd_load_fat"; loaderbootargs="root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"; run boot_if_mmc' setenv boot_try_usbE 'loadercmd="bootcmd_load_ext"; loaderbootargs="root=/dev/sda1 rootfstype=ext4 rootwait"; run boot_if_usb' setenv boot_try_usbF 'loadercmd="bootcmd_load_fat"; loaderbootargs="root=/dev/sda2 rootfstype=ext4 rootwait"; run boot_if_usb' setenv boot_try_ubi 'if ubi part ubi && ubi check rootfs; then loadercmd="bootcmd_load_ubi"; loaderbootargs="ubi.mtd=ubi rootfstype=ubifs"; run ${loadercondcmd}; fi' setenv bootcmd_fs 'echo "trying ${loaderprefix} kernels"; run boot_try_mmcE; run boot_try_mmcF; run boot_try_usbE; run boot_try_usbF;' setenv bootcmd_owt 'echo "trying OpenWRT recovery..."; loaderkernel="initramfs.bin"; loaderinitramfs="initrd"; loaderdtb="dtb"; run boot_try_usbF;' setenv bootcmd_ubi 'echo "trying UBI kernels"; loaderkernel="kernel"; loaderinitramfs="initrd"; loaderdtb="dtb"; run boot_try_ubi;' setenv bootcmd 'run boot_common_pre; loadercondcmd=boot_condscript; echo "trying script"; loaderprefix=/; run boot_try_usbF; run boot_common_fs; run bootcmd_fs; loaderprefix=/boot/; run bootcmd_fs; loaderprefix=""; run bootcmd_owt; run bootcmd_ubi; echo "Boot search failed"'
Note
If you’re wanting to extend the kernel’s command line, the xtrabootargs
environment variable is for you.
Note
Boot scripts are useful for making media that pave over the internal flash on boot, offering a kind of less-manual upgrade path.
Note
As of the latest attempt (U-Boot 174ac987), there are two deficiencies:
one has to drop netconsole before
bootm
(seenc_disable
, above) or the system will fail to loadthe standard boot machinery lacks a prober for UBI(fs), which is kind of sad. It would be neat to teach OpenWRT to land a U-Boot script in
/
or something of that ilk and remove much of the above environmental complexity.
How to Install Debian¶
Note
Unless you have particularly good reason, you probably want to run a system that’s more designed for… shall we say, politely, low-end devices. May I recommend OpenWRT.
Note
You will want a custom bootloader on the device for booting either USB or MMC, as Debian (or ALARM) will not fit on the onboard flash. I have one in my branch of OpenWRT that I use (https://github.com/nwf/openwrt), but there are others floating around that may be more convenient.
These instructions have been updated as of 2023 Jan to work with bullseye
.
Sadly, things are now a little more involved than they used to be, since the
installation seemingly can no longer take place in an emulator.
Fetch Debian Marvell arm installer images and the dtb
we’ll need:
vmlinuz-...
from https://d-i.debian.org/daily-images/armel/daily/kirkwood/netboot/https://d-i.debian.org/daily-images/armel/daily/kirkwood/netboot/initrd.gz
https://d-i.debian.org/daily-images/armel/daily/kirkwood/device-tree/kirkwood-pogoplug-series-4.dtb
Get these files onto your Pogoplug somehow; possibly the simplest is to land them in a FAT filesystem on a SD card or USB stick (which can be repurposed as your installation target, if desired). Concretely, using a USB stick, I was able to boot the kernel with
usb start
setenv bootargs console=ttyS0,115200n8 panic=10
fatload usb 0:1 0x00800000 vmlinuz-...
fatload usb 0:1 0x01100000 initrd.gz
fatload usb 0:1 0x00700000 kirkwood-pogoplug-series-4.dtb
fdt addr 0x00700000; fdt resize; fdt chosen
bootm 0x00800000 0x01100000 0x00700000
Proceed to install as a usual Debian box.
When making partitions, note that uBoot can understand only ext4 usefully, tho’, so either make
/
, if no/boot
, or/boot
ext4. If you do separate/boot
, be sure to adjust your uBoot environment’s kernel command line accordingly, as it likely defaults to the first partition as root and as the place to grab the kernel and so on.The Debian installer will not install a kernel for you. After it declares the installation finished, instead launch a shell (or just switch to one of the two
screen
panes it has open to shells).You’ll want to run, in the target (so, under
chroot /target /bin/dash
cat >> /etc/kernel-img.conf <<HERE do_symlinks=yes image_dest=/boot HERE apt install u-boot-tools mtd-utils linux-image-marvell
Optionally, and subject to local bootloader flavor, you may want to run something like the following so that
fw_printenv
andfw_setenv
work:echo /dev/mtd1 0x0 0x20000 0x20000 > /etc/fw_env.config
Fetch the
dtb
again into/boot
as/boot/pogo4.dtb
.Into
/boot/update_uimages.sh
place the following (andchmod +x
it)#!/bin/bash set -e -x -u cp /boot/uImage{,.old} || true cp /boot/uInitrd{,.old} || true mkimage -A arm -O linux -T kernel -C none -a 0x00008000 -e 0x00008000 \ -n Linux -d /boot/vmlinuz /boot/uImage mkimage -A arm -O linux -T ramdisk -C gzip -a 0x00000000 -e 0x00000000 \ -n initramfs -d /boot/initrd.img /boot/uInitrd
Symlink this program so that it gets run whenever a new kernel is added or deleted:
ln -s /boot/update_uimages.sh /etc/kernel/postinst.d ln -s /boot/update_uimages.sh /etc/kernel/postrm.d
Add
mvsdio
andmmc_block
to/etc/initramfs-tools/modules
if you’re installing to a SD card; the installer won’t grab it because it won’t see the need.Run
update-initramfs -k all -u
and/boot/update_uimages.sh
.systemd
wants to see 16M free in/run
, so we need to raise its default size (as 10% of 128M is, notably, less than 16M to begin with):echo RUN_SIZE=20M > /etc/default/tmpfs
Now exit the chroot and finish the installer. Assuming you’ve got
u-boot
set up correctly (itself a longer discussion), you should now have a Debian box.