FIT Image with initramfs as an artifact

Hi,

I would like to create an image where the rootfs are just FIT images that contain the kernel, device tree and initramfs root filesystem that stays resident in RAM.

I came across this post

which also mentions this. I’ve taken a look through the Mender Yocto layer and I don’t think it supports it out of the box but mender-ubimg.bbclass looks like the place where it creates the image using ubifs root filesystems. Should I create my own IMAGE_FSTYPE and create my own mender-myimg.bbclass so I can customise this to create my image?

Many Thanks,
Martin.

Hi @martin that feels like the right approach to me. If the changes end up being minimal then perhaps just a bitbake variable and some strategic changes to the existing image classes could work but that may be a bit unwieldy.

It kind of worked but I had a chicken and egg situation between the initramfs (which was the image that was being menderised) and the FIT Image that should be containing it. So it worked but as soon as I cleaned the sstate of the kernel and tried to rebuild it didn’t work.

I think I have worked out a way around this by creating a dummy image recipe that is the image that gets menderised and this dummy image depends the kernel and bootloader. So I get my FIT image and the bootloader which is all that is needed to create my custom image.

I’ll let you know how I get on.

One problem I have is that the FIT image isn’t in ${IMGDEPLOYDIR}. The original ubimg class file had

cat > ${WORKDIR}/ubimg-${IMAGE_NAME}.cfg <<EOF

[rootfsA]
mode=ubi
image={IMGDEPLOYDIR}/{IMAGE_LINK_NAME}.ubifs
vol_id=0
vol_size=${MENDER_CALC_ROOTFS_SIZE}KiB
vol_type=dynamic
vol_name=rootfsa

so for image= I could replace IMGDEPLOYDIR with DEPLOY_DIR_IMAGE and get the FIT image with the initramfs from there.

Is there any problem with this?

My instinct says I need to create my own mender-artifactimg.bbclass file that works with FIT images and not rootfs images. That way I should get a FIT image artifact for deploying as an update. I see when it creates the mender artifact it assumes the artifact is already in
${IMGDEPLOYDIR}

mender-artifact write rootfs-image \
    -n ${MENDER_ARTIFACT_NAME} \
    $extra_args \
    $image_flag ${IMGDEPLOYDIR}/${ARTIFACTIMG_NAME}.${ARTIFACTIMG_FSTYPE} \
    ${MENDER_ARTIFACT_EXTRA_ARGS} \
    -o ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.mender

I can’t find where this occurs, could someone point me to the file in meta-mender where this happens?

Cheers,
Martin.

@martin
I am running Xilinx Petalinux, and we implemented Mender on Zynq7. Our setup might be close, so I’ll do my best to explain what we did. We run a fitimage (device-tree,kernel,rootfs) in ramdisk. The fitImage is the only thing inside the mender image in EMMC aside from the uEnv, and our fpgabin. We use /data location as persistent disk partition area.

First, you have to get the fiIimage working. In our project.conf for our main image we have something like this:

KERNEL_CLASSES = “kernel-fitimage”
KERNEL_IMAGETYPES = “fitImage”
INITRAMFS_IMAGE_BUNDLE = “1”
INITRAMFS_IMAGE = “your-user-image”
INITRAMFS_MAXSIZE = “16777216”

Once we had that, test it by downloading it TFTP and make sure it works the way you like.

Next, I tried in many ways to get the mender image to build from the above image, but couldn’t get it to work. And I don’t think an image can depend on an image? So we created a second image recipe local-mender-image.bb. So, we have to do a 2-step build, and you have to remember to clean your local-mender-image each time, but it works. The trick is, how do you remove all the extra junk from the image. You don’t want a kernel image inside your mender image plus the fitImage!

Here’s some of the recipe so you can get an idea how we did that. Sorry if it’s not complete, I’m just hoping to point you down a path that may work.

my_postprocess_function() {
rm -rf {IMAGE_ROOTFS}/etc rm -rf {IMAGE_ROOTFS}/var
rm -rf {IMAGE_ROOTFS}/usr cp {DEPLOY_DIR_IMAGE}/fitImage-petalinux-user-image-plnx-zynq7-plnx-zynq7 {IMAGE_ROOTFS}/boot/ ln -sf fitImage-petalinux-user-image-plnx-zynq7-plnx-zynq7 {IMAGE_ROOTFS}/boot/fitImage
install -d {IMAGE_ROOTFS}/data/mender install -d {IMAGE_ROOTFS}/data/u-boot
echo “/dev/mmcblk0 0x800000 0x4000” >> {IMAGE_ROOTFS}/data/u-boot/fw_env.config echo "/dev/mmcblk0 0x1000000 0x4000" >> {IMAGE_ROOTFS}/data/u-boot/fw_env.config
}

my_preprocess_function() {
rm -rf ${IMAGE_ROOTFS}/var
}

ROOTFS_POSTPROCESS_COMMAND_append = “my_postprocess_function;”
IMAGE_PREPROCESS_COMMAND = " my_preprocess_function;"

inherit image
MACHINE_ESSENTIAL_EXTRA_RDEPENDS_remove_petalinux-user-image = " kernel-image kernel-devicetree"
MACHINE_ESSENTIAL_EXTRA_RDEPENDS_remove_mender-image_arm = " kernel-image kernel-devicetree"
PREFERRED_PROVIDER_u-boot-fw-utils = “u-boot-fw-utils”
PREFERRED_RPROVIDER_u-boot-fw-utils = “u-boot-fw-utils”
BIF_PARTITION_ATTR = “fsbl u-boot”
BOOTBIN_BASE_NAME = “BOOT”
IMAGE_BOOT_FILES = “uEnv.txt”

MENDER_STORAGE_DEVICE = “/dev/mmcblk0”
MENDER_STORAGE_TOTAL_SIZE_MB = “7168”
MENDER_DATA_PART_SIZE_MB = “6608”
MENDER_FEATURES_ENABLE_append = " mender-image mender-uboot mender-image-sd"
MENDER_FEATURES_DISABLE_append = " mender-grub mender-image-uefi mender-systemd"
MENDER_ARTIFACT_NAME=“local-image”

Hope that helps in some way. There’s probably a better way, but it was consuming too much time trying to find an answer, so this works for us.

Thanks for the reply. I’ll post my implementation as well soon when its working and hopefully both of our posts can help other people in the future. :slight_smile:

I managed to successfully get a menderised FIT Image using a custom bbclass. One thing I noticed is that I ended up cut and pasting a lot of code from

mender-artifactimg.bbclass

So I was wondering if there could be a way of turning this class into something that could easily be extended to create custom artifacts. In IMAGE_CMD_mender I copied most of this except the call to mender-artifact and I had to alter this line

rootfs_size=$(stat -Lc %s ${IMGDEPLOYDIR}/${ARTIFACTIMG_NAME}.${ARTIFACTIMG_FSTYPE})

to use the fitImage. Here’s the final mender-artifact-fit.bbclass

#
#
# By inherting this class you will automatically get a mender artifact of
# the FIT image containing the initramfs
#

inherit mender-helpers

# ------------------------------ CONFIGURATION ---------------------------------

# Extra arguments that should be passed to mender-artifact.
MENDER_ARTIFACT_EXTRA_ARGS ?= ""

# The key used to sign the mender update.
MENDER_ARTIFACT_SIGNING_KEY ?= ""

# --------------------------- END OF CONFIGURATION -----------------------------

# Piggyback on the back of do_deploy
do_deploy[postfuncs] += "do_deploy_artifact_fit"
# Ensure we have mender-artifact-native in the recipes native sysroot
do_deploy[depends] += "mender-artifact-native:do_populate_sysroot"

MENDER_ARTIFACT_NAME_DEPENDS ?= ""

MENDER_ARTIFACT_PROVIDES ?= ""
MENDER_ARTIFACT_PROVIDES_GROUP ?= ""

MENDER_ARTIFACT_DEPENDS ?= ""
MENDER_ARTIFACT_DEPENDS_GROUPS ?= ""

apply_arguments () {
	#
	# $1 -- the command line flag to apply to each element
	# $@ -- the list of arguments to give each its own flag
	#
	local res=""
	flag=$1
	shift
	for arg in $@; do
		res="$res $flag $arg"
	done
	cmd=$res
}

do_deploy_artifact_fit() {
	if [ -z "${MENDER_ARTIFACT_NAME}" ]; then
		bbfatal "Need to define MENDER_ARTIFACT_NAME variable."
	fi

	rootfs_size=$(stat -Lc %s ${DEPLOYDIR}/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.bin)
	calc_rootfs_size=$(expr ${MENDER_CALC_ROOTFS_SIZE} \* 1024)
	if [ $rootfs_size -gt $calc_rootfs_size ]; then
		bbfatal "Size of rootfs is greater than the calculated partition space ($rootfs_size > $calc_rootfs_size). This image won't fit on a device with the current storage configuration. Try reducing IMAGE_OVERHEAD_FACTOR if it is higher than 1.0, or raise MENDER_STORAGE_TOTAL_SIZE_MB if the device in fact has more storage."
	fi

	if [ -z "${MENDER_DEVICE_TYPES_COMPATIBLE}" ]; then
		bbfatal "MENDER_DEVICE_TYPES_COMPATIBLE variable cannot be empty."
	fi

	extra_args=

	for dev in ${MENDER_DEVICE_TYPES_COMPATIBLE}; do
		extra_args="$extra_args -t $dev"
	done

	if [ -n "${MENDER_ARTIFACT_SIGNING_KEY}" ]; then
		extra_args="$extra_args -k ${MENDER_ARTIFACT_SIGNING_KEY}"
	fi

	if [ -d "${DEPLOY_DIR_IMAGE}/mender-state-scripts" ]; then
		extra_args="$extra_args -s ${DEPLOY_DIR_IMAGE}/mender-state-scripts"
	fi

	if mender-artifact write rootfs-image --help | grep -e '-u FILE'; then
		image_flag=-u
	else
		image_flag=-f
	fi

	if [ -n "${MENDER_ARTIFACT_NAME_DEPENDS}" ]; then
		cmd=""
		apply_arguments "--artifact-name-depends" "${MENDER_ARTIFACT_NAME_DEPENDS}"
		extra_args="$extra_args  $cmd"
	fi

	if [ -n "${MENDER_ARTIFACT_PROVIDES}" ]; then
		cmd=""
		apply_arguments "--provides" "${MENDER_ARTIFACT_PROVIDES}"
		extra_args="$extra_args  $cmd"
	fi

	if [ -n "${MENDER_ARTIFACT_PROVIDES_GROUP}" ]; then
		cmd=""
		apply_arguments "--provides-group" "${MENDER_ARTIFACT_PROVIDES_GROUP}"
		extra_args="$extra_args $cmd"
	fi

	if [ -n "${MENDER_ARTIFACT_DEPENDS}" ]; then
		cmd=""
		apply_arguments "--depends" "${MENDER_ARTIFACT_DEPENDS}"
		extra_args="$extra_args $cmd"
	fi

	if [ -n "${MENDER_ARTIFACT_DEPENDS_GROUPS}" ]; then
		cmd=""
		apply_arguments "--depends-groups" "${MENDER_ARTIFACT_DEPENDS_GROUPS}"
		extra_args="$extra_args $cmd"
	fi

	# Create the mender FIT image artifact
	# KERNEL_FIT_NAME = -${PV}-${PR}${IMAGE_VERSION_SUFFIX} 
	mender-artifact write rootfs-image \
		-n ${MENDER_ARTIFACT_NAME} \
		$extra_args \
		-f ${DEPLOYDIR}/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.bin \
		${MENDER_ARTIFACT_EXTRA_ARGS} \
		-o ${DEPLOYDIR}/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.mender \

	# Deploy it, creating symlinks as it's in the DEPLOYDIR it should be removed by cleansstate
	ln -snf fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.mender ${DEPLOYDIR}/fitImage-${INITRAMFS_IMAGE_NAME}-${MACHINE}.mender
}

and then in the kernel recipe

inherit mender-artifact-fit

I have one query, the final menderised kernel image is slightly smaller than the original

-rw-r--r-- 2 martin martin 20631160 Jun 18 08:36 fitImage-octave-cad-image-octopuck-mt7688an--5.4.40-r0-octopuck-mt7688an-20200618072941.bin
-rw-rw-r-- 2 martin martin 20585472 Jun 18 08:36 fitImage-octave-cad-image-octopuck-mt7688an--5.4.40-r0-octopuck-mt7688an-20200618072941.mender

I was expecting it to be slightly larger due to the artifact mender, have I messed up somewhere with the above code or would you expect it to be smaller?

I would expect the .mender file to be smaller. The payload is compressed using gzip.

You can actually unpack it and inspect,

tar xvf fitImage-octave-cad-image-octopuck-mt7688an--5.4.40-r0-octopuck-mt7688an-20200618072941.mender

phew, that makes sense. The FIT image is already compressed I think hence why it’s only slightly smaller :slight_smile:

I can create my custom image now. Just waiting on a board with access to the SPI-NOR pins so I can program it to test it out.

In the initramfs image that I use I have disabled menderisation of the rootfs but left the dataimg partition creation as this is required. Note: octimg is my custom ubimg.

IMAGE_FSTYPES_remove = "mender mender.bmap octimg octimg.bmap mtdimg"
IMAGE_FSTYPES_append = " dataimg"

Then I created my dummy top level container image that is used just to create my custom image.

SUMMARY = "Dummy image for creating custom mender image octimg"

DESCRIPTION = "Blah Blah."

inherit image

DEPENDS = "virtual/kernel virtual/bootloader"
RDEPENDS = ""
# The Depends on virtual/kernel is not enough. This ensures that the kernel FIT image has
# been deployed to DEPLOY_DIR_IMAGE which is required by the mender-octimg.bbclass 
do_image[depends] += "virtual/kernel:do_deploy"

ROOTFS_BOOTSTRAP_INSTALL = ""

IMAGE_FSTYPES = "mender mender.bmap octimg octimg.bmap mtdimg"

IMAGE_INSTALL = "kernel-image"
IMAGE_INSTALL_remove  = "mender-client"
LINGUAS_INSTALL = ""

Then mender-octimg.bbclass is a rip off of the mender-ubimg.bbclass expect for the ubinize configuration:

[rootfsA]
mode=ubi
image=${DEPLOY_DIR_IMAGE}/fitImage-${INITRAMFS_IMAGE_NAME}-${MACHINE}
vol_id=0
vol_size=${MENDER_CALC_ROOTFS_SIZE}KiB
vol_type=dynamic
vol_name=rootfsa

[rootfsB]
mode=ubi
image=${DEPLOY_DIR_IMAGE}/fitImage-${INITRAMFS_IMAGE_NAME}-${MACHINE}
vol_id=1
vol_size=${MENDER_CALC_ROOTFS_SIZE}KiB
vol_type=dynamic
vol_name=rootfsb

[data]
mode=ubi
image=${DEPLOY_DIR_IMAGE}/${INITRAMFS_IMAGE_NAME}.dataimg
vol_id=2
vol_size=${MENDER_DATA_PART_SIZE_MB}MiB
vol_type=dynamic
vol_name=data

Note how this uses DEPLOY_DIR_IMAGE instead of the IMGDEPLOYDIR as our image is just a dummy image and the files we want to use have already been built and deployed.

Creates the image fine and cleansstate clears things up nicely. I’ll let you know how I get on when I get the board I can flash.

If anyone spots any problems or has any suggestions I would be grateful for any feedback.