blob: 326b2ac51fbf07daa2e2af0385e9358d8cf9398f [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0
Stephen Warrend2015062016-01-15 11:15:24 -07002# Copyright (c) 2015 Stephen Warren
3# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
Stephen Warrend2015062016-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 Warrenc10eb9d2016-01-22 12:30:09 -070016import u_boot_spawn
Stephen Warrend2015062016-01-15 11:15:24 -070017
18# Regexes for text we expect U-Boot to send to the console.
Heiko Schocher8fb23912018-12-05 11:29:54 +010019pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warrenc82ce042016-02-05 18:04:43 -070020pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warrend2015062016-01-15 11:15:24 -070021pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
22pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
23pattern_error_notification = re.compile('## Error: ')
Stephen Warren9129d9f2016-01-27 23:57:50 -070024pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
Stephen Warrend2015062016-01-15 11:15:24 -070025
Stephen Warrene4119eb2016-01-27 23:57:48 -070026PAT_ID = 0
27PAT_RE = 1
28
29bad_pattern_defs = (
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),
Stephen Warren9129d9f2016-01-27 23:57:50 -070035 ('error_please_reset', pattern_error_please_reset),
Stephen Warrene4119eb2016-01-27 23:57:48 -070036)
37
Stephen Warrend2015062016-01-15 11:15:24 -070038class ConsoleDisableCheck(object):
Stephen Warrene8debf32016-01-26 13:41:30 -070039 """Context manager (for Python's with statement) that temporarily disables
Stephen Warrend2015062016-01-15 11:15:24 -070040 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
Stephen Warrene8debf32016-01-26 13:41:30 -070044 direct usage."""
Stephen Warrend2015062016-01-15 11:15:24 -070045
46 def __init__(self, console, check_type):
47 self.console = console
48 self.check_type = check_type
49
50 def __enter__(self):
51 self.console.disable_check_count[self.check_type] += 1
Stephen Warrene4119eb2016-01-27 23:57:48 -070052 self.console.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -070053
54 def __exit__(self, extype, value, traceback):
55 self.console.disable_check_count[self.check_type] -= 1
Stephen Warrene4119eb2016-01-27 23:57:48 -070056 self.console.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -070057
Michal Simek87861c12016-05-19 07:57:41 +020058class 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
61 then default 30s."""
62
63 def __init__(self, console, timeout):
64 self.p = console.p
65 self.orig_timeout = self.p.timeout
66 self.p.timeout = timeout
67
68 def __enter__(self):
69 return self
70
71 def __exit__(self, extype, value, traceback):
72 self.p.timeout = self.orig_timeout
73
Stephen Warrend2015062016-01-15 11:15:24 -070074class ConsoleBase(object):
Stephen Warrene8debf32016-01-26 13:41:30 -070075 """The interface through which test functions interact with the U-Boot
Stephen Warrend2015062016-01-15 11:15:24 -070076 console. This primarily involves executing shell commands, capturing their
77 results, and checking for common error conditions. Some common utilities
Stephen Warrene8debf32016-01-26 13:41:30 -070078 are also provided too."""
Stephen Warrend2015062016-01-15 11:15:24 -070079
80 def __init__(self, log, config, max_fifo_fill):
Stephen Warrene8debf32016-01-26 13:41:30 -070081 """Initialize a U-Boot console connection.
Stephen Warrend2015062016-01-15 11:15:24 -070082
83 Can only usefully be called by sub-classes.
84
85 Args:
86 log: A mulptiplex_log.Logfile object, to which the U-Boot output
87 will be logged.
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.
95
96 Returns:
97 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -070098 """
Stephen Warrend2015062016-01-15 11:15:24 -070099
100 self.log = log
101 self.config = config
102 self.max_fifo_fill = max_fifo_fill
103
104 self.logstream = self.log.get_stream('console', sys.stdout)
105
106 # Array slice removes leading/trailing quotes
107 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
Stephen Warren4ba58bd2016-08-16 19:58:59 -0600108 self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
Stephen Warrend2015062016-01-15 11:15:24 -0700109 self.p = None
Stephen Warrene4119eb2016-01-27 23:57:48 -0700110 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
111 self.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -0700112
113 self.at_prompt = False
114 self.at_prompt_logevt = None
Stephen Warrend2015062016-01-15 11:15:24 -0700115
Stephen Warrene4119eb2016-01-27 23:57:48 -0700116 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]
121
Stephen Warrend2015062016-01-15 11:15:24 -0700122 def close(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700123 """Terminate the connection to the U-Boot console.
Stephen Warrend2015062016-01-15 11:15:24 -0700124
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.
128
129 Args:
130 None.
131
132 Returns:
133 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700134 """
Stephen Warrend2015062016-01-15 11:15:24 -0700135
136 if self.p:
137 self.p.close()
138 self.logstream.close()
139
140 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
141 wait_for_prompt=True):
Stephen Warrene8debf32016-01-26 13:41:30 -0700142 """Execute a command via the U-Boot console.
Stephen Warrend2015062016-01-15 11:15:24 -0700143
144 The command is always sent to U-Boot.
145
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".
150
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.
154
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".
159
160 Args:
161 cmd: The command to send.
Heinrich Schuchardtcd3e8a72017-09-14 12:27:07 +0200162 wait_for_echo: Boolean indicating whether to wait for U-Boot to
Stephen Warrend2015062016-01-15 11:15:24 -0700163 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.
169
170 Returns:
171 If wait_for_prompt == False:
172 Nothing.
173 Else:
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.
Stephen Warrene8debf32016-01-26 13:41:30 -0700177 """
Stephen Warrend2015062016-01-15 11:15:24 -0700178
Stephen Warrend2015062016-01-15 11:15:24 -0700179 if self.at_prompt and \
180 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
181 self.logstream.write(self.prompt, implicit=True)
182
Stephen Warrend2015062016-01-15 11:15:24 -0700183 try:
184 self.at_prompt = False
185 if send_nl:
186 cmd += '\n'
187 while cmd:
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:]
191 self.p.send(chunk)
192 if not wait_for_echo:
193 continue
194 chunk = re.escape(chunk)
195 chunk = chunk.replace('\\\n', '[\r\n]')
Stephen Warrene4119eb2016-01-27 23:57:48 -0700196 m = self.p.expect([chunk] + self.bad_patterns)
Stephen Warrend2015062016-01-15 11:15:24 -0700197 if m != 0:
198 self.at_prompt = False
199 raise Exception('Bad pattern found on console: ' +
Stephen Warrene4119eb2016-01-27 23:57:48 -0700200 self.bad_pattern_ids[m - 1])
Stephen Warrend2015062016-01-15 11:15:24 -0700201 if not wait_for_prompt:
202 return
Stephen Warren4ba58bd2016-08-16 19:58:59 -0600203 m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
Stephen Warrend2015062016-01-15 11:15:24 -0700204 if m != 0:
205 self.at_prompt = False
206 raise Exception('Bad pattern found on console: ' +
Stephen Warrene4119eb2016-01-27 23:57:48 -0700207 self.bad_pattern_ids[m - 1])
Stephen Warrend2015062016-01-15 11:15:24 -0700208 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
211 # indentation.
212 return self.p.before.strip('\r\n')
213 except Exception as ex:
214 self.log.error(str(ex))
215 self.cleanup_spawn()
216 raise
Stephen Warren9679d332017-10-27 11:04:08 -0600217 finally:
218 self.log.timestamp()
Stephen Warrend2015062016-01-15 11:15:24 -0700219
Simon Glass73a90542016-07-03 09:40:42 -0600220 def run_command_list(self, cmds):
221 """Run a list of commands.
222
223 This is a helper function to call run_command() with default arguments
224 for each command in a list.
225
226 Args:
Simon Glass72f52262016-07-31 17:35:04 -0600227 cmd: List of commands (each a string).
Simon Glass73a90542016-07-03 09:40:42 -0600228 Returns:
Simon Glassf6d34652016-07-31 17:35:09 -0600229 A list of output strings from each command, one element for each
230 command.
Simon Glass73a90542016-07-03 09:40:42 -0600231 """
Simon Glassf6d34652016-07-31 17:35:09 -0600232 output = []
Simon Glass73a90542016-07-03 09:40:42 -0600233 for cmd in cmds:
Simon Glassf6d34652016-07-31 17:35:09 -0600234 output.append(self.run_command(cmd))
Simon Glass73a90542016-07-03 09:40:42 -0600235 return output
236
Stephen Warrend2015062016-01-15 11:15:24 -0700237 def ctrlc(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700238 """Send a CTRL-C character to U-Boot.
Stephen Warrend2015062016-01-15 11:15:24 -0700239
240 This is useful in order to stop execution of long-running synchronous
241 commands such as "ums".
242
243 Args:
244 None.
245
246 Returns:
247 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700248 """
Stephen Warrend2015062016-01-15 11:15:24 -0700249
Stephen Warren783cbcd2016-01-22 12:30:10 -0700250 self.log.action('Sending Ctrl-C')
Stephen Warrend2015062016-01-15 11:15:24 -0700251 self.run_command(chr(3), wait_for_echo=False, send_nl=False)
252
Stephen Warren76b46932016-01-22 12:30:12 -0700253 def wait_for(self, text):
Stephen Warrene8debf32016-01-26 13:41:30 -0700254 """Wait for a pattern to be emitted by U-Boot.
Stephen Warren76b46932016-01-22 12:30:12 -0700255
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.
259
260 Args:
261 text: The text to wait for; either a string (containing raw text,
262 not a regular expression) or an re object.
263
264 Returns:
265 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700266 """
Stephen Warren76b46932016-01-22 12:30:12 -0700267
268 if type(text) == type(''):
269 text = re.escape(text)
Stephen Warren0c6189b2016-01-27 23:57:49 -0700270 m = self.p.expect([text] + self.bad_patterns)
271 if m != 0:
272 raise Exception('Bad pattern found on console: ' +
273 self.bad_pattern_ids[m - 1])
Stephen Warren76b46932016-01-22 12:30:12 -0700274
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700275 def drain_console(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700276 """Read from and log the U-Boot console for a short time.
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700277
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.
285
286 Args:
287 None.
288
289 Returns:
290 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700291 """
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700292
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.
297 if not self.p:
298 return
299
300 orig_timeout = self.p.timeout
301 try:
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'])
Stephen Warrendddea0d2018-09-20 16:55:03 -0600307 except:
308 # We expect a timeout, since U-Boot won't print what we waited
309 # for. Squash it when it happens.
310 #
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.
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700318 pass
319 finally:
320 self.p.timeout = orig_timeout
321
Stephen Warrend2015062016-01-15 11:15:24 -0700322 def ensure_spawned(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700323 """Ensure a connection to a correctly running U-Boot instance.
Stephen Warrend2015062016-01-15 11:15:24 -0700324
325 This may require spawning a new Sandbox process or resetting target
326 hardware, as defined by the implementation sub-class.
327
328 This is an internal function and should not be called directly.
329
330 Args:
331 None.
332
333 Returns:
334 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700335 """
Stephen Warrend2015062016-01-15 11:15:24 -0700336
337 if self.p:
338 return
339 try:
Stephen Warren97255432016-02-11 11:46:12 -0700340 self.log.start_section('Starting U-Boot')
Stephen Warrend2015062016-01-15 11:15:24 -0700341 self.at_prompt = False
Stephen Warrend2015062016-01-15 11:15:24 -0700342 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'.
Stephen Warren89ab8412016-02-04 16:11:50 -0700347 if not self.config.gdbserver:
348 self.p.timeout = 30000
Stephen Warrend2015062016-01-15 11:15:24 -0700349 self.p.logfile_read = self.logstream
Heiko Schocherb1309a22016-02-17 18:32:51 +0100350 bcfg = self.config.buildconfig
351 config_spl = bcfg.get('config_spl', 'n') == 'y'
352 config_spl_serial_support = bcfg.get('config_spl_serial_support',
353 'n') == 'y'
Michal Simek299e5bb2016-02-25 14:58:24 +0100354 env_spl_skipped = self.config.env.get('env__spl_skipped',
355 False)
356 if config_spl and config_spl_serial_support and not env_spl_skipped:
Heiko Schocherb1309a22016-02-17 18:32:51 +0100357 m = self.p.expect([pattern_u_boot_spl_signon] +
358 self.bad_patterns)
Stephen Warren0c6189b2016-01-27 23:57:49 -0700359 if m != 0:
Simon Glassc7f636f2016-07-04 11:58:38 -0600360 raise Exception('Bad pattern found on SPL console: ' +
Stephen Warren0c6189b2016-01-27 23:57:49 -0700361 self.bad_pattern_ids[m - 1])
362 m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
363 if m != 0:
364 raise Exception('Bad pattern found on console: ' +
365 self.bad_pattern_ids[m - 1])
Stephen Warrenc82ce042016-02-05 18:04:43 -0700366 self.u_boot_version_string = self.p.after
Stephen Warrend2015062016-01-15 11:15:24 -0700367 while True:
Stephen Warren4ba58bd2016-08-16 19:58:59 -0600368 m = self.p.expect([self.prompt_compiled,
Stephen Warren0c6189b2016-01-27 23:57:49 -0700369 pattern_stop_autoboot_prompt] + self.bad_patterns)
370 if m == 0:
371 break
372 if m == 1:
Stephen Warren38831ca2016-02-15 17:39:38 -0700373 self.p.send(' ')
Stephen Warrend2015062016-01-15 11:15:24 -0700374 continue
Stephen Warren0c6189b2016-01-27 23:57:49 -0700375 raise Exception('Bad pattern found on console: ' +
376 self.bad_pattern_ids[m - 2])
Stephen Warrend2015062016-01-15 11:15:24 -0700377 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))
381 self.cleanup_spawn()
382 raise
Stephen Warren97255432016-02-11 11:46:12 -0700383 finally:
Stephen Warren9679d332017-10-27 11:04:08 -0600384 self.log.timestamp()
Stephen Warren97255432016-02-11 11:46:12 -0700385 self.log.end_section('Starting U-Boot')
Stephen Warrend2015062016-01-15 11:15:24 -0700386
387 def cleanup_spawn(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700388 """Shut down all interaction with the U-Boot instance.
Stephen Warrend2015062016-01-15 11:15:24 -0700389
390 This is used when an error is detected prior to re-establishing a
391 connection with a fresh U-Boot instance.
392
393 This is an internal function and should not be called directly.
394
395 Args:
396 None.
397
398 Returns:
399 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700400 """
Stephen Warrend2015062016-01-15 11:15:24 -0700401
402 try:
403 if self.p:
404 self.p.close()
405 except:
406 pass
407 self.p = None
408
Simon Glass27c087d2016-07-31 17:35:08 -0600409 def restart_uboot(self):
410 """Shut down and restart U-Boot."""
411 self.cleanup_spawn()
412 self.ensure_spawned()
413
Simon Glassebec58f2016-07-04 11:58:39 -0600414 def get_spawn_output(self):
415 """Return the start-up output from U-Boot
416
417 Returns:
418 The output produced by ensure_spawed(), as a string.
419 """
420 if self.p:
421 return self.p.get_expect_output()
422 return None
423
Stephen Warrend2015062016-01-15 11:15:24 -0700424 def validate_version_string_in_text(self, text):
Stephen Warrene8debf32016-01-26 13:41:30 -0700425 """Assert that a command's output includes the U-Boot signon message.
Stephen Warrend2015062016-01-15 11:15:24 -0700426
427 This is primarily useful for validating the "version" command without
428 duplicating the signon text regex in a test function.
429
430 Args:
431 text: The command output text to check.
432
433 Returns:
434 Nothing. An exception is raised if the validation fails.
Stephen Warrene8debf32016-01-26 13:41:30 -0700435 """
Stephen Warrend2015062016-01-15 11:15:24 -0700436
437 assert(self.u_boot_version_string in text)
438
439 def disable_check(self, check_type):
Stephen Warrene8debf32016-01-26 13:41:30 -0700440 """Temporarily disable an error check of U-Boot's output.
Stephen Warrend2015062016-01-15 11:15:24 -0700441
442 Create a new context manager (for use with the "with" statement) which
443 temporarily disables a particular console output error check.
444
445 Args:
446 check_type: The type of error-check to disable. Valid values may
447 be found in self.disable_check_count above.
448
449 Returns:
450 A context manager object.
Stephen Warrene8debf32016-01-26 13:41:30 -0700451 """
Stephen Warrend2015062016-01-15 11:15:24 -0700452
453 return ConsoleDisableCheck(self, check_type)
Michal Simek87861c12016-05-19 07:57:41 +0200454
455 def temporary_timeout(self, timeout):
456 """Temporarily set up different timeout for commands.
457
458 Create a new context manager (for use with the "with" statement) which
459 temporarily change timeout.
460
461 Args:
462 timeout: Time in milliseconds.
463
464 Returns:
465 A context manager object.
466 """
467
468 return ConsoleSetupTimeout(self, timeout)