DIY Fully working Alpine Linux for Allwinner and Other ARM SOCs

From Alpine Linux
Revision as of 15:58, 10 April 2016 by Atlury (talk | contribs)

WORK IN PROGRESS

This is taken from multiple sources and is copyright of the respective authors, albiet copied shamelessly. If you are an author of a particular section and wish to be listed please dont hesitate to contact me at oneinsect@gmail.com

Prologue

  • Please note that most of the below is specific to Nanopi m1 but can be extended to any ARM Soc with understanding
  • Nanopi m1 and Orange Pi PC are very similar except for 512MB more RAM in Orange Pi PC, they have almost similar specifications (USBs, Ethernet, I/Os etc)

Sometimes these SOCs are always being fed with constant 1.3V, like NANO Pi m1 in this case or an Orange PI One. An Orange Pi PC however has programable voltage generator and will reduce voltage.

Primitive voltage regulator feed the H3 all the time with 1.3V in some boards. In this mode throttling is rather inefficient since temperatures do not decrease that much when only clockspeed will be reduced. Therefore expect severe performance problems unless you choose to apply a large heatsink and an additional fan.

Sometimes you have to modify your dts file if your board has a fixed voltage regulator to limit the temperature and prevent the board from completing burning out when you are not using a heat-sink. However I would suggest you use some kind of passive/active cooling for H3 boards.

At the time of writing this article the Nanopi M1 still doesnt have its own .dtb file, hence we use a similar board .dtb file (Orange Pi PC).

You are free to try legacy kernels and the process may vary a bit, please linux-sunxi.org for more information and similar sites.

Infact your SOC may not be supported by mainline kernel itself, however you can still use alpine linux.

Lot of people are writing various .dtb files for example below is mainline kernel dts configuration for passive cooling of h3 based boards with fixed voltage regulator

https://github.com/megous/linux/wiki/Fixed-voltage-regulator-test

Written for orange pi one, when treating it as if it had a fixed voltage regulator and it was limiting temperature to ~75°C during stress test. You are free to play around. The internet is huge. Hang out in the IRC channels.

There are work around for issue wherein display doesnt show up if you use DVI to HDMI converter etc. Suggest you use VGA to HDMI or play around with the settings of boot.scr

We expect as the Alpinelinux kernel versions progress further, you will be able to use Alpinelinux more directly without recompiling your own kernel, initramfs files etc

Comprehensive Introduction

Why do we need alpine linux when there are so many xyz distros available? Well it is one the most lightweight platforms with hot swap support for SD-cards and USB devices. There are options for allocation of SD-card free space for application storage.

There are possibilities to completely upgrade a device running remotely with minimum downtime. Console logins are possible with USB null modems for field servicing. Most importantly you can be rest assured that it can survive against power cuts, restarts. For devices without onboard mmc, especially working on sd-card its way more important.

The philosophy of commit only when required, keeps the entire OS read-only and in-memory without touching the storage at all. Thus devices can survive for longer times without crash.

Arm devices unlike x86 dont come with bios in general. BIOS in x86 PCs is generally, a firmware configuring and connecting the hardware to the operating system and it offers support for a variety of OS and supports new OS versions.

ARM use a different approach involving a boot loader for hardware configuration and operating system start-up. The boot loader is developed specifically for the application, adapted to one well-defined hardware SOC configuration, one operating system and only one version of it, which means you cannot probably use it for other SOCs without significant changes.

We here generally talk about Allwinner especially H3 SOCs but you could probably apply this philosophy to other SOCs. The nanopi m1, Orange pi pc and Orange pi lite are only around $10 making them great home servers, firewalls for lowest possible cost.

A linux os cannot be started just like that on an ARM Device, without a small amount of machine specific code to initialize that system.

There are typically 4 stages involved in the ARM Device boot-up process

  • (stage 1) ROM - Reads from initialized persistent storage (selected by boot mode), loads SPL into internal Ram
  • (stage 2) SPL Loader - SPL once loaded does additional setup and loads from persistent storage bootloader (u-boot) into DDR RAM
  • (stage 3) U-boot - U-boot once loaded continues the processor setup and reads the Linux Kernel into DDR RAM
  • (stage 4) Kernel - Once Kernel is loaded, it boots Linux and initializes the user run time environment

Each stage adds functionality. It is important to note that SPL, U-Boot and the Linux Kernel are all statically linked to start running at specific locations from a memory map defined by the CPU. That memory map is nothing but a layout that defines where both internal memory and DDR are mapped to.

In here we are concerned with SD-Cards, and depending on how the SD card is connected, the location to where to write the data to can be different.

OKAY WAIT!!! WRITING DATA???

Dont be confused, yes you will need to compile from source the SPL Loader and write it to SD-Card in a particular way and then compile u-boot from source and write it in a particular and same goes for the rest. Otherwise how will you boot our favorite alpine linux?

But where do you get the sources? Generally if the SOC is supported in Linux (its called mainline) you can directly use the latest kernel, otherwise you will need to get the sources from the SOC manufacturer.

Below is a SD-CARD layout for typical Allwinner which generally combine U-boot with the SPL loader from block 8. This then loads initramfs etc.

"start" is a 1k-block number here. (Multiply it by two to get the corresponding sector number - assuming 512 byte sectors). This layout works generally for Linux Mainline Kernels 4.x and above

start size usage
0 8KB Unused, available for partition table
8 1024KB Initial SPL loader + u-boot (raw format preferred/vfat)
1024 till end vmlinuz + initramfs + modloop + dtb (format fat + enable boot flag)

Notice I have combined SPL with U-boot, it is now possible to generate a single file say "u-boot-sunxi-with-spl.bin"

Boot-loaders aka U-Boot

Let us begin. The first is to understand U-boot.

Distros like alpine linux for that matter any linux do not need to manipulate any kind of bootloader-specific configuration data to indicate which storage device the system should boot from.

Distros simply need to copy the boot configuration files in an ext2/3/4 or FAT partition, mark the partition bootable (via the MBR bootable flag, or GPT legacy_bios_bootable attribute), and U-Boot (or any other bootloader) will find those boot files and execute them. This is conceptually identical to creating a grub2 configuration file on a desktop PC.

Note that in the absence of any partition that is explicitly marked bootable, U-Boot falls back to searching the first valid partition of a disk for boot configuration files. Other bootloaders are recommended to do the same, since I believe that partition table bootable flags aren't so commonly used outside the realm of x86 PCs.

U-Boot can also search for boot configuration files from a TFTP server. The standard format for boot configuration files is that of extlinux.conf, as handled by U-Boot's "syslinux" (disk) or "pxe boot" (network).

U-Boot searches for /extlinux/extlinux.conf then /boot/extlinux/extlinux.conf on disk, or pxelinux.cfg/default over the network.

One example extlinux.conf generated by the Alpine Linux installer is:

 LABEL grsec
 MENU DEFAULT
 MENU LABEL Linux grsec
 LINUX /boot/vmlinuz-grsec
 INITRD /boot/initramfs-grsec
 DEVICETREEDIR /boot/dtbs
 APPEND BOOT_IMAGE=/boot/vmlinuz-grsec modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-grsec console=${console}

Another example extlinux.conf is:

 LABEL Linux Mailine 4.6RC2
 LINUX /boot/vmlinuz-4.6.0-rc1-sunxi
 INITRD /boot/initramfs-new.uImage
 FDT /boot/sun8i-h3-orangepi-pc.dtb
 APPEND BOOT_IMAGE=/boot/vmlinuz-4.6.0-rc1-sunxi modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-sunxi console=${console}

What to compile

What are you basically looking for? What files do you need to compile, how and where do you need to compile from? And where do you need to put them? How do you need to put them? Well dont worry we will cover them here.

You will need the following files (incase of mainline or 4.x latest kernels incase your SOC is supported - In our case yes Allwinner H3)

  • U-boot bootloader (aka u-boot-sunxi-with-spl.bin file say for Orange pi pc Allwinner H3) - compiled separately
  • Device specific .dtb file (in here called sun8i-h3-orangepi-pc.dtb) generated as part of your mainline kernel compilation
  • Linux Kernel Image either uImage/zImage (In our case zImage called vmlinuz-4.6-rc1-sunxi)
  • Initramfs file (what is it? you will learn as we progress)
  • modloop file (what is it again? you will learn as we progress)
  • boot.scr or extlinux.conf file for telling U-boot where to find all our files

Incase of legacy kernels (3.x etc) you will need the following which we will not cover here

  • uImage (linux kernel)
  • script.bin (converted from fex format, notice we dont talk about dtb files here)
  • ext4/ext3 rootfs file system
  • U-boot bootloader etc

Let us write to the SD-Card, I will assume here you already have compiled you kernel, u-boot and move forward. Dont worry we will come back to it at the last.

When we need to actually put things on the SD card, be careful with dd it can and will wipe your hard disk if you make a mistake. In my case I had an SD card reader so I set things to point at that slot only by using the by-id links from udev. These by-id links which include the serial number which helped to avoid mistakes. The following commands assume you have done the same and are using by-id links.

How to burn to SD Card

In linux blank the first 1MB of the card using the following (my sd-card is /dev/sda1

  • dd if=/dev/zero of=/dev/sda bs=1M count=1

But you need to be careful when using dd on special files (i.e., devices).

For example, the below command will write one tape block of 1024 bytes;

  • dd if=(whatever input) of=(a magnetic tape device) bs=1024 count=1

However, the below command will write 1024 small blocks of one byte each

  • dd if=(whatever input) of=(a magnetic tape device) bs=1 count=1024

These are not the same; the 1024 small blocks will take up more room on the tape than the one large block, because of inter-record gaps, and may cause problems for reading the tape.

Now Assuming you have got the file u-boot-sunxi-with-spl.bin, it just has to be burned to sd card using the following command:

  • dd if=u-boot-sunxi-with-spl.bin of=/dev/sda bs=1024 seek=8

Sometimes depending on you compile you can generate spl and u-boot separately (it is not recommended by me) you can install the components separately using

  • dd if=spl/sunxi-spl.bin of=/dev/sda bs=1024 seek=8
  • dd if=u-boot.img of=/dev/sda bs=1024 seek=40

Please note, if you are using old source (v2013.07 or earlier, then the procedure is slightly different)

  • dd if=spl/sunxi-spl.bin of=/dev/sdX bs=1024 seek=8
  • dd if=u-boot.bin of=/dev/sdX bs=1024 seek=32

Typically for understanding, this is how the layout for SD-Card in above cases is;

start size usage
0 8KB Unused, available for partition table etc.
8 32KB Initial SPL loader (8 to 24KB prior to 2013.07 and earlier)
40 504KB U-Boot (32KB to 512KB for 2013.07 and earlier)
544 128KB environment
672 352KB reserved
1024 - Free for partitions

Remember to leave sufficient space for all u-boot files when partitioning the card. The u-boot will not have any partition type defined. Recommended to have first fat partition start at sector 2048 (1MB)

  • Next Using fdisk, create a new primary partition on the card (so far there are no partitions, only uboot has been burned on the card), set the starting sector of the card as 2048
  • Using fdisk, set the bootable flag of the partition

When setting the bootable flag of the partition, to create a FAT32 partition, if you are using fdisk, the FAT32 partition type is called W95 FAT32 (LBA) and its ID is 0xc

  • Now install fat file system on the newly created partition by running mkfs.fat /dev/sda1

Please note that with recent U-Boot it's fine to use ext2/ext3/ext4 as boot partition, or as in our case fat file-systems in the any partition.


There is debug serial port which you can use to connect to PC using putty set @ 115200 kbps. You should able to watch the following.

U-Boot SPL 2016.03-armbian (Apr 08 2016 - 01:09:18)
DRAM: 512 MiB
Failed to set core voltage! Can't set CPU frequency
Trying to boot from MMC1
U-Boot 2016.03-armbian (Apr 08 2016 - 01:09:18 +0530) Allwinner Technology
CPU:   Allwinner H3 (SUN8I 1680)
Model: Xunlong Orange Pi PC
I2C:   ready
DRAM:  512 MiB
MMC:   SUNXI SD/MMC: 0
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
Net:   No ethernet found.
starting USB...
USB0:   USB EHCI 1.00
USB1:   USB OHCI 1.0
USB2:   USB EHCI 1.00
USB3:   USB OHCI 1.0
USB4:   USB EHCI 1.00
USB5:   USB OHCI 1.0
scanning bus 0 for devices... 1 USB Device(s) found
scanning bus 2 for devices... 1 USB Device(s) found
scanning bus 4 for devices... 1 USB Device(s) found
Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found /boot/extlinux/extlinux.conf
Retrieving file: /boot/extlinux/extlinux.conf
reading /boot/extlinux/extlinux.conf
232 bytes read in 29 ms (7.8 KiB/s)
1:      Linux Mailine 4.6RC2
Retrieving file: /boot/uinitrd
reading /boot/uinitrd
 **Unable to read file /boot/uinitrd
 for failure retrieving initrd
SCRIPT FAILED: continuing...
USB device 0: unknown device
No ethernet found.
missing environment variable: pxeuuid
Retrieving file: /boot/extlinux/pxelinux.cfg/00000000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/0000000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/000000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/00000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/0000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/000
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/00
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/0
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default-arm-sunxi
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default-arm
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default
No ethernet found.
Config file not found
No ethernet found.
=>

You can use the following to browser through the sd-card

  • fatls mmc 0:1 boot

Next step would require simply copying the required files like boot configuration files, kernels etc in the above FAT partition (note there is only one FAT partition and it is marked as bootable) and U-Boot (or any other bootloader) will find those boot files and execute them.

  • Assuming you have compiled vmlinuz-4.6.0-rc1-sunxi, you can create a directory called /boot in the root of micro-sd fat partition and copy it there.

  • Similarly create another folder called /dtbs in the root of the micro-sd card and copy the corresponding sun8i-h3-orangepi-pc.dtb file
  • Similarly create boot.scr and copy it to the /boot folder

Basically while booting, U-boot will look for uEnv.txt or boot.scr in the first partition FAT or it will look for extlinux.conf. In here we will define boot.scr.

boot.scr contains needed uboot commands for loading kernel, initrd, setting kernel parameters and booting.

To create boot.scr first make a u-boot script boot.cmd with the u-boot commands you need for booting the system:

  • setenv fdt_high ffffffff
  • setenv machid 1029
  • setenv bootargs earlyprintk modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-sunxi console=${console}
  • load mmc 0:1 0x43000000 boot/dtbs/sun8i-h3-orangepi-pc.dtb
  • load mmc 0:1 0x42000000 boot/vmlinuz-4.6.0-rc1-sunxi
  • bootz 0x42000000 0x45000000 0x43000000

Then translate this to a boot.scr by using the mkimage command

mkimage -C none -A arm -T script -d boot.cmd boot.scr

  • Make sure you also create and copy the files modloop-sunxi, initramfs-new to the /boot folder
  • Lastly copy the apks folder to the root of the sdcard

The apks folder can be extracted by download the Generic ARM Image from http://www.alpinelinux.org/downloads/

Your SD-Card will look as below.

  • /boot
  • /apks

and under /boot directory, the following

  • /boot/dtbs/sun8i-h3-orangepi-pc.dtb
  • /boot/extlinux/extlinux.conf
  • /boot/initramfs-new
  • /boot/modloop-sunxi
  • /boot/vmlinuz-4.6.0-rc1-sunxi

DONT SCRATCH YOUR HEAD YET, WE WILL GET TO COMPILING ABOVE FILES Slowly in the next sections

Compiling Kernel with .dtb file

recompile kernel and make sqfs compiled in instead of a module

CONFIG_SQUASHFS=y CONFIG_BLK_DEV_LOOP=y

enabled squashfs 4.0 in misc under filesystems

"Device drivers" -> "Block devices" -> "Loopback device support"

kernel must support the same squashfs options/versions

that /lib/modules is unavailable, which suggests you should compile loop module INTO the kernel, not as module
  • Loading modules ...modprobe: can't change directory to '/lib/modules': No such file or directory

this means your initrd/initramfs doesnt include modules subdir Image Name: initramfs-grsec

Creating Initramfs File

mkimage -n initramfs-grsec -A arm -O linux -T ramdisk -C none -d initramfs-grsec initramfs-grsec.uImage

mksquashfs [source folder] [SquashFS target file] -b 1048576 -comp xz -Xdict-size 100%

Creating modloop File

The modloop file contains the folders like below...

  • /modules/4.6.0-rc1-sunxi
  • /modules/4.6.0-rc1-sunxi/kernel
  • /modules/4.6.0-rc1-sunxi/arch
  • /modules/firmware

If you run the following command on modloop-grsec or any other similar modloop file.

  • file modloop-grsec

You will realize, it lists as

Squashfs filesystem, little endian, version 4.0, xzy bytes, xyz nodes, blocksize: xzy bytes, created...

You can create a "squashfs-temp" directory and create a sub-dir "modules" and then copy above required files concerning your particular kernel and you are good to go. Then just run the command below.

  • mksquashfs [source folder] [SquashFS target file] -b 1048576 -comp xz -Xdict-size 100%
  • mksquashfs squashfs-temp modloop-sunxi -b 1048576 -comp xz -Xdict-size 100%

Please understand that mksquashfs compresses using gzip (deflate) as default, but if the compression time doesn’t matter as much, you should use the xz (LZMA2) option of more recent SquashFS version.

The command above activates xz using the highest possible compression options and the highest possible blocksize (1 MiB instead of the default 128 kiB). Therefore the process of creating the SquashFS file is slower than if using default options, but the resulting file is much smaller and might be (depending on disk IO time etc) a bit faster. LZMA2 is a highly asymmetric compression algorithm, so decompression is much faster than compression

The command compresses the entire source folder into the SquashFS target file.

Make sure that the SquashFS target file doesn’t exist before using this command. If it already exists, mksquashfs tries to update it, but this might yield undesirable results (I didn’t check that, it probably only produces a lot of error messages but a valid output file)

You can mount the resulting file using this command (assuming the target folder exists and is empty):

  • mount [SquashFS file] [folder you want to mount it in]

If this command fails, you might need to specify some options explicitly:

  • mount -o loop -t squashfs [SquashFS file] [folder you want to mount it in]

If you want to auto-mount the SquashFS, you can also add an _/etc/fstab _entry like this:

  • [SquashFS file] [folder you want to mount it in] squashfs auto,defaults 0 0