This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[FAQ] SK-AM62B-P1: How to implement encrypted filesystems using dm-crypt and initramfs

Part Number: SK-AM62B-P1

Tool/software:

Hello all,

This is a detailed guide on implementing an encrypted filesystem on an SK-AM62B-P1 using dm-crypt. Please note that similar steps with minimal changes should also work on other targets based on other SItara/Jacinto processors running TI SDK images.

Hardware pre-requisites

  • SK-AM62B-P1 (or any other alternate target based on Sitara/Jacinto SoC)
  • Micro SD card - memory density at least 2x of the size of TI SDK image
  • Necessary cables to power the target and get UART logs from the console

Software pre-requisites

  • A tested TI SDK Yocto setup - these steps have been tested on a Yocto setup based on SDK v9.1 documented here. These steps should also work with later versions of the SDK.
  • [OPTIONAL] It is advisable to have a build of the tisdk-base-image ready to save time - we will be building on this image. NOTE: These steps have also been tested with tisdk-tiny-image and tisdk-default-image. See note "Naming bbappend file" below if you plan to use an image other than tisdk-base-image.
  • Balena Etcher or your preferred way of writing a compressed WIC image to micro SD card

Ready? Here are the steps you need!

Step 1 - Kernel configuration changes

We will configure the device mapper framework and specifically the crypt target as a built-in module. The menuconfig snapshots below show the desired configurations.

These options are located at Menuconfig > Device Drivers > Multiple devices driver support (RAID and LVM)

Save config, exit and re-build the kernel.

Step 2 - Build busybox

We use busybox to generate the initramfs. The simplest way to do this to use the latest release from the downloads page and follow the build instructions with a cross-compiler documented here.

NOTE: Ensure that you use the right cross compiler here - else the image will not boot.

export CROSS_COMPILE=<path to aarch64 bin directory>
make CROSS_COMPILE=“$CROSS_COMPILE” defconfig
make CROSS_COMPILE=“$CROSS_COMPILE”

We will then install the busybox to a destination folder of our choice.

mkdir ../busybox_installdir
make CONFIG_PREFIX=../busybox_installdir install

Step 3 - Building an initramfs image using busybox

At the very minimum, we need busybox, cryptsetup, mke2fs and dev nodes for our micro SD card partition. Let us do these one-by-one.

Step 3a - Plugging busybox into an initramfs directory structure

Other than the outputs of busybox (bin, sbin, usr), we need directories in the root of the initramfs like dev,etc,lib,proc,run,sys. We create these in an empty directory and the recursively copy the busybox installation to this folder.

cd <directory containing busybox source and installdir>
mkdir final_initramfs
cd final_initramfs
cp -r ../busybox_installdir .
mkdir etc lib opt proc sys dev home plainroot encroot tmp run

Step 3b - Building cryptsetup

We choose to build cryptsetup dynamically here using bitbake. Once built, we copy the cryptsetup executable and its dependencies into our initramfs folder structure. This is a snapshot of the ldd output for the cryptsetup executable. These files can be copied to the final_initramfs folder from the TI SDK image outputs.

Step 3c - Create dev nodes for our SD card partitions

We use the mknod command to create the dev nodes we need.

NOTE: Doing this instead of udev reduces boot-time. You can choose to use udev as well.

sudo mknod urandom c 19
sudo mknod zero c 15
sudo mknod random c 1 8
sudo mknod null c 13
sudo mknod mmcblk1 b 179 96
sudo mknod mmcblk1p1 b 179 97
sudo mknod mmcblk1p2 b 179 98
sudo mknod mmcblk1p3 b 179 99

NOTE: We strictly need just the zero node and the mmcblk* nodes. The other nodes were created only for test purposes during the dev work.

Step 3e - Init file customisation

We create a new file named init in the final_initramfs folder and add the following content in it. This is executed when the control passes to our initramfs.

#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys
echo "Jumping to rootfs..."
sleep 1
#mount -o ro /dev/mmcblk1p2 /mnt
#umount /proc
#umount /sys
#exec switch_root /mnt /sbin/init
exec /bin/sh

Step 3f - Compress the initramfs image as a CPIO archive

Execute the below from the folder containing the initramfs.

find . -printo | cpio --null-ov --format=newc > initramfs.cpio

We can also compress the initramfs as a CPIO archive to reduce its size.

gzip ./initramfs.cpio


Step 4 - Add initramfs and a new partition using a Yocto bbappend

Create a new Yocto layer and then add the below code in a file named tisdk-base-image.bbappend. Place this file in a folder named recipes-core/images of the new Yocto layer.

NOTE: Provide the correct path to the initramfs.cpio.gz in the bbappend.

Navigate to the build folder of the Yocto setup and execute the below.

bitbake-layers create-layer ../sources/meta-<whatever you want>
bit bake-layers add-layer ../sources/meta-<whatever you want>

Navigate to your new layer and create a bbappend for the tisdk-base-image.

cd ../sources/meta-mylayer
mkdir recipes-core
mkdir recipes-core/images
cd recipes-core/images
vim tisdk-base-image.bbappend

Contents of the bbappend file should be as below.

INITRAMFS_IMAGE_NAME = "initramfs"
INITRAMFS_IMAGE_PATH = "/home/shashank/code/git/initramfs_busybox_test"
INITRAMFS_EXTENSION = "cpio.gz"

IMAGE_INSTALL:append = " cryptsetup"
IMAGE_INSTALL:remove = " resize-rootfs"

ROOTFS_POSTPROCESS_COMMAND += "add_initramfs_to_image; "

add_initramfs_to_image () {

    bbnote "Trying to add initramfs to the image now..."

    install -d ${IMAGE_ROOTFS}/boot

    install -m 0755 ${INITRAMFS_IMAGE_PATH}/${INITRAMFS_IMAGE_NAME}.${INITRAMFS_EXTENSION} ${IMAGE_ROOTFS}/boot/${INITRAMFS_IMAGE_NAME}.${INITRAMFS_EXTENSION}

    bbnote "Installed initramfs to the image..."

}

DEPENDS += " tisdk-tiny-initramfs"
MACHINE_FEATURES:remove = "efi"
WKS_FILE = "sdimage-3part.wks"

We also create a new folder called wic and create a new WIC kickstart file named sdimage-3part.wks that tells the build system that we need 3 partitions - boot, rootfs and a clone of rootfs.

mkdir wic
cd wic
vim sdimage-3part.wks

Contents of this file as below.

# short-description: Create SD card image with 3 partitions
# long-description: Creates a partitioned SD card image for TI platforms - one redundant partition for encrypted filesystem.
# Boot files are located in the first vfat partition with extra reserved space.

part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 1024 --fixed-size 128 --use-uuid
part / --source rootfs --ondisk mmcblk1 --fstype=ext4 --label root --align 1024 --use-uuid
part --source rootfs --ondisk mmcblk1 --fstype=ext4 --label rootcopy --align 1024 --use-uuid

Step 5 - Boot and stop at U-Boot

Use Balena Etcher or your favourite method to write the WIC image to the micro SD card. Plug in the card, power on the target and stop the execution at the U-Boot prompt.

Step 6 - Boot into the initramfs

While at the U-Boot prompt, we set the environment variables, copy the kernel image, device tree blob and the initramfs to designated addresses. Then we boot into the initramfs.

NOTE: Look for the print that we had put into the init script of the initramfs (“Jumping to roofs”) to confirm that we booted into the initramfs correctly.

setenv args_initramfs 'setenv bootargs console=${console} ${optargs}'
run args_all args_initramfs
load mmc 1:2 ${loadaddr} /boot/Image
load mmc 1:2 ${fdtaddr} /boot/dtb/ti/k3-am625-sk.dtb
load mmc 1:2 ${rdaddr} /boot/initramfs.cpio.gz
booti ${loadaddr} ${rdaddr}:0x${filesize} ${fdtaddr}

Step 7 - Initramfs operations

We will now do the usual crytpsetup stuff to convert the mmcblk1p3 partition into an encrypted partition. NOTE: The act of formatting the mmcblk1p3 as a LUKS volume and creating the EXT4 partition within it is a one-time step. For subsequent boots, it is sufficient to open, mount and then switch root.

  • [One-time] Format - cryptsetup luksFormat /dev/mmcblk1p3
  • Open Volume - cryptsetup luksOpen /dev/mmcblk1p3 encroot
  • [OPTIONAL] Status Check - cryptsetup -v status encroot
  • [OPTIONAL] Dump Headers - cryptsetup luksDump /dev/mmcblk1p3
  • [One-time] Zeroise - dd if=/dev/zero of=/dev/mapper/encroot bs=1M
  • [One-time]Format as EXT4 - /usr/bin/mke2fs -t ext4 /dev/mapper/encroot
  • Mount enc root - mount /dev/mapper/encroot /encroot
  • [One-time] Mount plain root - mount /dev/mmcblk1p2 /plainroot
  • [One-time] Recursive copy - cp -r /plainroot/* /encroot
  • Navigate to encroot - cd encroot/
  • Switch root! - exec switch_root . sbin/init

Conclusion

Once the switch root has executed, you should see prints from your filesystem. Execute df -H to confirm that the filesystem is mounted at /dev/mapper/<xyz>.