Block maps are a way of looking at various sources of data through the lens of a regular block device. It lets you treat devices that are not block devices, like RAM, as if they were. It also lets you export a slice of an existing block device, which does not have to correspond to a partition boundary, as a new block device.
This is primarily useful because U-Boot's filesystem drivers only operate on block devices, so a block map lets you access filesystems wherever they might be located.
The implementation is loosely modeled on Linux's "Device Mapper" subsystem, see kernel documentation for more information.
Say that our system is using an Ext4 filesystem as its rootfs, where
the kernel is stored in /boot. This image is then typically stored
in an eMMC partition. In this configuration, we can use something like
load mmc 0 ${kernel_addr_r} /boot/Image to load the kernel image
into the expected location, and then boot the system. No problems.
Now imagine that during development, or as a recovery mechanism, we want to boot the same type of image by downloading it over the network. Getting the image to the target is easy enough:
dhcp ${ramdisk_addr_r} rootfs.ext4
But now we are faced with a predicament: how do we extract the kernel image? Block maps to the rescue!
We start by creating a new device:
blkmap create netboot
Before setting up the mapping, we figure out the size of the downloaded file, in blocks:
setexpr fileblks ${filesize} + 0x1ff
setexpr fileblks ${fileblks} / 0x200
Then we can add a mapping to the start of our device, backed by the memory at ${loadaddr}:
blkmap map netboot 0 ${fileblks} mem ${fileaddr}
Now we can access the filesystem via the virtual device:
blkmap get netboot dev devnum
load blkmap ${devnum} ${kernel_addr_r} /boot/Image
In this example, an FIT image is stored in an eMMC partition. We would
like to read the file /etc/version, stored inside a Squashfs image
in the FIT. Since the Squashfs image is not stored on a partition
boundary, there is no way of accessing it via load mmc ....
What we can to instead is to first figure out the offset and size of the filesystem:
mmc dev 0
mmc read ${loadaddr} 0 0x100
fdt addr ${loadaddr}
fdt get value squashaddr /images/ramdisk data-position
fdt get value squashsize /images/ramdisk data-size
setexpr squashblk ${squashaddr} / 0x200
setexpr squashsize ${squashsize} + 0x1ff
setexpr squashsize ${squashsize} / 0x200
Then we can create a block map that maps to that slice of the full partition:
blkmap create sq
blkmap map sq 0 ${squashsize} linear mmc 0 ${squashblk}
Now we can access the filesystem:
blkmap get sq dev devnum
load blkmap ${devnum} ${loadaddr} /etc/version