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