Friday, January 15, 2016

Wandboard: Initial software.

iMX6 - Linux Kernel + u-Boot bootloader


Software
Fist of all, I'd like to mention, that any action you would take will require source code. You can get it either with "wandboard-sdk" (which is preferable) or from developer's website (in this case you should patch some packages like kernel for example).

IMPORTANT! To get your system up and running you'll need to "build things", so you should first prepare your environment.

Installing compiler

Compiler is important part of build process. First that you need to understand (!) is that in your host system (the one that we will use for build) will be two compilers: standard gcc for x86_64 architecture that comes with most linux distributions and gcc for arm boards. Latter one is called cross compiler because it builds code for other architecture (i.e. resulting code can not be used on system where we build it, but needs another hardware).

This cross compiler we need to install.
# wget -c https://releases.linaro.org/14.09/components/toolchain/binaries/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux.tar.xz
# tar xf gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux.tar.xz
# export CC=`pwd`/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin/arm-linux-gnueabihf-

Getting source

Now, that we have compiler, we need to get u-boot and kernel source. To do this we will use git.
We will do all the "building" in unprivileged mode (i.e. with our user account, not root). 
let's create folders:
# mkdir ~/src

and then we just:
# git clone git://git.denx.de/u-boot.git
# cd u-boot/

# git checkout v2015.04 -b tmp

patch it:
# wget -c https://rcn-ee.com/repos/git/u-boot-patches/v2015.04/0001-wandboard-uEnv.txt-bootz-n-fixes.patc

# patch -p1 < 0001-wandboard-uEnv.txt-bootz-n-fixes.patch

and finally make it:
make ARCH=arm CROSS_COMPILE=${CC} distclean
make ARCH=arm CROSS_COMPILE=${CC} wandboard_dl_defconfig
make ARCH=arm CROSS_COMPILE=${CC}

As a result you will get u-boot.imx file, which can be installed via:
sudo dd if=./u-boot/u-boot.imx of=/dev/mmcblk0 seek=2 bs=512

Now, here's 2016 update:

You can use RNelson's netInstaller (good job, man!), which can be found here: https://github.com/RobertCNelson/netinstall.
After you clone source code it would be good idea to make temporary block device and write image to that device instead of direct SD card write. (In my case it was because VMware workstation refused to see SD card, thanks guys, great job breaking down something that works!).
Lets make 4Gb virtual disk with 1MB block size.
# create a 100M file in /opt
dd if=/dev/zero of=/opt/dev0-backstore bs=1M count=4000

# create the loopback block device 
# where 7 is the major number of loop device driver, grep loop /proc/devices
mknod /dev/fake-dev0 b 7 200 

losetup /dev/fake-dev0  /opt/dev0-backstore

SD card layout

Now, you have to be aware of iMX6 (and all u-boot) partitioning: first 512 bytes are FAT, don't touch it. Then after 1k goes u-boot with ~250kB weight. Then for some unknown reason ppl write raw kernel (which is really not what is described in wandboard.h file).
| 0-446: FAT | 447-1024: 0000000 | 1k-aprox 250k u-boot | 250k-1M: 0000000 | 1M-3M zImage |

In my case I desided to quit having headache with raw image (luckily wandboard.h file was aligned towards normal linux partitioning) and made simple structure.
/dev/sda1: boot (starts at 1MB of drive, we need to write u-boot, remember?)
/dev/sda2: root
/dev/sda3: swap
boot - contains: uEnv.txt, zImage (exaclty zImage named and packed so, unlike said above), and kernel config (just in case).
root - "/" partition.
swap - no comments.

Creating bootable SD

INFO! In this example I would not create swap partition, but you can do it if you need to.
# fdisk /dev/sdd (assuming /dev/sdd is your SDcard)
Command: n
Select: p
Partition number: 1
First sector: 2048
Last sector: +128M 
(->Now the same again, but)
Partition number: 2
First sector: (propsed, in my case it was 264192)
Last sector: (enter, to use all remaining space)
(->Enable 1-st partition to be bootable)
Command: a
Partition number: 1
(->Write everything and exit)
Command: w

cfdisk on sdd should show 1Mb of free space, then boot partition, then root partition. Now, let's create file system:
mkfs.ext4 /dev/sdd1
mkfs.ext4 /dev/sdd2

"As easy as killing bunnies with an axe" (c) Carmageddon.

Next up, mount sdd2 to /mnt/root (or whatever you wish), an sdd1 to /mnt/root/boot (this should be inside your "whatever" folder.

Now, all you have to do is to unpack contents of archive to newly mounted folder (I hope no need to tell, that you should change to source dir). This could take some time... Check the result, mine unpacked to /mnt/root/binary/boot/filesystem.dir, if so then change to that folder and do mv * ../../.. (boot folder may not move). Get rid of garbage.
# tar xvzf linaro-precise-ubuntu-desktop-20121124-560.tar.gz -C /mnt/root/
Next there will be u-boot and kernel setup (see below).

U-Boot

File uEnv.txt should be put in /mnt/root/boot folder (in this example).
Then you should write bootloader to SD card like:
dd if=u-boot.imx of=/dev/sdd bs=1k seek=1
Then you should make (or use provided, if you have such) kernel, see below.

uEnv.txt

Command quick reference

  • bootargs: The contents of this variable are passed to the Linux kernel as boot arguments (aka "command line").
  • bootcmd: This variable defines a command string that is automatically executed when the initial countdown is not interrupted.
  • bootdelay: After reset, U-Boot will wait this number of seconds before it executes the contents of the bootcmd variable. During this time a countdown is printed, which can be interrupted by pressing any key.Set this variable to 0 boot without delay.
    Be careful: depending on the contents of your bootcmd variable, this can prevent you from entering interactive commands again forever!
  • bootfile: name of the default image to load with TFTP.
  • ethaddr: Ethernet MAC address for first/only ethernet interface (= eth0 in Linux).This variable can be set only once (usually during manufacturing of the board). U-Boot refuses to delete or overwrite this variable once it has been set.
  • ipaddr: IP address; needed for tftp command
  • loadaddr: Default load address for commands like tftp or loads.
  • loads_echo: If set to 1, all characters received during a serial download (using the loads command) are echoed back. This might be needed by some terminal emulations (like cu), but may as well just take time on others.
  • mtdparts: This variable (usually defined using the mtdparts command) allows to share a common MTD partition scheme between U-Boot and the Linux kernel.
  • serverip: TFTP server IP address; needed for tftp command.
Env variables that can be set by DHCP protocol and likes
  • dnsip: IP address of your Domain Name Server
  • gatewayip: IP address of the Gateway (Router) to use
  • hostname: Target hostname
  • ipaddr: same as above
  • netmask: Subnet Mask
  • rootpath: Pathname of the root filesystem on the NFS server
  • serverip: same as above
  • filesize: Size (as hex number in bytes) of the file downloaded using the last bootp, dhcp, or tftp command. 


This is config file that has to be put to boot partition besides kernel. This file will command bootloader how to behave. Here's mine (be advised, that I'm not fan of raw_writing kernel every time, so I use "separate boot partition layout", so root is located on mmcblk0p2, see layout above).

Now, u-Boot loads two general components: zImage (or uImage) which is kernel, and dtb file. Each of this components have it's place (the address) in memory to be loaded:

  • zImage - 0x12000000
  • dtd file - 0x11000000

Netconsole

U-Boot has technology, that allows one to have WB console working over LAN. To make it work with faulty RS232 we should modify U-Boot sources, compile our own bootloader and write it to SD card. Don't worry it's not that complex.
Plan A
From u-boot folder open file include/configs/wandboard.h and add following lines:
// Define addresses:
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.10.99
#define CONFIG_GATEWAYIP 192.168.10.1
#define CONFIG_SERVERIP 192.168.10.100

#define CONFIG_NETCONSOLE 1
#define CONFIG_SYS_CONSOLE_IS_IN_ENV 1
Note, that 10.99 is your board IP and 10.100 is address of PC that accepts your netconsole. You can find complete pastebin of my wandboard.h here, be sure to adjust IP addresses before use!

Plan B
If you wish to have opportunity turning netconsole off, you can compile this variant of wandboard.h file and make the rest of settings with uEnv.txt file like this:
ip_dyn=no
ethaddr=
ipaddr=192.168.10.99
netmask=255.255.255.0
serverip=192.168.10.126
ncip=192.168.10.126
nc=setenv stdin nc;setenv stdout nc;setenv sterr nc
stdin=nc
stdout=nc
stderr=nc
mmcargs=setenv bootargs netconsole=6666@192.168.10.99/,@192.168.10.126/ ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
Well, the downside of this flexible approach is that you get console output slightly later than in "Plan A", so you'll see only "booting mmc" part.

Then you should compile image as usual with "make" command, and write it to boot sector:
# dd if=u-boot.imx of=/dev/sdd bs=1k seek=1 
There are two ways to listen to netconsole. Ubuntu way is to install nc.traditional and run:
# nc.traditional -l -u -p 6666 & nc.traditional -u 192.168.10.99 6666

or you can use ncb command which is under tools folder in u-boot dir.

IMPORTANT! If at boot time WB will not find remote PC for netconsole IT WOULD NOT BOOT!

No comments:

VIM cheat sheet

Basic basics :) i - start editing, current symbol a - start editing, next symbol Esc - stop editing :w - write to disk :w <filename> -...