1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2020 3# Author: Sam Protsenko <joe.skb7@gmail.com> 4 5# Test U-Boot's "abootimg" commands. 6 7import os 8import pytest 9import u_boot_utils 10 11""" 12These tests rely on disk image (boot.img), which is automatically created by 13the test from the stored hex dump. This is done to avoid the dependency on the 14most recent mkbootimg tool from AOSP/master. Here is the list of commands which 15was used to generate the boot.img and obtain compressed hex dump from it: 16 17 $ echo '/dts-v1/; / { model = "x1"; compatible = "y1,z1"; };' > test1.dts 18 $ echo '/dts-v1/; / { model = "x2"; compatible = "y2,z2"; };' > test2.dts 19 $ dtc test1.dts > dt1.dtb 20 $ dtc test2.dts > dt2.dtb 21 $ cat dt1.dtb dt2.dtb > dtb.img 22 $ echo 'kernel payload' > kernel 23 $ echo 'ramdisk payload' > ramdisk.img 24 $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ 25 --cmdline "cmdline test" --dtb ./dtb.img \ 26 --os_version R --os_patch_level 2019-06-05 \ 27 --header_version 2 --output boot.img 28 $ gzip -9 boot.img 29 $ xxd -p boot.img.gz > boot.img.gz.hex 30 31Now one can obtain original boot.img from this hex dump like this: 32 33 $ xxd -r -p boot.img.gz.hex boot.img.gz 34 $ gunzip -9 boot.img.gz 35 36For boot image header version 4, these tests rely on two images that are generated 37using the same steps above : 38 391- boot.img : 40 $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ 41 --cmdline "cmdline test" --dtb ./dtb.img \ 42 --os_version R --os_patch_level 2019-06-05 \ 43 --header_version 4 --output ./boot.img 44 452- vendor_boot.img 46 $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ 47 --cmdline "cmdline test" --dtb ./dtb.img \ 48 --os_version R --os_patch_level 2019-06-05 \ 49 --pagesize 4096 --vendor_ramdisk ./ramdisk.img \ 50 --header_version 4 --vendor_boot ./vboot.img \ 51 52""" 53 54# boot.img.gz hex dump 55img_hex = """1f8b08084844af5d0203626f6f742e696d670073f47309f2f77451e46700 56820606010106301084501f04181819041838181898803c3346060c909c9b 5792939997aa50925a5cc2300a461c3078b2e1793c4b876fd92db97939fb6c 58b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253 598bf25273140a122b73f21353b8460364148c8251300a46c1281801a02831 603725b3387bb401300a46c1281805a360148c207081f7df5b20550bc41640 619c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34 62d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5 63463a5546f82c1f05a360148c8251300a462e000085bf67f200200000""" 64 65# boot img v4 hex dump 66boot_img_hex = """1f8b080827b0cd630203626f6f742e696d6700edd8bd0d82601885d1d7c4 6758d8c808b88195bd098d8d246e40e42b083f1aa0717be99d003d277916b8 68e5bddc8a7b792d8e8788c896ce9b88d32ebe6c971e7ddd3543cae734cd01 69c0ffc84c0000b0766d1a87d4e5afeadd3dab7a6f10000000f84163d5d7cd 70d43a000000000000000060c53e7544995700400000""" 71 72# vendor boot image v4 hex dump 73vboot_img_hex = """1f8b0808baaecd63020376626f6f742e696d6700edd8310b824018c6f1b3 74222a08f41b3436b4280dcdd19c11d16ee9109d18d59042d047ec8b04cd0d 75d19d5a4345534bf6ffc173ef29272f38e93b1d0ec67dd79d548462aa1cd2 76d5d20b0000f8438678f90c18d584b8a4bbb3a557991ecb2a0000f80d6b2f 77f4179b656be5c532f2fc066f040000000080e23936af2755f62a3d918df1 78db2a7ab67f9ffdeb7df7cda3465ecb79c4ce7e5c577562bb9364b74449a5 791e467e20c53c0a57de763193c1779b3b4fcd9d4ee27c6a0e00000000c0ff 80309ffea7010000000040f1dc004129855400400000""" 81 82# Expected response for "abootimg dtb_dump" command 83dtb_dump_resp="""## DTB area contents (concat format): 84 - DTB #0: 85 (DTB)size = 125 86 (DTB)model = x1 87 (DTB)compatible = y1,z1 88 - DTB #1: 89 (DTB)size = 125 90 (DTB)model = x2 91 (DTB)compatible = y2,z2""" 92# Address in RAM where to load the boot image ('abootimg' looks in $loadaddr) 93loadaddr = 0x1000 94# Address in RAM where to load the vendor boot image ('abootimg' looks in $vloadaddr) 95vloadaddr= 0x10000 96# Expected DTB #1 offset from the boot image start address 97dtb1_offset = 0x187d 98# Expected DTB offset from the vendor boot image start address 99dtb2_offset = 0x207d 100# DTB #1 start address in RAM 101dtb1_addr = loadaddr + dtb1_offset 102# DTB #2 start address in RAM 103dtb2_addr = vloadaddr + dtb2_offset 104 105class AbootimgTestDiskImage(object): 106 """Disk image used by abootimg tests.""" 107 108 def __init__(self, u_boot_console, image_name, hex_img): 109 """Initialize a new AbootimgDiskImage object. 110 111 Args: 112 u_boot_console: A U-Boot console. 113 114 Returns: 115 Nothing. 116 """ 117 118 gz_hex = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz.hex' 119 gz = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz' 120 121 filename = image_name 122 persistent = u_boot_console.config.persistent_data_dir + '/' + filename 123 self.path = u_boot_console.config.result_dir + '/' + filename 124 u_boot_console.log.action('persistent is ' + persistent) 125 with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): 126 if os.path.exists(persistent): 127 u_boot_console.log.action('Disk image file ' + persistent + 128 ' already exists') 129 else: 130 u_boot_console.log.action('Generating ' + persistent) 131 132 f = open(gz_hex, "w") 133 f.write(hex_img) 134 f.close() 135 cmd = ('xxd', '-r', '-p', gz_hex, gz) 136 u_boot_utils.run_and_log(u_boot_console, cmd) 137 cmd = ('gunzip', '-9', gz) 138 u_boot_utils.run_and_log(u_boot_console, cmd) 139 140 cmd = ('cp', persistent, self.path) 141 u_boot_utils.run_and_log(u_boot_console, cmd) 142 143gtdi1 = None 144@pytest.fixture(scope='function') 145def abootimg_disk_image(u_boot_console): 146 """pytest fixture to provide a AbootimgTestDiskImage object to tests. 147 This is function-scoped because it uses u_boot_console, which is also 148 function-scoped. However, we don't need to actually do any function-scope 149 work, so this simply returns the same object over and over each time.""" 150 151 global gtdi1 152 if not gtdi1: 153 gtdi1 = AbootimgTestDiskImage(u_boot_console, 'boot.img', img_hex) 154 return gtdi1 155 156gtdi2 = None 157@pytest.fixture(scope='function') 158def abootimgv4_disk_image_vboot(u_boot_console): 159 """pytest fixture to provide a AbootimgTestDiskImage object to tests. 160 This is function-scoped because it uses u_boot_console, which is also 161 function-scoped. However, we don't need to actually do any function-scope 162 work, so this simply returns the same object over and over each time.""" 163 164 global gtdi2 165 if not gtdi2: 166 gtdi2 = AbootimgTestDiskImage(u_boot_console, 'vendor_boot.img', vboot_img_hex) 167 return gtdi2 168 169gtdi3 = None 170@pytest.fixture(scope='function') 171def abootimgv4_disk_image_boot(u_boot_console): 172 """pytest fixture to provide a AbootimgTestDiskImage object to tests. 173 This is function-scoped because it uses u_boot_console, which is also 174 function-scoped. However, we don't need to actually do any function-scope 175 work, so this simply returns the same object over and over each time.""" 176 177 global gtdi3 178 if not gtdi3: 179 gtdi3 = AbootimgTestDiskImage(u_boot_console, 'bootv4.img', boot_img_hex) 180 return gtdi3 181 182@pytest.mark.boardspec('sandbox') 183@pytest.mark.buildconfigspec('android_boot_image') 184@pytest.mark.buildconfigspec('cmd_abootimg') 185@pytest.mark.buildconfigspec('cmd_fdt') 186@pytest.mark.requiredtool('xxd') 187@pytest.mark.requiredtool('gunzip') 188def test_abootimg(abootimg_disk_image, u_boot_console): 189 """Test the 'abootimg' command.""" 190 191 u_boot_console.log.action('Loading disk image to RAM...') 192 u_boot_console.run_command('setenv loadaddr 0x%x' % (loadaddr)) 193 u_boot_console.run_command('host load hostfs - 0x%x %s' % (loadaddr, 194 abootimg_disk_image.path)) 195 196 u_boot_console.log.action('Testing \'abootimg get ver\'...') 197 response = u_boot_console.run_command('abootimg get ver') 198 assert response == "2" 199 u_boot_console.run_command('abootimg get ver v') 200 response = u_boot_console.run_command('env print v') 201 assert response == 'v=2' 202 203 u_boot_console.log.action('Testing \'abootimg get recovery_dtbo\'...') 204 response = u_boot_console.run_command('abootimg get recovery_dtbo a') 205 assert response == 'Error: recovery_dtbo_size is 0' 206 207 u_boot_console.log.action('Testing \'abootimg dump dtb\'...') 208 response = u_boot_console.run_command('abootimg dump dtb').replace('\r', '') 209 assert response == dtb_dump_resp 210 211 u_boot_console.log.action('Testing \'abootimg get dtb_load_addr\'...') 212 u_boot_console.run_command('abootimg get dtb_load_addr a') 213 response = u_boot_console.run_command('env print a') 214 assert response == 'a=11f00000' 215 216 u_boot_console.log.action('Testing \'abootimg get dtb --index\'...') 217 u_boot_console.run_command('abootimg get dtb --index=1 dtb1_start') 218 response = u_boot_console.run_command('env print dtb1_start') 219 correct_str = "dtb1_start=%x" % (dtb1_addr) 220 assert response == correct_str 221 u_boot_console.run_command('fdt addr $dtb1_start') 222 u_boot_console.run_command('fdt get value v / model') 223 response = u_boot_console.run_command('env print v') 224 assert response == 'v=x2' 225 226@pytest.mark.boardspec('sandbox') 227@pytest.mark.buildconfigspec('android_boot_image') 228@pytest.mark.buildconfigspec('cmd_abootimg') 229@pytest.mark.buildconfigspec('cmd_fdt') 230@pytest.mark.requiredtool('xxd') 231@pytest.mark.requiredtool('gunzip') 232def test_abootimgv4(abootimgv4_disk_image_vboot, abootimgv4_disk_image_boot, u_boot_console): 233 """Test the 'abootimg' command with boot image header v4.""" 234 235 cons = u_boot_console 236 cons.log.action('Loading disk image to RAM...') 237 cons.run_command('setenv loadaddr 0x%x' % (loadaddr)) 238 cons.run_command('setenv vloadaddr 0x%x' % (vloadaddr)) 239 cons.run_command('host load hostfs - 0x%x %s' % (vloadaddr, 240 abootimgv4_disk_image_vboot.path)) 241 cons.run_command('host load hostfs - 0x%x %s' % (loadaddr, 242 abootimgv4_disk_image_boot.path)) 243 cons.run_command('abootimg addr 0x%x 0x%x' % (loadaddr, vloadaddr)) 244 cons.log.action('Testing \'abootimg get ver\'...') 245 response = cons.run_command('abootimg get ver') 246 assert response == "4" 247 cons.run_command('abootimg get ver v') 248 response = cons.run_command('env print v') 249 assert response == 'v=4' 250 251 cons.log.action('Testing \'abootimg get recovery_dtbo\'...') 252 response = cons.run_command('abootimg get recovery_dtbo a') 253 assert response == 'Error: header version must be >= 1 and <= 2 to get dtbo' 254 255 cons.log.action('Testing \'abootimg get dtb_load_addr\'...') 256 cons.run_command('abootimg get dtb_load_addr a') 257 response = cons.run_command('env print a') 258 assert response == 'a=11f00000' 259 260 cons.log.action('Testing \'abootimg get dtb --index\'...') 261 cons.run_command('abootimg get dtb --index=1 dtb2_start') 262 response = cons.run_command('env print dtb2_start') 263 correct_str = "dtb2_start=%x" % (dtb2_addr) 264 assert response == correct_str 265 266 cons.run_command('fdt addr $dtb2_start') 267 cons.run_command('fdt get value v / model') 268 response = cons.run_command('env print v') 269 assert response == 'v=x2' 270