Skip to content

bootloader: detect boot #1191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1789,12 +1789,16 @@ Arguments:
- init_commands (tuple): optional, tuple of commands to execute after matching the
prompt
- password_prompt (str, default="enter Password: "): regex to match the U-Boot password prompt
- boot_expression (regex, default="[\n]U-Boot 20\\d+"): regex to match the U-Boot start string
- bootstring (str): optional, regex to match on Linux Kernel boot
- boot_command (str, default="run bootcmd"): boot command for booting target
- boot_commands (dict, default={}): boot commands by name for LinuxBootProtocol boot command
- login_timeout (int, default=30): timeout for login prompt detection in seconds
- boot_timeout (int, default=30): timeout for initial Linux Kernel version detection

Attribute:
- boot_detected (bool, defaul=False): boot_expression seen after driver activation

SmallUBootDriver
~~~~~~~~~~~~~~~~
A :any:`SmallUBootDriver` interfaces with stripped-down *U-Boot* variants that
Expand Down Expand Up @@ -1872,9 +1876,14 @@ Arguments:
- interrupt (str, default="\\n"): string to interrupt autoboot (use "\\x03" for CTRL-C)
- bootstring (regex, default="Linux version \\d"): regex that indicating that the Linux Kernel is
booting
- boot_expression (regex, default="[\n]barebox 20\d+"): string that indicates that Barebox is
starting
- password (str): optional, password to use for access to the shell
- login_timeout (int, default=60): timeout for access to the shell

Attribute:
- boot_detected (bool, defaul=False): boot_expression seen after driver activation

ExternalConsoleDriver
~~~~~~~~~~~~~~~~~~~~~
An :any:`ExternalConsoleDriver` implements the :any:`ConsoleProtocol` on top of
Expand Down
13 changes: 12 additions & 1 deletion labgrid/driver/bareboxdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from ..util import gen_marker, Timeout, re_vt100
from .common import Driver
from .commandmixin import CommandMixin
from .exception import ExecutionError


@target_factory.reg_driver
Expand All @@ -25,6 +26,7 @@ class BareboxDriver(CommandMixin, Driver, CommandProtocol, LinuxBootProtocol):
prompt (str): barebox prompt to match
autoboot (regex): optional, autoboot message to match
interrupt (str): optional, string to interrupt autoboot (use "\x03" for CTRL-C)
boot_expression (regex): optional, string to search for on barebox start
bootstring (regex): optional, regex indicating that the Linux Kernel is booting
password (str): optional, password to use for access to the shell
login_timeout (int): optional, timeout for access to the shell
Expand All @@ -33,9 +35,11 @@ class BareboxDriver(CommandMixin, Driver, CommandProtocol, LinuxBootProtocol):
prompt = attr.ib(default="", validator=attr.validators.instance_of(str))
autoboot = attr.ib(default="stop autoboot", validator=attr.validators.instance_of(str))
interrupt = attr.ib(default="\n", validator=attr.validators.instance_of(str))
boot_expression = attr.ib(default=r"[\n]barebox 20\d+", validator=attr.validators.instance_of(str))
bootstring = attr.ib(default=r"Linux version \d", validator=attr.validators.instance_of(str))
password = attr.ib(default="", validator=attr.validators.instance_of(str))
login_timeout = attr.ib(default=60, validator=attr.validators.instance_of(int))
boot_detected = attr.ib(default=False, validator=attr.validators.instance_of(bool))

def __attrs_post_init__(self):
super().__attrs_post_init__()
Expand Down Expand Up @@ -108,6 +112,8 @@ def reset(self):
self._status = 0
self.console.sendline("reset")
self._await_prompt()
if not self.boot_detected:
raise ExecutionError(f"No reboot message detected: {self.boot_expression}")

def get_status(self):
"""Retrieve status of the BareboxDriver
Expand Down Expand Up @@ -148,8 +154,9 @@ def _await_prompt(self):
# occours, we can't lose any data this way.
last_before = None
password_entered = False
self.boot_detected = False

expectations = [self.prompt, self.autoboot, "Password: ", TIMEOUT]
expectations = [self.prompt, self.autoboot, "Password: ", self.boot_expression, TIMEOUT]
while True:
index, before, _, _ = self.console.expect(
expectations,
Expand Down Expand Up @@ -177,6 +184,10 @@ def _await_prompt(self):
password_entered = True

elif index == 3:
# we detect a boot
self.boot_detected = True

elif index == 4:
# expect hit a timeout while waiting for a match
if before == last_before:
# we did not receive anything during the previous expect cycle
Expand Down
21 changes: 13 additions & 8 deletions labgrid/driver/ubootdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ..step import step
from .common import Driver
from .commandmixin import CommandMixin
from .exception import ExecutionError


@target_factory.reg_driver
Expand All @@ -23,8 +24,8 @@ class UBootDriver(CommandMixin, Driver, CommandProtocol, LinuxBootProtocol):
autoboot (str): optional, string to search for to interrupt autoboot
interrupt (str): optional, character to interrupt autoboot and go to prompt
password_prompt (str): optional, string to detect the password prompt
boot_expression (str): optional, deprecated
bootstring (str): optional, string that indicates that the Kernel is booting
boot_expression (regex): optional, string to search for on U-Boot start
bootstring (regex): optional, string that indicates that the Kernel is booting
boot_command (str): optional boot command to boot target
login_timeout (int): optional, timeout for login prompt detection
boot_timeout (int): optional, timeout for initial Linux Kernel version detection
Expand All @@ -37,21 +38,18 @@ class UBootDriver(CommandMixin, Driver, CommandProtocol, LinuxBootProtocol):
interrupt = attr.ib(default="\n", validator=attr.validators.instance_of(str))
init_commands = attr.ib(default=attr.Factory(tuple), converter=tuple)
password_prompt = attr.ib(default="enter Password:", validator=attr.validators.instance_of(str))
boot_expression = attr.ib(default="", validator=attr.validators.instance_of(str))
boot_expression = attr.ib(default=r"[\n]U-Boot 20\d+", validator=attr.validators.instance_of(str))
bootstring = attr.ib(default=r"Linux version \d", validator=attr.validators.instance_of(str))
boot_command = attr.ib(default="run bootcmd", validator=attr.validators.instance_of(str))
boot_commands = attr.ib(default=attr.Factory(dict), validator=attr.validators.instance_of(dict))
login_timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))
boot_timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))
boot_detected = attr.ib(default=False, validator=attr.validators.instance_of(bool))

def __attrs_post_init__(self):
super().__attrs_post_init__()
self._status = 0

if self.boot_expression:
import warnings
warnings.warn("boot_expression is deprecated and will be ignored", DeprecationWarning)

def on_activate(self):
"""Activate the UBootDriver

Expand Down Expand Up @@ -137,6 +135,8 @@ def reset(self):
self._status = 0
self.console.sendline("reset")
self._await_prompt()
if not self.boot_detected:
raise ExecutionError(f"No reboot message detected: {self.boot_expression}")

@step()
def _await_prompt(self):
Expand All @@ -151,8 +151,9 @@ def _await_prompt(self):
# Because pexpect keeps any read data in it's buffer when a timeout
# occours, we can't lose any data this way.
last_before = None
self.boot_detected = False

expectations = [self.prompt, self.autoboot, self.password_prompt, TIMEOUT]
expectations = [self.prompt, self.autoboot, self.password_prompt, self.boot_expression, TIMEOUT]
while True:
index, before, _, _ = self.console.expect(
expectations,
Expand All @@ -171,6 +172,10 @@ def _await_prompt(self):
self.console.sendline(self.password)

elif index == 3:
# we detect a boot
self.boot_detected = True

elif index == 4:
# expect hit a timeout while waiting for a match
if before == last_before:
# we did not receive anything during the previous expect cycle
Expand Down
42 changes: 30 additions & 12 deletions tests/test_bareboxdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
from labgrid.driver import ExecutionError


def _get_active_barebox(target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = BareboxDriver(t, "barebox")
d = t.get_driver(BareboxDriver, activate=False)
# mock for d._run('echo $global.loglevel')
d._run = mocker.MagicMock(return_value=(['7'], [], 0))
t.activate(d)
return d


class TestBareboxDriver:
def test_create(self):
t = Target('dummy')
Expand All @@ -17,27 +27,35 @@ def test_create(self):
assert (isinstance(d, LinuxBootProtocol))

def test_barebox_run(self, target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = BareboxDriver(t, "barebox")
d = t.get_driver(BareboxDriver, activate=False)
# mock for d._run('echo $global.loglevel')
d._run = mocker.MagicMock(return_value=(['7'], [], 0))
t.activate(d)
d = _get_active_barebox(target_with_fakeconsole, mocker)
d._run = mocker.MagicMock(return_value=(['success'], [], 0))
res = d.run_check("test")
assert res == ['success']
res = d.run("test")
assert res == (['success'], [], 0)

def test_barebox_run_error(self, target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = BareboxDriver(t, "barebox")
d = t.get_driver(BareboxDriver, activate=False)
# mock for d._run('echo $global.loglevel')
d._run = mocker.MagicMock(return_value=(['7'], [], 0))
t.activate(d)
d = _get_active_barebox(target_with_fakeconsole, mocker)
d._run = mocker.MagicMock(return_value=(['error'], [], 1))
with pytest.raises(ExecutionError):
res = d.run_check("test")
res = d.run("test")
assert res == (['error'], [], 1)

def test_barebox_reset(self, target_with_fakeconsole, mocker):
d = _get_active_barebox(target_with_fakeconsole, mocker)

d.boot_expression = "[\n]barebox 2345"
d.prompt = prompt = ">="
d._check_prompt = mocker.MagicMock()
d.console.rxq.extend([prompt.encode(), b"\nbarebox 2345"])
d.reset()

assert d.console.txq.pop() == b"reset\n"
assert d._status == 1
assert d.boot_detected

d.console.rxq.append(prompt.encode())
with pytest.raises(ExecutionError):
d.reset()

35 changes: 27 additions & 8 deletions tests/test_ubootdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
from labgrid.driver import ExecutionError


def _get_active_uboot(target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = UBootDriver(t, "uboot")
d.console.rxq.append(b"U-Boot 2019\n")
d = t.get_driver(UBootDriver)
return d


class TestUBootDriver:
def test_create(self):
t = Target('dummy')
Expand All @@ -17,10 +25,7 @@ def test_create(self):
assert (isinstance(d, LinuxBootProtocol))

def test_uboot_run(self, target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = UBootDriver(t, "uboot")
d.console.rxq.append(b"U-Boot 2019\n")
d = t.get_driver(UBootDriver)
d = _get_active_uboot(target_with_fakeconsole, mocker)
d._run = mocker.MagicMock(return_value=(['success'], [], 0))
res = d.run_check("test")
assert res == ['success']
Expand All @@ -31,10 +36,7 @@ def test_uboot_run(self, target_with_fakeconsole, mocker):
d._run.assert_called_once_with("test", timeout=30)

def test_uboot_run_error(self, target_with_fakeconsole, mocker):
t = target_with_fakeconsole
d = UBootDriver(t, "uboot")
d.console.rxq.append(b"U-Boot 2019\n")
d = t.get_driver(UBootDriver)
d = _get_active_uboot(target_with_fakeconsole, mocker)
d._run = mocker.MagicMock(return_value=(['error'], [], 1))
with pytest.raises(ExecutionError):
res = d.run_check("test")
Expand All @@ -54,3 +56,20 @@ def test_uboot_boot(self, target_with_fakeconsole):
assert d.console.txq.pop() == b"bar\n"
with pytest.raises(Exception):
d.boot('nop')

def test_uboot_reset(self, target_with_fakeconsole, mocker):
d = _get_active_uboot(target_with_fakeconsole, mocker)

d.boot_expression = "[\n]U-Boot 2345"
d.prompt = prompt = "u-boot=>"
d._check_prompt = mocker.MagicMock()
d.console.rxq.extend([prompt.encode(), b"\nU-Boot 2345"])
d.reset()

assert d.console.txq.pop() == b"reset\n"
assert d._status == 1
assert d.boot_detected

d.console.rxq.append(prompt.encode())
with pytest.raises(ExecutionError):
d.reset()