Skip to content

Commit afe5548

Browse files
horwhfudev
authored andcommitted
feat: add support for efuse in qemu
1 parent 5069f98 commit afe5548

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed

pytest-embedded-qemu/pytest_embedded_qemu/qemu.py

+98
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import asyncio
2+
import binascii
3+
import logging
24
import os
35
import shlex
46
import socket
@@ -12,6 +14,43 @@
1214
if t.TYPE_CHECKING:
1315
from .app import QemuApp
1416

17+
QEMU_DEFAULT_EFUSE = {
18+
'esp32': binascii.unhexlify(
19+
'00000000000000000000000000800000000000000000100000000000000000000000000000000000'
20+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
21+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
22+
'00000000'
23+
),
24+
'esp32c3': binascii.unhexlify(
25+
'00000000000000000000000000000000000000000000000000000000000000000000000000000c00'
26+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
27+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
28+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
29+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
30+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
31+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
32+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
33+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
34+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
35+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
36+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
37+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
38+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
39+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
40+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
41+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
42+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
43+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
44+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
45+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
46+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
47+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
48+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
49+
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
50+
'000000000000000000000000000000000000000000000000'
51+
),
52+
}
53+
1554

1655
class Qemu(DuplicateStdoutPopen):
1756
"""
@@ -37,6 +76,7 @@ def __init__(
3776
qemu_prog_path: t.Optional[str] = None,
3877
qemu_cli_args: t.Optional[str] = None,
3978
qemu_extra_args: t.Optional[str] = None,
79+
qemu_efuse_path: t.Optional[str] = None,
4080
app: t.Optional['QemuApp'] = None,
4181
**kwargs,
4282
):
@@ -55,11 +95,28 @@ def __init__(
5595

5696
qemu_prog_path = qemu_prog_path or self.qemu_prog_name
5797

98+
self.current_qemu_executable_path = qemu_prog_path
99+
self.image_path = image_path
100+
self.efuse_path = qemu_efuse_path
101+
58102
if qemu_cli_args:
59103
qemu_cli_args = qemu_cli_args.strip('"').strip("'")
60104
qemu_cli_args = shlex.split(qemu_cli_args or self.qemu_default_args)
61105
qemu_extra_args = shlex.split(qemu_extra_args or '')
62106

107+
if self.efuse_path:
108+
logging.debug('The eFuse file will be saved to: %s', self.efuse_path)
109+
with open(self.efuse_path, 'wb') as f:
110+
f.write(QEMU_DEFAULT_EFUSE[self.app.target])
111+
qemu_extra_args += [
112+
'-global',
113+
f'driver={self.app.target}.gpio,property=strap_mode,value=0x08',
114+
'-drive',
115+
f'file={self.efuse_path},if=none,format=raw,id=efuse',
116+
'-global',
117+
f'driver=nvram.{self.app.target}.efuse,property=drive,value=efuse',
118+
]
119+
63120
self.qmp_addr = None
64121
self.qmp_port = None
65122

@@ -87,6 +144,47 @@ def __init__(
87144
**kwargs,
88145
)
89146

147+
def execute_efuse_command(self, command: str):
148+
import espefuse
149+
import pexpect
150+
151+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
152+
s.bind(('127.0.0.1', 0))
153+
_, available_port = s.getsockname()
154+
155+
run_qemu_command = [
156+
'-nographic',
157+
'-machine',
158+
self.app.target,
159+
'-drive',
160+
f'file={self.image_path},if=mtd,format=raw',
161+
'-global',
162+
f'driver={self.app.target}.gpio,property=strap_mode,value=0x0f',
163+
'-drive',
164+
f'file={self.efuse_path},if=none,format=raw,id=efuse',
165+
'-global',
166+
f'driver=nvram.{self.app.target}.efuse,property=drive,value=efuse',
167+
'-serial',
168+
f'tcp::{available_port},server,nowait',
169+
]
170+
try:
171+
child = pexpect.spawn(self.current_qemu_executable_path, run_qemu_command)
172+
res = shlex.split(command)
173+
child.expect('qemu')
174+
175+
res = [r for r in res if r != '--do-not-confirm']
176+
espefuse.main([
177+
'--port',
178+
f'socket://localhost:{available_port}',
179+
'--before',
180+
'no_reset',
181+
'--do-not-confirm',
182+
*res,
183+
])
184+
self._hard_reset()
185+
finally:
186+
child.terminate()
187+
90188
@property
91189
def qemu_prog_name(self):
92190
if self.app:

pytest-embedded-qemu/tests/test_qemu.py

+44
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,50 @@
1111
)
1212

1313

14+
@qemu_bin_required
15+
def test_pexpect_write_efuse(testdir):
16+
testdir.makepyfile("""
17+
import pexpect
18+
import pytest
19+
20+
def test_pexpect_by_qemu(dut):
21+
dut.qemu.execute_efuse_command('burn_custom_mac 00:11:22:33:44:55')
22+
dut.expect('')
23+
24+
with open('/tmp/test.test', 'rb') as f:
25+
content = f.read()
26+
27+
expected_output = [
28+
'00000000:00000000000000000000000000800000',
29+
'00000010:00000000000010000000000000000000',
30+
'00000020:00000000000000000000000000000000',
31+
'00000030:00000000000000000000000000000000',
32+
'00000040:00000000000000000000000000000000',
33+
'00000050:000000000000000000000000b8001122',
34+
'00000060:33445500000000000000000000000000',
35+
'00000070:000000010000000000000000'
36+
]
37+
38+
lines = []
39+
for i in range(0, len(content), 16):
40+
line = content[i:i+16]
41+
hex_values = ''.join(f'{byte:02x}' for byte in line)
42+
lines.append(f'{i:08x}:{hex_values}')
43+
assert lines == expected_output
44+
45+
""")
46+
47+
result = testdir.runpytest(
48+
'-s',
49+
'--embedded-services', 'idf,qemu',
50+
'--app-path', os.path.join(testdir.tmpdir, 'hello_world_esp32'),
51+
'--qemu-cli-args="-machine esp32 -nographic"',
52+
'--qemu-efuse-path', '/tmp/test.test'
53+
)
54+
55+
result.assert_outcomes(passed=1)
56+
57+
1458
@qemu_bin_required
1559
def test_pexpect_by_qemu_xtensa(testdir):
1660
testdir.makepyfile("""

pytest-embedded/pytest_embedded/dut_factory.py

+6
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def _fixture_classes_and_options_fn(
146146
qemu_prog_path,
147147
qemu_cli_args,
148148
qemu_extra_args,
149+
qemu_efuse_path,
149150
wokwi_cli_path,
150151
wokwi_timeout,
151152
wokwi_scenario,
@@ -181,6 +182,7 @@ def _fixture_classes_and_options_fn(
181182
'encrypt': encrypt,
182183
'keyfile': keyfile,
183184
'qemu_prog_path': qemu_prog_path,
185+
'qemu_efuse_path': qemu_efuse_path,
184186
})
185187
else:
186188
from pytest_embedded_idf import IdfApp
@@ -290,6 +292,7 @@ def _fixture_classes_and_options_fn(
290292
'qemu_prog_path': qemu_prog_path,
291293
'qemu_cli_args': qemu_cli_args,
292294
'qemu_extra_args': qemu_extra_args,
295+
'qemu_efuse_path': qemu_efuse_path,
293296
'app': None,
294297
'meta': _meta,
295298
'dut_index': dut_index,
@@ -607,6 +610,7 @@ def create(
607610
qemu_prog_path: t.Optional[str] = None,
608611
qemu_cli_args: t.Optional[str] = None,
609612
qemu_extra_args: t.Optional[str] = None,
613+
qemu_efuse_path: t.Optional[str] = None,
610614
wokwi_cli_path: t.Optional[str] = None,
611615
wokwi_timeout: t.Optional[int] = 0,
612616
wokwi_scenario: t.Optional[str] = None,
@@ -655,6 +659,7 @@ def create(
655659
qemu_prog_path: QEMU program path.
656660
qemu_cli_args: QEMU CLI arguments.
657661
qemu_extra_args: Additional QEMU arguments.
662+
qemu_efuse_path: Efuse binary path.
658663
wokwi_cli_path: Wokwi CLI path.
659664
wokwi_timeout: Wokwi timeout.
660665
wokwi_scenario: Wokwi scenario path.
@@ -719,6 +724,7 @@ def create(
719724
'qemu_prog_path': qemu_prog_path,
720725
'qemu_cli_args': qemu_cli_args,
721726
'qemu_extra_args': qemu_extra_args,
727+
'qemu_efuse_path': qemu_efuse_path,
722728
'wokwi_cli_path': wokwi_cli_path,
723729
'wokwi_timeout': wokwi_timeout,
724730
'wokwi_scenario': wokwi_scenario,

pytest-embedded/pytest_embedded/plugin.py

+12
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ def pytest_addoption(parser):
253253
'--qemu-extra-args',
254254
help='QEMU cli extra arguments, will append to the argument list. (Default: None)',
255255
)
256+
qemu_group.addoption(
257+
'--qemu-efuse-path',
258+
help='This option makes it possible to use efuse in QEMU when it is set up.',
259+
)
256260
qemu_group.addoption(
257261
'--skip-regenerate-image',
258262
help='y/yes/true for True and n/no/false for False. '
@@ -932,6 +936,13 @@ def qemu_extra_args(request: FixtureRequest) -> t.Optional[str]:
932936
return _request_param_or_config_option_or_default(request, 'qemu_extra_args', None)
933937

934938

939+
@pytest.fixture
940+
@multi_dut_argument
941+
def qemu_efuse_path(request: FixtureRequest) -> t.Optional[str]:
942+
"""Enable parametrization for the same cli option"""
943+
return _request_param_or_config_option_or_default(request, 'qemu_efuse_path', None)
944+
945+
935946
@pytest.fixture
936947
@multi_dut_argument
937948
def skip_regenerate_image(request: FixtureRequest) -> t.Optional[str]:
@@ -1039,6 +1050,7 @@ def parametrize_fixtures(
10391050
qemu_prog_path,
10401051
qemu_cli_args,
10411052
qemu_extra_args,
1053+
qemu_efuse_path,
10421054
wokwi_cli_path,
10431055
wokwi_timeout,
10441056
wokwi_scenario,

0 commit comments

Comments
 (0)