1 # SPDX-License-Identifier: GPL-2.0
2 # Copyright (c) 2015 Stephen Warren
3 # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
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.
11 import multiplexed_log
18 # Regexes for text we expect U-Boot to send to the console.
19 pattern_u_boot_spl_signon = re.compile('(U-Boot spl \\d{4}\\.\\d{2}[^\r\n]*\\))')
20 pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
21 pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
22 pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
23 pattern_error_notification = re.compile('## Error: ')
24 pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
30 ('spl_signon', pattern_u_boot_spl_signon),
31 ('main_signon', pattern_u_boot_main_signon),
32 ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
33 ('unknown_command', pattern_unknown_command),
34 ('error_notification', pattern_error_notification),
35 ('error_please_reset', pattern_error_please_reset),
38 class ConsoleDisableCheck(object):
39 """Context manager (for Python's with statement) that temporarily disables
40 the specified console output error check. This is useful when deliberately
41 executing a command that is known to trigger one of the error checks, in
42 order to test that the error condition is actually raised. This class is
43 used internally by ConsoleBase::disable_check(); it is not intended for
46 def __init__(self, console, check_type):
47 self.console = console
48 self.check_type = check_type
51 self.console.disable_check_count[self.check_type] += 1
52 self.console.eval_bad_patterns()
54 def __exit__(self, extype, value, traceback):
55 self.console.disable_check_count[self.check_type] -= 1
56 self.console.eval_bad_patterns()
58 class ConsoleSetupTimeout(object):
59 """Context manager (for Python's with statement) that temporarily sets up
60 timeout for specific command. This is useful when execution time is greater
63 def __init__(self, console, timeout):
65 self.orig_timeout = self.p.timeout
66 self.p.timeout = timeout
71 def __exit__(self, extype, value, traceback):
72 self.p.timeout = self.orig_timeout
74 class ConsoleBase(object):
75 """The interface through which test functions interact with the U-Boot
76 console. This primarily involves executing shell commands, capturing their
77 results, and checking for common error conditions. Some common utilities
78 are also provided too."""
80 def __init__(self, log, config, max_fifo_fill):
81 """Initialize a U-Boot console connection.
83 Can only usefully be called by sub-classes.
86 log: A mulptiplex_log.Logfile object, to which the U-Boot output
88 config: A configuration data structure, as built by conftest.py.
89 max_fifo_fill: The maximum number of characters to send to U-Boot
90 command-line before waiting for U-Boot to echo the characters
91 back. For UART-based HW without HW flow control, this value
92 should be set less than the UART RX FIFO size to avoid
93 overflow, assuming that U-Boot can't keep up with full-rate
94 traffic at the baud rate.
102 self.max_fifo_fill = max_fifo_fill
104 self.logstream = self.log.get_stream('console', sys.stdout)
106 # Array slice removes leading/trailing quotes
107 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
108 self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
110 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
111 self.eval_bad_patterns()
113 self.at_prompt = False
114 self.at_prompt_logevt = None
116 def eval_bad_patterns(self):
117 self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
118 if self.disable_check_count[pat[PAT_ID]] == 0]
119 self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
120 if self.disable_check_count[pat[PAT_ID]] == 0]
123 """Terminate the connection to the U-Boot console.
125 This function is only useful once all interaction with U-Boot is
126 complete. Once this function is called, data cannot be sent to or
127 received from U-Boot.
138 self.logstream.close()
140 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
141 wait_for_prompt=True):
142 """Execute a command via the U-Boot console.
144 The command is always sent to U-Boot.
146 U-Boot echoes any command back to its output, and this function
147 typically waits for that to occur. The wait can be disabled by setting
148 wait_for_echo=False, which is useful e.g. when sending CTRL-C to
149 interrupt a long-running command such as "ums".
151 Command execution is typically triggered by sending a newline
152 character. This can be disabled by setting send_nl=False, which is
153 also useful when sending CTRL-C.
155 This function typically waits for the command to finish executing, and
156 returns the console output that it generated. This can be disabled by
157 setting wait_for_prompt=False, which is useful when invoking a long-
158 running command such as "ums".
161 cmd: The command to send.
162 wait_for_echo: Boolean indicating whether to wait for U-Boot to
163 echo the command text back to its output.
164 send_nl: Boolean indicating whether to send a newline character
165 after the command string.
166 wait_for_prompt: Boolean indicating whether to wait for the
167 command prompt to be sent by U-Boot. This typically occurs
168 immediately after the command has been executed.
171 If wait_for_prompt == False:
174 The output from U-Boot during command execution. In other
175 words, the text U-Boot emitted between the point it echod the
176 command string and emitted the subsequent command prompts.
179 if self.at_prompt and \
180 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
181 self.logstream.write(self.prompt, implicit=True)
184 self.at_prompt = False
188 # Limit max outstanding data, so UART FIFOs don't overflow
189 chunk = cmd[:self.max_fifo_fill]
190 cmd = cmd[self.max_fifo_fill:]
192 if not wait_for_echo:
194 chunk = re.escape(chunk)
195 chunk = chunk.replace('\\\n', '[\r\n]')
196 m = self.p.expect([chunk] + self.bad_patterns)
198 self.at_prompt = False
199 raise Exception('Bad pattern found on console: ' +
200 self.bad_pattern_ids[m - 1])
201 if not wait_for_prompt:
203 m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
205 self.at_prompt = False
206 raise Exception('Bad pattern found on console: ' +
207 self.bad_pattern_ids[m - 1])
208 self.at_prompt = True
209 self.at_prompt_logevt = self.logstream.logfile.cur_evt
210 # Only strip \r\n; space/TAB might be significant if testing
212 return self.p.before.strip('\r\n')
213 except Exception as ex:
214 self.log.error(str(ex))
220 def run_command_list(self, cmds):
221 """Run a list of commands.
223 This is a helper function to call run_command() with default arguments
224 for each command in a list.
227 cmd: List of commands (each a string).
229 A list of output strings from each command, one element for each
234 output.append(self.run_command(cmd))
238 """Send a CTRL-C character to U-Boot.
240 This is useful in order to stop execution of long-running synchronous
241 commands such as "ums".
250 self.log.action('Sending Ctrl-C')
251 self.run_command(chr(3), wait_for_echo=False, send_nl=False)
253 def wait_for(self, text):
254 """Wait for a pattern to be emitted by U-Boot.
256 This is useful when a long-running command such as "dfu" is executing,
257 and it periodically emits some text that should show up at a specific
258 location in the log file.
261 text: The text to wait for; either a string (containing raw text,
262 not a regular expression) or an re object.
268 if type(text) == type(''):
269 text = re.escape(text)
270 m = self.p.expect([text] + self.bad_patterns)
272 raise Exception('Bad pattern found on console: ' +
273 self.bad_pattern_ids[m - 1])
275 def drain_console(self):
276 """Read from and log the U-Boot console for a short time.
278 U-Boot's console output is only logged when the test code actively
279 waits for U-Boot to emit specific data. There are cases where tests
280 can fail without doing this. For example, if a test asks U-Boot to
281 enable USB device mode, then polls until a host-side device node
282 exists. In such a case, it is useful to log U-Boot's console output
283 in case U-Boot printed clues as to why the host-side even did not
284 occur. This function will do that.
293 # If we are already not connected to U-Boot, there's nothing to drain.
294 # This should only happen when a previous call to run_command() or
295 # wait_for() failed (and hence the output has already been logged), or
296 # the system is shutting down.
300 orig_timeout = self.p.timeout
302 # Drain the log for a relatively short time.
303 self.p.timeout = 1000
304 # Wait for something U-Boot will likely never send. This will
305 # cause the console output to be read and logged.
306 self.p.expect(['This should never match U-Boot output'])
308 # We expect a timeout, since U-Boot won't print what we waited
309 # for. Squash it when it happens.
311 # Squash any other exception too. This function is only used to
312 # drain (and log) the U-Boot console output after a failed test.
313 # The U-Boot process will be restarted, or target board reset, once
314 # this function returns. So, we don't care about detecting any
315 # additional errors, so they're squashed so that the rest of the
316 # post-test-failure cleanup code can continue operation, and
317 # correctly terminate any log sections, etc.
320 self.p.timeout = orig_timeout
322 def ensure_spawned(self):
323 """Ensure a connection to a correctly running U-Boot instance.
325 This may require spawning a new Sandbox process or resetting target
326 hardware, as defined by the implementation sub-class.
328 This is an internal function and should not be called directly.
340 self.log.start_section('Starting U-Boot')
341 self.at_prompt = False
342 self.p = self.get_spawn()
343 # Real targets can take a long time to scroll large amounts of
344 # text if LCD is enabled. This value may need tweaking in the
345 # future, possibly per-test to be optimal. This works for 'help'
346 # on board 'seaboard'.
347 if not self.config.gdbserver:
348 self.p.timeout = 30000
349 self.p.logfile_read = self.logstream
350 bcfg = self.config.buildconfig
351 config_spl = bcfg.get('config_spl', 'n') == 'y'
352 config_spl_serial_support = bcfg.get('config_spl_serial_support',
354 env_spl_skipped = self.config.env.get('env__spl_skipped',
356 if config_spl and config_spl_serial_support and not env_spl_skipped:
357 m = self.p.expect([pattern_u_boot_spl_signon] +
360 raise Exception('Bad pattern found on SPL console: ' +
361 self.bad_pattern_ids[m - 1])
362 m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
364 raise Exception('Bad pattern found on console: ' +
365 self.bad_pattern_ids[m - 1])
366 self.u_boot_version_string = self.p.after
368 m = self.p.expect([self.prompt_compiled,
369 pattern_stop_autoboot_prompt] + self.bad_patterns)
375 raise Exception('Bad pattern found on console: ' +
376 self.bad_pattern_ids[m - 2])
377 self.at_prompt = True
378 self.at_prompt_logevt = self.logstream.logfile.cur_evt
379 except Exception as ex:
380 self.log.error(str(ex))
385 self.log.end_section('Starting U-Boot')
387 def cleanup_spawn(self):
388 """Shut down all interaction with the U-Boot instance.
390 This is used when an error is detected prior to re-establishing a
391 connection with a fresh U-Boot instance.
393 This is an internal function and should not be called directly.
409 def restart_uboot(self):
410 """Shut down and restart U-Boot."""
412 self.ensure_spawned()
414 def get_spawn_output(self):
415 """Return the start-up output from U-Boot
418 The output produced by ensure_spawed(), as a string.
421 return self.p.get_expect_output()
424 def validate_version_string_in_text(self, text):
425 """Assert that a command's output includes the U-Boot signon message.
427 This is primarily useful for validating the "version" command without
428 duplicating the signon text regex in a test function.
431 text: The command output text to check.
434 Nothing. An exception is raised if the validation fails.
437 assert(self.u_boot_version_string in text)
439 def disable_check(self, check_type):
440 """Temporarily disable an error check of U-Boot's output.
442 Create a new context manager (for use with the "with" statement) which
443 temporarily disables a particular console output error check.
446 check_type: The type of error-check to disable. Valid values may
447 be found in self.disable_check_count above.
450 A context manager object.
453 return ConsoleDisableCheck(self, check_type)
455 def temporary_timeout(self, timeout):
456 """Temporarily set up different timeout for commands.
458 Create a new context manager (for use with the "with" statement) which
459 temporarily change timeout.
462 timeout: Time in milliseconds.
465 A context manager object.
468 return ConsoleSetupTimeout(self, timeout)