blob: d6502c6e64cb1e2f563d1d925ebe50edd83c3db0 [file] [log] [blame]
Stephen Warrend2015062016-01-15 11:15:24 -07001# Copyright (c) 2015 Stephen Warren
2# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
3#
4# SPDX-License-Identifier: GPL-2.0
5
6# Common logic to interact with U-Boot via the console. This class provides
7# the interface that tests use to execute U-Boot shell commands and wait for
8# their results. Sub-classes exist to perform board-type-specific setup
9# operations, such as spawning a sub-process for Sandbox, or attaching to the
10# serial console of real hardware.
11
12import multiplexed_log
13import os
14import pytest
15import re
16import sys
Stephen Warrenc10eb9d2016-01-22 12:30:09 -070017import u_boot_spawn
Stephen Warrend2015062016-01-15 11:15:24 -070018
19# Regexes for text we expect U-Boot to send to the console.
Stephen Warrenc82ce042016-02-05 18:04:43 -070020pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
21pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
Stephen Warrend2015062016-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 Warren9129d9f2016-01-27 23:57:50 -070025pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
Stephen Warrend2015062016-01-15 11:15:24 -070026
Stephen Warrene4119eb2016-01-27 23:57:48 -070027PAT_ID = 0
28PAT_RE = 1
29
30bad_pattern_defs = (
31 ('spl_signon', pattern_u_boot_spl_signon),
32 ('main_signon', pattern_u_boot_main_signon),
33 ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
34 ('unknown_command', pattern_unknown_command),
35 ('error_notification', pattern_error_notification),
Stephen Warren9129d9f2016-01-27 23:57:50 -070036 ('error_please_reset', pattern_error_please_reset),
Stephen Warrene4119eb2016-01-27 23:57:48 -070037)
38
Stephen Warrend2015062016-01-15 11:15:24 -070039class ConsoleDisableCheck(object):
Stephen Warrene8debf32016-01-26 13:41:30 -070040 """Context manager (for Python's with statement) that temporarily disables
Stephen Warrend2015062016-01-15 11:15:24 -070041 the specified console output error check. This is useful when deliberately
42 executing a command that is known to trigger one of the error checks, in
43 order to test that the error condition is actually raised. This class is
44 used internally by ConsoleBase::disable_check(); it is not intended for
Stephen Warrene8debf32016-01-26 13:41:30 -070045 direct usage."""
Stephen Warrend2015062016-01-15 11:15:24 -070046
47 def __init__(self, console, check_type):
48 self.console = console
49 self.check_type = check_type
50
51 def __enter__(self):
52 self.console.disable_check_count[self.check_type] += 1
Stephen Warrene4119eb2016-01-27 23:57:48 -070053 self.console.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -070054
55 def __exit__(self, extype, value, traceback):
56 self.console.disable_check_count[self.check_type] -= 1
Stephen Warrene4119eb2016-01-27 23:57:48 -070057 self.console.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -070058
59class ConsoleBase(object):
Stephen Warrene8debf32016-01-26 13:41:30 -070060 """The interface through which test functions interact with the U-Boot
Stephen Warrend2015062016-01-15 11:15:24 -070061 console. This primarily involves executing shell commands, capturing their
62 results, and checking for common error conditions. Some common utilities
Stephen Warrene8debf32016-01-26 13:41:30 -070063 are also provided too."""
Stephen Warrend2015062016-01-15 11:15:24 -070064
65 def __init__(self, log, config, max_fifo_fill):
Stephen Warrene8debf32016-01-26 13:41:30 -070066 """Initialize a U-Boot console connection.
Stephen Warrend2015062016-01-15 11:15:24 -070067
68 Can only usefully be called by sub-classes.
69
70 Args:
71 log: A mulptiplex_log.Logfile object, to which the U-Boot output
72 will be logged.
73 config: A configuration data structure, as built by conftest.py.
74 max_fifo_fill: The maximum number of characters to send to U-Boot
75 command-line before waiting for U-Boot to echo the characters
76 back. For UART-based HW without HW flow control, this value
77 should be set less than the UART RX FIFO size to avoid
78 overflow, assuming that U-Boot can't keep up with full-rate
79 traffic at the baud rate.
80
81 Returns:
82 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -070083 """
Stephen Warrend2015062016-01-15 11:15:24 -070084
85 self.log = log
86 self.config = config
87 self.max_fifo_fill = max_fifo_fill
88
89 self.logstream = self.log.get_stream('console', sys.stdout)
90
91 # Array slice removes leading/trailing quotes
92 self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
93 self.prompt_escaped = re.escape(self.prompt)
94 self.p = None
Stephen Warrene4119eb2016-01-27 23:57:48 -070095 self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
96 self.eval_bad_patterns()
Stephen Warrend2015062016-01-15 11:15:24 -070097
98 self.at_prompt = False
99 self.at_prompt_logevt = None
Stephen Warrend2015062016-01-15 11:15:24 -0700100
Stephen Warrene4119eb2016-01-27 23:57:48 -0700101 def eval_bad_patterns(self):
102 self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
103 if self.disable_check_count[pat[PAT_ID]] == 0]
104 self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
105 if self.disable_check_count[pat[PAT_ID]] == 0]
106
Stephen Warrend2015062016-01-15 11:15:24 -0700107 def close(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700108 """Terminate the connection to the U-Boot console.
Stephen Warrend2015062016-01-15 11:15:24 -0700109
110 This function is only useful once all interaction with U-Boot is
111 complete. Once this function is called, data cannot be sent to or
112 received from U-Boot.
113
114 Args:
115 None.
116
117 Returns:
118 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700119 """
Stephen Warrend2015062016-01-15 11:15:24 -0700120
121 if self.p:
122 self.p.close()
123 self.logstream.close()
124
125 def run_command(self, cmd, wait_for_echo=True, send_nl=True,
126 wait_for_prompt=True):
Stephen Warrene8debf32016-01-26 13:41:30 -0700127 """Execute a command via the U-Boot console.
Stephen Warrend2015062016-01-15 11:15:24 -0700128
129 The command is always sent to U-Boot.
130
131 U-Boot echoes any command back to its output, and this function
132 typically waits for that to occur. The wait can be disabled by setting
133 wait_for_echo=False, which is useful e.g. when sending CTRL-C to
134 interrupt a long-running command such as "ums".
135
136 Command execution is typically triggered by sending a newline
137 character. This can be disabled by setting send_nl=False, which is
138 also useful when sending CTRL-C.
139
140 This function typically waits for the command to finish executing, and
141 returns the console output that it generated. This can be disabled by
142 setting wait_for_prompt=False, which is useful when invoking a long-
143 running command such as "ums".
144
145 Args:
146 cmd: The command to send.
147 wait_for_each: Boolean indicating whether to wait for U-Boot to
148 echo the command text back to its output.
149 send_nl: Boolean indicating whether to send a newline character
150 after the command string.
151 wait_for_prompt: Boolean indicating whether to wait for the
152 command prompt to be sent by U-Boot. This typically occurs
153 immediately after the command has been executed.
154
155 Returns:
156 If wait_for_prompt == False:
157 Nothing.
158 Else:
159 The output from U-Boot during command execution. In other
160 words, the text U-Boot emitted between the point it echod the
161 command string and emitted the subsequent command prompts.
Stephen Warrene8debf32016-01-26 13:41:30 -0700162 """
Stephen Warrend2015062016-01-15 11:15:24 -0700163
Stephen Warrend2015062016-01-15 11:15:24 -0700164 if self.at_prompt and \
165 self.at_prompt_logevt != self.logstream.logfile.cur_evt:
166 self.logstream.write(self.prompt, implicit=True)
167
Stephen Warrend2015062016-01-15 11:15:24 -0700168 try:
169 self.at_prompt = False
170 if send_nl:
171 cmd += '\n'
172 while cmd:
173 # Limit max outstanding data, so UART FIFOs don't overflow
174 chunk = cmd[:self.max_fifo_fill]
175 cmd = cmd[self.max_fifo_fill:]
176 self.p.send(chunk)
177 if not wait_for_echo:
178 continue
179 chunk = re.escape(chunk)
180 chunk = chunk.replace('\\\n', '[\r\n]')
Stephen Warrene4119eb2016-01-27 23:57:48 -0700181 m = self.p.expect([chunk] + self.bad_patterns)
Stephen Warrend2015062016-01-15 11:15:24 -0700182 if m != 0:
183 self.at_prompt = False
184 raise Exception('Bad pattern found on console: ' +
Stephen Warrene4119eb2016-01-27 23:57:48 -0700185 self.bad_pattern_ids[m - 1])
Stephen Warrend2015062016-01-15 11:15:24 -0700186 if not wait_for_prompt:
187 return
Stephen Warrene4119eb2016-01-27 23:57:48 -0700188 m = self.p.expect([self.prompt_escaped] + self.bad_patterns)
Stephen Warrend2015062016-01-15 11:15:24 -0700189 if m != 0:
190 self.at_prompt = False
191 raise Exception('Bad pattern found on console: ' +
Stephen Warrene4119eb2016-01-27 23:57:48 -0700192 self.bad_pattern_ids[m - 1])
Stephen Warrend2015062016-01-15 11:15:24 -0700193 self.at_prompt = True
194 self.at_prompt_logevt = self.logstream.logfile.cur_evt
195 # Only strip \r\n; space/TAB might be significant if testing
196 # indentation.
197 return self.p.before.strip('\r\n')
198 except Exception as ex:
199 self.log.error(str(ex))
200 self.cleanup_spawn()
201 raise
202
203 def ctrlc(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700204 """Send a CTRL-C character to U-Boot.
Stephen Warrend2015062016-01-15 11:15:24 -0700205
206 This is useful in order to stop execution of long-running synchronous
207 commands such as "ums".
208
209 Args:
210 None.
211
212 Returns:
213 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700214 """
Stephen Warrend2015062016-01-15 11:15:24 -0700215
Stephen Warren783cbcd2016-01-22 12:30:10 -0700216 self.log.action('Sending Ctrl-C')
Stephen Warrend2015062016-01-15 11:15:24 -0700217 self.run_command(chr(3), wait_for_echo=False, send_nl=False)
218
Stephen Warren76b46932016-01-22 12:30:12 -0700219 def wait_for(self, text):
Stephen Warrene8debf32016-01-26 13:41:30 -0700220 """Wait for a pattern to be emitted by U-Boot.
Stephen Warren76b46932016-01-22 12:30:12 -0700221
222 This is useful when a long-running command such as "dfu" is executing,
223 and it periodically emits some text that should show up at a specific
224 location in the log file.
225
226 Args:
227 text: The text to wait for; either a string (containing raw text,
228 not a regular expression) or an re object.
229
230 Returns:
231 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700232 """
Stephen Warren76b46932016-01-22 12:30:12 -0700233
234 if type(text) == type(''):
235 text = re.escape(text)
Stephen Warren0c6189b2016-01-27 23:57:49 -0700236 m = self.p.expect([text] + self.bad_patterns)
237 if m != 0:
238 raise Exception('Bad pattern found on console: ' +
239 self.bad_pattern_ids[m - 1])
Stephen Warren76b46932016-01-22 12:30:12 -0700240
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700241 def drain_console(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700242 """Read from and log the U-Boot console for a short time.
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700243
244 U-Boot's console output is only logged when the test code actively
245 waits for U-Boot to emit specific data. There are cases where tests
246 can fail without doing this. For example, if a test asks U-Boot to
247 enable USB device mode, then polls until a host-side device node
248 exists. In such a case, it is useful to log U-Boot's console output
249 in case U-Boot printed clues as to why the host-side even did not
250 occur. This function will do that.
251
252 Args:
253 None.
254
255 Returns:
256 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700257 """
Stephen Warrenc10eb9d2016-01-22 12:30:09 -0700258
259 # If we are already not connected to U-Boot, there's nothing to drain.
260 # This should only happen when a previous call to run_command() or
261 # wait_for() failed (and hence the output has already been logged), or
262 # the system is shutting down.
263 if not self.p:
264 return
265
266 orig_timeout = self.p.timeout
267 try:
268 # Drain the log for a relatively short time.
269 self.p.timeout = 1000
270 # Wait for something U-Boot will likely never send. This will
271 # cause the console output to be read and logged.
272 self.p.expect(['This should never match U-Boot output'])
273 except u_boot_spawn.Timeout:
274 pass
275 finally:
276 self.p.timeout = orig_timeout
277
Stephen Warrend2015062016-01-15 11:15:24 -0700278 def ensure_spawned(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700279 """Ensure a connection to a correctly running U-Boot instance.
Stephen Warrend2015062016-01-15 11:15:24 -0700280
281 This may require spawning a new Sandbox process or resetting target
282 hardware, as defined by the implementation sub-class.
283
284 This is an internal function and should not be called directly.
285
286 Args:
287 None.
288
289 Returns:
290 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700291 """
Stephen Warrend2015062016-01-15 11:15:24 -0700292
293 if self.p:
294 return
295 try:
Stephen Warren97255432016-02-11 11:46:12 -0700296 self.log.start_section('Starting U-Boot')
Stephen Warrend2015062016-01-15 11:15:24 -0700297 self.at_prompt = False
Stephen Warrend2015062016-01-15 11:15:24 -0700298 self.p = self.get_spawn()
299 # Real targets can take a long time to scroll large amounts of
300 # text if LCD is enabled. This value may need tweaking in the
301 # future, possibly per-test to be optimal. This works for 'help'
302 # on board 'seaboard'.
Stephen Warren89ab8412016-02-04 16:11:50 -0700303 if not self.config.gdbserver:
304 self.p.timeout = 30000
Stephen Warrend2015062016-01-15 11:15:24 -0700305 self.p.logfile_read = self.logstream
Heiko Schocherb1309a22016-02-17 18:32:51 +0100306 bcfg = self.config.buildconfig
307 config_spl = bcfg.get('config_spl', 'n') == 'y'
308 config_spl_serial_support = bcfg.get('config_spl_serial_support',
309 'n') == 'y'
310 if config_spl and config_spl_serial_support:
311 m = self.p.expect([pattern_u_boot_spl_signon] +
312 self.bad_patterns)
Stephen Warren0c6189b2016-01-27 23:57:49 -0700313 if m != 0:
314 raise Exception('Bad pattern found on console: ' +
315 self.bad_pattern_ids[m - 1])
316 m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
317 if m != 0:
318 raise Exception('Bad pattern found on console: ' +
319 self.bad_pattern_ids[m - 1])
Stephen Warrenc82ce042016-02-05 18:04:43 -0700320 self.u_boot_version_string = self.p.after
Stephen Warrend2015062016-01-15 11:15:24 -0700321 while True:
Stephen Warren0c6189b2016-01-27 23:57:49 -0700322 m = self.p.expect([self.prompt_escaped,
323 pattern_stop_autoboot_prompt] + self.bad_patterns)
324 if m == 0:
325 break
326 if m == 1:
Stephen Warrend2015062016-01-15 11:15:24 -0700327 self.p.send(chr(3)) # CTRL-C
328 continue
Stephen Warren0c6189b2016-01-27 23:57:49 -0700329 raise Exception('Bad pattern found on console: ' +
330 self.bad_pattern_ids[m - 2])
Stephen Warrend2015062016-01-15 11:15:24 -0700331 self.at_prompt = True
332 self.at_prompt_logevt = self.logstream.logfile.cur_evt
333 except Exception as ex:
334 self.log.error(str(ex))
335 self.cleanup_spawn()
336 raise
Stephen Warren97255432016-02-11 11:46:12 -0700337 finally:
338 self.log.end_section('Starting U-Boot')
Stephen Warrend2015062016-01-15 11:15:24 -0700339
340 def cleanup_spawn(self):
Stephen Warrene8debf32016-01-26 13:41:30 -0700341 """Shut down all interaction with the U-Boot instance.
Stephen Warrend2015062016-01-15 11:15:24 -0700342
343 This is used when an error is detected prior to re-establishing a
344 connection with a fresh U-Boot instance.
345
346 This is an internal function and should not be called directly.
347
348 Args:
349 None.
350
351 Returns:
352 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700353 """
Stephen Warrend2015062016-01-15 11:15:24 -0700354
355 try:
356 if self.p:
357 self.p.close()
358 except:
359 pass
360 self.p = None
361
362 def validate_version_string_in_text(self, text):
Stephen Warrene8debf32016-01-26 13:41:30 -0700363 """Assert that a command's output includes the U-Boot signon message.
Stephen Warrend2015062016-01-15 11:15:24 -0700364
365 This is primarily useful for validating the "version" command without
366 duplicating the signon text regex in a test function.
367
368 Args:
369 text: The command output text to check.
370
371 Returns:
372 Nothing. An exception is raised if the validation fails.
Stephen Warrene8debf32016-01-26 13:41:30 -0700373 """
Stephen Warrend2015062016-01-15 11:15:24 -0700374
375 assert(self.u_boot_version_string in text)
376
377 def disable_check(self, check_type):
Stephen Warrene8debf32016-01-26 13:41:30 -0700378 """Temporarily disable an error check of U-Boot's output.
Stephen Warrend2015062016-01-15 11:15:24 -0700379
380 Create a new context manager (for use with the "with" statement) which
381 temporarily disables a particular console output error check.
382
383 Args:
384 check_type: The type of error-check to disable. Valid values may
385 be found in self.disable_check_count above.
386
387 Returns:
388 A context manager object.
Stephen Warrene8debf32016-01-26 13:41:30 -0700389 """
Stephen Warrend2015062016-01-15 11:15:24 -0700390
391 return ConsoleDisableCheck(self, check_type)