Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2020 # Author: Sam Protsenko <joe.skb7@gmail.com> # Test U-Boot's "abootimg" commands. import os import pytest import utils """ These tests rely on disk image (boot.img), which is automatically created by the test from the stored hex dump. This is done to avoid the dependency on the most recent mkbootimg tool from AOSP/master. Here is the list of commands which was used to generate the boot.img and obtain compressed hex dump from it: $ echo '/dts-v1/; / { model = "x1"; compatible = "y1,z1"; };' > test1.dts $ echo '/dts-v1/; / { model = "x2"; compatible = "y2,z2"; };' > test2.dts $ dtc test1.dts > dt1.dtb $ dtc test2.dts > dt2.dtb $ cat dt1.dtb dt2.dtb > dtb.img $ echo 'kernel payload' > kernel $ echo 'ramdisk payload' > ramdisk.img $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ --cmdline "cmdline test" --dtb ./dtb.img \ --os_version R --os_patch_level 2019-06-05 \ --header_version 2 --output boot.img $ gzip -9 boot.img $ xxd -p boot.img.gz > boot.img.gz.hex Now one can obtain original boot.img from this hex dump like this: $ xxd -r -p boot.img.gz.hex boot.img.gz $ gunzip -9 boot.img.gz For boot image header version 4, these tests rely on two images that are generated using the same steps above : 1- boot.img : $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ --cmdline "cmdline test" --dtb ./dtb.img \ --os_version R --os_patch_level 2019-06-05 \ --header_version 4 --output ./boot.img 2- vendor_boot.img $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ --cmdline "cmdline test" --dtb ./dtb.img \ --os_version R --os_patch_level 2019-06-05 \ --pagesize 4096 --vendor_ramdisk ./ramdisk.img \ --header_version 4 --vendor_boot ./vboot.img \ """ # boot.img.gz hex dump img_hex = """1f8b08084844af5d0203626f6f742e696d670073f47309f2f77451e46700 820606010106301084501f04181819041838181898803c3346060c909c9b 92939997aa50925a5cc2300a461c3078b2e1793c4b876fd92db97939fb6c b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253 8bf25273140a122b73f21353b8460364148c8251300a46c1281801a02831 3725b3387bb401300a46c1281805a360148c207081f7df5b20550bc41640 9c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34 d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5 463a5546f82c1f05a360148c8251300a462e000085bf67f200200000""" # bootable boot image v4 hex dump (with DTBs) boot_img_hex = """1f8b0808e2dd60690203626f6f745f6e65772e696d6700edd8ab0d836018 40d14f900a0c8c5036e80a24180c24dde04f4034d04740b13d02d90920e7 8c70e5adbbe6d9b74d5544441987c72dfe64010000009cd5342e9f71beff d2367fd3900b0200000017b4a4f7f05a2703000000002e6c0765d9bd6000 300000""" # bootable vendor boot image v4 hex dump (with DTBs + bootconfig) vboot_img_hex = """1f8b0808e2dd6069020376656e646f725f626f6f745f6e65772e696d6700 eddb316bc24014c0f14b2dd20a425c3b393a48c1d0c1a54be9aca588fbc5 0b3434e9c979d04628f811fd22426787d2bbc43a68e9d4a5f1ff83f7de25 5c787053f220d3d1fde3dd783c39174ee86255e68e4f0000e00405e2e835 e0e142886db9fae8f89c95dbaa7ac5890100f02f1899ab74f1dc9dcb22d3 52b538110000000000ea67ddfedcb8f2ee6228aa317ecf859fed7fcffefd fae68747835d6dec42bc0df6d74d1fc5a0bfac6e89331797b9564926663a 9f4b9bc659f2b7cda383e6517f19fdd61c0000000080d3111e7c4f030000 000080fa912fcae854c55adbeb2769d4ab34c9ad4d16963f010000000000 a88d2fb468951800500000""" # Expected response for "abootimg dtb_dump" command dtb_dump_resp="""## DTB area contents (concat format): - DTB #0: (DTB)size = 125 (DTB)model = x1 (DTB)compatible = y1,z1 - DTB #1: (DTB)size = 125 (DTB)model = x2 (DTB)compatible = y2,z2""" # Address in RAM where to load the boot image ('abootimg' looks in $loadaddr) loadaddr = 0x1000 # Address in RAM where to load the vendor boot image ('abootimg' looks in $vloadaddr) vloadaddr= 0x10000 # Expected DTB #1 size dtb1_size = 0x7d # Expected DTB #2 size dtb2_size = 0x7d # Expected DTB #1 offset from the boot image start address dtb1_offset = 0x1800 + dtb1_size # Expected DTB offset from the vendor boot image start address dtb2_offset = 0x2000 + dtb2_size # Expected DTB aligned offset dtba_offset = 0x4000 # DTB #1 start address in RAM dtb1_addr = loadaddr + dtb1_offset # DTB #2 start address in RAM dtb2_addr = vloadaddr + dtb2_offset class AbootimgTestDiskImage(object): """Disk image used by abootimg tests.""" def __init__(self, ubman, image_name, hex_img): """Initialize a new AbootimgDiskImage object. Args: ubman: A U-Boot console. Returns: Nothing. """ gz_hex = ubman.config.persistent_data_dir + '/' + image_name + '.gz.hex' gz = ubman.config.persistent_data_dir + '/' + image_name + '.gz' filename = image_name persistent = ubman.config.persistent_data_dir + '/' + filename self.path = ubman.config.result_dir + '/' + filename ubman.log.action('persistent is ' + persistent) with utils.persistent_file_helper(ubman.log, persistent): if os.path.exists(persistent): ubman.log.action('Disk image file ' + persistent + ' already exists') else: ubman.log.action('Generating ' + persistent) f = open(gz_hex, "w") f.write(hex_img) f.close() cmd = ('xxd', '-r', '-p', gz_hex, gz) utils.run_and_log(ubman, cmd) cmd = ('gunzip', '-9', gz) utils.run_and_log(ubman, cmd) cmd = ('cp', persistent, self.path) utils.run_and_log(ubman, cmd) gtdi1 = None @pytest.fixture(scope='function') def abootimg_disk_image(ubman): """pytest fixture to provide a AbootimgTestDiskImage object to tests. This is function-scoped because it uses ubman, which is also function-scoped. However, we don't need to actually do any function-scope work, so this simply returns the same object over and over each time.""" global gtdi1 if not gtdi1: gtdi1 = AbootimgTestDiskImage(ubman, 'boot.img', img_hex) return gtdi1 gtdi2 = None @pytest.fixture(scope='function') def abootimgv4_disk_image_vboot(ubman): """pytest fixture to provide a AbootimgTestDiskImage object to tests. This is function-scoped because it uses ubman, which is also function-scoped. However, we don't need to actually do any function-scope work, so this simply returns the same object over and over each time.""" global gtdi2 if not gtdi2: gtdi2 = AbootimgTestDiskImage(ubman, 'vendor_boot.img', vboot_img_hex) return gtdi2 gtdi3 = None @pytest.fixture(scope='function') def abootimgv4_disk_image_boot(ubman): """pytest fixture to provide a AbootimgTestDiskImage object to tests. This is function-scoped because it uses ubman, which is also function-scoped. However, we don't need to actually do any function-scope work, so this simply returns the same object over and over each time.""" global gtdi3 if not gtdi3: gtdi3 = AbootimgTestDiskImage(ubman, 'bootv4.img', boot_img_hex) return gtdi3 @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('android_boot_image') @pytest.mark.buildconfigspec('cmd_abootimg') @pytest.mark.buildconfigspec('cmd_fdt') @pytest.mark.requiredtool('xxd') @pytest.mark.requiredtool('gunzip') def test_abootimg(abootimg_disk_image, ubman): """Test the 'abootimg' command.""" ubman.log.action('Loading disk image to RAM...') ubman.run_command('setenv loadaddr 0x%x' % (loadaddr)) ubman.run_command('host load hostfs - 0x%x %s' % (loadaddr, abootimg_disk_image.path)) ubman.log.action('Testing \'abootimg get ver\'...') response = ubman.run_command('abootimg get ver') assert response == "2" ubman.run_command('abootimg get ver v') response = ubman.run_command('env print v') assert response == 'v=2' ubman.log.action('Testing \'abootimg get recovery_dtbo\'...') response = ubman.run_command('abootimg get recovery_dtbo a') assert response == 'Error: recovery_dtbo_size is 0' ubman.log.action('Testing \'abootimg dump dtb\'...') response = ubman.run_command('abootimg dump dtb').replace('\r', '') assert response == dtb_dump_resp ubman.log.action('Testing \'abootimg get dtb_load_addr\'...') ubman.run_command('abootimg get dtb_load_addr a') response = ubman.run_command('env print a') assert response == 'a=11f00000' ubman.log.action('Testing \'abootimg get dtb --index\'...') ubman.run_command('abootimg get dtb --index=1 dtb1_start dtb1_size') response = ubman.run_command('env print dtb1_start') correct_str = "dtb1_start=%x" % (dtb1_addr) assert response == correct_str response = ubman.run_command('env print dtb1_size') correct_str = "dtb1_size=%x" % (dtb1_size) assert response == correct_str ubman.run_command('setenv dtbaaddr 0x%x' % (dtba_offset)) ubman.run_command('cp.b $dtb1_start $dtbaaddr $dtb1_size') ubman.run_command('fdt addr $dtbaaddr') ubman.run_command('fdt get value v / model') response = ubman.run_command('env print v') assert response == 'v=x2' @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('android_boot_image') @pytest.mark.buildconfigspec('cmd_abootimg') @pytest.mark.buildconfigspec('cmd_fdt') @pytest.mark.requiredtool('xxd') @pytest.mark.requiredtool('gunzip') def test_abootimgv4(abootimgv4_disk_image_vboot, abootimgv4_disk_image_boot, ubman): """Test the 'abootimg' command with boot image header v4.""" ubman.log.action('Loading disk image to RAM...') ubman.run_command('setenv loadaddr 0x%x' % (loadaddr)) ubman.run_command('setenv vloadaddr 0x%x' % (vloadaddr)) ubman.run_command('host load hostfs - 0x%x %s' % (vloadaddr, abootimgv4_disk_image_vboot.path)) ubman.run_command('host load hostfs - 0x%x %s' % (loadaddr, abootimgv4_disk_image_boot.path)) ubman.run_command('abootimg addr 0x%x 0x%x' % (loadaddr, vloadaddr)) ubman.log.action('Testing \'abootimg get ver\'...') response = ubman.run_command('abootimg get ver') assert response == "4" ubman.run_command('abootimg get ver v') response = ubman.run_command('env print v') assert response == 'v=4' ubman.log.action('Testing \'abootimg get recovery_dtbo\'...') response = ubman.run_command('abootimg get recovery_dtbo a') assert response == 'Error: header version must be >= 1 and <= 2 to get dtbo' ubman.log.action('Testing \'abootimg get dtb_load_addr\'...') ubman.run_command('abootimg get dtb_load_addr a') response = ubman.run_command('env print a') assert response == 'a=11f00000' ubman.log.action('Testing \'abootimg get dtb --index\'...') ubman.run_command('abootimg get dtb --index=1 dtb2_start dtb2_size') response = ubman.run_command('env print dtb2_start') correct_str = "dtb2_start=%x" % (dtb2_addr) assert response == correct_str response = ubman.run_command('env print dtb2_size') correct_str = "dtb2_size=%x" % (dtb2_size) assert response == correct_str ubman.run_command('setenv dtbaaddr 0x%x' % (dtba_offset)) ubman.run_command('cp.b $dtb2_start $dtbaaddr $dtb2_size') ubman.run_command('fdt addr $dtbaaddr') ubman.run_command('fdt get value v / model') response = ubman.run_command('env print v') assert response == 'v=x2' @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('android_boot_image') @pytest.mark.buildconfigspec('cmd_abootimg') @pytest.mark.requiredtool('xxd') @pytest.mark.requiredtool('gunzip') def test_abootimg_bootconfig(abootimgv4_disk_image_vboot, abootimgv4_disk_image_boot, ubman): """Test bootconfig handling with boot image v4. Verifies that androidboot.* parameters from bootargs are appended to the bootconfig section in vendor_boot image in memory, and that non-androidboot parameters remain in bootargs. """ # Setup addresses ram_base = utils.find_ram_base(ubman) ramdisk_addr_r = ram_base + 0x4000000 ubman.run_command('setenv ramdisk_addr_r 0x%x' % ramdisk_addr_r) ubman.run_command('setenv loadaddr 0x%x' % loadaddr) ubman.run_command('setenv vloadaddr 0x%x' % vloadaddr) # Set bootargs with androidboot.* parameters ubman.run_command('setenv bootargs "androidboot.serialno=ABC123 androidboot.mode=recovery console=ttyS0"') # Load images ubman.run_command('host load hostfs - 0x%x %s' % (vloadaddr, abootimgv4_disk_image_vboot.path)) ubman.run_command('host load hostfs - 0x%x %s' % (loadaddr, abootimgv4_disk_image_boot.path)) ubman.run_command('abootimg addr 0x%x 0x%x' % (loadaddr, vloadaddr)) # Extract ramdisk (triggers bootconfig append) ubman.run_command('abootimg get ramdisk ramdisk_addr ramdisk_size') # Get ramdisk address response = ubman.run_command('env print ramdisk_addr') ramdisk_start = int(response.split('=')[1], 16) # Verify androidboot.* parameters were removed from bootargs response = ubman.run_command('env print bootargs') assert 'androidboot.' not in response assert 'console=ttyS0' in response # Get ramdisk size and verify BOOTCONFIG magic at the end response = ubman.run_command('env print ramdisk_size') ramdisk_size = int(response.split('=')[1], 16) # Dump the end of the ramdisk where BOOTCONFIG trailer should be # The trailer is at the end, so dump the last 48 bytes response = ubman.run_command('md.b 0x%x 48' % (ramdisk_start + ramdisk_size - 48)) # Verify BOOTCONFIG magic is present assert 'BOOTCONFIG' in response |