xref: /rk3399_rockchip-uboot/test/py/u_boot_console_base.py (revision 76b4693928920d7c30fa935b3c46a02b637a29e1)
1d201506cSStephen Warren# Copyright (c) 2015 Stephen Warren
2d201506cSStephen Warren# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
3d201506cSStephen Warren#
4d201506cSStephen Warren# SPDX-License-Identifier: GPL-2.0
5d201506cSStephen Warren
6d201506cSStephen Warren# Common logic to interact with U-Boot via the console. This class provides
7d201506cSStephen Warren# the interface that tests use to execute U-Boot shell commands and wait for
8d201506cSStephen Warren# their results. Sub-classes exist to perform board-type-specific setup
9d201506cSStephen Warren# operations, such as spawning a sub-process for Sandbox, or attaching to the
10d201506cSStephen Warren# serial console of real hardware.
11d201506cSStephen Warren
12d201506cSStephen Warrenimport multiplexed_log
13d201506cSStephen Warrenimport os
14d201506cSStephen Warrenimport pytest
15d201506cSStephen Warrenimport re
16d201506cSStephen Warrenimport sys
17c10eb9d3SStephen Warrenimport u_boot_spawn
18d201506cSStephen Warren
19d201506cSStephen Warren# Regexes for text we expect U-Boot to send to the console.
20d201506cSStephen Warrenpattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)')
21d201506cSStephen Warrenpattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}-[^\r\n]*)')
22d201506cSStephen Warrenpattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
23d201506cSStephen Warrenpattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
24d201506cSStephen Warrenpattern_error_notification = re.compile('## Error: ')
25d201506cSStephen Warren
26d201506cSStephen Warrenclass ConsoleDisableCheck(object):
27d201506cSStephen Warren    '''Context manager (for Python's with statement) that temporarily disables
28d201506cSStephen Warren    the specified console output error check. This is useful when deliberately
29d201506cSStephen Warren    executing a command that is known to trigger one of the error checks, in
30d201506cSStephen Warren    order to test that the error condition is actually raised. This class is
31d201506cSStephen Warren    used internally by ConsoleBase::disable_check(); it is not intended for
32d201506cSStephen Warren    direct usage.'''
33d201506cSStephen Warren
34d201506cSStephen Warren    def __init__(self, console, check_type):
35d201506cSStephen Warren        self.console = console
36d201506cSStephen Warren        self.check_type = check_type
37d201506cSStephen Warren
38d201506cSStephen Warren    def __enter__(self):
39d201506cSStephen Warren        self.console.disable_check_count[self.check_type] += 1
40d201506cSStephen Warren
41d201506cSStephen Warren    def __exit__(self, extype, value, traceback):
42d201506cSStephen Warren        self.console.disable_check_count[self.check_type] -= 1
43d201506cSStephen Warren
44d201506cSStephen Warrenclass ConsoleBase(object):
45d201506cSStephen Warren    '''The interface through which test functions interact with the U-Boot
46d201506cSStephen Warren    console. This primarily involves executing shell commands, capturing their
47d201506cSStephen Warren    results, and checking for common error conditions. Some common utilities
48d201506cSStephen Warren    are also provided too.'''
49d201506cSStephen Warren
50d201506cSStephen Warren    def __init__(self, log, config, max_fifo_fill):
51d201506cSStephen Warren        '''Initialize a U-Boot console connection.
52d201506cSStephen Warren
53d201506cSStephen Warren        Can only usefully be called by sub-classes.
54d201506cSStephen Warren
55d201506cSStephen Warren        Args:
56d201506cSStephen Warren            log: A mulptiplex_log.Logfile object, to which the U-Boot output
57d201506cSStephen Warren                will be logged.
58d201506cSStephen Warren            config: A configuration data structure, as built by conftest.py.
59d201506cSStephen Warren            max_fifo_fill: The maximum number of characters to send to U-Boot
60d201506cSStephen Warren                command-line before waiting for U-Boot to echo the characters
61d201506cSStephen Warren                back. For UART-based HW without HW flow control, this value
62d201506cSStephen Warren                should be set less than the UART RX FIFO size to avoid
63d201506cSStephen Warren                overflow, assuming that U-Boot can't keep up with full-rate
64d201506cSStephen Warren                traffic at the baud rate.
65d201506cSStephen Warren
66d201506cSStephen Warren        Returns:
67d201506cSStephen Warren            Nothing.
68d201506cSStephen Warren        '''
69d201506cSStephen Warren
70d201506cSStephen Warren        self.log = log
71d201506cSStephen Warren        self.config = config
72d201506cSStephen Warren        self.max_fifo_fill = max_fifo_fill
73d201506cSStephen Warren
74d201506cSStephen Warren        self.logstream = self.log.get_stream('console', sys.stdout)
75d201506cSStephen Warren
76d201506cSStephen Warren        # Array slice removes leading/trailing quotes
77d201506cSStephen Warren        self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
78d201506cSStephen Warren        self.prompt_escaped = re.escape(self.prompt)
79d201506cSStephen Warren        self.p = None
80d201506cSStephen Warren        self.disable_check_count = {
81d201506cSStephen Warren            'spl_signon': 0,
82d201506cSStephen Warren            'main_signon': 0,
83d201506cSStephen Warren            'unknown_command': 0,
84d201506cSStephen Warren            'error_notification': 0,
85d201506cSStephen Warren        }
86d201506cSStephen Warren
87d201506cSStephen Warren        self.at_prompt = False
88d201506cSStephen Warren        self.at_prompt_logevt = None
89d201506cSStephen Warren        self.ram_base = None
90d201506cSStephen Warren
91d201506cSStephen Warren    def close(self):
92d201506cSStephen Warren        '''Terminate the connection to the U-Boot console.
93d201506cSStephen Warren
94d201506cSStephen Warren        This function is only useful once all interaction with U-Boot is
95d201506cSStephen Warren        complete. Once this function is called, data cannot be sent to or
96d201506cSStephen Warren        received from U-Boot.
97d201506cSStephen Warren
98d201506cSStephen Warren        Args:
99d201506cSStephen Warren            None.
100d201506cSStephen Warren
101d201506cSStephen Warren        Returns:
102d201506cSStephen Warren            Nothing.
103d201506cSStephen Warren        '''
104d201506cSStephen Warren
105d201506cSStephen Warren        if self.p:
106d201506cSStephen Warren            self.p.close()
107d201506cSStephen Warren        self.logstream.close()
108d201506cSStephen Warren
109d201506cSStephen Warren    def run_command(self, cmd, wait_for_echo=True, send_nl=True,
110d201506cSStephen Warren            wait_for_prompt=True):
111d201506cSStephen Warren        '''Execute a command via the U-Boot console.
112d201506cSStephen Warren
113d201506cSStephen Warren        The command is always sent to U-Boot.
114d201506cSStephen Warren
115d201506cSStephen Warren        U-Boot echoes any command back to its output, and this function
116d201506cSStephen Warren        typically waits for that to occur. The wait can be disabled by setting
117d201506cSStephen Warren        wait_for_echo=False, which is useful e.g. when sending CTRL-C to
118d201506cSStephen Warren        interrupt a long-running command such as "ums".
119d201506cSStephen Warren
120d201506cSStephen Warren        Command execution is typically triggered by sending a newline
121d201506cSStephen Warren        character. This can be disabled by setting send_nl=False, which is
122d201506cSStephen Warren        also useful when sending CTRL-C.
123d201506cSStephen Warren
124d201506cSStephen Warren        This function typically waits for the command to finish executing, and
125d201506cSStephen Warren        returns the console output that it generated. This can be disabled by
126d201506cSStephen Warren        setting wait_for_prompt=False, which is useful when invoking a long-
127d201506cSStephen Warren        running command such as "ums".
128d201506cSStephen Warren
129d201506cSStephen Warren        Args:
130d201506cSStephen Warren            cmd: The command to send.
131d201506cSStephen Warren            wait_for_each: Boolean indicating whether to wait for U-Boot to
132d201506cSStephen Warren                echo the command text back to its output.
133d201506cSStephen Warren            send_nl: Boolean indicating whether to send a newline character
134d201506cSStephen Warren                after the command string.
135d201506cSStephen Warren            wait_for_prompt: Boolean indicating whether to wait for the
136d201506cSStephen Warren                command prompt to be sent by U-Boot. This typically occurs
137d201506cSStephen Warren                immediately after the command has been executed.
138d201506cSStephen Warren
139d201506cSStephen Warren        Returns:
140d201506cSStephen Warren            If wait_for_prompt == False:
141d201506cSStephen Warren                Nothing.
142d201506cSStephen Warren            Else:
143d201506cSStephen Warren                The output from U-Boot during command execution. In other
144d201506cSStephen Warren                words, the text U-Boot emitted between the point it echod the
145d201506cSStephen Warren                command string and emitted the subsequent command prompts.
146d201506cSStephen Warren        '''
147d201506cSStephen Warren
148d201506cSStephen Warren        if self.at_prompt and \
149d201506cSStephen Warren                self.at_prompt_logevt != self.logstream.logfile.cur_evt:
150d201506cSStephen Warren            self.logstream.write(self.prompt, implicit=True)
151d201506cSStephen Warren
152d201506cSStephen Warren        bad_patterns = []
153d201506cSStephen Warren        bad_pattern_ids = []
154d201506cSStephen Warren        if (self.disable_check_count['spl_signon'] == 0 and
155d201506cSStephen Warren                self.u_boot_spl_signon):
156d201506cSStephen Warren            bad_patterns.append(self.u_boot_spl_signon_escaped)
157d201506cSStephen Warren            bad_pattern_ids.append('SPL signon')
158d201506cSStephen Warren        if self.disable_check_count['main_signon'] == 0:
159d201506cSStephen Warren            bad_patterns.append(self.u_boot_main_signon_escaped)
160d201506cSStephen Warren            bad_pattern_ids.append('U-Boot main signon')
161d201506cSStephen Warren        if self.disable_check_count['unknown_command'] == 0:
162d201506cSStephen Warren            bad_patterns.append(pattern_unknown_command)
163d201506cSStephen Warren            bad_pattern_ids.append('Unknown command')
164d201506cSStephen Warren        if self.disable_check_count['error_notification'] == 0:
165d201506cSStephen Warren            bad_patterns.append(pattern_error_notification)
166d201506cSStephen Warren            bad_pattern_ids.append('Error notification')
167d201506cSStephen Warren        try:
168d201506cSStephen Warren            self.at_prompt = False
169d201506cSStephen Warren            if send_nl:
170d201506cSStephen Warren                cmd += '\n'
171d201506cSStephen Warren            while cmd:
172d201506cSStephen Warren                # Limit max outstanding data, so UART FIFOs don't overflow
173d201506cSStephen Warren                chunk = cmd[:self.max_fifo_fill]
174d201506cSStephen Warren                cmd = cmd[self.max_fifo_fill:]
175d201506cSStephen Warren                self.p.send(chunk)
176d201506cSStephen Warren                if not wait_for_echo:
177d201506cSStephen Warren                    continue
178d201506cSStephen Warren                chunk = re.escape(chunk)
179d201506cSStephen Warren                chunk = chunk.replace('\\\n', '[\r\n]')
180d201506cSStephen Warren                m = self.p.expect([chunk] + bad_patterns)
181d201506cSStephen Warren                if m != 0:
182d201506cSStephen Warren                    self.at_prompt = False
183d201506cSStephen Warren                    raise Exception('Bad pattern found on console: ' +
184d201506cSStephen Warren                                    bad_pattern_ids[m - 1])
185d201506cSStephen Warren            if not wait_for_prompt:
186d201506cSStephen Warren                return
187d201506cSStephen Warren            m = self.p.expect([self.prompt_escaped] + bad_patterns)
188d201506cSStephen Warren            if m != 0:
189d201506cSStephen Warren                self.at_prompt = False
190d201506cSStephen Warren                raise Exception('Bad pattern found on console: ' +
191d201506cSStephen Warren                                bad_pattern_ids[m - 1])
192d201506cSStephen Warren            self.at_prompt = True
193d201506cSStephen Warren            self.at_prompt_logevt = self.logstream.logfile.cur_evt
194d201506cSStephen Warren            # Only strip \r\n; space/TAB might be significant if testing
195d201506cSStephen Warren            # indentation.
196d201506cSStephen Warren            return self.p.before.strip('\r\n')
197d201506cSStephen Warren        except Exception as ex:
198d201506cSStephen Warren            self.log.error(str(ex))
199d201506cSStephen Warren            self.cleanup_spawn()
200d201506cSStephen Warren            raise
201d201506cSStephen Warren
202d201506cSStephen Warren    def ctrlc(self):
203d201506cSStephen Warren        '''Send a CTRL-C character to U-Boot.
204d201506cSStephen Warren
205d201506cSStephen Warren        This is useful in order to stop execution of long-running synchronous
206d201506cSStephen Warren        commands such as "ums".
207d201506cSStephen Warren
208d201506cSStephen Warren        Args:
209d201506cSStephen Warren            None.
210d201506cSStephen Warren
211d201506cSStephen Warren        Returns:
212d201506cSStephen Warren            Nothing.
213d201506cSStephen Warren        '''
214d201506cSStephen Warren
215783cbcd3SStephen Warren        self.log.action('Sending Ctrl-C')
216d201506cSStephen Warren        self.run_command(chr(3), wait_for_echo=False, send_nl=False)
217d201506cSStephen Warren
218*76b46939SStephen Warren    def wait_for(self, text):
219*76b46939SStephen Warren        '''Wait for a pattern to be emitted by U-Boot.
220*76b46939SStephen Warren
221*76b46939SStephen Warren        This is useful when a long-running command such as "dfu" is executing,
222*76b46939SStephen Warren        and it periodically emits some text that should show up at a specific
223*76b46939SStephen Warren        location in the log file.
224*76b46939SStephen Warren
225*76b46939SStephen Warren        Args:
226*76b46939SStephen Warren            text: The text to wait for; either a string (containing raw text,
227*76b46939SStephen Warren                not a regular expression) or an re object.
228*76b46939SStephen Warren
229*76b46939SStephen Warren        Returns:
230*76b46939SStephen Warren            Nothing.
231*76b46939SStephen Warren        '''
232*76b46939SStephen Warren
233*76b46939SStephen Warren        if type(text) == type(''):
234*76b46939SStephen Warren            text = re.escape(text)
235*76b46939SStephen Warren        self.p.expect([text])
236*76b46939SStephen Warren
237c10eb9d3SStephen Warren    def drain_console(self):
238c10eb9d3SStephen Warren        '''Read from and log the U-Boot console for a short time.
239c10eb9d3SStephen Warren
240c10eb9d3SStephen Warren        U-Boot's console output is only logged when the test code actively
241c10eb9d3SStephen Warren        waits for U-Boot to emit specific data. There are cases where tests
242c10eb9d3SStephen Warren        can fail without doing this. For example, if a test asks U-Boot to
243c10eb9d3SStephen Warren        enable USB device mode, then polls until a host-side device node
244c10eb9d3SStephen Warren        exists. In such a case, it is useful to log U-Boot's console output
245c10eb9d3SStephen Warren        in case U-Boot printed clues as to why the host-side even did not
246c10eb9d3SStephen Warren        occur. This function will do that.
247c10eb9d3SStephen Warren
248c10eb9d3SStephen Warren        Args:
249c10eb9d3SStephen Warren            None.
250c10eb9d3SStephen Warren
251c10eb9d3SStephen Warren        Returns:
252c10eb9d3SStephen Warren            Nothing.
253c10eb9d3SStephen Warren        '''
254c10eb9d3SStephen Warren
255c10eb9d3SStephen Warren        # If we are already not connected to U-Boot, there's nothing to drain.
256c10eb9d3SStephen Warren        # This should only happen when a previous call to run_command() or
257c10eb9d3SStephen Warren        # wait_for() failed (and hence the output has already been logged), or
258c10eb9d3SStephen Warren        # the system is shutting down.
259c10eb9d3SStephen Warren        if not self.p:
260c10eb9d3SStephen Warren            return
261c10eb9d3SStephen Warren
262c10eb9d3SStephen Warren        orig_timeout = self.p.timeout
263c10eb9d3SStephen Warren        try:
264c10eb9d3SStephen Warren            # Drain the log for a relatively short time.
265c10eb9d3SStephen Warren            self.p.timeout = 1000
266c10eb9d3SStephen Warren            # Wait for something U-Boot will likely never send. This will
267c10eb9d3SStephen Warren            # cause the console output to be read and logged.
268c10eb9d3SStephen Warren            self.p.expect(['This should never match U-Boot output'])
269c10eb9d3SStephen Warren        except u_boot_spawn.Timeout:
270c10eb9d3SStephen Warren            pass
271c10eb9d3SStephen Warren        finally:
272c10eb9d3SStephen Warren            self.p.timeout = orig_timeout
273c10eb9d3SStephen Warren
274d201506cSStephen Warren    def ensure_spawned(self):
275d201506cSStephen Warren        '''Ensure a connection to a correctly running U-Boot instance.
276d201506cSStephen Warren
277d201506cSStephen Warren        This may require spawning a new Sandbox process or resetting target
278d201506cSStephen Warren        hardware, as defined by the implementation sub-class.
279d201506cSStephen Warren
280d201506cSStephen Warren        This is an internal function and should not be called directly.
281d201506cSStephen Warren
282d201506cSStephen Warren        Args:
283d201506cSStephen Warren            None.
284d201506cSStephen Warren
285d201506cSStephen Warren        Returns:
286d201506cSStephen Warren            Nothing.
287d201506cSStephen Warren        '''
288d201506cSStephen Warren
289d201506cSStephen Warren        if self.p:
290d201506cSStephen Warren            return
291d201506cSStephen Warren        try:
292d201506cSStephen Warren            self.at_prompt = False
293d201506cSStephen Warren            self.log.action('Starting U-Boot')
294d201506cSStephen Warren            self.p = self.get_spawn()
295d201506cSStephen Warren            # Real targets can take a long time to scroll large amounts of
296d201506cSStephen Warren            # text if LCD is enabled. This value may need tweaking in the
297d201506cSStephen Warren            # future, possibly per-test to be optimal. This works for 'help'
298d201506cSStephen Warren            # on board 'seaboard'.
299d201506cSStephen Warren            self.p.timeout = 30000
300d201506cSStephen Warren            self.p.logfile_read = self.logstream
301d201506cSStephen Warren            if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
302d201506cSStephen Warren                self.p.expect([pattern_u_boot_spl_signon])
303d201506cSStephen Warren                self.u_boot_spl_signon = self.p.after
304d201506cSStephen Warren                self.u_boot_spl_signon_escaped = re.escape(self.p.after)
305d201506cSStephen Warren            else:
306d201506cSStephen Warren                self.u_boot_spl_signon = None
307d201506cSStephen Warren            self.p.expect([pattern_u_boot_main_signon])
308d201506cSStephen Warren            self.u_boot_main_signon = self.p.after
309d201506cSStephen Warren            self.u_boot_main_signon_escaped = re.escape(self.p.after)
310d201506cSStephen Warren            build_idx = self.u_boot_main_signon.find(', Build:')
311d201506cSStephen Warren            if build_idx == -1:
312d201506cSStephen Warren                self.u_boot_version_string = self.u_boot_main_signon
313d201506cSStephen Warren            else:
314d201506cSStephen Warren                self.u_boot_version_string = self.u_boot_main_signon[:build_idx]
315d201506cSStephen Warren            while True:
316d201506cSStephen Warren                match = self.p.expect([self.prompt_escaped,
317d201506cSStephen Warren                                       pattern_stop_autoboot_prompt])
318d201506cSStephen Warren                if match == 1:
319d201506cSStephen Warren                    self.p.send(chr(3)) # CTRL-C
320d201506cSStephen Warren                    continue
321d201506cSStephen Warren                break
322d201506cSStephen Warren            self.at_prompt = True
323d201506cSStephen Warren            self.at_prompt_logevt = self.logstream.logfile.cur_evt
324d201506cSStephen Warren        except Exception as ex:
325d201506cSStephen Warren            self.log.error(str(ex))
326d201506cSStephen Warren            self.cleanup_spawn()
327d201506cSStephen Warren            raise
328d201506cSStephen Warren
329d201506cSStephen Warren    def cleanup_spawn(self):
330d201506cSStephen Warren        '''Shut down all interaction with the U-Boot instance.
331d201506cSStephen Warren
332d201506cSStephen Warren        This is used when an error is detected prior to re-establishing a
333d201506cSStephen Warren        connection with a fresh U-Boot instance.
334d201506cSStephen Warren
335d201506cSStephen Warren        This is an internal function and should not be called directly.
336d201506cSStephen Warren
337d201506cSStephen Warren        Args:
338d201506cSStephen Warren            None.
339d201506cSStephen Warren
340d201506cSStephen Warren        Returns:
341d201506cSStephen Warren            Nothing.
342d201506cSStephen Warren        '''
343d201506cSStephen Warren
344d201506cSStephen Warren        try:
345d201506cSStephen Warren            if self.p:
346d201506cSStephen Warren                self.p.close()
347d201506cSStephen Warren        except:
348d201506cSStephen Warren            pass
349d201506cSStephen Warren        self.p = None
350d201506cSStephen Warren
351d201506cSStephen Warren    def validate_version_string_in_text(self, text):
352d201506cSStephen Warren        '''Assert that a command's output includes the U-Boot signon message.
353d201506cSStephen Warren
354d201506cSStephen Warren        This is primarily useful for validating the "version" command without
355d201506cSStephen Warren        duplicating the signon text regex in a test function.
356d201506cSStephen Warren
357d201506cSStephen Warren        Args:
358d201506cSStephen Warren            text: The command output text to check.
359d201506cSStephen Warren
360d201506cSStephen Warren        Returns:
361d201506cSStephen Warren            Nothing. An exception is raised if the validation fails.
362d201506cSStephen Warren        '''
363d201506cSStephen Warren
364d201506cSStephen Warren        assert(self.u_boot_version_string in text)
365d201506cSStephen Warren
366d201506cSStephen Warren    def disable_check(self, check_type):
367d201506cSStephen Warren        '''Temporarily disable an error check of U-Boot's output.
368d201506cSStephen Warren
369d201506cSStephen Warren        Create a new context manager (for use with the "with" statement) which
370d201506cSStephen Warren        temporarily disables a particular console output error check.
371d201506cSStephen Warren
372d201506cSStephen Warren        Args:
373d201506cSStephen Warren            check_type: The type of error-check to disable. Valid values may
374d201506cSStephen Warren            be found in self.disable_check_count above.
375d201506cSStephen Warren
376d201506cSStephen Warren        Returns:
377d201506cSStephen Warren            A context manager object.
378d201506cSStephen Warren        '''
379d201506cSStephen Warren
380d201506cSStephen Warren        return ConsoleDisableCheck(self, check_type)
381d201506cSStephen Warren
382d201506cSStephen Warren    def find_ram_base(self):
383d201506cSStephen Warren        '''Find the running U-Boot's RAM location.
384d201506cSStephen Warren
385d201506cSStephen Warren        Probe the running U-Boot to determine the address of the first bank
386d201506cSStephen Warren        of RAM. This is useful for tests that test reading/writing RAM, or
387d201506cSStephen Warren        load/save files that aren't associated with some standard address
388d201506cSStephen Warren        typically represented in an environment variable such as
389d201506cSStephen Warren        ${kernel_addr_r}. The value is cached so that it only needs to be
390d201506cSStephen Warren        actively read once.
391d201506cSStephen Warren
392d201506cSStephen Warren        Args:
393d201506cSStephen Warren            None.
394d201506cSStephen Warren
395d201506cSStephen Warren        Returns:
396d201506cSStephen Warren            The address of U-Boot's first RAM bank, as an integer.
397d201506cSStephen Warren        '''
398d201506cSStephen Warren
399d201506cSStephen Warren        if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
400d201506cSStephen Warren            pytest.skip('bdinfo command not supported')
401d201506cSStephen Warren        if self.ram_base == -1:
402d201506cSStephen Warren            pytest.skip('Previously failed to find RAM bank start')
403d201506cSStephen Warren        if self.ram_base is not None:
404d201506cSStephen Warren            return self.ram_base
405d201506cSStephen Warren
406d201506cSStephen Warren        with self.log.section('find_ram_base'):
407d201506cSStephen Warren            response = self.run_command('bdinfo')
408d201506cSStephen Warren            for l in response.split('\n'):
409d201506cSStephen Warren                if '-> start' in l:
410d201506cSStephen Warren                    self.ram_base = int(l.split('=')[1].strip(), 16)
411d201506cSStephen Warren                    break
412d201506cSStephen Warren            if self.ram_base is None:
413d201506cSStephen Warren                self.ram_base = -1
414d201506cSStephen Warren                raise Exception('Failed to find RAM bank start in `bdinfo`')
415d201506cSStephen Warren
416d201506cSStephen Warren        return self.ram_base
417