This guide will get you started on porting snappy to your device. It covers setting the tools on your development machine and walks you through the basics of snappy porting by looking at the runtime requirement of a snappy system from a partition layout perspective. It will then go in depth explaining what is needed to make a snappy bootloader, kernel and initrd and will touch briefly on the requirements for installing/flashing images using ubuntu-device-flash.
First of all, you should make sure you have enabled the Snappy Tools PPA, as explained in the snappy introduction.
To get started, please download the device tarball for one of the supported architecures build used for the 15.04 release. Run the folliwing to obtain the url to download from,
ubuntu-device-flash query --device generic_arm64 --show-image --channel ubuntu-core/15.04/edge | grep device-
Change generic_arm64
accordingly.
You will need to create an oem
snap, some bases for this can be found in
http://bazaar.launchpad.net/~snappy-dev/snappy-hub/snappy-systems/files
Once an oem
snap is created for the target and the device
tarball is available, an image can be
created by running,
sudo ubuntu-device-flash core 15.04 -o my-snappy.img \
--channel edge --oem <oem.snap> \
--enable-ssh --device-part=./device.tar.xz
Let's break that down into smaller chunks, so we can see more easily what's going on:
sudo ubuntu-device-flash core 15.04 \ # create an ubuntu core image for 15.04 series
-o my-snappy.img \ # create your snappy.img
--channel edge \ # use the latest build from 15.04 series (see Channels)
--oem <oem.snap> \ # oem enablement for the device to port to
--enable-ssh \ # enable ssh for convenience
--device-part=./device.tar.xz # make use of the local device part
This will create a snappy Ubuntu Core image for the target board that uses the local enablement bits provided in the ./device.tar.xz
we have downloaded above.
To flash the my-snappy.img we produced to an SD card, simply use the dd command:
# NOTE: replace /dev/sdX with the the block device of your SD card,
# maybe it's also something like /dev/mmcblk0
sudo dd if=my-snappy.img of=/dev/sdX bs=32M
sync
The image we just created has four partitions, which map directly to the required partitions to support the snappy system image runtime.
Number Start End Size File system Name Flags
1 ##MiB ##MiB ##MiB fat32 system-boot
2 ##MiB ##MiB ##MiB ext4 system-a
3 ##MiB ##MiB ##MiB ext4 system-b
4 ##MiB ##MiB ##MiB ext4 writable
It’s worth noticing that those partitions have the appropriate label and that the kernel can resolve them through that label as standard block devices. Besides that, snappy Ubuntu Core has no hard requirements on exactly what storage medium you put these on your device.
However, to keep things simple we recommend to stick to a pure SD card layout, which as a side effect will make it easy to reuse our standard flashing tool (ubuntu-device-flash) without much effort.
Let’s look at the partitions and how their content is composed by ubuntu-device-flash.
The system-boot partition is where all the boot essential artifacts are stored. On snappy systems that use
the u-boot bootloader, this is a FAT32 partition that is mounted to /boot/uboot on a booted snappy device.
The size of this partition is not specified, but system builders should pick a comfortable size that easily
fits 3 kernels and 3 initrds in - with buffer. It contains a few top level artifacts as well as two directories
called a/
and b/
that contain the enablement bits needed by the bootloader to boot into ubuntu systems
installed in the system-a and system-b labeled partitions.
In general a a/
or b/
file will contain
/boot/uboot/a/dtbs/am335x-boneblack.dtb
, the device tree binary for the platform/boot/uboot/a/vmlinuz
, the snappy enabled kernel/boot/uboot/a/initrd.img
, the snappy enabled initrd
To define which files to put in place here you can configure in the hardware.yaml file that is inside the device tarball. While not strictly needed for the snappy runtime behaviour, the hardware.yaml file that was used to install the boot essential enablement bits is kept for reference in the matching boot directory.
Every device tar has a hardware.yaml
that looks like:
kernel: assets/vmlinuz # where in device tarball is the kernel?
initrd: assets/initrd.img # where in device tarball is the initrd?
dtbs: assets/dtbs # where in device tarball are the dtbs available
Once you do an initial upgrade of your snappy system you will find the same layout described above in the /boot/uboot/b
directory. The files just match the ones needed to boot into snappy Ubuntu system installed on the system-b
partition.
If you step one level up in the file system, you will see that there are still files that are neither associated with
a/
nor with b/
:
$ find /boot/uboot/ -maxdepth 1 -type f
/boot/uboot/uEnv.txt
/boot/uboot/snappy-system.txt
To realize the transactional paradigm of system updates, snappy supports a system-ab partition scheme that allows to atomically update and rollback the system running. For that to work, the snappy tool requirements those partitions to be labelled
system-a
and system-b
. They need to be of same size (recommended: 1GB) which must fit the ubuntu rootfs. For products these
partition currently must use the ext4 filesystem, but as long as your bootloader can resolve the partitions by label and your
kernel and initrd can fsck and mount the that partition you are free to experiment with whatever filesystem you’re interested
in. Let us know if you plan to go for production with anything but ext4 so we can discuss if and how to support such vision.
Besides those facts, the the system-a
and system-b
partitions are pretty boring from an enablers point of view as there is
not much to do for that role. The majority of content in there is a 1:1 copy of the device independent Ubuntu image (aka
ubuntu-core). The only bits that the device enablers contributes to system-a/-b
partitions are kernel modules that are not
boot essential and can be loaded on demand after the system partition is mounted. Your device tarball has to ship those in the
system/lib/modules/KERNEL-VERSION
directory that matches the version of the kernel shipped for that system.
In our device tarball you downloaded in the beginning you can observe that we ship something similar to a system/lib/modules/3.16.0-28-generic
folder that ships all the typical peripheral support one expects from a fully
working Ubuntu system.
IMPORTANT: The device enabler must not try to use the system/
directory to ship any other files than kernel modules residing
in the directory above as we are likely to break all potential uses except that of shipping kernel modules in that location as
part of the upcoming snappy milestones. If you feel the need to ship something beyond this, please discuss your requirement on
the snappy-devel mailing list.
The writable partition is not that interesting for this porting guide. Again for products we require this partition to be an
ext4
formatted Linux partition, but nothing speaks against you picking another filesystem if you don’t plan to ship a snappy
powered product with it. Since the “writable” partition is used not only for user data, but also for installing snapps we
recommend to size this partition generously to deliver a nice and extensible device experience to your users.
The writable partition will be mounted to /writable
and maps to various places of the standard LFS to enable snappy system
config mechanism to work. Since this is not relevant for enablers we won’t look into the mount mounts used, but you can use the
mount command on any snappy system if you are interested to learn more about the places we use the writable partition.
Since we realize that for products the flashing approach can vary, this guide is explicitly focussed on specifying the runtime requirements of the partitioning scheme used. However, we believe that for development boards providing a straightforward and standardized way to flash builds to a standard block device such as SD card or usb disk that then can be directly booted on your platform can greatly improve the convenience and productivity that developers experience.. If you want to provide a development platform, we highly recommend you to make your platform so that images for your platform can be produced using our ubuntu-device-flashing tool and then dd’ed to a block device and then simply plugged into your development board for booting snappy.
If you need help how to realize that vision as part of your BSP roadmap, please don’t hesitate to reach out to us on snappy-devel mailing list or through your official partner contact in Canonical. Snappy enablement basics and the device tarball.
In this section we want to take a look at enablement from the perspective of the device tarball, which is the main artifact for system enablers to bundle and distribute their development BSPs for snappy.
All the runtime relevant elements in here have already been explained in the section “Snappy enablement basics & partitions” above; hence only a quick reference explaining how the individual pieces get mapped to the snappy system partitions:
hardware.yaml
: config file referencing runtime assets used byubuntu-device-flash
.assets
: directory shipping the boot essential assets of a snappy BSP (kernel, initrd and device tree binary where supported.system
: directory shipping kernel modules that are only needed after the rootfs has been mounted; you must not use this directory for anything but shipping ABI compatible kernel modules matching your kernel referenced in thehardware.yaml
and shipped in the assets directory
If you want to use ubuntu-device-flash
to also support creating convenient block devices images that just boot for
your platform you might be happy to check out the flash.yaml file which gives you some facilities to customize the
behaviour for your needs. flash.yaml
is known to be in very early stages, so if you have input on how to make
ubuntu-device-flash useful for your specific case to allow producing block devices that can be used booted, please reach out to snappy development team on the [mailto:[email protected]](mailing list).
“System A/B” is a Snappy feature to have two sets of root filesystems and boot files (typically kernel, initrd and optionally fdt/device tree), hence “A/B”. When an update breaks the system preventing it from boot to a healthy state, snappy will automatically fall back to the previously use system. Read this section to achieve this setup with Snappy and U-Boot.
Modern U-Boot configs on many hacker boards will read an uEnv.txt
file on the first partition formatted as FAT and merge
the values in the U-Boot environment. This can be extended by adding a snappy-system.txt
file which is under Snappy’s
control and implements the A/B boot logic. The U-Boot defaults for your board or your uEnv.txt file need to define some
variables that the Snappy logic relies on (which are described further below), and then call into the Snappy boot procedure.
Snappy installation provides a snappy-system.txt
which is under Snappy’s control and defines snappy_boot
. snappy_boot
is an U-Boot variable defining a procedure which will implement the A/B selection and fallback. It’s behavior is described
in this pseudo-code:
if trying to boot a new version (snappy_mode=try); then
if snappy stamp file (snappy-stamp.txt) exists; then
# previous boot attempt failed; recover by using old version
boot old version
else
# touch snappy stamp file to record we’ve attempted a boot
write stamp file
boot new version
else (snappy_mode=regular)
boot current version
endif
The state needed to implement that logic is also stored and updated by the snappy system upgrader in snappy-system.txt. The snapppy_boot logic will create a snappy-stamp.txt to detect whether a first boot into a new system failed to boot and if thats the case will automatically fall back on next boot to the previously used system partition. Only if the boot succeeds the snappy-stamp.txt file will be removed and the changes to the primary boot partitions will be made permanent by the snappy runtime.
For Snappy’s U-Boot logic to work, these are the required U-Boot environment variables and U-Boot features:
CONFIG_SYS_HUSH_PARSER
for things like if/then/else logic- FAT and MMC write support
${mmcdev}:${mmcpart}
is expected to point at the FAT boot partition${fdtfile}
has the filename of the fdt file to load for this board${loadaddr}
,${initrd_addr}
,${fdt_addr}
are the memory load addresses of the kernel, initrd and fdtCONFIG_SUPPORT_RAW_INITRD
is required to load a plain initrd (avoids the need to convert withmkimage
)
Kernel requirements for Snappy (ubuntu and vanilla/android)
Snappy Kernels are based on the multiv7_defconfig plus some mandatory ubuntu configs available here. Based on the version of your kernel, rebase one of these tree on top your branch:
- 3.10.x: http://kernel.ubuntu.com/git/ppisati/ubuntu-vivid.git/log/?h=snappy_v3.10
- 3.14.x: http://kernel.ubuntu.com/git/ppisati/ubuntu-vivid.git/log/?h=snappy_v3.14
- 3.18.x: http://kernel.ubuntu.com/git/ppisati/ubuntu-vivid.git/log/?h=snappy_v3.18
If your board does not work with the ubuntu generic armhf kernel, we have backports of apparmor patches to a few common android kernels available.
If you have a different kernel version, we recommend to port the patches that are committed on top of of our presquash kernel branches in those kernel trees.
Remember that all snappy systems must have all the latest apparmor patches applied to be fully functional, so even if you make a BSP not meant to power products you must enable those for your kernel.
In snappy systems the initrd is mandatory as it contains important logic for our transactional system image upgrades. For porters the main usage for touching this file is to include driver modules that need to be available at boot time before the main system partition is mounted. For all other use cases, please contact the snappy development team to find a best practice that can be supported.
The ubuntu initrd.img
is by default a lzma
compressed init ramfs and is expected to be in the device tarballs
assets/
directory. On a standard snappy disk layout you will find the initrd.img on the system-boot
partition
in the a/ directory, e.g.
$ sudo mount /dev/disk/by-label/system-boot /mnt
$ ls /mnt/a/
dtbs hardware.yaml initrd.img vmlinuz
On modern Ubuntu installs as well as on your native snappy instance it can be unpacked using the following command line:
mkdir /tmp/myplace; cd /tmp/myplace
cat /mnt/a/initrd.img | unxz -c | cpio -i
This will yield a directory structure with the following layout:
$ ls
bin conf etc init lib run sbin scripts
The modules for enabling early boot can be found in lib/modules/VERSION/kernel/, while the ubuntu-core system image logic can be found in scripts/ubuntu-core-rootfs. This logic needs to be shipped by all snappy initrds. After modifying this initrd you can package it up using the command:
find . | cpio --create --format=newc | lzma > /mnt/a/initrd.img
The initrd.img will then be used on next boot from that system-a
partition.
And now … happy Snapping!