blob: 573d76dd83226abd136746e712b73590b331f846 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0
Stephen Warren10e50632016-01-15 11:15:24 -07002# Copyright (c) 2015 Stephen Warren
3# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
Stephen Warren10e50632016-01-15 11:15:24 -07004
5# Common logic to interact with U-Boot via the console. This class provides
6# the interface that tests use to execute U-Boot shell commands and wait for
7# their results. Sub-classes exist to perform board-type-specific setup
8# operations, such as spawning a sub-process for Sandbox, or attaching to the
9# serial console of real hardware.
10
11import multiplexed_log
12import os
13import pytest
14import re
15import sys
Stephen Warren97a54662016-01-22 12:30:09 -070016import u_boot_spawn
Simon Glass7a1e9b42024-10-09 18:29:04 -060017from u_boot_spawn import BootFail, Timeout, Unexpected, handle_exception
Stephen Warren10e50632016-01-15 11:15:24 -070018
19# Regexes for text we expect U-Boot to send to the console.
Heiko Schocherce251232018-12-05 11:29:54 +010020pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warren5af83c42016-02-05 18:04:43 -070021pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warren10e50632016-01-15 11:15:24 -070022pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
23pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
24pattern_error_notification = re.compile('## Error: ')
Stephen Warren3bd79d32016-01-27 23:57:50 -070025pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
Simon Glassf8456fa2024-11-12 07:13:19 -070026pattern_ready_prompt = re.compile('{lab ready in (.*)s: (.*)}')
27pattern_lab_mode = re.compile('{lab mode.*}')
Stephen Warren10e50632016-01-15 11:15:24 -070028
Stephen Warren1115a972016-01-27 23:57:48 -070029PAT_ID = 0
30PAT_RE = 1
31
Simon Glass64914152024-10-09 18:28:58 -060032# Timeout before expecting the console to be ready (in milliseconds)
Simon Glassf8456fa2024-11-12 07:13:19 -070033TIMEOUT_MS = 30000 # Standard timeout
34
35# Timeout for board preparation in lab mode. This needs to be enough to build
36# U-Boot, write it to the board and then boot the board. Since this process is
37# under the control of another program (e.g. Labgrid), it will failure sooner
38# if something goes way. So use a very long timeout here to cover all possible
39# situations.
40TIMEOUT_PREPARE_MS = 3 * 60 * 1000
Simon Glass64914152024-10-09 18:28:58 -060041
Stephen Warren1115a972016-01-27 23:57:48 -070042bad_pattern_defs = (
43 ('spl_signon', pattern_u_boot_spl_signon),
44 ('main_signon', pattern_u_boot_main_signon),
45 ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
46 ('unknown_command', pattern_unknown_command),
47 ('error_notification', pattern_error_notification),
Stephen Warren3bd79d32016-01-27 23:57:50 -070048 ('error_please_reset', pattern_error_please_reset),
Stephen Warren1115a972016-01-27 23:57:48 -070049)
50
Stephen Warren10e50632016-01-15 11:15:24 -070051class ConsoleDisableCheck(object):
Stephen Warren75e731e2016-01-26 13:41:30 -070052 """Context manager (for Python's with statement) that temporarily disables
Stephen Warren10e50632016-01-15 11:15:24 -070053 the specified console output error check. This is useful when deliberately
54 executing a command that is known to trigger one of the error checks, in
55 order to test that the error condition is actually raised. This class is
56 used internally by ConsoleBase::disable_check(); it is not intended for
Stephen Warren75e731e2016-01-26 13:41:30 -070057 direct usage."""
Stephen Warren10e50632016-01-15 11:15:24 -070058
59 def __init__(self, console, check_type):
60 self.console = console
61 self.check_type = check_type
62
63 def __enter__(self):
64 self.console.disable_check_count[self.check_type] += 1
Stephen Warren1115a972016-01-27 23:57:48 -070065 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070066
67 def __exit__(self, extype, value, traceback):
68 self.console.disable_check_count[self.check_type] -= 1
Stephen Warren1115a972016-01-27 23:57:48 -070069 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070070
Love Kumarc35e66e2024-05-22 18:45:13 +053071class ConsoleEnableCheck(object):
72 """Context manager (for Python's with statement) that temporarily enables
73 the specified console output error check. This is useful when executing a
74 command that might raise an extra bad pattern, beyond the default bad
75 patterns, in order to validate that the extra bad pattern is actually
76 detected. This class is used internally by ConsoleBase::enable_check(); it
77 is not intended for direct usage."""
78
79 def __init__(self, console, check_type, check_pattern):
80 self.console = console
81 self.check_type = check_type
82 self.check_pattern = check_pattern
83
84 def __enter__(self):
85 global bad_pattern_defs
86 self.default_bad_patterns = bad_pattern_defs
87 bad_pattern_defs += ((self.check_type, self.check_pattern),)
88 self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
89 self.console.eval_bad_patterns()
90
91 def __exit__(self, extype, value, traceback):
92 global bad_pattern_defs
93 bad_pattern_defs = self.default_bad_patterns
94 self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
95 self.console.eval_bad_patterns()
96
Michal Simek6b463182016-05-19 07:57:41 +020097class ConsoleSetupTimeout(object):
98 """Context manager (for Python's with statement) that temporarily sets up
99 timeout for specific command. This is useful when execution time is greater
100 then default 30s."""
101
102 def __init__(self, console, timeout):
103 self.p = console.p
104 self.orig_timeout = self.p.timeout
105 self.p.timeout = timeout
106
107 def __enter__(self):
108 return self
109
110 def __exit__(self, extype, value, traceback):
111 self.p.timeout = self.orig_timeout
112
Stephen Warren10e50632016-01-15 11:15:24 -0700113class ConsoleBase(object):
Stephen Warren75e731e2016-01-26 13:41:30 -0700114 """The interface through which test functions interact with the U-Boot
Stephen Warren10e50632016-01-15 11:15:24 -0700115 console. This primarily involves executing shell commands, capturing their
116 results, and checking for common error conditions. Some common utilities
Stephen Warren75e731e2016-01-26 13:41:30 -0700117 are also provided too."""
Stephen Warren10e50632016-01-15 11:15:24 -0700118
119 def __init__(self, log, config, max_fifo_fill):
Stephen Warren75e731e2016-01-26 13:41:30 -0700120 """Initialize a U-Boot console connection.
Stephen Warren10e50632016-01-15 11:15:24 -0700121
122 Can only usefully be called by sub-classes.
123
124 Args:
Simon Glass53d65832024-10-09 18:29:05 -0600125 log: A multiplexed_log.Logfile object, to which the U-Boot output
Stephen Warren10e50632016-01-15 11:15:24 -0700126 will be logged.
127 config: A configuration data structure, as built by conftest.py.
128 max_fifo_fill: The maximum number of characters to send to U-Boot
129 command-line before waiting for U-Boot to echo the characters
130 back. For UART-based HW without HW flow control, this value
131 should be set less than the UART RX FIFO size to avoid
132 overflow, assuming that U-Boot can't keep up with full-rate
133 traffic at the baud rate.
134
135 Returns:
136 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700137 """
Stephen Warren10e50632016-01-15 11:15:24 -0700138
139 self.log = log
140 self.config = config
141 self.max_fifo_fill = max_fifo_fill
142
143 self.logstream = self.log.get_stream('console', sys.stdout)
144
145 # Array slice removes leading/trailing quotes
146 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
Stephen Warren6d083402016-08-16 19:58:59 -0600147 self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
Stephen Warren10e50632016-01-15 11:15:24 -0700148 self.p = None
Stephen Warren1115a972016-01-27 23:57:48 -0700149 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
150 self.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -0700151
152 self.at_prompt = False
153 self.at_prompt_logevt = None
Simon Glassf8456fa2024-11-12 07:13:19 -0700154 self.lab_mode = False
Stephen Warren10e50632016-01-15 11:15:24 -0700155
Simon Glassf7990762022-02-11 13:23:23 -0700156 def get_spawn(self):
157 # This is not called, ssubclass must define this.
158 # Return a value to avoid:
159 # u_boot_console_base.py:348:12: E1128: Assigning result of a function
160 # call, where the function returns None (assignment-from-none)
161 return u_boot_spawn.Spawn([])
162
163
Stephen Warren1115a972016-01-27 23:57:48 -0700164 def eval_bad_patterns(self):
165 self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
166 if self.disable_check_count[pat[PAT_ID]] == 0]
167 self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
168 if self.disable_check_count[pat[PAT_ID]] == 0]
169
Stephen Warren10e50632016-01-15 11:15:24 -0700170 def close(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700171 """Terminate the connection to the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700172
173 This function is only useful once all interaction with U-Boot is
174 complete. Once this function is called, data cannot be sent to or
175 received from U-Boot.
176
177 Args:
178 None.
179
180 Returns:
181 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700182 """
Stephen Warren10e50632016-01-15 11:15:24 -0700183
184 if self.p:
185 self.p.close()
186 self.logstream.close()
187
Simon Glassf8456fa2024-11-12 07:13:19 -0700188 def set_lab_mode(self):
189 """Select lab mode
190
191 This tells us that we will get a 'lab ready' message when the board is
192 ready for use. We don't need to look for signon messages.
193 """
194 self.log.info(f'test.py: Lab mode is active')
195 self.p.timeout = TIMEOUT_PREPARE_MS
196 self.lab_mode = True
197
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900198 def wait_for_boot_prompt(self, loop_num = 1):
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900199 """Wait for the boot up until command prompt. This is for internal use only.
200 """
201 try:
Simon Glassf8456fa2024-11-12 07:13:19 -0700202 self.log.info('Waiting for U-Boot to be ready')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900203 bcfg = self.config.buildconfig
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900204 config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
205 env_spl_skipped = self.config.env.get('env__spl_skipped', False)
Tom Rinidec7ea02024-05-20 13:35:03 -0600206 env_spl_banner_times = self.config.env.get('env__spl_banner_times', 1)
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900207
Simon Glassf8456fa2024-11-12 07:13:19 -0700208 while not self.lab_mode and loop_num > 0:
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900209 loop_num -= 1
Tom Rinidec7ea02024-05-20 13:35:03 -0600210 while config_spl_serial and not env_spl_skipped and env_spl_banner_times > 0:
Simon Glassf8456fa2024-11-12 07:13:19 -0700211 m = self.p.expect([pattern_u_boot_spl_signon,
212 pattern_lab_mode] + self.bad_patterns)
213 if m == 1:
214 self.set_lab_mode()
215 break
216 elif m != 0:
Simon Glass573171e2024-10-09 18:29:02 -0600217 raise BootFail('Bad pattern found on SPL console: ' +
Simon Glassf8456fa2024-11-12 07:13:19 -0700218 self.bad_pattern_ids[m - 1])
Tom Rinidec7ea02024-05-20 13:35:03 -0600219 env_spl_banner_times -= 1
220
Simon Glassf8456fa2024-11-12 07:13:19 -0700221 if not self.lab_mode:
222 m = self.p.expect([pattern_u_boot_main_signon,
223 pattern_lab_mode] + self.bad_patterns)
224 if m == 1:
225 self.set_lab_mode()
226 elif m != 0:
227 raise BootFail('Bad pattern found on console: ' +
228 self.bad_pattern_ids[m - 1])
229 if not self.lab_mode:
230 self.u_boot_version_string = self.p.after
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900231 while True:
Simon Glass6cffce92024-11-12 07:13:15 -0700232 m = self.p.expect([self.prompt_compiled, pattern_ready_prompt,
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900233 pattern_stop_autoboot_prompt] + self.bad_patterns)
Simon Glassf8456fa2024-11-12 07:13:19 -0700234 if m == 0:
235 self.log.info(f'Found ready prompt {m}')
236 break
237 elif m == 1:
238 m = pattern_ready_prompt.search(self.p.after)
239 self.u_boot_version_string = m.group(2)
240 self.log.info(f'Lab: Board is ready')
241 self.p.timeout = TIMEOUT_MS
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900242 break
Simon Glass6cffce92024-11-12 07:13:15 -0700243 if m == 2:
Simon Glassf8456fa2024-11-12 07:13:19 -0700244 self.log.info(f'Found autoboot prompt {m}')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900245 self.p.send(' ')
246 continue
Simon Glassf8456fa2024-11-12 07:13:19 -0700247 if not self.lab_mode:
248 raise BootFail('Missing prompt / ready message on console: ' +
249 self.bad_pattern_ids[m - 3])
250 self.log.info(f'U-Boot is ready')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900251
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900252 finally:
253 self.log.timestamp()
254
Stephen Warren10e50632016-01-15 11:15:24 -0700255 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900256 wait_for_prompt=True, wait_for_reboot=False):
Stephen Warren75e731e2016-01-26 13:41:30 -0700257 """Execute a command via the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700258
259 The command is always sent to U-Boot.
260
261 U-Boot echoes any command back to its output, and this function
262 typically waits for that to occur. The wait can be disabled by setting
263 wait_for_echo=False, which is useful e.g. when sending CTRL-C to
264 interrupt a long-running command such as "ums".
265
266 Command execution is typically triggered by sending a newline
267 character. This can be disabled by setting send_nl=False, which is
268 also useful when sending CTRL-C.
269
270 This function typically waits for the command to finish executing, and
271 returns the console output that it generated. This can be disabled by
272 setting wait_for_prompt=False, which is useful when invoking a long-
273 running command such as "ums".
274
275 Args:
276 cmd: The command to send.
Heinrich Schuchardtbec160a2017-09-14 12:27:07 +0200277 wait_for_echo: Boolean indicating whether to wait for U-Boot to
Stephen Warren10e50632016-01-15 11:15:24 -0700278 echo the command text back to its output.
279 send_nl: Boolean indicating whether to send a newline character
280 after the command string.
281 wait_for_prompt: Boolean indicating whether to wait for the
282 command prompt to be sent by U-Boot. This typically occurs
283 immediately after the command has been executed.
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900284 wait_for_reboot: Boolean indication whether to wait for the
285 reboot U-Boot. If this sets True, wait_for_prompt must also
286 be True.
Stephen Warren10e50632016-01-15 11:15:24 -0700287
288 Returns:
289 If wait_for_prompt == False:
290 Nothing.
291 Else:
292 The output from U-Boot during command execution. In other
293 words, the text U-Boot emitted between the point it echod the
294 command string and emitted the subsequent command prompts.
Stephen Warren75e731e2016-01-26 13:41:30 -0700295 """
Stephen Warren10e50632016-01-15 11:15:24 -0700296
Stephen Warren10e50632016-01-15 11:15:24 -0700297 if self.at_prompt and \
298 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
299 self.logstream.write(self.prompt, implicit=True)
300
Stephen Warren10e50632016-01-15 11:15:24 -0700301 try:
302 self.at_prompt = False
303 if send_nl:
304 cmd += '\n'
305 while cmd:
306 # Limit max outstanding data, so UART FIFOs don't overflow
307 chunk = cmd[:self.max_fifo_fill]
308 cmd = cmd[self.max_fifo_fill:]
309 self.p.send(chunk)
310 if not wait_for_echo:
311 continue
312 chunk = re.escape(chunk)
313 chunk = chunk.replace('\\\n', '[\r\n]')
Stephen Warren1115a972016-01-27 23:57:48 -0700314 m = self.p.expect([chunk] + self.bad_patterns)
Stephen Warren10e50632016-01-15 11:15:24 -0700315 if m != 0:
316 self.at_prompt = False
Simon Glass573171e2024-10-09 18:29:02 -0600317 raise BootFail('Bad pattern found on console: ' +
Stephen Warren1115a972016-01-27 23:57:48 -0700318 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700319 if not wait_for_prompt:
320 return
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900321 if wait_for_reboot:
322 self.wait_for_boot_prompt()
323 else:
324 m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
325 if m != 0:
326 self.at_prompt = False
Simon Glass573171e2024-10-09 18:29:02 -0600327 raise BootFail('Missing prompt on console: ' +
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900328 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700329 self.at_prompt = True
330 self.at_prompt_logevt = self.logstream.logfile.cur_evt
331 # Only strip \r\n; space/TAB might be significant if testing
332 # indentation.
333 return self.p.before.strip('\r\n')
Simon Glass573171e2024-10-09 18:29:02 -0600334 except Timeout as exc:
Simon Glass7a1e9b42024-10-09 18:29:04 -0600335 handle_exception(self.config, self, self.log, exc, 'Lab failure',
336 True)
Simon Glass573171e2024-10-09 18:29:02 -0600337 raise
Simon Glass7a1e9b42024-10-09 18:29:04 -0600338 except BootFail as exc:
339 handle_exception(self.config, self, self.log, exc, 'Boot fail',
340 True, self.get_spawn_output())
Stephen Warren10e50632016-01-15 11:15:24 -0700341 raise
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600342 finally:
343 self.log.timestamp()
Stephen Warren10e50632016-01-15 11:15:24 -0700344
Simon Glass2436bb02016-07-03 09:40:42 -0600345 def run_command_list(self, cmds):
346 """Run a list of commands.
347
348 This is a helper function to call run_command() with default arguments
349 for each command in a list.
350
351 Args:
Simon Glassd5deca02016-07-31 17:35:04 -0600352 cmd: List of commands (each a string).
Simon Glass2436bb02016-07-03 09:40:42 -0600353 Returns:
Simon Glass2ca73112016-07-31 17:35:09 -0600354 A list of output strings from each command, one element for each
355 command.
Simon Glass2436bb02016-07-03 09:40:42 -0600356 """
Simon Glass2ca73112016-07-31 17:35:09 -0600357 output = []
Simon Glass2436bb02016-07-03 09:40:42 -0600358 for cmd in cmds:
Simon Glass2ca73112016-07-31 17:35:09 -0600359 output.append(self.run_command(cmd))
Simon Glass2436bb02016-07-03 09:40:42 -0600360 return output
361
Stephen Warren10e50632016-01-15 11:15:24 -0700362 def ctrlc(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700363 """Send a CTRL-C character to U-Boot.
Stephen Warren10e50632016-01-15 11:15:24 -0700364
365 This is useful in order to stop execution of long-running synchronous
366 commands such as "ums".
367
368 Args:
369 None.
370
371 Returns:
372 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700373 """
Stephen Warren10e50632016-01-15 11:15:24 -0700374
Stephen Warrena88c4172016-01-22 12:30:10 -0700375 self.log.action('Sending Ctrl-C')
Stephen Warren10e50632016-01-15 11:15:24 -0700376 self.run_command(chr(3), wait_for_echo=False, send_nl=False)
377
Stephen Warrenef824f52016-01-22 12:30:12 -0700378 def wait_for(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700379 """Wait for a pattern to be emitted by U-Boot.
Stephen Warrenef824f52016-01-22 12:30:12 -0700380
381 This is useful when a long-running command such as "dfu" is executing,
382 and it periodically emits some text that should show up at a specific
383 location in the log file.
384
385 Args:
386 text: The text to wait for; either a string (containing raw text,
387 not a regular expression) or an re object.
388
389 Returns:
390 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700391 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700392
393 if type(text) == type(''):
394 text = re.escape(text)
Stephen Warren68a9bb62016-01-27 23:57:49 -0700395 m = self.p.expect([text] + self.bad_patterns)
396 if m != 0:
Simon Glass573171e2024-10-09 18:29:02 -0600397 raise Unexpected(
398 "Unexpected pattern found on console (exp '{text}': " +
399 self.bad_pattern_ids[m - 1])
Stephen Warrenef824f52016-01-22 12:30:12 -0700400
Stephen Warren97a54662016-01-22 12:30:09 -0700401 def drain_console(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700402 """Read from and log the U-Boot console for a short time.
Stephen Warren97a54662016-01-22 12:30:09 -0700403
404 U-Boot's console output is only logged when the test code actively
405 waits for U-Boot to emit specific data. There are cases where tests
406 can fail without doing this. For example, if a test asks U-Boot to
407 enable USB device mode, then polls until a host-side device node
408 exists. In such a case, it is useful to log U-Boot's console output
409 in case U-Boot printed clues as to why the host-side even did not
410 occur. This function will do that.
411
412 Args:
413 None.
414
415 Returns:
416 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700417 """
Stephen Warren97a54662016-01-22 12:30:09 -0700418
419 # If we are already not connected to U-Boot, there's nothing to drain.
420 # This should only happen when a previous call to run_command() or
421 # wait_for() failed (and hence the output has already been logged), or
422 # the system is shutting down.
423 if not self.p:
424 return
425
426 orig_timeout = self.p.timeout
427 try:
428 # Drain the log for a relatively short time.
429 self.p.timeout = 1000
430 # Wait for something U-Boot will likely never send. This will
431 # cause the console output to be read and logged.
432 self.p.expect(['This should never match U-Boot output'])
Stephen Warren677b9cc2018-09-20 16:55:03 -0600433 except:
434 # We expect a timeout, since U-Boot won't print what we waited
435 # for. Squash it when it happens.
436 #
437 # Squash any other exception too. This function is only used to
438 # drain (and log) the U-Boot console output after a failed test.
439 # The U-Boot process will be restarted, or target board reset, once
440 # this function returns. So, we don't care about detecting any
441 # additional errors, so they're squashed so that the rest of the
442 # post-test-failure cleanup code can continue operation, and
443 # correctly terminate any log sections, etc.
Stephen Warren97a54662016-01-22 12:30:09 -0700444 pass
445 finally:
446 self.p.timeout = orig_timeout
447
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900448 def ensure_spawned(self, expect_reset=False):
Stephen Warren75e731e2016-01-26 13:41:30 -0700449 """Ensure a connection to a correctly running U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700450
451 This may require spawning a new Sandbox process or resetting target
452 hardware, as defined by the implementation sub-class.
453
454 This is an internal function and should not be called directly.
455
456 Args:
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900457 expect_reset: Boolean indication whether this boot is expected
458 to be reset while the 1st boot process after main boot before
459 prompt. False by default.
Stephen Warren10e50632016-01-15 11:15:24 -0700460
461 Returns:
462 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700463 """
Stephen Warren10e50632016-01-15 11:15:24 -0700464
465 if self.p:
Bin Meng739b3862022-05-17 23:24:43 +0800466 # Reset the console timeout value as some tests may change
467 # its default value during the execution
468 if not self.config.gdbserver:
Simon Glass64914152024-10-09 18:28:58 -0600469 self.p.timeout = TIMEOUT_MS
Stephen Warren10e50632016-01-15 11:15:24 -0700470 return
471 try:
Stephen Warren80eea632016-02-11 11:46:12 -0700472 self.log.start_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700473 self.at_prompt = False
Stephen Warren10e50632016-01-15 11:15:24 -0700474 self.p = self.get_spawn()
475 # Real targets can take a long time to scroll large amounts of
476 # text if LCD is enabled. This value may need tweaking in the
477 # future, possibly per-test to be optimal. This works for 'help'
478 # on board 'seaboard'.
Stephen Warren33db1ee2016-02-04 16:11:50 -0700479 if not self.config.gdbserver:
Simon Glass64914152024-10-09 18:28:58 -0600480 self.p.timeout = TIMEOUT_MS
Stephen Warren10e50632016-01-15 11:15:24 -0700481 self.p.logfile_read = self.logstream
Simon Glassf1b1bb82024-11-12 07:13:17 -0700482 if self.config.use_running_system:
483 # Send an empty command to set up the 'expect' logic. This has
484 # the side effect of ensuring that there was no partial command
485 # line entered
486 self.run_command(' ')
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900487 else:
Simon Glassf1b1bb82024-11-12 07:13:17 -0700488 if expect_reset:
489 loop_num = 2
490 else:
491 loop_num = 1
492 self.wait_for_boot_prompt(loop_num = loop_num)
Stephen Warren10e50632016-01-15 11:15:24 -0700493 self.at_prompt = True
494 self.at_prompt_logevt = self.logstream.logfile.cur_evt
495 except Exception as ex:
496 self.log.error(str(ex))
497 self.cleanup_spawn()
498 raise
Stephen Warren80eea632016-02-11 11:46:12 -0700499 finally:
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600500 self.log.timestamp()
Stephen Warren80eea632016-02-11 11:46:12 -0700501 self.log.end_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700502
503 def cleanup_spawn(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700504 """Shut down all interaction with the U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700505
506 This is used when an error is detected prior to re-establishing a
507 connection with a fresh U-Boot instance.
508
509 This is an internal function and should not be called directly.
510
511 Args:
512 None.
513
514 Returns:
515 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700516 """
Stephen Warren10e50632016-01-15 11:15:24 -0700517
518 try:
519 if self.p:
520 self.p.close()
521 except:
522 pass
523 self.p = None
524
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900525 def restart_uboot(self, expect_reset=False):
Simon Glass37c2ce12016-07-31 17:35:08 -0600526 """Shut down and restart U-Boot."""
527 self.cleanup_spawn()
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900528 self.ensure_spawned(expect_reset)
Simon Glass37c2ce12016-07-31 17:35:08 -0600529
Simon Glass9bc20832016-07-04 11:58:39 -0600530 def get_spawn_output(self):
531 """Return the start-up output from U-Boot
532
533 Returns:
534 The output produced by ensure_spawed(), as a string.
535 """
536 if self.p:
537 return self.p.get_expect_output()
538 return None
539
Stephen Warren10e50632016-01-15 11:15:24 -0700540 def validate_version_string_in_text(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700541 """Assert that a command's output includes the U-Boot signon message.
Stephen Warren10e50632016-01-15 11:15:24 -0700542
543 This is primarily useful for validating the "version" command without
544 duplicating the signon text regex in a test function.
545
546 Args:
547 text: The command output text to check.
548
549 Returns:
550 Nothing. An exception is raised if the validation fails.
Stephen Warren75e731e2016-01-26 13:41:30 -0700551 """
Stephen Warren10e50632016-01-15 11:15:24 -0700552
553 assert(self.u_boot_version_string in text)
554
555 def disable_check(self, check_type):
Stephen Warren75e731e2016-01-26 13:41:30 -0700556 """Temporarily disable an error check of U-Boot's output.
Stephen Warren10e50632016-01-15 11:15:24 -0700557
558 Create a new context manager (for use with the "with" statement) which
559 temporarily disables a particular console output error check.
560
561 Args:
562 check_type: The type of error-check to disable. Valid values may
563 be found in self.disable_check_count above.
564
565 Returns:
566 A context manager object.
Stephen Warren75e731e2016-01-26 13:41:30 -0700567 """
Stephen Warren10e50632016-01-15 11:15:24 -0700568
569 return ConsoleDisableCheck(self, check_type)
Michal Simek6b463182016-05-19 07:57:41 +0200570
Love Kumarc35e66e2024-05-22 18:45:13 +0530571 def enable_check(self, check_type, check_pattern):
572 """Temporarily enable an error check of U-Boot's output.
573
574 Create a new context manager (for use with the "with" statement) which
575 temporarily enables a particular console output error check. The
576 arguments form a new element of bad_pattern_defs defined above.
577
578 Args:
579 check_type: The type of error-check or bad pattern to enable.
580 check_pattern: The regexes for text error pattern or bad pattern
581 to be checked.
582
583 Returns:
584 A context manager object.
585 """
586
587 return ConsoleEnableCheck(self, check_type, check_pattern)
588
Michal Simek6b463182016-05-19 07:57:41 +0200589 def temporary_timeout(self, timeout):
590 """Temporarily set up different timeout for commands.
591
592 Create a new context manager (for use with the "with" statement) which
593 temporarily change timeout.
594
595 Args:
596 timeout: Time in milliseconds.
597
598 Returns:
599 A context manager object.
600 """
601
602 return ConsoleSetupTimeout(self, timeout)