blob: 88d444b44b886eac66796210703fe6ee3c2c5d4d [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
Simon Glassfb916372025-02-09 09:07:15 -070016import spawn
17from 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
Simon Glass2f909122024-11-12 07:13:20 -070034TIMEOUT_CMD_MS = 10000 # Command-echo timeout
Simon Glassf8456fa2024-11-12 07:13:19 -070035
36# Timeout for board preparation in lab mode. This needs to be enough to build
37# U-Boot, write it to the board and then boot the board. Since this process is
38# under the control of another program (e.g. Labgrid), it will failure sooner
39# if something goes way. So use a very long timeout here to cover all possible
40# situations.
41TIMEOUT_PREPARE_MS = 3 * 60 * 1000
Simon Glass64914152024-10-09 18:28:58 -060042
Stephen Warren1115a972016-01-27 23:57:48 -070043bad_pattern_defs = (
44 ('spl_signon', pattern_u_boot_spl_signon),
45 ('main_signon', pattern_u_boot_main_signon),
46 ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
47 ('unknown_command', pattern_unknown_command),
48 ('error_notification', pattern_error_notification),
Stephen Warren3bd79d32016-01-27 23:57:50 -070049 ('error_please_reset', pattern_error_please_reset),
Stephen Warren1115a972016-01-27 23:57:48 -070050)
51
Stephen Warren10e50632016-01-15 11:15:24 -070052class ConsoleDisableCheck(object):
Stephen Warren75e731e2016-01-26 13:41:30 -070053 """Context manager (for Python's with statement) that temporarily disables
Stephen Warren10e50632016-01-15 11:15:24 -070054 the specified console output error check. This is useful when deliberately
55 executing a command that is known to trigger one of the error checks, in
56 order to test that the error condition is actually raised. This class is
57 used internally by ConsoleBase::disable_check(); it is not intended for
Stephen Warren75e731e2016-01-26 13:41:30 -070058 direct usage."""
Stephen Warren10e50632016-01-15 11:15:24 -070059
60 def __init__(self, console, check_type):
61 self.console = console
62 self.check_type = check_type
63
64 def __enter__(self):
65 self.console.disable_check_count[self.check_type] += 1
Stephen Warren1115a972016-01-27 23:57:48 -070066 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070067
68 def __exit__(self, extype, value, traceback):
69 self.console.disable_check_count[self.check_type] -= 1
Stephen Warren1115a972016-01-27 23:57:48 -070070 self.console.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -070071
Love Kumarc35e66e2024-05-22 18:45:13 +053072class ConsoleEnableCheck(object):
73 """Context manager (for Python's with statement) that temporarily enables
74 the specified console output error check. This is useful when executing a
75 command that might raise an extra bad pattern, beyond the default bad
76 patterns, in order to validate that the extra bad pattern is actually
77 detected. This class is used internally by ConsoleBase::enable_check(); it
78 is not intended for direct usage."""
79
80 def __init__(self, console, check_type, check_pattern):
81 self.console = console
82 self.check_type = check_type
83 self.check_pattern = check_pattern
84
85 def __enter__(self):
86 global bad_pattern_defs
87 self.default_bad_patterns = bad_pattern_defs
88 bad_pattern_defs += ((self.check_type, self.check_pattern),)
89 self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
90 self.console.eval_bad_patterns()
91
92 def __exit__(self, extype, value, traceback):
93 global bad_pattern_defs
94 bad_pattern_defs = self.default_bad_patterns
95 self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
96 self.console.eval_bad_patterns()
97
Michal Simek6b463182016-05-19 07:57:41 +020098class ConsoleSetupTimeout(object):
99 """Context manager (for Python's with statement) that temporarily sets up
100 timeout for specific command. This is useful when execution time is greater
101 then default 30s."""
102
103 def __init__(self, console, timeout):
104 self.p = console.p
105 self.orig_timeout = self.p.timeout
106 self.p.timeout = timeout
107
108 def __enter__(self):
109 return self
110
111 def __exit__(self, extype, value, traceback):
112 self.p.timeout = self.orig_timeout
113
Stephen Warren10e50632016-01-15 11:15:24 -0700114class ConsoleBase(object):
Stephen Warren75e731e2016-01-26 13:41:30 -0700115 """The interface through which test functions interact with the U-Boot
Stephen Warren10e50632016-01-15 11:15:24 -0700116 console. This primarily involves executing shell commands, capturing their
117 results, and checking for common error conditions. Some common utilities
Stephen Warren75e731e2016-01-26 13:41:30 -0700118 are also provided too."""
Stephen Warren10e50632016-01-15 11:15:24 -0700119
120 def __init__(self, log, config, max_fifo_fill):
Stephen Warren75e731e2016-01-26 13:41:30 -0700121 """Initialize a U-Boot console connection.
Stephen Warren10e50632016-01-15 11:15:24 -0700122
123 Can only usefully be called by sub-classes.
124
125 Args:
Simon Glass53d65832024-10-09 18:29:05 -0600126 log: A multiplexed_log.Logfile object, to which the U-Boot output
Stephen Warren10e50632016-01-15 11:15:24 -0700127 will be logged.
128 config: A configuration data structure, as built by conftest.py.
129 max_fifo_fill: The maximum number of characters to send to U-Boot
130 command-line before waiting for U-Boot to echo the characters
131 back. For UART-based HW without HW flow control, this value
132 should be set less than the UART RX FIFO size to avoid
133 overflow, assuming that U-Boot can't keep up with full-rate
134 traffic at the baud rate.
135
136 Returns:
137 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700138 """
Stephen Warren10e50632016-01-15 11:15:24 -0700139
140 self.log = log
141 self.config = config
142 self.max_fifo_fill = max_fifo_fill
143
144 self.logstream = self.log.get_stream('console', sys.stdout)
145
146 # Array slice removes leading/trailing quotes
147 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
Stephen Warren6d083402016-08-16 19:58:59 -0600148 self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
Stephen Warren10e50632016-01-15 11:15:24 -0700149 self.p = None
Stephen Warren1115a972016-01-27 23:57:48 -0700150 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
151 self.eval_bad_patterns()
Stephen Warren10e50632016-01-15 11:15:24 -0700152
153 self.at_prompt = False
154 self.at_prompt_logevt = None
Simon Glassf8456fa2024-11-12 07:13:19 -0700155 self.lab_mode = False
Stephen Warren10e50632016-01-15 11:15:24 -0700156
Simon Glassf7990762022-02-11 13:23:23 -0700157 def get_spawn(self):
158 # This is not called, ssubclass must define this.
159 # Return a value to avoid:
Simon Glassfb916372025-02-09 09:07:15 -0700160 # console_base.py:348:12: E1128: Assigning result of a function
Simon Glassf7990762022-02-11 13:23:23 -0700161 # call, where the function returns None (assignment-from-none)
Simon Glassfb916372025-02-09 09:07:15 -0700162 return spawn.Spawn([])
Simon Glassf7990762022-02-11 13:23:23 -0700163
164
Stephen Warren1115a972016-01-27 23:57:48 -0700165 def eval_bad_patterns(self):
166 self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
167 if self.disable_check_count[pat[PAT_ID]] == 0]
168 self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
169 if self.disable_check_count[pat[PAT_ID]] == 0]
170
Stephen Warren10e50632016-01-15 11:15:24 -0700171 def close(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700172 """Terminate the connection to the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700173
174 This function is only useful once all interaction with U-Boot is
175 complete. Once this function is called, data cannot be sent to or
176 received from U-Boot.
177
178 Args:
179 None.
180
181 Returns:
182 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700183 """
Stephen Warren10e50632016-01-15 11:15:24 -0700184
185 if self.p:
Simon Glassf5285a22024-11-12 07:13:23 -0700186 self.log.start_section('Stopping U-Boot')
187 close_type = self.p.close()
188 self.log.info(f'Close type: {close_type}')
189 self.log.end_section('Stopping U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700190 self.logstream.close()
191
Simon Glassf8456fa2024-11-12 07:13:19 -0700192 def set_lab_mode(self):
193 """Select lab mode
194
195 This tells us that we will get a 'lab ready' message when the board is
196 ready for use. We don't need to look for signon messages.
197 """
198 self.log.info(f'test.py: Lab mode is active')
199 self.p.timeout = TIMEOUT_PREPARE_MS
200 self.lab_mode = True
201
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900202 def wait_for_boot_prompt(self, loop_num = 1):
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900203 """Wait for the boot up until command prompt. This is for internal use only.
204 """
205 try:
Simon Glassf8456fa2024-11-12 07:13:19 -0700206 self.log.info('Waiting for U-Boot to be ready')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900207 bcfg = self.config.buildconfig
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900208 config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
209 env_spl_skipped = self.config.env.get('env__spl_skipped', False)
Tom Rinidec7ea02024-05-20 13:35:03 -0600210 env_spl_banner_times = self.config.env.get('env__spl_banner_times', 1)
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900211
Simon Glassf8456fa2024-11-12 07:13:19 -0700212 while not self.lab_mode and loop_num > 0:
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900213 loop_num -= 1
Tom Rinidec7ea02024-05-20 13:35:03 -0600214 while config_spl_serial and not env_spl_skipped and env_spl_banner_times > 0:
Simon Glassf8456fa2024-11-12 07:13:19 -0700215 m = self.p.expect([pattern_u_boot_spl_signon,
216 pattern_lab_mode] + self.bad_patterns)
217 if m == 1:
218 self.set_lab_mode()
219 break
220 elif m != 0:
Simon Glass573171e2024-10-09 18:29:02 -0600221 raise BootFail('Bad pattern found on SPL console: ' +
Simon Glassf8456fa2024-11-12 07:13:19 -0700222 self.bad_pattern_ids[m - 1])
Tom Rinidec7ea02024-05-20 13:35:03 -0600223 env_spl_banner_times -= 1
224
Simon Glassf8456fa2024-11-12 07:13:19 -0700225 if not self.lab_mode:
226 m = self.p.expect([pattern_u_boot_main_signon,
227 pattern_lab_mode] + self.bad_patterns)
228 if m == 1:
229 self.set_lab_mode()
230 elif m != 0:
231 raise BootFail('Bad pattern found on console: ' +
232 self.bad_pattern_ids[m - 1])
233 if not self.lab_mode:
234 self.u_boot_version_string = self.p.after
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900235 while True:
Simon Glass6cffce92024-11-12 07:13:15 -0700236 m = self.p.expect([self.prompt_compiled, pattern_ready_prompt,
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900237 pattern_stop_autoboot_prompt] + self.bad_patterns)
Simon Glassf8456fa2024-11-12 07:13:19 -0700238 if m == 0:
239 self.log.info(f'Found ready prompt {m}')
240 break
241 elif m == 1:
242 m = pattern_ready_prompt.search(self.p.after)
243 self.u_boot_version_string = m.group(2)
244 self.log.info(f'Lab: Board is ready')
245 self.p.timeout = TIMEOUT_MS
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900246 break
Simon Glass6cffce92024-11-12 07:13:15 -0700247 if m == 2:
Simon Glassf8456fa2024-11-12 07:13:19 -0700248 self.log.info(f'Found autoboot prompt {m}')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900249 self.p.send(' ')
250 continue
Simon Glassf8456fa2024-11-12 07:13:19 -0700251 if not self.lab_mode:
252 raise BootFail('Missing prompt / ready message on console: ' +
253 self.bad_pattern_ids[m - 3])
254 self.log.info(f'U-Boot is ready')
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900255
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900256 finally:
257 self.log.timestamp()
258
Stephen Warren10e50632016-01-15 11:15:24 -0700259 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900260 wait_for_prompt=True, wait_for_reboot=False):
Stephen Warren75e731e2016-01-26 13:41:30 -0700261 """Execute a command via the U-Boot console.
Stephen Warren10e50632016-01-15 11:15:24 -0700262
263 The command is always sent to U-Boot.
264
265 U-Boot echoes any command back to its output, and this function
266 typically waits for that to occur. The wait can be disabled by setting
267 wait_for_echo=False, which is useful e.g. when sending CTRL-C to
268 interrupt a long-running command such as "ums".
269
270 Command execution is typically triggered by sending a newline
271 character. This can be disabled by setting send_nl=False, which is
272 also useful when sending CTRL-C.
273
274 This function typically waits for the command to finish executing, and
275 returns the console output that it generated. This can be disabled by
276 setting wait_for_prompt=False, which is useful when invoking a long-
277 running command such as "ums".
278
279 Args:
280 cmd: The command to send.
Heinrich Schuchardtbec160a2017-09-14 12:27:07 +0200281 wait_for_echo: Boolean indicating whether to wait for U-Boot to
Stephen Warren10e50632016-01-15 11:15:24 -0700282 echo the command text back to its output.
283 send_nl: Boolean indicating whether to send a newline character
284 after the command string.
285 wait_for_prompt: Boolean indicating whether to wait for the
286 command prompt to be sent by U-Boot. This typically occurs
287 immediately after the command has been executed.
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900288 wait_for_reboot: Boolean indication whether to wait for the
289 reboot U-Boot. If this sets True, wait_for_prompt must also
290 be True.
Stephen Warren10e50632016-01-15 11:15:24 -0700291
292 Returns:
293 If wait_for_prompt == False:
294 Nothing.
295 Else:
296 The output from U-Boot during command execution. In other
297 words, the text U-Boot emitted between the point it echod the
298 command string and emitted the subsequent command prompts.
Stephen Warren75e731e2016-01-26 13:41:30 -0700299 """
Stephen Warren10e50632016-01-15 11:15:24 -0700300
Stephen Warren10e50632016-01-15 11:15:24 -0700301 if self.at_prompt and \
302 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
303 self.logstream.write(self.prompt, implicit=True)
304
Stephen Warren10e50632016-01-15 11:15:24 -0700305 try:
306 self.at_prompt = False
Simon Glass2f909122024-11-12 07:13:20 -0700307 if not self.p:
308 raise BootFail(
309 f"Lab failure: Connection lost when sending command '{cmd}'")
310
Stephen Warren10e50632016-01-15 11:15:24 -0700311 if send_nl:
312 cmd += '\n'
Simon Glass2f909122024-11-12 07:13:20 -0700313 rem = cmd # Remaining to be sent
314 with self.temporary_timeout(TIMEOUT_CMD_MS):
315 while rem:
316 # Limit max outstanding data, so UART FIFOs don't overflow
317 chunk = rem[:self.max_fifo_fill]
318 rem = rem[self.max_fifo_fill:]
319 self.p.send(chunk)
320 if not wait_for_echo:
321 continue
322 chunk = re.escape(chunk)
323 chunk = chunk.replace('\\\n', '[\r\n]')
324 m = self.p.expect([chunk] + self.bad_patterns)
325 if m != 0:
326 self.at_prompt = False
327 raise BootFail(f"Failed to get echo on console (cmd '{cmd}':rem '{rem}'): " +
328 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700329 if not wait_for_prompt:
330 return
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900331 if wait_for_reboot:
332 self.wait_for_boot_prompt()
333 else:
334 m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
335 if m != 0:
336 self.at_prompt = False
Simon Glass573171e2024-10-09 18:29:02 -0600337 raise BootFail('Missing prompt on console: ' +
Masami Hiramatsu0ff22782022-02-16 15:15:52 +0900338 self.bad_pattern_ids[m - 1])
Stephen Warren10e50632016-01-15 11:15:24 -0700339 self.at_prompt = True
340 self.at_prompt_logevt = self.logstream.logfile.cur_evt
341 # Only strip \r\n; space/TAB might be significant if testing
342 # indentation.
343 return self.p.before.strip('\r\n')
Simon Glass573171e2024-10-09 18:29:02 -0600344 except Timeout as exc:
Heinrich Schuchardte73d68b2024-11-23 22:29:21 +0100345 handle_exception(self.config, self, self.log, exc,
346 f"Lab failure: Timeout executing '{cmd}'", True)
Simon Glass573171e2024-10-09 18:29:02 -0600347 raise
Simon Glass7a1e9b42024-10-09 18:29:04 -0600348 except BootFail as exc:
Heinrich Schuchardt572d51f2024-11-23 22:29:22 +0100349 handle_exception(self.config, self, self.log, exc,
350 f"'Boot fail '{cmd}'",
Simon Glass7a1e9b42024-10-09 18:29:04 -0600351 True, self.get_spawn_output())
Stephen Warren10e50632016-01-15 11:15:24 -0700352 raise
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600353 finally:
354 self.log.timestamp()
Stephen Warren10e50632016-01-15 11:15:24 -0700355
Simon Glass2436bb02016-07-03 09:40:42 -0600356 def run_command_list(self, cmds):
357 """Run a list of commands.
358
359 This is a helper function to call run_command() with default arguments
360 for each command in a list.
361
362 Args:
Simon Glassd5deca02016-07-31 17:35:04 -0600363 cmd: List of commands (each a string).
Simon Glass2436bb02016-07-03 09:40:42 -0600364 Returns:
Simon Glass2ca73112016-07-31 17:35:09 -0600365 A list of output strings from each command, one element for each
366 command.
Simon Glass2436bb02016-07-03 09:40:42 -0600367 """
Simon Glass2ca73112016-07-31 17:35:09 -0600368 output = []
Simon Glass2436bb02016-07-03 09:40:42 -0600369 for cmd in cmds:
Simon Glass2ca73112016-07-31 17:35:09 -0600370 output.append(self.run_command(cmd))
Simon Glass2436bb02016-07-03 09:40:42 -0600371 return output
372
Simon Glassa39a8232025-03-15 14:25:50 +0000373 def send(self, msg):
374 """Send characters without waiting for echo, etc."""
375 self.run_command(msg, wait_for_prompt=False, wait_for_echo=False,
376 send_nl=False)
377
378 def ctrl(self, char):
379 """Send a CTRL- character to U-Boot.
Stephen Warren10e50632016-01-15 11:15:24 -0700380
381 This is useful in order to stop execution of long-running synchronous
382 commands such as "ums".
383
384 Args:
Simon Glassa39a8232025-03-15 14:25:50 +0000385 char (str): Character to send, e.g. 'C' to send Ctrl-C
Stephen Warren75e731e2016-01-26 13:41:30 -0700386 """
Simon Glassa39a8232025-03-15 14:25:50 +0000387 self.log.action(f'Sending Ctrl-{char}')
388 self.send(chr(ord(char) - ord('@')))
389
390 def ctrlc(self):
391 """Send a CTRL-C character to U-Boot.
Stephen Warren10e50632016-01-15 11:15:24 -0700392
Simon Glassa39a8232025-03-15 14:25:50 +0000393 This is useful in order to stop execution of long-running synchronous
394 commands such as "ums".
395 """
396 self.ctrl('C')
Stephen Warren10e50632016-01-15 11:15:24 -0700397
Stephen Warrenef824f52016-01-22 12:30:12 -0700398 def wait_for(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700399 """Wait for a pattern to be emitted by U-Boot.
Stephen Warrenef824f52016-01-22 12:30:12 -0700400
401 This is useful when a long-running command such as "dfu" is executing,
402 and it periodically emits some text that should show up at a specific
403 location in the log file.
404
405 Args:
406 text: The text to wait for; either a string (containing raw text,
407 not a regular expression) or an re object.
408
409 Returns:
410 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700411 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700412
413 if type(text) == type(''):
414 text = re.escape(text)
Stephen Warren68a9bb62016-01-27 23:57:49 -0700415 m = self.p.expect([text] + self.bad_patterns)
416 if m != 0:
Simon Glass573171e2024-10-09 18:29:02 -0600417 raise Unexpected(
418 "Unexpected pattern found on console (exp '{text}': " +
419 self.bad_pattern_ids[m - 1])
Stephen Warrenef824f52016-01-22 12:30:12 -0700420
Stephen Warren97a54662016-01-22 12:30:09 -0700421 def drain_console(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700422 """Read from and log the U-Boot console for a short time.
Stephen Warren97a54662016-01-22 12:30:09 -0700423
424 U-Boot's console output is only logged when the test code actively
425 waits for U-Boot to emit specific data. There are cases where tests
426 can fail without doing this. For example, if a test asks U-Boot to
427 enable USB device mode, then polls until a host-side device node
428 exists. In such a case, it is useful to log U-Boot's console output
429 in case U-Boot printed clues as to why the host-side even did not
430 occur. This function will do that.
431
432 Args:
433 None.
434
435 Returns:
436 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700437 """
Stephen Warren97a54662016-01-22 12:30:09 -0700438
439 # If we are already not connected to U-Boot, there's nothing to drain.
440 # This should only happen when a previous call to run_command() or
441 # wait_for() failed (and hence the output has already been logged), or
442 # the system is shutting down.
443 if not self.p:
444 return
445
446 orig_timeout = self.p.timeout
447 try:
448 # Drain the log for a relatively short time.
449 self.p.timeout = 1000
450 # Wait for something U-Boot will likely never send. This will
451 # cause the console output to be read and logged.
452 self.p.expect(['This should never match U-Boot output'])
Stephen Warren677b9cc2018-09-20 16:55:03 -0600453 except:
454 # We expect a timeout, since U-Boot won't print what we waited
455 # for. Squash it when it happens.
456 #
457 # Squash any other exception too. This function is only used to
458 # drain (and log) the U-Boot console output after a failed test.
459 # The U-Boot process will be restarted, or target board reset, once
460 # this function returns. So, we don't care about detecting any
461 # additional errors, so they're squashed so that the rest of the
462 # post-test-failure cleanup code can continue operation, and
463 # correctly terminate any log sections, etc.
Stephen Warren97a54662016-01-22 12:30:09 -0700464 pass
465 finally:
466 self.p.timeout = orig_timeout
467
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900468 def ensure_spawned(self, expect_reset=False):
Stephen Warren75e731e2016-01-26 13:41:30 -0700469 """Ensure a connection to a correctly running U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700470
471 This may require spawning a new Sandbox process or resetting target
472 hardware, as defined by the implementation sub-class.
473
474 This is an internal function and should not be called directly.
475
476 Args:
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900477 expect_reset: Boolean indication whether this boot is expected
478 to be reset while the 1st boot process after main boot before
479 prompt. False by default.
Stephen Warren10e50632016-01-15 11:15:24 -0700480
481 Returns:
482 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700483 """
Stephen Warren10e50632016-01-15 11:15:24 -0700484
485 if self.p:
Bin Meng739b3862022-05-17 23:24:43 +0800486 # Reset the console timeout value as some tests may change
487 # its default value during the execution
488 if not self.config.gdbserver:
Simon Glass64914152024-10-09 18:28:58 -0600489 self.p.timeout = TIMEOUT_MS
Stephen Warren10e50632016-01-15 11:15:24 -0700490 return
491 try:
Stephen Warren80eea632016-02-11 11:46:12 -0700492 self.log.start_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700493 self.at_prompt = False
Stephen Warren10e50632016-01-15 11:15:24 -0700494 self.p = self.get_spawn()
495 # Real targets can take a long time to scroll large amounts of
496 # text if LCD is enabled. This value may need tweaking in the
497 # future, possibly per-test to be optimal. This works for 'help'
498 # on board 'seaboard'.
Stephen Warren33db1ee2016-02-04 16:11:50 -0700499 if not self.config.gdbserver:
Simon Glass64914152024-10-09 18:28:58 -0600500 self.p.timeout = TIMEOUT_MS
Stephen Warren10e50632016-01-15 11:15:24 -0700501 self.p.logfile_read = self.logstream
Simon Glassf1b1bb82024-11-12 07:13:17 -0700502 if self.config.use_running_system:
503 # Send an empty command to set up the 'expect' logic. This has
504 # the side effect of ensuring that there was no partial command
505 # line entered
506 self.run_command(' ')
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900507 else:
Simon Glassf1b1bb82024-11-12 07:13:17 -0700508 if expect_reset:
509 loop_num = 2
510 else:
511 loop_num = 1
512 self.wait_for_boot_prompt(loop_num = loop_num)
Stephen Warren10e50632016-01-15 11:15:24 -0700513 self.at_prompt = True
514 self.at_prompt_logevt = self.logstream.logfile.cur_evt
515 except Exception as ex:
516 self.log.error(str(ex))
517 self.cleanup_spawn()
518 raise
Stephen Warren80eea632016-02-11 11:46:12 -0700519 finally:
Stephen Warrenb1c556a2017-10-27 11:04:08 -0600520 self.log.timestamp()
Stephen Warren80eea632016-02-11 11:46:12 -0700521 self.log.end_section('Starting U-Boot')
Stephen Warren10e50632016-01-15 11:15:24 -0700522
523 def cleanup_spawn(self):
Stephen Warren75e731e2016-01-26 13:41:30 -0700524 """Shut down all interaction with the U-Boot instance.
Stephen Warren10e50632016-01-15 11:15:24 -0700525
526 This is used when an error is detected prior to re-establishing a
527 connection with a fresh U-Boot instance.
528
529 This is an internal function and should not be called directly.
530
531 Args:
532 None.
533
534 Returns:
535 Nothing.
Stephen Warren75e731e2016-01-26 13:41:30 -0700536 """
Stephen Warren10e50632016-01-15 11:15:24 -0700537
538 try:
539 if self.p:
540 self.p.close()
541 except:
542 pass
543 self.p = None
544
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900545 def restart_uboot(self, expect_reset=False):
Simon Glass37c2ce12016-07-31 17:35:08 -0600546 """Shut down and restart U-Boot."""
547 self.cleanup_spawn()
Masami Hiramatsu4d3b9962022-02-16 15:16:02 +0900548 self.ensure_spawned(expect_reset)
Simon Glass37c2ce12016-07-31 17:35:08 -0600549
Simon Glass9bc20832016-07-04 11:58:39 -0600550 def get_spawn_output(self):
551 """Return the start-up output from U-Boot
552
553 Returns:
554 The output produced by ensure_spawed(), as a string.
555 """
556 if self.p:
557 return self.p.get_expect_output()
558 return None
559
Stephen Warren10e50632016-01-15 11:15:24 -0700560 def validate_version_string_in_text(self, text):
Stephen Warren75e731e2016-01-26 13:41:30 -0700561 """Assert that a command's output includes the U-Boot signon message.
Stephen Warren10e50632016-01-15 11:15:24 -0700562
563 This is primarily useful for validating the "version" command without
564 duplicating the signon text regex in a test function.
565
566 Args:
567 text: The command output text to check.
568
569 Returns:
570 Nothing. An exception is raised if the validation fails.
Stephen Warren75e731e2016-01-26 13:41:30 -0700571 """
Stephen Warren10e50632016-01-15 11:15:24 -0700572
573 assert(self.u_boot_version_string in text)
574
575 def disable_check(self, check_type):
Stephen Warren75e731e2016-01-26 13:41:30 -0700576 """Temporarily disable an error check of U-Boot's output.
Stephen Warren10e50632016-01-15 11:15:24 -0700577
578 Create a new context manager (for use with the "with" statement) which
579 temporarily disables a particular console output error check.
580
581 Args:
582 check_type: The type of error-check to disable. Valid values may
583 be found in self.disable_check_count above.
584
585 Returns:
586 A context manager object.
Stephen Warren75e731e2016-01-26 13:41:30 -0700587 """
Stephen Warren10e50632016-01-15 11:15:24 -0700588
589 return ConsoleDisableCheck(self, check_type)
Michal Simek6b463182016-05-19 07:57:41 +0200590
Love Kumarc35e66e2024-05-22 18:45:13 +0530591 def enable_check(self, check_type, check_pattern):
592 """Temporarily enable an error check of U-Boot's output.
593
594 Create a new context manager (for use with the "with" statement) which
595 temporarily enables a particular console output error check. The
596 arguments form a new element of bad_pattern_defs defined above.
597
598 Args:
599 check_type: The type of error-check or bad pattern to enable.
600 check_pattern: The regexes for text error pattern or bad pattern
601 to be checked.
602
603 Returns:
604 A context manager object.
605 """
606
607 return ConsoleEnableCheck(self, check_type, check_pattern)
608
Michal Simek6b463182016-05-19 07:57:41 +0200609 def temporary_timeout(self, timeout):
610 """Temporarily set up different timeout for commands.
611
612 Create a new context manager (for use with the "with" statement) which
613 temporarily change timeout.
614
615 Args:
616 timeout: Time in milliseconds.
617
618 Returns:
619 A context manager object.
620 """
621
622 return ConsoleSetupTimeout(self, timeout)