Skip to content

Commit

Permalink
feat: add support for efuse in qemu
Browse files Browse the repository at this point in the history
  • Loading branch information
horw authored and hfudev committed Jul 30, 2024
1 parent 5069f98 commit afe5548
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 0 deletions.
98 changes: 98 additions & 0 deletions pytest-embedded-qemu/pytest_embedded_qemu/qemu.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import asyncio
import binascii
import logging
import os
import shlex
import socket
Expand All @@ -12,6 +14,43 @@
if t.TYPE_CHECKING:
from .app import QemuApp

QEMU_DEFAULT_EFUSE = {
'esp32': binascii.unhexlify(
'00000000000000000000000000800000000000000000100000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000'
),
'esp32c3': binascii.unhexlify(
'00000000000000000000000000000000000000000000000000000000000000000000000000000c00'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
'000000000000000000000000000000000000000000000000'
),
}


class Qemu(DuplicateStdoutPopen):
"""
Expand All @@ -37,6 +76,7 @@ def __init__(
qemu_prog_path: t.Optional[str] = None,
qemu_cli_args: t.Optional[str] = None,
qemu_extra_args: t.Optional[str] = None,
qemu_efuse_path: t.Optional[str] = None,
app: t.Optional['QemuApp'] = None,
**kwargs,
):
Expand All @@ -55,11 +95,28 @@ def __init__(

qemu_prog_path = qemu_prog_path or self.qemu_prog_name

self.current_qemu_executable_path = qemu_prog_path
self.image_path = image_path
self.efuse_path = qemu_efuse_path

if qemu_cli_args:
qemu_cli_args = qemu_cli_args.strip('"').strip("'")
qemu_cli_args = shlex.split(qemu_cli_args or self.qemu_default_args)
qemu_extra_args = shlex.split(qemu_extra_args or '')

if self.efuse_path:
logging.debug('The eFuse file will be saved to: %s', self.efuse_path)
with open(self.efuse_path, 'wb') as f:
f.write(QEMU_DEFAULT_EFUSE[self.app.target])
qemu_extra_args += [
'-global',
f'driver={self.app.target}.gpio,property=strap_mode,value=0x08',
'-drive',
f'file={self.efuse_path},if=none,format=raw,id=efuse',
'-global',
f'driver=nvram.{self.app.target}.efuse,property=drive,value=efuse',
]

self.qmp_addr = None
self.qmp_port = None

Expand Down Expand Up @@ -87,6 +144,47 @@ def __init__(
**kwargs,
)

def execute_efuse_command(self, command: str):
import espefuse
import pexpect

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('127.0.0.1', 0))
_, available_port = s.getsockname()

run_qemu_command = [
'-nographic',
'-machine',
self.app.target,
'-drive',
f'file={self.image_path},if=mtd,format=raw',
'-global',
f'driver={self.app.target}.gpio,property=strap_mode,value=0x0f',
'-drive',
f'file={self.efuse_path},if=none,format=raw,id=efuse',
'-global',
f'driver=nvram.{self.app.target}.efuse,property=drive,value=efuse',
'-serial',
f'tcp::{available_port},server,nowait',
]
try:
child = pexpect.spawn(self.current_qemu_executable_path, run_qemu_command)
res = shlex.split(command)
child.expect('qemu')

res = [r for r in res if r != '--do-not-confirm']
espefuse.main([
'--port',
f'socket://localhost:{available_port}',
'--before',
'no_reset',
'--do-not-confirm',
*res,
])
self._hard_reset()
finally:
child.terminate()

@property
def qemu_prog_name(self):
if self.app:
Expand Down
44 changes: 44 additions & 0 deletions pytest-embedded-qemu/tests/test_qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,50 @@
)


@qemu_bin_required
def test_pexpect_write_efuse(testdir):
testdir.makepyfile("""
import pexpect
import pytest
def test_pexpect_by_qemu(dut):
dut.qemu.execute_efuse_command('burn_custom_mac 00:11:22:33:44:55')
dut.expect('')
with open('/tmp/test.test', 'rb') as f:
content = f.read()
expected_output = [
'00000000:00000000000000000000000000800000',
'00000010:00000000000010000000000000000000',
'00000020:00000000000000000000000000000000',
'00000030:00000000000000000000000000000000',
'00000040:00000000000000000000000000000000',
'00000050:000000000000000000000000b8001122',
'00000060:33445500000000000000000000000000',
'00000070:000000010000000000000000'
]
lines = []
for i in range(0, len(content), 16):
line = content[i:i+16]
hex_values = ''.join(f'{byte:02x}' for byte in line)
lines.append(f'{i:08x}:{hex_values}')
assert lines == expected_output
""")

result = testdir.runpytest(
'-s',
'--embedded-services', 'idf,qemu',
'--app-path', os.path.join(testdir.tmpdir, 'hello_world_esp32'),
'--qemu-cli-args="-machine esp32 -nographic"',
'--qemu-efuse-path', '/tmp/test.test'
)

result.assert_outcomes(passed=1)


@qemu_bin_required
def test_pexpect_by_qemu_xtensa(testdir):
testdir.makepyfile("""
Expand Down
6 changes: 6 additions & 0 deletions pytest-embedded/pytest_embedded/dut_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def _fixture_classes_and_options_fn(
qemu_prog_path,
qemu_cli_args,
qemu_extra_args,
qemu_efuse_path,
wokwi_cli_path,
wokwi_timeout,
wokwi_scenario,
Expand Down Expand Up @@ -181,6 +182,7 @@ def _fixture_classes_and_options_fn(
'encrypt': encrypt,
'keyfile': keyfile,
'qemu_prog_path': qemu_prog_path,
'qemu_efuse_path': qemu_efuse_path,
})
else:
from pytest_embedded_idf import IdfApp
Expand Down Expand Up @@ -290,6 +292,7 @@ def _fixture_classes_and_options_fn(
'qemu_prog_path': qemu_prog_path,
'qemu_cli_args': qemu_cli_args,
'qemu_extra_args': qemu_extra_args,
'qemu_efuse_path': qemu_efuse_path,
'app': None,
'meta': _meta,
'dut_index': dut_index,
Expand Down Expand Up @@ -607,6 +610,7 @@ def create(
qemu_prog_path: t.Optional[str] = None,
qemu_cli_args: t.Optional[str] = None,
qemu_extra_args: t.Optional[str] = None,
qemu_efuse_path: t.Optional[str] = None,
wokwi_cli_path: t.Optional[str] = None,
wokwi_timeout: t.Optional[int] = 0,
wokwi_scenario: t.Optional[str] = None,
Expand Down Expand Up @@ -655,6 +659,7 @@ def create(
qemu_prog_path: QEMU program path.
qemu_cli_args: QEMU CLI arguments.
qemu_extra_args: Additional QEMU arguments.
qemu_efuse_path: Efuse binary path.
wokwi_cli_path: Wokwi CLI path.
wokwi_timeout: Wokwi timeout.
wokwi_scenario: Wokwi scenario path.
Expand Down Expand Up @@ -719,6 +724,7 @@ def create(
'qemu_prog_path': qemu_prog_path,
'qemu_cli_args': qemu_cli_args,
'qemu_extra_args': qemu_extra_args,
'qemu_efuse_path': qemu_efuse_path,
'wokwi_cli_path': wokwi_cli_path,
'wokwi_timeout': wokwi_timeout,
'wokwi_scenario': wokwi_scenario,
Expand Down
12 changes: 12 additions & 0 deletions pytest-embedded/pytest_embedded/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ def pytest_addoption(parser):
'--qemu-extra-args',
help='QEMU cli extra arguments, will append to the argument list. (Default: None)',
)
qemu_group.addoption(
'--qemu-efuse-path',
help='This option makes it possible to use efuse in QEMU when it is set up.',
)
qemu_group.addoption(
'--skip-regenerate-image',
help='y/yes/true for True and n/no/false for False. '
Expand Down Expand Up @@ -932,6 +936,13 @@ def qemu_extra_args(request: FixtureRequest) -> t.Optional[str]:
return _request_param_or_config_option_or_default(request, 'qemu_extra_args', None)


@pytest.fixture
@multi_dut_argument
def qemu_efuse_path(request: FixtureRequest) -> t.Optional[str]:
"""Enable parametrization for the same cli option"""
return _request_param_or_config_option_or_default(request, 'qemu_efuse_path', None)


@pytest.fixture
@multi_dut_argument
def skip_regenerate_image(request: FixtureRequest) -> t.Optional[str]:
Expand Down Expand Up @@ -1039,6 +1050,7 @@ def parametrize_fixtures(
qemu_prog_path,
qemu_cli_args,
qemu_extra_args,
qemu_efuse_path,
wokwi_cli_path,
wokwi_timeout,
wokwi_scenario,
Expand Down

0 comments on commit afe5548

Please sign in to comment.