labgrid-boardkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- labgrid_boardkit/__init__.py +0 -0
- labgrid_boardkit/fixtures.py +77 -0
- labgrid_boardkit/helpers.py +58 -0
- labgrid_boardkit/loader.py +27 -0
- labgrid_boardkit/manifest.py +53 -0
- labgrid_boardkit/ptest/__init__.py +0 -0
- labgrid_boardkit/ptest/test_ptests.py +28 -0
- labgrid_boardkit/render.py +147 -0
- labgrid_boardkit/schema/board.schema.json +145 -0
- labgrid_boardkit/strategies/__init__.py +25 -0
- labgrid_boardkit/strategies/base.py +32 -0
- labgrid_boardkit/strategies/fastboot.py +141 -0
- labgrid_boardkit/strategies/qemu.py +86 -0
- labgrid_boardkit/strategies/sdmux.py +81 -0
- labgrid_boardkit/strategies/tftp.py +109 -0
- labgrid_boardkit/templates/client-fastboot.yaml.j2 +55 -0
- labgrid_boardkit/templates/client-qemu.yaml.j2 +44 -0
- labgrid_boardkit/templates/client-sdmux.yaml.j2 +58 -0
- labgrid_boardkit/templates/client-tftp.yaml.j2 +59 -0
- labgrid_boardkit/templates/strategy.py.j2 +19 -0
- labgrid_boardkit/tests/__init__.py +0 -0
- labgrid_boardkit/tests/test_fastboot.py +23 -0
- labgrid_boardkit/tests/test_sd.py +28 -0
- labgrid_boardkit/tests/test_tftp.py +35 -0
- labgrid_boardkit/validate.py +72 -0
- labgrid_boardkit-0.1.0.dist-info/METADATA +108 -0
- labgrid_boardkit-0.1.0.dist-info/RECORD +30 -0
- labgrid_boardkit-0.1.0.dist-info/WHEEL +4 -0
- labgrid_boardkit-0.1.0.dist-info/entry_points.txt +13 -0
- labgrid_boardkit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import attr
|
|
7
|
+
from labgrid.strategy.common import StrategyError
|
|
8
|
+
from labgrid.util.managedfile import ManagedFile
|
|
9
|
+
|
|
10
|
+
from labgrid_boardkit.strategies.base import BoardStrategy
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FastbootStatus(enum.Enum):
|
|
14
|
+
unknown = 0
|
|
15
|
+
off = 1
|
|
16
|
+
flashed = 2
|
|
17
|
+
shell = 3
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@attr.s(eq=False)
|
|
21
|
+
class FastbootFlashStrategy(BoardStrategy):
|
|
22
|
+
"""Flash a physical board's storage over USB fastboot, then boot it.
|
|
23
|
+
|
|
24
|
+
Holding the ``recovery`` button while power comes up, then pulsing
|
|
25
|
+
``reset``, drops the board into USB download mode. Each ``fastboot.stage`` image is
|
|
26
|
+
then staged and continued in order (e.g. SPL, then a fastboot-capable
|
|
27
|
+
U-Boot), and each ``fastboot.flash`` partition is written, except those
|
|
28
|
+
set to null (how a variant drops one of the base board's). File names are
|
|
29
|
+
relative to ``fastboot.image_dir``.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
bindings = {
|
|
33
|
+
"power": "PowerProtocol",
|
|
34
|
+
"console": "ConsoleProtocol",
|
|
35
|
+
"shell": "ShellDriver",
|
|
36
|
+
"fastboot": "AndroidFastbootDriver",
|
|
37
|
+
"recovery": "DigitalOutputProtocol",
|
|
38
|
+
"reset": "DigitalOutputProtocol",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
status = attr.ib(default=FastbootStatus.unknown)
|
|
42
|
+
|
|
43
|
+
def __attrs_post_init__(self):
|
|
44
|
+
super().__attrs_post_init__()
|
|
45
|
+
self.flashed = False
|
|
46
|
+
|
|
47
|
+
def _image(self, name: str) -> str:
|
|
48
|
+
return str(Path(self.manifest["fastboot"]["image_dir"]) / name)
|
|
49
|
+
|
|
50
|
+
def _enter_download_mode(self):
|
|
51
|
+
fb = self.manifest["fastboot"]
|
|
52
|
+
press = fb.get("press_time", 1)
|
|
53
|
+
self.target.activate(self.recovery)
|
|
54
|
+
self.target.activate(self.reset)
|
|
55
|
+
self.target.activate(self.power)
|
|
56
|
+
# Hold recovery across power-up (enters download mode if the board
|
|
57
|
+
# starts on power) and a reset pulse (if it was already running).
|
|
58
|
+
self.recovery.set(True)
|
|
59
|
+
time.sleep(press)
|
|
60
|
+
# cycle(), not on(): transition(off) only just cut power, and a relay
|
|
61
|
+
# flipped back within milliseconds never actually drops the board.
|
|
62
|
+
self.power.cycle()
|
|
63
|
+
time.sleep(fb.get("power_on_delay", 2))
|
|
64
|
+
self.reset.set(True)
|
|
65
|
+
time.sleep(press)
|
|
66
|
+
self.reset.set(False)
|
|
67
|
+
# Keep holding recovery until the boot ROM has sampled it.
|
|
68
|
+
time.sleep(press)
|
|
69
|
+
self.recovery.set(False)
|
|
70
|
+
|
|
71
|
+
def _stage(self, remote_path: str):
|
|
72
|
+
fb = self.manifest["fastboot"]
|
|
73
|
+
retries = fb.get("stage_retries", 5)
|
|
74
|
+
# After a continue, the previous stage's USB device lingers until the
|
|
75
|
+
# next one (e.g. FSBL, after DDR training) re-enumerates, and writes to
|
|
76
|
+
# it fail. fastboot waits for a missing device but not a stale one.
|
|
77
|
+
for attempt in range(retries):
|
|
78
|
+
try:
|
|
79
|
+
self.fastboot("stage", remote_path)
|
|
80
|
+
return
|
|
81
|
+
except subprocess.CalledProcessError:
|
|
82
|
+
if attempt == retries - 1:
|
|
83
|
+
raise
|
|
84
|
+
time.sleep(fb.get("continue_delay", 3))
|
|
85
|
+
|
|
86
|
+
def _flash(self):
|
|
87
|
+
fb = self.manifest["fastboot"]
|
|
88
|
+
# Copy staged images to the exporter up front: the boot ROM's download
|
|
89
|
+
# mode doesn't wait around for slow transfers.
|
|
90
|
+
staged = []
|
|
91
|
+
for image in fb.get("stage", []):
|
|
92
|
+
mf = ManagedFile(self._image(image), self.fastboot.fastboot)
|
|
93
|
+
mf.sync_to_resource()
|
|
94
|
+
staged.append(mf.get_remote_path())
|
|
95
|
+
self._enter_download_mode()
|
|
96
|
+
self.target.activate(self.fastboot)
|
|
97
|
+
for remote_path in staged:
|
|
98
|
+
self._stage(remote_path)
|
|
99
|
+
self.fastboot.continue_boot()
|
|
100
|
+
time.sleep(fb.get("continue_delay", 3))
|
|
101
|
+
for partition, image in fb["flash"].items():
|
|
102
|
+
if image is None: # a variant nulls out a base-board partition
|
|
103
|
+
continue
|
|
104
|
+
self.fastboot.flash(partition, self._image(image))
|
|
105
|
+
self.target.deactivate(self.fastboot)
|
|
106
|
+
|
|
107
|
+
def transition(self, status):
|
|
108
|
+
if not isinstance(status, FastbootStatus):
|
|
109
|
+
status = FastbootStatus[status]
|
|
110
|
+
if status == FastbootStatus.unknown:
|
|
111
|
+
raise StrategyError(f"can not transition to {status}")
|
|
112
|
+
elif status == self.status:
|
|
113
|
+
return
|
|
114
|
+
elif status == FastbootStatus.off:
|
|
115
|
+
self.target.deactivate(self.console)
|
|
116
|
+
self.target.activate(self.power)
|
|
117
|
+
self.power.off()
|
|
118
|
+
elif status == FastbootStatus.flashed:
|
|
119
|
+
self.transition(FastbootStatus.off)
|
|
120
|
+
if not self.flashed:
|
|
121
|
+
self._flash()
|
|
122
|
+
self.flashed = True
|
|
123
|
+
elif status == FastbootStatus.shell:
|
|
124
|
+
self.transition(FastbootStatus.flashed)
|
|
125
|
+
self.target.activate(self.console)
|
|
126
|
+
self.power.cycle()
|
|
127
|
+
self.target.activate(self.shell)
|
|
128
|
+
else:
|
|
129
|
+
raise StrategyError(f"no transition found from {self.status} to {status}")
|
|
130
|
+
self.status = status
|
|
131
|
+
|
|
132
|
+
def force(self, status):
|
|
133
|
+
if not isinstance(status, FastbootStatus):
|
|
134
|
+
status = FastbootStatus[status]
|
|
135
|
+
if status == FastbootStatus.off:
|
|
136
|
+
self.target.activate(self.power)
|
|
137
|
+
elif status == FastbootStatus.shell:
|
|
138
|
+
self.target.activate(self.shell)
|
|
139
|
+
else:
|
|
140
|
+
raise StrategyError(f"can not force state {status}")
|
|
141
|
+
self.status = status
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
import attr
|
|
4
|
+
from labgrid.factory import target_factory
|
|
5
|
+
from labgrid.step import step
|
|
6
|
+
from labgrid.strategy.common import StrategyError
|
|
7
|
+
|
|
8
|
+
from labgrid_boardkit.strategies.base import BoardStrategy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class QemuStatus(enum.Enum):
|
|
12
|
+
unknown = 0
|
|
13
|
+
off = 1
|
|
14
|
+
booting = 2
|
|
15
|
+
shell = 3
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@attr.s(eq=False)
|
|
19
|
+
class QemuBootStrategy(BoardStrategy):
|
|
20
|
+
"""Boot a QEMU-based target."""
|
|
21
|
+
|
|
22
|
+
bindings = {
|
|
23
|
+
"qemu_driver": "QEMUDriver",
|
|
24
|
+
"shell": "ShellDriver",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
status = attr.ib(default=QemuStatus.unknown)
|
|
28
|
+
|
|
29
|
+
@step(title="transition")
|
|
30
|
+
def transition(self, status):
|
|
31
|
+
if not isinstance(status, QemuStatus):
|
|
32
|
+
status = QemuStatus[status]
|
|
33
|
+
|
|
34
|
+
if status == QemuStatus.off:
|
|
35
|
+
if self.status == QemuStatus.shell:
|
|
36
|
+
self.target.deactivate(self.shell)
|
|
37
|
+
self.qemu_driver.off()
|
|
38
|
+
self.target.deactivate(self.qemu_driver)
|
|
39
|
+
self.status = QemuStatus.off
|
|
40
|
+
elif status == QemuStatus.shell:
|
|
41
|
+
if self.status == QemuStatus.shell:
|
|
42
|
+
return
|
|
43
|
+
if self.status == QemuStatus.booting:
|
|
44
|
+
raise StrategyError("Cannot go to shell directly from booting; turn off first.")
|
|
45
|
+
self.target.activate(self.qemu_driver)
|
|
46
|
+
self.qemu_driver.on()
|
|
47
|
+
self.status = QemuStatus.booting
|
|
48
|
+
self.target.activate(self.shell)
|
|
49
|
+
self.status = QemuStatus.shell
|
|
50
|
+
else:
|
|
51
|
+
raise StrategyError(f"Unknown status {status}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@target_factory.reg_driver
|
|
55
|
+
@attr.s(eq=False)
|
|
56
|
+
class ExternalQemuStrategy(BoardStrategy):
|
|
57
|
+
"""Drive a QEMU guest whose serial console is a subprocess's stdio.
|
|
58
|
+
|
|
59
|
+
Used with ``ExternalConsoleDriver`` when QEMU runs somewhere labgrid's
|
|
60
|
+
``QEMUDriver`` can't reach, e.g. ``ssh -tt <board> qemu-system-* -nographic``.
|
|
61
|
+
Activating the console starts the subprocess (boots the guest);
|
|
62
|
+
deactivating it kills the subprocess (and, via SIGHUP, the guest).
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
bindings = {
|
|
66
|
+
"console": "ConsoleProtocol",
|
|
67
|
+
"shell": "ShellDriver",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
status = attr.ib(default=QemuStatus.unknown)
|
|
71
|
+
|
|
72
|
+
@step(title="transition")
|
|
73
|
+
def transition(self, status):
|
|
74
|
+
if not isinstance(status, QemuStatus):
|
|
75
|
+
status = QemuStatus[status]
|
|
76
|
+
|
|
77
|
+
if status == QemuStatus.off:
|
|
78
|
+
self.target.deactivate(self.console)
|
|
79
|
+
elif status == QemuStatus.shell:
|
|
80
|
+
if self.status == QemuStatus.shell:
|
|
81
|
+
return
|
|
82
|
+
self.target.activate(self.console)
|
|
83
|
+
self.target.activate(self.shell)
|
|
84
|
+
else:
|
|
85
|
+
raise StrategyError(f"can not transition to {status}")
|
|
86
|
+
self.status = status
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
import attr
|
|
4
|
+
from labgrid.driver.usbstoragedriver import Mode
|
|
5
|
+
from labgrid.strategy.common import StrategyError
|
|
6
|
+
|
|
7
|
+
from labgrid_boardkit.strategies.base import BoardStrategy
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SdMuxStatus(enum.Enum):
|
|
11
|
+
unknown = 0
|
|
12
|
+
off = 1
|
|
13
|
+
flashed = 2
|
|
14
|
+
sd = 3
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@attr.s(eq=False)
|
|
18
|
+
class SdMuxBootStrategy(BoardStrategy):
|
|
19
|
+
"""Boot a physical board from an SD card written via USB SD-Mux.
|
|
20
|
+
|
|
21
|
+
``image_label`` names the image entry in the labgrid client.yaml
|
|
22
|
+
``images:`` section that holds the wic/gz path.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
bindings = {
|
|
26
|
+
"power": "PowerProtocol",
|
|
27
|
+
"console": "ConsoleProtocol",
|
|
28
|
+
"shell": "ShellDriver",
|
|
29
|
+
"sdmux": "USBSDMuxDriver",
|
|
30
|
+
"sdcard": "USBStorageDriver",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
status = attr.ib(default=SdMuxStatus.unknown)
|
|
34
|
+
image_label = attr.ib(default="sdimage")
|
|
35
|
+
|
|
36
|
+
def __attrs_post_init__(self):
|
|
37
|
+
super().__attrs_post_init__()
|
|
38
|
+
self.flashed = False
|
|
39
|
+
|
|
40
|
+
def transition(self, status):
|
|
41
|
+
if not isinstance(status, SdMuxStatus):
|
|
42
|
+
status = SdMuxStatus[status]
|
|
43
|
+
if status == SdMuxStatus.unknown:
|
|
44
|
+
raise StrategyError(f"can not transition to {status}")
|
|
45
|
+
elif status == self.status:
|
|
46
|
+
return
|
|
47
|
+
elif status == SdMuxStatus.off:
|
|
48
|
+
self.target.deactivate(self.console)
|
|
49
|
+
self.target.activate(self.power)
|
|
50
|
+
self.power.off()
|
|
51
|
+
elif status == SdMuxStatus.flashed:
|
|
52
|
+
image = self.target.env.config.get_image_path(self.image_label)
|
|
53
|
+
self.transition(SdMuxStatus.off)
|
|
54
|
+
if not self.flashed:
|
|
55
|
+
self.target.activate(self.sdmux)
|
|
56
|
+
self.sdmux.set_mode("host")
|
|
57
|
+
self.target.activate(self.sdcard)
|
|
58
|
+
self.sdcard.write_image(image, Mode.BMAPTOOL)
|
|
59
|
+
self.target.deactivate(self.sdcard)
|
|
60
|
+
self.sdmux.set_mode("dut")
|
|
61
|
+
self.target.deactivate(self.sdmux)
|
|
62
|
+
self.flashed = True
|
|
63
|
+
elif status == SdMuxStatus.sd:
|
|
64
|
+
self.transition(SdMuxStatus.flashed)
|
|
65
|
+
self.target.activate(self.console)
|
|
66
|
+
self.power.cycle()
|
|
67
|
+
self.target.activate(self.shell)
|
|
68
|
+
else:
|
|
69
|
+
raise StrategyError(f"no transition found from {self.status} to {status}")
|
|
70
|
+
self.status = status
|
|
71
|
+
|
|
72
|
+
def force(self, status):
|
|
73
|
+
if not isinstance(status, SdMuxStatus):
|
|
74
|
+
status = SdMuxStatus[status]
|
|
75
|
+
if status == SdMuxStatus.off:
|
|
76
|
+
self.target.activate(self.power)
|
|
77
|
+
elif status == SdMuxStatus.sd:
|
|
78
|
+
self.target.activate(self.shell)
|
|
79
|
+
else:
|
|
80
|
+
raise StrategyError(f"can not force state {status}")
|
|
81
|
+
self.status = status
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
import attr
|
|
4
|
+
from labgrid.strategy.common import StrategyError
|
|
5
|
+
|
|
6
|
+
from labgrid_boardkit import helpers
|
|
7
|
+
from labgrid_boardkit.strategies.base import BoardStrategy
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TftpStatus(enum.Enum):
|
|
11
|
+
unknown = 0
|
|
12
|
+
off = 1
|
|
13
|
+
uboot = 2
|
|
14
|
+
emmc = 3
|
|
15
|
+
tftp = 4
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@attr.s(eq=False)
|
|
19
|
+
class TftpBootStrategy(BoardStrategy):
|
|
20
|
+
"""Boot a physical board via U-Boot + TFTP.
|
|
21
|
+
|
|
22
|
+
Reads bootargs, tftp subdir/kernel/dtb, and load addresses from
|
|
23
|
+
``self.manifest`` (populated by the concrete per-board subclass).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
bindings = {
|
|
27
|
+
"power": "PowerProtocol",
|
|
28
|
+
"console": "ConsoleProtocol",
|
|
29
|
+
"uboot": "UBootDriver",
|
|
30
|
+
"shell": "ShellDriver",
|
|
31
|
+
"tftp": "TFTPProviderDriver",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
status = attr.ib(default=TftpStatus.unknown)
|
|
35
|
+
|
|
36
|
+
def __attrs_post_init__(self):
|
|
37
|
+
super().__attrs_post_init__()
|
|
38
|
+
self.staged = False
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def bootargs(self) -> str:
|
|
42
|
+
return self.manifest["boot"]["bootargs"].strip()
|
|
43
|
+
|
|
44
|
+
def _stage(self):
|
|
45
|
+
helpers.uboot_stage(self)
|
|
46
|
+
|
|
47
|
+
def _tftp_stage_files(self):
|
|
48
|
+
boot = self.manifest["boot"]
|
|
49
|
+
tftp = self.manifest["tftp"]
|
|
50
|
+
helpers.uboot_set_server_ip(self)
|
|
51
|
+
helpers.uboot_set_bootargs(self, self.bootargs)
|
|
52
|
+
helpers.uboot_tftpboot_file(self, boot["kernel_addr"], tftp["subdir"], tftp["kernel"])
|
|
53
|
+
if "dtb" in tftp:
|
|
54
|
+
# A FIT image carries its own DTB.
|
|
55
|
+
helpers.uboot_tftpboot_file(self, boot["dtb_addr"], tftp["subdir"], tftp["dtb"])
|
|
56
|
+
if "initramfs" in tftp:
|
|
57
|
+
# Staged last so U-Boot's $filesize (set by the most recent tftpboot)
|
|
58
|
+
# reflects the initramfs size for a `$ramdisk_addr_r:$filesize` boot arg.
|
|
59
|
+
helpers.uboot_tftpboot_file(
|
|
60
|
+
self, boot["ramdisk_addr"], tftp["subdir"], tftp["initramfs"]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def transition(self, status):
|
|
64
|
+
if not isinstance(status, TftpStatus):
|
|
65
|
+
status = TftpStatus[status]
|
|
66
|
+
if status == TftpStatus.unknown:
|
|
67
|
+
raise StrategyError(f"can not transition to {status}")
|
|
68
|
+
elif status == self.status:
|
|
69
|
+
return
|
|
70
|
+
elif status == TftpStatus.off:
|
|
71
|
+
self.target.deactivate(self.console)
|
|
72
|
+
self.target.activate(self.power)
|
|
73
|
+
self.power.off()
|
|
74
|
+
elif status == TftpStatus.uboot:
|
|
75
|
+
self.transition(TftpStatus.off)
|
|
76
|
+
self.target.activate(self.console)
|
|
77
|
+
self.power.cycle()
|
|
78
|
+
self.target.activate(self.uboot)
|
|
79
|
+
self._stage()
|
|
80
|
+
elif status == TftpStatus.emmc:
|
|
81
|
+
self.transition(TftpStatus.uboot)
|
|
82
|
+
self.uboot.boot("emmc")
|
|
83
|
+
self.uboot.await_boot()
|
|
84
|
+
self.target.activate(self.shell)
|
|
85
|
+
elif status == TftpStatus.tftp:
|
|
86
|
+
self.transition(TftpStatus.uboot)
|
|
87
|
+
self._tftp_stage_files()
|
|
88
|
+
self.uboot.boot("tftp")
|
|
89
|
+
self.uboot.await_boot()
|
|
90
|
+
self.target.activate(self.shell)
|
|
91
|
+
# Keep late kernel messages (e.g. async driver probe errors) off the
|
|
92
|
+
# console, where they interleave with command output; dmesg keeps them.
|
|
93
|
+
self.shell.run("dmesg -n 1")
|
|
94
|
+
else:
|
|
95
|
+
raise StrategyError(f"no transition found from {self.status} to {status}")
|
|
96
|
+
self.status = status
|
|
97
|
+
|
|
98
|
+
def force(self, status):
|
|
99
|
+
if not isinstance(status, TftpStatus):
|
|
100
|
+
status = TftpStatus[status]
|
|
101
|
+
if status == TftpStatus.off:
|
|
102
|
+
self.target.activate(self.power)
|
|
103
|
+
elif status == TftpStatus.uboot:
|
|
104
|
+
self.target.activate(self.uboot)
|
|
105
|
+
elif status in (TftpStatus.emmc, TftpStatus.tftp):
|
|
106
|
+
self.target.activate(self.shell)
|
|
107
|
+
else:
|
|
108
|
+
raise StrategyError(f"can not force state {status}")
|
|
109
|
+
self.status = status
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{#- Jinja2 template for a fastboot-flash labgrid client.yaml. -#}
|
|
2
|
+
{%- set n = manifest.name -%}
|
|
3
|
+
{%- set fb = manifest.fastboot -%}
|
|
4
|
+
{%- set strategy_class = manifest.strategy_class | default("FastbootFlashStrategy") -%}
|
|
5
|
+
targets:
|
|
6
|
+
main:
|
|
7
|
+
resources:
|
|
8
|
+
RemotePlace:
|
|
9
|
+
name: {{ manifest.remote_place }}
|
|
10
|
+
drivers:
|
|
11
|
+
- SerialDriver:
|
|
12
|
+
name: '{{ n }}-console-1'
|
|
13
|
+
bindings:
|
|
14
|
+
port: 'serial'
|
|
15
|
+
- GpioDigitalOutputDriver:
|
|
16
|
+
name: '{{ n }}-power-gpio-1'
|
|
17
|
+
bindings:
|
|
18
|
+
gpio: 'power-gpio'
|
|
19
|
+
- DigitalOutputPowerDriver:
|
|
20
|
+
name: '{{ n }}-power-1'
|
|
21
|
+
bindings:
|
|
22
|
+
output: '{{ n }}-power-gpio-1'
|
|
23
|
+
{%- for button in manifest.buttons %}
|
|
24
|
+
- GpioDigitalOutputDriver:
|
|
25
|
+
name: '{{ n }}-{{ button }}-gpio-1'
|
|
26
|
+
bindings:
|
|
27
|
+
gpio: '{{ button }}-gpio'
|
|
28
|
+
{%- endfor %}
|
|
29
|
+
- AndroidFastbootDriver:
|
|
30
|
+
name: '{{ n }}-fastboot-1'
|
|
31
|
+
bindings:
|
|
32
|
+
fastboot: 'fastboot'
|
|
33
|
+
- ShellDriver:
|
|
34
|
+
prompt: '{{ manifest.shell.prompt }}'
|
|
35
|
+
login_prompt: '{{ manifest.shell.login_prompt }}'
|
|
36
|
+
login_timeout: 120
|
|
37
|
+
username: '{{ manifest.shell.username }}'
|
|
38
|
+
{%- if manifest.shell.password %}
|
|
39
|
+
password: '{{ manifest.shell.password }}'
|
|
40
|
+
{%- endif %}
|
|
41
|
+
name: '{{ n }}-shell-1'
|
|
42
|
+
bindings:
|
|
43
|
+
console: '{{ n }}-console-1'
|
|
44
|
+
- {{ strategy_class }}:
|
|
45
|
+
name: '{{ n }}-strategy-1'
|
|
46
|
+
bindings:
|
|
47
|
+
power: '{{ n }}-power-1'
|
|
48
|
+
console: '{{ n }}-console-1'
|
|
49
|
+
shell: '{{ n }}-shell-1'
|
|
50
|
+
fastboot: '{{ n }}-fastboot-1'
|
|
51
|
+
recovery: '{{ n }}-{{ fb.recovery_button }}-gpio-1'
|
|
52
|
+
reset: '{{ n }}-{{ fb.reset_button }}-gpio-1'
|
|
53
|
+
|
|
54
|
+
imports:
|
|
55
|
+
- strategy.py
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{#- Jinja2 template for a QEMU labgrid env.yaml. -#}
|
|
2
|
+
{%- set n = manifest.name -%}
|
|
3
|
+
{%- set strategy_class = manifest.strategy_class | default("QemuBootStrategy") -%}
|
|
4
|
+
{#- virtio-mmio ("-device") for e.g. riscv/arm virt, "-pci" for x86 q35/pc -#}
|
|
5
|
+
{%- set virtio = "pci" if manifest.qemu.virtio_bus | default("mmio") == "pci" else "device" -%}
|
|
6
|
+
imports:
|
|
7
|
+
- strategy.py
|
|
8
|
+
|
|
9
|
+
targets:
|
|
10
|
+
main:
|
|
11
|
+
drivers:
|
|
12
|
+
QEMUDriver:
|
|
13
|
+
qemu_bin: {{ manifest.qemu.binary }}
|
|
14
|
+
{%- if manifest.images.bios is defined %}
|
|
15
|
+
bios: {{ manifest.name }}_bios
|
|
16
|
+
{%- endif %}
|
|
17
|
+
kernel: {{ manifest.name }}_kernel
|
|
18
|
+
cpu: '{{ manifest.qemu.cpu }}'
|
|
19
|
+
memory: "{{ manifest.qemu.memory }}"
|
|
20
|
+
machine: '{{ manifest.qemu.machine }}'
|
|
21
|
+
disk: {{ manifest.name }}_ext4
|
|
22
|
+
boot_args: '{{ manifest.boot.bootargs }}'
|
|
23
|
+
extra_args: >-
|
|
24
|
+
-display none
|
|
25
|
+
-device virtio-net-{{ virtio }},netdev=net0,mac=52:54:00:12:35:02
|
|
26
|
+
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:{{ manifest.qemu.ssh_port }}-:22,hostfwd=tcp:127.0.0.1:{{ manifest.qemu.telnet_port }}-:23
|
|
27
|
+
-object rng-random,filename=/dev/urandom,id=rng0
|
|
28
|
+
-device virtio-rng-{{ virtio }},rng=rng0
|
|
29
|
+
{%- for accel in manifest.qemu.accel | default([]) %}
|
|
30
|
+
-accel {{ accel }}
|
|
31
|
+
{%- endfor %}
|
|
32
|
+
ShellDriver:
|
|
33
|
+
prompt: 'root@[a-zA-Z0-9]+:~#\s'
|
|
34
|
+
login_prompt: ' login: '
|
|
35
|
+
username: '{{ manifest.shell.username }}'
|
|
36
|
+
login_timeout: 300
|
|
37
|
+
{{ strategy_class }}: {}
|
|
38
|
+
|
|
39
|
+
images:
|
|
40
|
+
{%- if manifest.images.bios is defined %}
|
|
41
|
+
{{ manifest.name }}_bios: '{{ manifest.images.bios }}'
|
|
42
|
+
{%- endif %}
|
|
43
|
+
{{ manifest.name }}_ext4: '{{ manifest.images.ext4 }}'
|
|
44
|
+
{{ manifest.name }}_kernel: '{{ manifest.images.kernel }}'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{#- Jinja2 template for an SD-Mux labgrid client.yaml. -#}
|
|
2
|
+
{%- set n = manifest.name -%}
|
|
3
|
+
{%- set strategy_class = manifest.strategy_class | default("SdMuxBootStrategy") -%}
|
|
4
|
+
targets:
|
|
5
|
+
main:
|
|
6
|
+
resources:
|
|
7
|
+
RemotePlace:
|
|
8
|
+
name: {{ manifest.remote_place }}
|
|
9
|
+
drivers:
|
|
10
|
+
- SerialDriver:
|
|
11
|
+
name: '{{ n }}-console-1'
|
|
12
|
+
bindings:
|
|
13
|
+
port: 'serial'
|
|
14
|
+
- GpioDigitalOutputDriver:
|
|
15
|
+
name: '{{ n }}-power-gpio-1'
|
|
16
|
+
bindings:
|
|
17
|
+
gpio: 'power-gpio'
|
|
18
|
+
- DigitalOutputPowerDriver:
|
|
19
|
+
name: '{{ n }}-power-1'
|
|
20
|
+
bindings:
|
|
21
|
+
output: '{{ n }}-power-gpio-1'
|
|
22
|
+
- USBStorageDriver:
|
|
23
|
+
image: '{{ manifest.image.label }}'
|
|
24
|
+
bindings:
|
|
25
|
+
storage: 'sdmux'
|
|
26
|
+
- USBSDMuxDriver:
|
|
27
|
+
bindings:
|
|
28
|
+
mux: 'sdmux'
|
|
29
|
+
- ShellDriver:
|
|
30
|
+
prompt: '(\x1b\[\?2004h|\x1b\\)?root@[\w-]+:[^ ]+[#$] '
|
|
31
|
+
login_prompt: 'login: '
|
|
32
|
+
login_timeout: 120
|
|
33
|
+
username: '{{ manifest.shell.username }}'
|
|
34
|
+
{%- if manifest.shell.password %}
|
|
35
|
+
password: '{{ manifest.shell.password }}'
|
|
36
|
+
{%- endif %}
|
|
37
|
+
name: '{{ n }}-shell-1'
|
|
38
|
+
bindings:
|
|
39
|
+
console: '{{ n }}-console-1'
|
|
40
|
+
- {{ strategy_class }}:
|
|
41
|
+
name: '{{ n }}-strategy-1'
|
|
42
|
+
bindings:
|
|
43
|
+
power: '{{ n }}-power-1'
|
|
44
|
+
console: '{{ n }}-console-1'
|
|
45
|
+
shell: '{{ n }}-shell-1'
|
|
46
|
+
|
|
47
|
+
images:
|
|
48
|
+
{{ manifest.image.label }}: "{{ manifest.image.path }}"
|
|
49
|
+
|
|
50
|
+
{%- if manifest.tools %}
|
|
51
|
+
tools:
|
|
52
|
+
{%- for k, v in manifest.tools.items() %}
|
|
53
|
+
{{ k }}: '{{ v }}'
|
|
54
|
+
{%- endfor %}
|
|
55
|
+
{%- endif %}
|
|
56
|
+
|
|
57
|
+
imports:
|
|
58
|
+
- strategy.py
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{#- Jinja2 template for a TFTP-boot labgrid client.yaml. -#}
|
|
2
|
+
{%- set n = manifest.name -%}
|
|
3
|
+
{%- set strategy_class = manifest.strategy_class | default("TftpBootStrategy") -%}
|
|
4
|
+
targets:
|
|
5
|
+
main:
|
|
6
|
+
resources:
|
|
7
|
+
RemotePlace:
|
|
8
|
+
name: {{ manifest.remote_place }}
|
|
9
|
+
drivers:
|
|
10
|
+
- SerialDriver:
|
|
11
|
+
name: '{{ n }}-console-1'
|
|
12
|
+
bindings:
|
|
13
|
+
port: 'serial'
|
|
14
|
+
- GpioDigitalOutputDriver:
|
|
15
|
+
name: '{{ n }}-power-gpio-1'
|
|
16
|
+
bindings:
|
|
17
|
+
gpio: 'power-gpio'
|
|
18
|
+
- DigitalOutputPowerDriver:
|
|
19
|
+
name: '{{ n }}-power-1'
|
|
20
|
+
bindings:
|
|
21
|
+
output: '{{ n }}-power-gpio-1'
|
|
22
|
+
- TFTPProviderDriver:
|
|
23
|
+
name: '{{ n }}-tftp-1'
|
|
24
|
+
bindings:
|
|
25
|
+
provider: 'tftp'
|
|
26
|
+
- UBootDriver:
|
|
27
|
+
prompt: '{{ manifest.uboot.prompt }}'
|
|
28
|
+
autoboot: '{{ manifest.uboot.autoboot }}'
|
|
29
|
+
interrupt: '{{ manifest.uboot.interrupt }}'
|
|
30
|
+
name: '{{ n }}-uboot-1'
|
|
31
|
+
boot_commands:
|
|
32
|
+
emmc: '{{ manifest.uboot.emmc_cmd }}'
|
|
33
|
+
tftp: '{{ manifest.uboot.tftp_cmd }}'
|
|
34
|
+
bindings:
|
|
35
|
+
console: '{{ n }}-console-1'
|
|
36
|
+
login_timeout: 30
|
|
37
|
+
boot_timeout: 30
|
|
38
|
+
strip_timestamp: True
|
|
39
|
+
- ShellDriver:
|
|
40
|
+
prompt: '{{ manifest.shell.prompt }}'
|
|
41
|
+
login_prompt: '{{ manifest.shell.login_prompt }}'
|
|
42
|
+
username: '{{ manifest.shell.username }}'
|
|
43
|
+
{%- if manifest.shell.password %}
|
|
44
|
+
password: '{{ manifest.shell.password }}'
|
|
45
|
+
{%- endif %}
|
|
46
|
+
name: '{{ n }}-shell-1'
|
|
47
|
+
bindings:
|
|
48
|
+
console: '{{ n }}-console-1'
|
|
49
|
+
- {{ strategy_class }}:
|
|
50
|
+
name: '{{ n }}-strategy-1'
|
|
51
|
+
bindings:
|
|
52
|
+
power: '{{ n }}-power-1'
|
|
53
|
+
console: '{{ n }}-console-1'
|
|
54
|
+
uboot: '{{ n }}-uboot-1'
|
|
55
|
+
shell: '{{ n }}-shell-1'
|
|
56
|
+
tftp: '{{ n }}-tftp-1'
|
|
57
|
+
|
|
58
|
+
imports:
|
|
59
|
+
- strategy.py
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Generated by boardkit-render from board.yaml. Do not edit.
|
|
2
|
+
|
|
3
|
+
Regenerate with: uv run boardkit-render
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import attr
|
|
9
|
+
from labgrid_boardkit.manifest import load_board
|
|
10
|
+
from labgrid_boardkit.strategies import {{ family_class }}
|
|
11
|
+
from labgrid.factory import target_factory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@target_factory.reg_driver
|
|
15
|
+
@attr.s(eq=False)
|
|
16
|
+
class {{ class_name }}({{ family_class }}):
|
|
17
|
+
"""{{ description }}"""
|
|
18
|
+
|
|
19
|
+
manifest = load_board(Path(__file__).parent)
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Shared smoke tests for boards flashed over fastboot. Auto-skipped for boards
|
|
2
|
+
whose strategy isn't a FastbootFlashStrategy (see ``fastboot`` fixture in
|
|
3
|
+
``labgrid_boardkit.fixtures``).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from labgrid.driver import ExecutionError
|
|
9
|
+
|
|
10
|
+
from labgrid_boardkit import helpers
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_uname_a(fastboot):
|
|
14
|
+
stdout = fastboot.run_check("/bin/uname -a", timeout=60.0)
|
|
15
|
+
assert helpers.kernel_version_in(os.environ.get("VERSION"), stdout[0]), stdout[0]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_buildinfo(fastboot):
|
|
19
|
+
try:
|
|
20
|
+
fastboot.run_check("cat /etc/buildinfo", timeout=60.0)
|
|
21
|
+
except ExecutionError:
|
|
22
|
+
fastboot.run("ls /etc/")
|
|
23
|
+
raise
|