Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 8fdc5a2

Browse files
authored
Merge pull request #350 from jumpstarter-dev/uboot-console-debug
UbootConsole: implement debug mode and document
2 parents 268ae9e + 5edf06a commit 8fdc5a2

6 files changed

Lines changed: 83 additions & 4 deletions

File tree

docs/source/api-reference/drivers/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ sdwire.md
2020
shell.md
2121
snmp.md
2222
tftp.md
23+
uboot.md
2324
ustreamer.md
2425
yepkit.md
2526
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# U-Boot driver
2+
3+
The U-Boot driver is a driver for interacting with the U-Boot bootloader.
4+
This driver does not interact with the DUT directly, instead it should be
5+
configured with backing power and serial drivers.
6+
7+
## Driver configuration
8+
9+
```{literalinclude} uboot.yaml
10+
:language: yaml
11+
```
12+
13+
```{doctest}
14+
:hide:
15+
>>> from jumpstarter.config import ExporterConfigV1Alpha1DriverInstance
16+
>>> ExporterConfigV1Alpha1DriverInstance.from_path("source/api-reference/drivers/uboot.yaml").instantiate()
17+
UbootConsole(...)
18+
```
19+
20+
## Client API
21+
22+
```{eval-rst}
23+
.. autoclass:: jumpstarter_driver_uboot.client.UbootConsoleClient()
24+
:members:
25+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type: "jumpstarter_driver_uboot.driver.UbootConsole"
2+
children:
3+
power:
4+
type: "jumpstarter_driver_power.driver.MockPower"
5+
config: {} # omitted, power driver configuration
6+
serial:
7+
type: "jumpstarter_driver_pyserial.driver.PySerial"
8+
config: # omitted, serial driver configuration
9+
url: "loop://"
10+
# instead of configuring the power and serial driver inline
11+
# other drivers configured on the exporter can also be referenced
12+
# power:
13+
# ref: "dutlink.power"
14+
# serial:
15+
# ref: "dutlink.console"
16+
config:
17+
prompt: "=>" # the u-boot command prompt to expect, defaults to "=>"

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def bootloader_shell(self):
6161
self.logger.info("Setting up flasher bundle files in exporter")
6262
self.call("setup_flasher_bundle")
6363
with self._services_up():
64-
with self.uboot.reboot_to_console():
64+
with self.uboot.reboot_to_console(debug=self._console_debug):
6565
pass
6666
yield self.serial
6767

@@ -375,7 +375,7 @@ def _busybox(self):
375375
"""
376376

377377
# make sure that the device is booted into the uboot console
378-
with self.uboot.reboot_to_console():
378+
with self.uboot.reboot_to_console(debug=self._console_debug):
379379
# run dhcp discovery and gather details useful for later
380380
self._dhcp_details = self.uboot.setup_dhcp()
381381
self.logger.info(f"discovered dhcp details: {self._dhcp_details}")

packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/client.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from contextlib import contextmanager
23
from functools import cached_property
34

@@ -10,14 +11,26 @@
1011
class UbootConsoleClient(CompositeClient):
1112
@cached_property
1213
def prompt(self) -> str:
14+
"""
15+
U-Boot prompt to expect
16+
"""
17+
1318
return self.call("get_prompt")
1419

1520
@contextmanager
16-
def reboot_to_console(self) -> None:
21+
def reboot_to_console(self, *, debug=False) -> None:
1722
"""
1823
Reboot to U-Boot console
1924
2025
Power cycle the target and wait for the U-Boot prompt
26+
27+
Must be used as a context manager, other methods can only be
28+
used within the reboot_to_console context
29+
30+
>>> with uboot.reboot_to_console(debug=True): # doctest: +SKIP
31+
... uboot.set_env("foo", "bar")
32+
... uboot.setup_dhcp()
33+
>>> # uboot.set_env("foo", "baz") # invalid use
2134
"""
2235

2336
self.logger.info("Power cycling target...")
@@ -26,6 +39,9 @@ def reboot_to_console(self) -> None:
2639
self.logger.info("Waiting for U-Boot prompt...")
2740

2841
with self.serial.pexpect() as p:
42+
if debug:
43+
p.logfile_read = sys.stdout.buffer
44+
2945
for _ in range(100): # TODO: configurable retries
3046
try:
3147
p.send(ESC)
@@ -44,6 +60,10 @@ def reboot_to_console(self) -> None:
4460
delattr(self, "p")
4561

4662
def run_command(self, cmd: str, timeout: int = 60, *, _internal_log=True) -> bytes:
63+
"""
64+
Run raw command in the U-Boot console
65+
"""
66+
4767
if _internal_log:
4868
self.logger.info(f"Running command: {cmd}")
4969
if not hasattr(self, "p"):
@@ -55,6 +75,10 @@ def run_command(self, cmd: str, timeout: int = 60, *, _internal_log=True) -> byt
5575
return self.p.before
5676

5777
def run_command_checked(self, cmd: str, timeout: int = 60, check=True) -> list[str]:
78+
"""
79+
Run command in the U-Boot console and check the exit code
80+
"""
81+
5882
self.logger.info(f"Running command checked: {cmd}")
5983
output = self.run_command("{}; echo $?".format(cmd), _internal_log=False)
6084
parsed = output.strip().decode().splitlines()
@@ -73,6 +97,10 @@ def run_command_checked(self, cmd: str, timeout: int = 60, check=True) -> list[s
7397
return parsed[1:-1]
7498

7599
def setup_dhcp(self, timeout: int = 60) -> DhcpInfo:
100+
"""
101+
Setup dhcp in U-Boot
102+
"""
103+
76104
self.logger.info("Running DHCP to obtain network configuration...")
77105

78106
autoload = self.get_env("autoload", timeout=timeout)
@@ -116,6 +144,10 @@ def get_env(self, key: str, timeout: int = 5) -> str | None:
116144
raise TimeoutError(f"Timed out getting var {key}") from err
117145

118146
def set_env(self, key: str, value: str | None, timeout: int = 5) -> None:
147+
"""
148+
Set U-Boot environment variable value
149+
"""
150+
119151
if value is not None:
120152
cmd = "setenv {} '{}'".format(key, value)
121153
else:
@@ -127,5 +159,9 @@ def set_env(self, key: str, value: str | None, timeout: int = 5) -> None:
127159
raise TimeoutError(f"Timed out setting var {key}") from err
128160

129161
def set_env_dict(self, env: dict[str, str | None]) -> None:
162+
"""
163+
Set multiple U-Boot environment variable value
164+
"""
165+
130166
for key, value in env.items():
131167
self.set_env(key, value)

packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def test_driver_uboot_console(uboot_image):
4747

4848
uboot = root.uboot
4949

50-
with uboot.reboot_to_console():
50+
with uboot.reboot_to_console(debug=True):
5151
assert uboot.run_command_checked("version") == [
5252
"U-Boot 2024.10 (Oct 11 2024 - 00:00:00 +0000)",
5353
"",

0 commit comments

Comments
 (0)