1# SPDX-License-Identifier: GPL-2.0+ 2""" Unit test for UEFI menu-driven configuration 3""" 4 5import pytest 6import shutil 7import pytest 8import time 9from subprocess import call, check_call, CalledProcessError 10from tests import fs_helper 11 12@pytest.mark.boardspec('sandbox') 13@pytest.mark.buildconfigspec('cmd_eficonfig') 14@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr') 15def test_efi_eficonfig(ubman): 16 17 def prepare_image(u_boot_config): 18 """Set up a file system to be used in UEFI "eficonfig" command 19 tests. This creates a disk image with the following files: 20 initrd-1.img 21 initrd-2.img 22 initrddump.efi 23 24 Args: 25 u_boot_config -- U-Boot configuration. 26 27 Return: 28 A path to disk image to be used for testing 29 30 """ 31 try: 32 image_path, mnt_point = fs_helper.setup_image(u_boot_config, 0, 33 0xc, 34 basename='test_eficonfig') 35 36 with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file: 37 file.write("initrd 1") 38 39 with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file: 40 file.write("initrd 2") 41 42 shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi', 43 mnt_point + '/initrddump.efi') 44 45 fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000, 46 'test_eficonfig', mnt_point) 47 check_call(f'dd if={fsfile} of={image_path} bs=1M seek=1', shell=True) 48 49 yield image_path 50 except CalledProcessError as err: 51 pytest.skip('Preparing test_eficonfig image failed') 52 call('rm -f %s' % image_path, shell=True) 53 finally: 54 call('rm -rf %s' % mnt_point, shell=True) 55 call('rm -f %s' % image_path, shell=True) 56 57 def send_user_input_and_wait(user_str, expect_str): 58 time.sleep(0.1) # TODO: does not work correctly without sleep 59 ubman.run_command(cmd=user_str, wait_for_prompt=False, 60 wait_for_echo=True, send_nl=False) 61 ubman.run_command(cmd='\x0d', wait_for_prompt=False, 62 wait_for_echo=False, send_nl=False) 63 if expect_str is not None: 64 for i in expect_str: 65 ubman.p.expect([i]) 66 67 def press_up_down_enter_and_wait(up_count, down_count, enter, expect_str): 68 # press UP key 69 for i in range(up_count): 70 ubman.run_command(cmd='\x1b\x5b\x41', wait_for_prompt=False, 71 wait_for_echo=False, send_nl=False) 72 # press DOWN key 73 for i in range(down_count): 74 ubman.run_command(cmd='\x1b\x5b\x42', wait_for_prompt=False, 75 wait_for_echo=False, send_nl=False) 76 # press ENTER if requested 77 if enter: 78 ubman.run_command(cmd='\x0d', wait_for_prompt=False, 79 wait_for_echo=False, send_nl=False) 80 # wait expected output 81 if expect_str is not None: 82 for i in expect_str: 83 ubman.p.expect([i]) 84 85 def press_escape_key(wait_prompt): 86 ubman.run_command(cmd='\x1b', wait_for_prompt=wait_prompt, wait_for_echo=False, send_nl=False) 87 88 def press_enter_key(wait_prompt): 89 ubman.run_command(cmd='\x0d', wait_for_prompt=wait_prompt, 90 wait_for_echo=False, send_nl=False) 91 92 def check_current_is_maintenance_menu(): 93 for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option', 94 'Change Boot Order', 'Delete Boot Option', 'Quit'): 95 ubman.p.expect([i]) 96 97 """ Unit test for "eficonfig" command 98 The menu-driven interface is used to set up UEFI load options. 99 The bootefi bootmgr loads initrddump.efi as a payload. 100 The crc32 of the loaded initrd.img is checked 101 102 Args: 103 ubman -- U-Boot console 104 """ 105 # This test passes for unknown reasons in the bowels of U-Boot. It needs to 106 # be replaced with a unit test. 107 return 108 109 # Restart the system to clean the previous state 110 ubman.restart_uboot() 111 112 efi_eficonfig_data = prepare_image(ubman.config) 113 with ubman.temporary_timeout(500): 114 # 115 # Test Case 1: Check the menu is displayed 116 # 117 ubman.run_command('eficonfig', wait_for_prompt=False) 118 for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option', 119 'Change Boot Order', 'Delete Boot Option', 'Quit'): 120 ubman.p.expect([i]) 121 # Select "Add Boot Option" 122 press_enter_key(False) 123 for i in ('Add Boot Option', 'Description:', 'File', 'Initrd File', 'Optional Data', 124 'Save', 'Quit'): 125 ubman.p.expect([i]) 126 press_escape_key(False) 127 check_current_is_maintenance_menu() 128 # return to U-Boot console 129 press_escape_key(True) 130 131 # 132 # Test Case 2: check auto generated media device entry 133 # 134 135 # bind the test disk image for succeeding tests 136 ubman.run_command(cmd = f'host bind 0 {efi_eficonfig_data}') 137 138 ubman.run_command('eficonfig', wait_for_prompt=False) 139 140 # Change the Boot Order 141 press_up_down_enter_and_wait(0, 2, True, 'Quit') 142 for i in ('host 0:1', 'Save', 'Quit'): 143 ubman.p.expect([i]) 144 # disable auto generated boot option for succeeding test 145 ubman.run_command(cmd=' ', wait_for_prompt=False, 146 wait_for_echo=False, send_nl=False) 147 # Save the BootOrder 148 press_up_down_enter_and_wait(0, 1, True, None) 149 check_current_is_maintenance_menu() 150 151 # 152 # Test Case 3: Add first Boot Option and load it 153 # 154 155 # Select 'Add Boot Option' 156 press_up_down_enter_and_wait(0, 0, True, 'Quit') 157 158 # Press the enter key to select 'Description:' entry, then enter Description 159 press_up_down_enter_and_wait(0, 0, True, 'Enter description:') 160 # Send Description user input, press ENTER key to complete 161 send_user_input_and_wait('test 1', 'Quit') 162 163 # Set EFI image(initrddump.efi) 164 press_up_down_enter_and_wait(0, 1, True, 'Quit') 165 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 166 # Select 'host 0:1' 167 press_up_down_enter_and_wait(0, 0, True, 'Quit') 168 # Press down key to select "initrddump.efi" entry followed by the enter key 169 press_up_down_enter_and_wait(0, 2, True, 'Quit') 170 171 # Set Initrd file(initrd-1.img) 172 press_up_down_enter_and_wait(0, 2, True, 'Quit') 173 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 174 # Select 'host 0:1' 175 press_up_down_enter_and_wait(0, 0, True, 'Quit') 176 # Press down key to select "initrd-1.img" entry followed by the enter key 177 press_up_down_enter_and_wait(0, 0, True, 'Quit') 178 179 # Set optional_data 180 press_up_down_enter_and_wait(0, 3, True, 'Optional Data:') 181 # Send Description user input, press ENTER key to complete 182 send_user_input_and_wait('nocolor', None) 183 for i in ('Description: test 1', 'File: host 0:1/initrddump.efi', 184 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'): 185 ubman.p.expect([i]) 186 187 # Save the Boot Option 188 press_up_down_enter_and_wait(0, 4, True, None) 189 check_current_is_maintenance_menu() 190 191 # Check the newly added Boot Option is handled correctly 192 # Return to U-Boot console 193 press_escape_key(True) 194 ubman.run_command(cmd = 'bootefi bootmgr') 195 response = ubman.run_command(cmd = 'load', wait_for_echo=False) 196 assert 'crc32: 0x181464af' in response 197 ubman.run_command(cmd = 'exit', wait_for_echo=False) 198 199 # 200 # Test Case 4: Add second Boot Option and load it 201 # 202 ubman.run_command('eficonfig', wait_for_prompt=False) 203 204 # Select 'Add Boot Option' 205 press_up_down_enter_and_wait(0, 0, True, 'Quit') 206 207 # Press the enter key to select 'Description:' entry, then enter Description 208 press_up_down_enter_and_wait(0, 0, True, 'Enter description:') 209 # Send Description user input, press ENTER key to complete 210 send_user_input_and_wait('test 2', 'Quit') 211 212 # Set EFI image(initrddump.efi) 213 press_up_down_enter_and_wait(0, 1, True, 'Quit') 214 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 215 # Select 'host 0:1' 216 press_up_down_enter_and_wait(0, 0, True, 'Quit') 217 # Press down key to select "initrddump.efi" entry followed by the enter key 218 press_up_down_enter_and_wait(0, 2, True, 'Quit') 219 220 # Set Initrd file(initrd-2.img) 221 press_up_down_enter_and_wait(0, 2, True, 'Quit') 222 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 223 # Select 'host 0:1' 224 press_up_down_enter_and_wait(0, 0, True, 'Quit') 225 # Press down key to select "initrd-2.img" entry followed by the enter key 226 press_up_down_enter_and_wait(0, 1, True, 'Quit') 227 228 # Set optional_data 229 press_up_down_enter_and_wait(0, 3, True, 'Optional Data:') 230 # Send Description user input, press ENTER key to complete 231 send_user_input_and_wait('nocolor', None) 232 for i in ('Description: test 2', 'File: host 0:1/initrddump.efi', 233 'Initrd File: host 0:1/initrd-2.img', 'Optional Data: nocolor', 'Save', 'Quit'): 234 ubman.p.expect([i]) 235 236 # Save the Boot Option 237 press_up_down_enter_and_wait(0, 4, True, 'Quit') 238 239 # Change the Boot Order 240 press_up_down_enter_and_wait(0, 2, True, 'Quit') 241 press_up_down_enter_and_wait(0, 1, False, 'Quit') 242 # move 'test 1' to the second entry 243 ubman.run_command(cmd='+', wait_for_prompt=False, 244 wait_for_echo=False, send_nl=False) 245 for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'): 246 ubman.p.expect([i]) 247 # Save the BootOrder 248 press_up_down_enter_and_wait(0, 3, True, None) 249 check_current_is_maintenance_menu() 250 251 # Check the newly added Boot Option is handled correctly 252 # Return to U-Boot console 253 press_escape_key(True) 254 ubman.run_command(cmd = 'bootefi bootmgr') 255 response = ubman.run_command(cmd = 'load', wait_for_echo=False) 256 assert 'crc32: 0x811d3515' in response 257 ubman.run_command(cmd = 'exit', wait_for_echo=False) 258 259 # 260 # Test Case 5: Change BootOrder and load it 261 # 262 ubman.run_command('eficonfig', wait_for_prompt=False) 263 264 # Change the Boot Order 265 press_up_down_enter_and_wait(0, 2, True, None) 266 # Check the current BootOrder 267 for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'): 268 ubman.p.expect([i]) 269 # move 'test 2' to the second entry 270 ubman.run_command(cmd='-', wait_for_prompt=False, 271 wait_for_echo=False, send_nl=False) 272 for i in ('test 1', 'test 2', 'host 0:1', 'Save', 'Quit'): 273 ubman.p.expect([i]) 274 # Save the BootOrder 275 press_up_down_enter_and_wait(0, 2, True, None) 276 check_current_is_maintenance_menu() 277 278 # Return to U-Boot console 279 press_escape_key(True) 280 ubman.run_command(cmd = 'bootefi bootmgr') 281 response = ubman.run_command(cmd = 'load', wait_for_echo=False) 282 assert 'crc32: 0x181464af' in response 283 ubman.run_command(cmd = 'exit', wait_for_echo=False) 284 285 # 286 # Test Case 6: Delete Boot Option(label:test 2) 287 # 288 ubman.run_command('eficonfig', wait_for_prompt=False) 289 290 # Select 'Delete Boot Option' 291 press_up_down_enter_and_wait(0, 3, True, None) 292 # Check the current BootOrder 293 for i in ('test 1', 'test 2', 'Quit'): 294 ubman.p.expect([i]) 295 296 # Delete 'test 2' 297 press_up_down_enter_and_wait(0, 1, True, None) 298 for i in ('test 1', 'Quit'): 299 ubman.p.expect([i]) 300 press_escape_key(False) 301 check_current_is_maintenance_menu() 302 # Return to U-Boot console 303 press_escape_key(True) 304 305 # 306 # Test Case 7: Edit Boot Option 307 # 308 ubman.run_command('eficonfig', wait_for_prompt=False) 309 # Select 'Edit Boot Option' 310 press_up_down_enter_and_wait(0, 1, True, None) 311 # Check the current BootOrder 312 for i in ('test 1', 'Quit'): 313 ubman.p.expect([i]) 314 press_up_down_enter_and_wait(0, 0, True, None) 315 for i in ('Description: test 1', 'File: host 0:1/initrddump.efi', 316 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'): 317 ubman.p.expect([i]) 318 319 # Press the enter key to select 'Description:' entry, then enter Description 320 press_up_down_enter_and_wait(0, 0, True, 'Enter description:') 321 # Send Description user input, press ENTER key to complete 322 send_user_input_and_wait('test 3', 'Quit') 323 324 # Set EFI image(initrddump.efi) 325 press_up_down_enter_and_wait(0, 1, True, 'Quit') 326 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 327 # Select 'host 0:1' 328 press_up_down_enter_and_wait(0, 0, True, 'Quit') 329 # Press down key to select "initrddump.efi" entry followed by the enter key 330 press_up_down_enter_and_wait(0, 2, True, 'Quit') 331 332 # Set Initrd file(initrd-2.img) 333 press_up_down_enter_and_wait(0, 2, True, 'Quit') 334 press_up_down_enter_and_wait(0, 0, True, 'host 0:1') 335 # Select 'host 0:1' 336 press_up_down_enter_and_wait(0, 0, True, 'Quit') 337 # Press down key to select "initrd-1.img" entry followed by the enter key 338 press_up_down_enter_and_wait(0, 1, True, 'Quit') 339 340 # Set optional_data 341 press_up_down_enter_and_wait(0, 3, True, 'Optional Data:') 342 # Send Description user input, press ENTER key to complete 343 send_user_input_and_wait('', None) 344 for i in ('Description: test 3', 'File: host 0:1/initrddump.efi', 345 'Initrd File: host 0:1/initrd-2.img', 'Optional Data:', 'Save', 'Quit'): 346 ubman.p.expect([i]) 347 348 # Save the Boot Option 349 press_up_down_enter_and_wait(0, 4, True, 'Quit') 350 press_escape_key(False) 351 check_current_is_maintenance_menu() 352 353 # Check the updated Boot Option is handled correctly 354 # Return to U-Boot console 355 press_escape_key(True) 356 ubman.run_command(cmd = 'bootefi bootmgr') 357 response = ubman.run_command(cmd = 'load', wait_for_echo=False) 358 assert 'crc32: 0x811d3515' in response 359 ubman.run_command(cmd = 'exit', wait_for_echo=False) 360 361 # 362 # Test Case 8: Delete Boot Option(label:test 3) 363 # 364 ubman.run_command('eficonfig', wait_for_prompt=False) 365 366 # Select 'Delete Boot Option' 367 press_up_down_enter_and_wait(0, 3, True, None) 368 # Check the current BootOrder 369 for i in ('test 3', 'Quit'): 370 ubman.p.expect([i]) 371 372 # Delete 'test 3' 373 press_up_down_enter_and_wait(0, 0, True, 'Quit') 374 press_escape_key(False) 375 check_current_is_maintenance_menu() 376 # Return to U-Boot console 377 press_escape_key(True) 378 379 # remove the host device 380 ubman.run_command(cmd = f'host bind -r 0') 381 382 # 383 # Test Case 9: No block device found 384 # 385 ubman.run_command('eficonfig', wait_for_prompt=False) 386 387 # Select 'Add Boot Option' 388 press_up_down_enter_and_wait(0, 0, True, 'Quit') 389 390 # Set EFI image 391 press_up_down_enter_and_wait(0, 1, True, 'Quit') 392 press_up_down_enter_and_wait(0, 0, True, 'No block device found!') 393 press_escape_key(False) 394 press_escape_key(False) 395 check_current_is_maintenance_menu() 396 # Return to U-Boot console 397 press_escape_key(True) 398