2018-05-06 21:58:06 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0
|
2016-01-15 18:15:24 +00:00
|
|
|
# Copyright (c) 2015 Stephen Warren
|
|
|
|
# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
|
|
|
|
|
|
|
# Common logic to interact with U-Boot via the console. This class provides
|
|
|
|
# the interface that tests use to execute U-Boot shell commands and wait for
|
|
|
|
# their results. Sub-classes exist to perform board-type-specific setup
|
|
|
|
# operations, such as spawning a sub-process for Sandbox, or attaching to the
|
|
|
|
# serial console of real hardware.
|
|
|
|
|
|
|
|
import multiplexed_log
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
import re
|
|
|
|
import sys
|
2016-01-22 19:30:09 +00:00
|
|
|
import u_boot_spawn
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
# Regexes for text we expect U-Boot to send to the console.
|
2018-12-05 10:29:54 +00:00
|
|
|
pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
|
2020-07-21 15:12:05 +00:00
|
|
|
pattern_u_boot_spl2_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
|
2016-02-06 01:04:43 +00:00
|
|
|
pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
|
2016-01-15 18:15:24 +00:00
|
|
|
pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
|
|
|
|
pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
|
|
|
|
pattern_error_notification = re.compile('## Error: ')
|
2016-01-28 06:57:50 +00:00
|
|
|
pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
|
2016-01-15 18:15:24 +00:00
|
|
|
|
2016-01-28 06:57:48 +00:00
|
|
|
PAT_ID = 0
|
|
|
|
PAT_RE = 1
|
|
|
|
|
|
|
|
bad_pattern_defs = (
|
|
|
|
('spl_signon', pattern_u_boot_spl_signon),
|
2020-07-21 15:12:05 +00:00
|
|
|
('spl2_signon', pattern_u_boot_spl2_signon),
|
2016-01-28 06:57:48 +00:00
|
|
|
('main_signon', pattern_u_boot_main_signon),
|
|
|
|
('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
|
|
|
|
('unknown_command', pattern_unknown_command),
|
|
|
|
('error_notification', pattern_error_notification),
|
2016-01-28 06:57:50 +00:00
|
|
|
('error_please_reset', pattern_error_please_reset),
|
2016-01-28 06:57:48 +00:00
|
|
|
)
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
class ConsoleDisableCheck(object):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Context manager (for Python's with statement) that temporarily disables
|
2016-01-15 18:15:24 +00:00
|
|
|
the specified console output error check. This is useful when deliberately
|
|
|
|
executing a command that is known to trigger one of the error checks, in
|
|
|
|
order to test that the error condition is actually raised. This class is
|
|
|
|
used internally by ConsoleBase::disable_check(); it is not intended for
|
2016-01-26 20:41:30 +00:00
|
|
|
direct usage."""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
def __init__(self, console, check_type):
|
|
|
|
self.console = console
|
|
|
|
self.check_type = check_type
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
self.console.disable_check_count[self.check_type] += 1
|
2016-01-28 06:57:48 +00:00
|
|
|
self.console.eval_bad_patterns()
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
def __exit__(self, extype, value, traceback):
|
|
|
|
self.console.disable_check_count[self.check_type] -= 1
|
2016-01-28 06:57:48 +00:00
|
|
|
self.console.eval_bad_patterns()
|
2016-01-15 18:15:24 +00:00
|
|
|
|
2016-05-19 05:57:41 +00:00
|
|
|
class ConsoleSetupTimeout(object):
|
|
|
|
"""Context manager (for Python's with statement) that temporarily sets up
|
|
|
|
timeout for specific command. This is useful when execution time is greater
|
|
|
|
then default 30s."""
|
|
|
|
|
|
|
|
def __init__(self, console, timeout):
|
|
|
|
self.p = console.p
|
|
|
|
self.orig_timeout = self.p.timeout
|
|
|
|
self.p.timeout = timeout
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __exit__(self, extype, value, traceback):
|
|
|
|
self.p.timeout = self.orig_timeout
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
class ConsoleBase(object):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""The interface through which test functions interact with the U-Boot
|
2016-01-15 18:15:24 +00:00
|
|
|
console. This primarily involves executing shell commands, capturing their
|
|
|
|
results, and checking for common error conditions. Some common utilities
|
2016-01-26 20:41:30 +00:00
|
|
|
are also provided too."""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
def __init__(self, log, config, max_fifo_fill):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Initialize a U-Boot console connection.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
Can only usefully be called by sub-classes.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
log: A mulptiplex_log.Logfile object, to which the U-Boot output
|
|
|
|
will be logged.
|
|
|
|
config: A configuration data structure, as built by conftest.py.
|
|
|
|
max_fifo_fill: The maximum number of characters to send to U-Boot
|
|
|
|
command-line before waiting for U-Boot to echo the characters
|
|
|
|
back. For UART-based HW without HW flow control, this value
|
|
|
|
should be set less than the UART RX FIFO size to avoid
|
|
|
|
overflow, assuming that U-Boot can't keep up with full-rate
|
|
|
|
traffic at the baud rate.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
self.log = log
|
|
|
|
self.config = config
|
|
|
|
self.max_fifo_fill = max_fifo_fill
|
|
|
|
|
|
|
|
self.logstream = self.log.get_stream('console', sys.stdout)
|
|
|
|
|
|
|
|
# Array slice removes leading/trailing quotes
|
|
|
|
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
|
2016-08-17 01:58:59 +00:00
|
|
|
self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
|
2016-01-15 18:15:24 +00:00
|
|
|
self.p = None
|
2016-01-28 06:57:48 +00:00
|
|
|
self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
|
|
|
|
self.eval_bad_patterns()
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
self.at_prompt = False
|
|
|
|
self.at_prompt_logevt = None
|
|
|
|
|
2022-02-11 20:23:23 +00:00
|
|
|
def get_spawn(self):
|
|
|
|
# This is not called, ssubclass must define this.
|
|
|
|
# Return a value to avoid:
|
|
|
|
# u_boot_console_base.py:348:12: E1128: Assigning result of a function
|
|
|
|
# call, where the function returns None (assignment-from-none)
|
|
|
|
return u_boot_spawn.Spawn([])
|
|
|
|
|
|
|
|
|
2016-01-28 06:57:48 +00:00
|
|
|
def eval_bad_patterns(self):
|
|
|
|
self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
|
|
|
|
if self.disable_check_count[pat[PAT_ID]] == 0]
|
|
|
|
self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
|
|
|
|
if self.disable_check_count[pat[PAT_ID]] == 0]
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
def close(self):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Terminate the connection to the U-Boot console.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
This function is only useful once all interaction with U-Boot is
|
|
|
|
complete. Once this function is called, data cannot be sent to or
|
|
|
|
received from U-Boot.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
None.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
if self.p:
|
|
|
|
self.p.close()
|
|
|
|
self.logstream.close()
|
|
|
|
|
2022-02-16 06:16:02 +00:00
|
|
|
def wait_for_boot_prompt(self, loop_num = 1):
|
2022-02-16 06:15:52 +00:00
|
|
|
"""Wait for the boot up until command prompt. This is for internal use only.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
bcfg = self.config.buildconfig
|
|
|
|
config_spl = bcfg.get('config_spl', 'n') == 'y'
|
|
|
|
config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
|
|
|
|
env_spl_skipped = self.config.env.get('env__spl_skipped', False)
|
|
|
|
env_spl2_skipped = self.config.env.get('env__spl2_skipped', True)
|
|
|
|
|
2022-02-16 06:16:02 +00:00
|
|
|
while loop_num > 0:
|
|
|
|
loop_num -= 1
|
|
|
|
if config_spl and config_spl_serial and not env_spl_skipped:
|
|
|
|
m = self.p.expect([pattern_u_boot_spl_signon] +
|
|
|
|
self.bad_patterns)
|
|
|
|
if m != 0:
|
|
|
|
raise Exception('Bad pattern found on SPL console: ' +
|
|
|
|
self.bad_pattern_ids[m - 1])
|
|
|
|
if not env_spl2_skipped:
|
|
|
|
m = self.p.expect([pattern_u_boot_spl2_signon] +
|
|
|
|
self.bad_patterns)
|
|
|
|
if m != 0:
|
|
|
|
raise Exception('Bad pattern found on SPL2 console: ' +
|
|
|
|
self.bad_pattern_ids[m - 1])
|
|
|
|
m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
|
2022-02-16 06:15:52 +00:00
|
|
|
if m != 0:
|
2022-02-16 06:16:02 +00:00
|
|
|
raise Exception('Bad pattern found on console: ' +
|
2022-02-16 06:15:52 +00:00
|
|
|
self.bad_pattern_ids[m - 1])
|
|
|
|
self.u_boot_version_string = self.p.after
|
|
|
|
while True:
|
|
|
|
m = self.p.expect([self.prompt_compiled,
|
|
|
|
pattern_stop_autoboot_prompt] + self.bad_patterns)
|
|
|
|
if m == 0:
|
|
|
|
break
|
|
|
|
if m == 1:
|
|
|
|
self.p.send(' ')
|
|
|
|
continue
|
|
|
|
raise Exception('Bad pattern found on console: ' +
|
|
|
|
self.bad_pattern_ids[m - 2])
|
|
|
|
|
|
|
|
except Exception as ex:
|
|
|
|
self.log.error(str(ex))
|
|
|
|
self.cleanup_spawn()
|
|
|
|
raise
|
|
|
|
finally:
|
|
|
|
self.log.timestamp()
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
def run_command(self, cmd, wait_for_echo=True, send_nl=True,
|
2022-02-16 06:15:52 +00:00
|
|
|
wait_for_prompt=True, wait_for_reboot=False):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Execute a command via the U-Boot console.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
The command is always sent to U-Boot.
|
|
|
|
|
|
|
|
U-Boot echoes any command back to its output, and this function
|
|
|
|
typically waits for that to occur. The wait can be disabled by setting
|
|
|
|
wait_for_echo=False, which is useful e.g. when sending CTRL-C to
|
|
|
|
interrupt a long-running command such as "ums".
|
|
|
|
|
|
|
|
Command execution is typically triggered by sending a newline
|
|
|
|
character. This can be disabled by setting send_nl=False, which is
|
|
|
|
also useful when sending CTRL-C.
|
|
|
|
|
|
|
|
This function typically waits for the command to finish executing, and
|
|
|
|
returns the console output that it generated. This can be disabled by
|
|
|
|
setting wait_for_prompt=False, which is useful when invoking a long-
|
|
|
|
running command such as "ums".
|
|
|
|
|
|
|
|
Args:
|
|
|
|
cmd: The command to send.
|
2017-09-14 10:27:07 +00:00
|
|
|
wait_for_echo: Boolean indicating whether to wait for U-Boot to
|
2016-01-15 18:15:24 +00:00
|
|
|
echo the command text back to its output.
|
|
|
|
send_nl: Boolean indicating whether to send a newline character
|
|
|
|
after the command string.
|
|
|
|
wait_for_prompt: Boolean indicating whether to wait for the
|
|
|
|
command prompt to be sent by U-Boot. This typically occurs
|
|
|
|
immediately after the command has been executed.
|
2022-02-16 06:15:52 +00:00
|
|
|
wait_for_reboot: Boolean indication whether to wait for the
|
|
|
|
reboot U-Boot. If this sets True, wait_for_prompt must also
|
|
|
|
be True.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
If wait_for_prompt == False:
|
|
|
|
Nothing.
|
|
|
|
Else:
|
|
|
|
The output from U-Boot during command execution. In other
|
|
|
|
words, the text U-Boot emitted between the point it echod the
|
|
|
|
command string and emitted the subsequent command prompts.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
if self.at_prompt and \
|
|
|
|
self.at_prompt_logevt != self.logstream.logfile.cur_evt:
|
|
|
|
self.logstream.write(self.prompt, implicit=True)
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.at_prompt = False
|
|
|
|
if send_nl:
|
|
|
|
cmd += '\n'
|
|
|
|
while cmd:
|
|
|
|
# Limit max outstanding data, so UART FIFOs don't overflow
|
|
|
|
chunk = cmd[:self.max_fifo_fill]
|
|
|
|
cmd = cmd[self.max_fifo_fill:]
|
|
|
|
self.p.send(chunk)
|
|
|
|
if not wait_for_echo:
|
|
|
|
continue
|
|
|
|
chunk = re.escape(chunk)
|
|
|
|
chunk = chunk.replace('\\\n', '[\r\n]')
|
2016-01-28 06:57:48 +00:00
|
|
|
m = self.p.expect([chunk] + self.bad_patterns)
|
2016-01-15 18:15:24 +00:00
|
|
|
if m != 0:
|
|
|
|
self.at_prompt = False
|
|
|
|
raise Exception('Bad pattern found on console: ' +
|
2016-01-28 06:57:48 +00:00
|
|
|
self.bad_pattern_ids[m - 1])
|
2016-01-15 18:15:24 +00:00
|
|
|
if not wait_for_prompt:
|
|
|
|
return
|
2022-02-16 06:15:52 +00:00
|
|
|
if wait_for_reboot:
|
|
|
|
self.wait_for_boot_prompt()
|
|
|
|
else:
|
|
|
|
m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
|
|
|
|
if m != 0:
|
|
|
|
self.at_prompt = False
|
|
|
|
raise Exception('Bad pattern found on console: ' +
|
|
|
|
self.bad_pattern_ids[m - 1])
|
2016-01-15 18:15:24 +00:00
|
|
|
self.at_prompt = True
|
|
|
|
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
|
|
|
# Only strip \r\n; space/TAB might be significant if testing
|
|
|
|
# indentation.
|
|
|
|
return self.p.before.strip('\r\n')
|
|
|
|
except Exception as ex:
|
|
|
|
self.log.error(str(ex))
|
|
|
|
self.cleanup_spawn()
|
|
|
|
raise
|
2017-10-27 17:04:08 +00:00
|
|
|
finally:
|
|
|
|
self.log.timestamp()
|
2016-01-15 18:15:24 +00:00
|
|
|
|
2016-07-03 15:40:42 +00:00
|
|
|
def run_command_list(self, cmds):
|
|
|
|
"""Run a list of commands.
|
|
|
|
|
|
|
|
This is a helper function to call run_command() with default arguments
|
|
|
|
for each command in a list.
|
|
|
|
|
|
|
|
Args:
|
2016-07-31 23:35:04 +00:00
|
|
|
cmd: List of commands (each a string).
|
2016-07-03 15:40:42 +00:00
|
|
|
Returns:
|
2016-07-31 23:35:09 +00:00
|
|
|
A list of output strings from each command, one element for each
|
|
|
|
command.
|
2016-07-03 15:40:42 +00:00
|
|
|
"""
|
2016-07-31 23:35:09 +00:00
|
|
|
output = []
|
2016-07-03 15:40:42 +00:00
|
|
|
for cmd in cmds:
|
2016-07-31 23:35:09 +00:00
|
|
|
output.append(self.run_command(cmd))
|
2016-07-03 15:40:42 +00:00
|
|
|
return output
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
def ctrlc(self):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Send a CTRL-C character to U-Boot.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
This is useful in order to stop execution of long-running synchronous
|
|
|
|
commands such as "ums".
|
|
|
|
|
|
|
|
Args:
|
|
|
|
None.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
2016-01-22 19:30:10 +00:00
|
|
|
self.log.action('Sending Ctrl-C')
|
2016-01-15 18:15:24 +00:00
|
|
|
self.run_command(chr(3), wait_for_echo=False, send_nl=False)
|
|
|
|
|
2016-01-22 19:30:12 +00:00
|
|
|
def wait_for(self, text):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Wait for a pattern to be emitted by U-Boot.
|
2016-01-22 19:30:12 +00:00
|
|
|
|
|
|
|
This is useful when a long-running command such as "dfu" is executing,
|
|
|
|
and it periodically emits some text that should show up at a specific
|
|
|
|
location in the log file.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
text: The text to wait for; either a string (containing raw text,
|
|
|
|
not a regular expression) or an re object.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-22 19:30:12 +00:00
|
|
|
|
|
|
|
if type(text) == type(''):
|
|
|
|
text = re.escape(text)
|
2016-01-28 06:57:49 +00:00
|
|
|
m = self.p.expect([text] + self.bad_patterns)
|
|
|
|
if m != 0:
|
|
|
|
raise Exception('Bad pattern found on console: ' +
|
|
|
|
self.bad_pattern_ids[m - 1])
|
2016-01-22 19:30:12 +00:00
|
|
|
|
2016-01-22 19:30:09 +00:00
|
|
|
def drain_console(self):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Read from and log the U-Boot console for a short time.
|
2016-01-22 19:30:09 +00:00
|
|
|
|
|
|
|
U-Boot's console output is only logged when the test code actively
|
|
|
|
waits for U-Boot to emit specific data. There are cases where tests
|
|
|
|
can fail without doing this. For example, if a test asks U-Boot to
|
|
|
|
enable USB device mode, then polls until a host-side device node
|
|
|
|
exists. In such a case, it is useful to log U-Boot's console output
|
|
|
|
in case U-Boot printed clues as to why the host-side even did not
|
|
|
|
occur. This function will do that.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
None.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-22 19:30:09 +00:00
|
|
|
|
|
|
|
# If we are already not connected to U-Boot, there's nothing to drain.
|
|
|
|
# This should only happen when a previous call to run_command() or
|
|
|
|
# wait_for() failed (and hence the output has already been logged), or
|
|
|
|
# the system is shutting down.
|
|
|
|
if not self.p:
|
|
|
|
return
|
|
|
|
|
|
|
|
orig_timeout = self.p.timeout
|
|
|
|
try:
|
|
|
|
# Drain the log for a relatively short time.
|
|
|
|
self.p.timeout = 1000
|
|
|
|
# Wait for something U-Boot will likely never send. This will
|
|
|
|
# cause the console output to be read and logged.
|
|
|
|
self.p.expect(['This should never match U-Boot output'])
|
2018-09-20 22:55:03 +00:00
|
|
|
except:
|
|
|
|
# We expect a timeout, since U-Boot won't print what we waited
|
|
|
|
# for. Squash it when it happens.
|
|
|
|
#
|
|
|
|
# Squash any other exception too. This function is only used to
|
|
|
|
# drain (and log) the U-Boot console output after a failed test.
|
|
|
|
# The U-Boot process will be restarted, or target board reset, once
|
|
|
|
# this function returns. So, we don't care about detecting any
|
|
|
|
# additional errors, so they're squashed so that the rest of the
|
|
|
|
# post-test-failure cleanup code can continue operation, and
|
|
|
|
# correctly terminate any log sections, etc.
|
2016-01-22 19:30:09 +00:00
|
|
|
pass
|
|
|
|
finally:
|
|
|
|
self.p.timeout = orig_timeout
|
|
|
|
|
2022-02-16 06:16:02 +00:00
|
|
|
def ensure_spawned(self, expect_reset=False):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Ensure a connection to a correctly running U-Boot instance.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
This may require spawning a new Sandbox process or resetting target
|
|
|
|
hardware, as defined by the implementation sub-class.
|
|
|
|
|
|
|
|
This is an internal function and should not be called directly.
|
|
|
|
|
|
|
|
Args:
|
2022-02-16 06:16:02 +00:00
|
|
|
expect_reset: Boolean indication whether this boot is expected
|
|
|
|
to be reset while the 1st boot process after main boot before
|
|
|
|
prompt. False by default.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
if self.p:
|
|
|
|
return
|
|
|
|
try:
|
2016-02-11 18:46:12 +00:00
|
|
|
self.log.start_section('Starting U-Boot')
|
2016-01-15 18:15:24 +00:00
|
|
|
self.at_prompt = False
|
|
|
|
self.p = self.get_spawn()
|
|
|
|
# Real targets can take a long time to scroll large amounts of
|
|
|
|
# text if LCD is enabled. This value may need tweaking in the
|
|
|
|
# future, possibly per-test to be optimal. This works for 'help'
|
|
|
|
# on board 'seaboard'.
|
test/py: support running sandbox under gdbserver
Implement command--line option --gdbserver COMM, which does two things:
a) Run the sandbox process under gdbserver, using COMM as gdbserver's
communication channel.
b) Disables all timeouts, so that if U-Boot is halted under the debugger,
tests don't fail. If the user gives up in the middle of a debugging
session, they can simply CTRL-C the test script to abort it.
This allows easy debugging of test failures without having to manually
re-create the failure conditions. Usage is:
Window 1:
./test/py/test.py --bd sandbox --gdbserver localhost:1234
Window 2:
gdb ./build-sandbox/u-boot -ex 'target remote localhost:1234'
When using this option, it likely makes sense to use pytest's -k option
to limit the set of tests that are executed.
Simply running U-Boot directly under gdb (rather than gdbserver) was
also considered. However, this was rejected because:
a) gdb's output would then be processed by the test script, and likely
confuse it causing false failures.
b) pytest by default hides stdout from tests, which would prevent the
user from interacting with gdb.
While gdb can be told to redirect the debugee's stdio to a separate
PTY, this would appear to leave gdb's stdio directed at the test
scripts and the debugee's stdio directed elsewhere, which is the
opposite of the desired effect. Perhaps some complicated PTY muxing
and process hierarchy could invert this. However, the current scheme
is simple to implement and use, so it doesn't seem worth complicating
matters.
c) Using gdbserver allows arbitrary debuggers to be used, even those with
a GUI. If the test scripts invoked the debugger themselves, they'd have
to know how to execute arbitary applications. While the user could hide
this all in a wrapper script, this feels like extra complication.
An interesting future idea might be a --gdb-screen option, which could
spawn both U-Boot and gdb separately, and spawn the screen into a newly
created window under screen. Similar options could be envisaged for
creating a new xterm/... too.
--gdbserver currently only supports sandbox, and not real hardware.
That's primarily because the test hooks are responsible for all aspects of
hardware control, so there's nothing for the test scripts themselves can
do to enable gdbserver on real hardware. We might consider introducing a
separate --disable-timeouts option to support use of debuggers on real
hardware, and having --gdbserver imply that option.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
2016-02-04 23:11:50 +00:00
|
|
|
if not self.config.gdbserver:
|
|
|
|
self.p.timeout = 30000
|
2016-01-15 18:15:24 +00:00
|
|
|
self.p.logfile_read = self.logstream
|
2022-02-16 06:16:02 +00:00
|
|
|
if expect_reset:
|
|
|
|
loop_num = 2
|
|
|
|
else:
|
|
|
|
loop_num = 1
|
|
|
|
self.wait_for_boot_prompt(loop_num = loop_num)
|
2016-01-15 18:15:24 +00:00
|
|
|
self.at_prompt = True
|
|
|
|
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
|
|
|
except Exception as ex:
|
|
|
|
self.log.error(str(ex))
|
|
|
|
self.cleanup_spawn()
|
|
|
|
raise
|
2016-02-11 18:46:12 +00:00
|
|
|
finally:
|
2017-10-27 17:04:08 +00:00
|
|
|
self.log.timestamp()
|
2016-02-11 18:46:12 +00:00
|
|
|
self.log.end_section('Starting U-Boot')
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
def cleanup_spawn(self):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Shut down all interaction with the U-Boot instance.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
This is used when an error is detected prior to re-establishing a
|
|
|
|
connection with a fresh U-Boot instance.
|
|
|
|
|
|
|
|
This is an internal function and should not be called directly.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
None.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
if self.p:
|
|
|
|
self.p.close()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
self.p = None
|
|
|
|
|
2022-02-16 06:16:02 +00:00
|
|
|
def restart_uboot(self, expect_reset=False):
|
2016-07-31 23:35:08 +00:00
|
|
|
"""Shut down and restart U-Boot."""
|
|
|
|
self.cleanup_spawn()
|
2022-02-16 06:16:02 +00:00
|
|
|
self.ensure_spawned(expect_reset)
|
2016-07-31 23:35:08 +00:00
|
|
|
|
2016-07-04 17:58:39 +00:00
|
|
|
def get_spawn_output(self):
|
|
|
|
"""Return the start-up output from U-Boot
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The output produced by ensure_spawed(), as a string.
|
|
|
|
"""
|
|
|
|
if self.p:
|
|
|
|
return self.p.get_expect_output()
|
|
|
|
return None
|
|
|
|
|
2016-01-15 18:15:24 +00:00
|
|
|
def validate_version_string_in_text(self, text):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Assert that a command's output includes the U-Boot signon message.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
This is primarily useful for validating the "version" command without
|
|
|
|
duplicating the signon text regex in a test function.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
text: The command output text to check.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing. An exception is raised if the validation fails.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
assert(self.u_boot_version_string in text)
|
|
|
|
|
|
|
|
def disable_check(self, check_type):
|
2016-01-26 20:41:30 +00:00
|
|
|
"""Temporarily disable an error check of U-Boot's output.
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
Create a new context manager (for use with the "with" statement) which
|
|
|
|
temporarily disables a particular console output error check.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
check_type: The type of error-check to disable. Valid values may
|
|
|
|
be found in self.disable_check_count above.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A context manager object.
|
2016-01-26 20:41:30 +00:00
|
|
|
"""
|
2016-01-15 18:15:24 +00:00
|
|
|
|
|
|
|
return ConsoleDisableCheck(self, check_type)
|
2016-05-19 05:57:41 +00:00
|
|
|
|
|
|
|
def temporary_timeout(self, timeout):
|
|
|
|
"""Temporarily set up different timeout for commands.
|
|
|
|
|
|
|
|
Create a new context manager (for use with the "with" statement) which
|
|
|
|
temporarily change timeout.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
timeout: Time in milliseconds.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A context manager object.
|
|
|
|
"""
|
|
|
|
|
|
|
|
return ConsoleSetupTimeout(self, timeout)
|