Tobias Waldekranz | 135d147 | 2023-02-16 16:33:54 +0100 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0+ |
| 2 | .. |
| 3 | .. Copyright (c) 2023 Addiva Elektronik |
| 4 | .. Author: Tobias Waldekranz <tobias@waldekranz.com> |
| 5 | |
| 6 | Block Maps (blkmap) |
| 7 | =================== |
| 8 | |
| 9 | Block maps are a way of looking at various sources of data through the |
| 10 | lens of a regular block device. It lets you treat devices that are not |
| 11 | block devices, like RAM, as if they were. It also lets you export a |
| 12 | slice of an existing block device, which does not have to correspond |
| 13 | to a partition boundary, as a new block device. |
| 14 | |
| 15 | This is primarily useful because U-Boot's filesystem drivers only |
| 16 | operate on block devices, so a block map lets you access filesystems |
| 17 | wherever they might be located. |
| 18 | |
| 19 | The implementation is loosely modeled on Linux's "Device Mapper" |
| 20 | subsystem, see `kernel documentation`_ for more information. |
| 21 | |
Heinrich Schuchardt | a82f396 | 2023-07-09 04:00:06 +0200 | [diff] [blame] | 22 | .. _kernel documentation: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/index.html |
Tobias Waldekranz | 135d147 | 2023-02-16 16:33:54 +0100 | [diff] [blame] | 23 | |
| 24 | |
| 25 | Example: Netbooting an Ext4 Image |
| 26 | --------------------------------- |
| 27 | |
| 28 | Say that our system is using an Ext4 filesystem as its rootfs, where |
| 29 | the kernel is stored in ``/boot``. This image is then typically stored |
| 30 | in an eMMC partition. In this configuration, we can use something like |
| 31 | ``load mmc 0 ${kernel_addr_r} /boot/Image`` to load the kernel image |
| 32 | into the expected location, and then boot the system. No problems. |
| 33 | |
| 34 | Now imagine that during development, or as a recovery mechanism, we |
| 35 | want to boot the same type of image by downloading it over the |
| 36 | network. Getting the image to the target is easy enough: |
| 37 | |
| 38 | :: |
| 39 | |
| 40 | dhcp ${ramdisk_addr_r} rootfs.ext4 |
| 41 | |
| 42 | But now we are faced with a predicament: how to we extract the kernel |
| 43 | image? Block maps to the rescue! |
| 44 | |
| 45 | We start by creating a new device: |
| 46 | |
| 47 | :: |
| 48 | |
| 49 | blkmap create netboot |
| 50 | |
| 51 | Before setting up the mapping, we figure out the size of the |
| 52 | downloaded file, in blocks: |
| 53 | |
| 54 | :: |
| 55 | |
| 56 | setexpr fileblks ${filesize} + 0x1ff |
Ken Kurematsu | 2356c22 | 2024-09-26 09:26:05 +0000 | [diff] [blame] | 57 | setexpr fileblks ${fileblks} / 0x200 |
Tobias Waldekranz | 135d147 | 2023-02-16 16:33:54 +0100 | [diff] [blame] | 58 | |
| 59 | Then we can add a mapping to the start of our device, backed by the |
| 60 | memory at `${loadaddr}`: |
| 61 | |
| 62 | :: |
| 63 | |
| 64 | blkmap map netboot 0 ${fileblks} mem ${fileaddr} |
| 65 | |
| 66 | Now we can access the filesystem via the virtual device: |
| 67 | |
| 68 | :: |
| 69 | |
| 70 | blkmap get netboot dev devnum |
| 71 | load blkmap ${devnum} ${kernel_addr_r} /boot/Image |
| 72 | |
| 73 | |
| 74 | Example: Accessing a filesystem inside an FIT image |
| 75 | --------------------------------------------------- |
| 76 | |
| 77 | In this example, an FIT image is stored in an eMMC partition. We would |
| 78 | like to read the file ``/etc/version``, stored inside a Squashfs image |
| 79 | in the FIT. Since the Squashfs image is not stored on a partition |
| 80 | boundary, there is no way of accessing it via ``load mmc ...``. |
| 81 | |
| 82 | What we can to instead is to first figure out the offset and size of |
| 83 | the filesystem: |
| 84 | |
| 85 | :: |
| 86 | |
| 87 | mmc dev 0 |
| 88 | mmc read ${loadaddr} 0 0x100 |
| 89 | |
| 90 | fdt addr ${loadaddr} |
| 91 | fdt get value squashaddr /images/ramdisk data-position |
| 92 | fdt get value squashsize /images/ramdisk data-size |
| 93 | |
| 94 | setexpr squashblk ${squashaddr} / 0x200 |
| 95 | setexpr squashsize ${squashsize} + 0x1ff |
| 96 | setexpr squashsize ${squashsize} / 0x200 |
| 97 | |
| 98 | Then we can create a block map that maps to that slice of the full |
| 99 | partition: |
| 100 | |
| 101 | :: |
| 102 | |
| 103 | blkmap create sq |
| 104 | blkmap map sq 0 ${squashsize} linear mmc 0 ${squashblk} |
| 105 | |
| 106 | Now we can access the filesystem: |
| 107 | |
| 108 | :: |
| 109 | |
| 110 | blkmap get sq dev devnum |
| 111 | load blkmap ${devnum} ${loadaddr} /etc/version |