mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(idf.py): allow running idf.py efuse-* commands with QEMU
This commit is contained in:
parent
75eb7fe379
commit
7b228ce8ab
@ -111,3 +111,21 @@ To launch QEMU with a virtual framebuffer device enabled, use the following comm
|
||||
When the ``--graphics`` option is used, QEMU opens an additional window where the framebuffer contents are displayed.
|
||||
|
||||
To use the virtual framebuffer device in your application, you can add the `espressif/esp_lcd_qemu_rgb <https://components.espressif.com/components/espressif/esp_lcd_qemu_rgb>`_ component to your project. This component provides an esp_lcd compatible driver for the virtual framebuffer device.
|
||||
|
||||
Efuse Emulation
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
QEMU supports emulation of eFuses. This can be a convenient way to test security-related features, such as secure boot and flash encryption, without having to perform irreversible operations on real hardware.
|
||||
|
||||
You can use :doc:`idf.py<idf-py>` eFuse-related commands to program eFuses. When you run any of these commands together with ``qemu`` command, the eFuses are programmed in QEMU, and the ``qemu_efuse.bin`` file is updated. For example,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
idf.py qemu efuse-burn FLASH_CRYPT_CNT 1
|
||||
|
||||
By default, the values of eFuses are read from and written to the ``qemu_efuse.bin`` file in the build directory. You can specify a different file using the ``--efuse-file`` option. For example,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
idf.py qemu --efuse-file my_efuse.bin efuse-burn FLASH_CRYPT_CNT 1
|
||||
idf.py qemu --efuse-file my_efuse.bin monitor
|
||||
|
@ -111,3 +111,21 @@ QEMU 支持虚拟帧缓冲设备。帧缓冲设备在真实的 {IDF_TARGET_NAME}
|
||||
当启用 ``--graphics`` 选项时,QEMU 会打开一个额外的窗口,显示帧缓冲内容。
|
||||
|
||||
要在应用程序中使用虚拟帧缓冲设备,可以将 `espressif/esp_lcd_qemu_rgb <https://components.espressif.com/components/espressif/esp_lcd_qemu_rgb>`_ 组件添加到项目中。此组件为虚拟帧缓冲设备提供了一个与 esp_lcd 兼容的驱动程序。
|
||||
|
||||
eFuse 仿真
|
||||
~~~~~~~~~~~
|
||||
|
||||
QEMU 支持 eFuse 的仿真,可用来测试安全启动和 flash 加密等与安全相关的功能,而无需在真实硬件上执行不可逆操作。
|
||||
|
||||
使用 :doc:`idf.py<idf-py>` eFuse 相关命令来编程 eFuse。当这些命令与 ``qemu`` 命令一起运行时,eFuse 会在 QEMU 中编程,并且 ``qemu_efuse.bin`` 文件会更新。例如,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
idf.py qemu efuse-burn FLASH_CRYPT_CNT 1
|
||||
|
||||
默认情况下,eFuse 的值从编译文件夹里的 ``qemu_efuse.bin`` 文件中读取和写入。也可以使用 ``--efuse-file`` 选项指定不同的文件。例如,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
idf.py qemu --efuse-file my_efuse.bin efuse-burn FLASH_CRYPT_CNT 1
|
||||
idf.py qemu --efuse-file my_efuse.bin monitor
|
||||
|
@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import atexit
|
||||
import binascii
|
||||
import fnmatch
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
@ -41,6 +42,8 @@ class QemuTarget:
|
||||
install_package: str # name of the tools.json package from which to install the QEMU binary
|
||||
qemu_args: str # chip-specific arguments to pass to QEMU
|
||||
default_efuse: bytes # default efuse values for the target
|
||||
boot_mode_arg: str = '' # additional arguments to pass to QEMU when booting in download mode
|
||||
efuse_device: str = '' # efuse device name, if different from the target nvram.{target}.efuse
|
||||
|
||||
|
||||
# To generate the default eFuse values, follow the instructions in
|
||||
@ -57,7 +60,9 @@ QEMU_TARGETS: Dict[str, QemuTarget] = {
|
||||
'00000000000000000000000000800000000000000000100000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000')),
|
||||
'00000000'),
|
||||
'-global driver=esp32.gpio,property=strap_mode,value=0x0f',
|
||||
'nvram.esp32.efuse'),
|
||||
|
||||
'esp32c3': QemuTarget(
|
||||
'esp32c3',
|
||||
@ -91,7 +96,10 @@ QEMU_TARGETS: Dict[str, QemuTarget] = {
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'000000000000000000000000000000000000000000000000')),
|
||||
'000000000000000000000000000000000000000000000000'),
|
||||
'-global driver=esp32c3.gpio,property=strap_mode,value=0x02',
|
||||
'nvram.esp32c3.efuse'),
|
||||
|
||||
'esp32s3': QemuTarget(
|
||||
'esp32s3',
|
||||
'qemu-system-xtensa',
|
||||
@ -124,7 +132,9 @@ QEMU_TARGETS: Dict[str, QemuTarget] = {
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
'000000000000000000000000000000000000000000000000')),
|
||||
'000000000000000000000000000000000000000000000000'),
|
||||
'-global driver=esp32s3.gpio,property=strap_mode,value=0x07',
|
||||
'nvram.esp32c3.efuse'), # Not esp32s3, QEMU-201
|
||||
}
|
||||
|
||||
|
||||
@ -136,6 +146,7 @@ class QemuTaskRunOptions:
|
||||
self.bg_mode = False
|
||||
self.wait_for_gdb = False
|
||||
self.wait_for_monitor = False
|
||||
self.boot_mode = False
|
||||
|
||||
|
||||
def wait_for_socket(port: int, timeout_sec: float = 10.0) -> None:
|
||||
@ -165,11 +176,12 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
def global_callback(ctx: Context, global_args: Dict, tasks: List) -> None:
|
||||
# This callback lets us customize QEMU launch arguments depending on the presence of other tasks.
|
||||
def have_task(name: str) -> bool:
|
||||
return any(task.name == name for task in tasks)
|
||||
return any(fnmatch.fnmatch(task.name, name) for task in tasks)
|
||||
|
||||
have_qemu = have_task('qemu')
|
||||
have_gdb = have_task('gdb')
|
||||
have_monitor = have_task('monitor')
|
||||
have_efuse = have_task('efuse-*')
|
||||
|
||||
if have_qemu:
|
||||
if have_gdb and have_monitor:
|
||||
@ -183,6 +195,15 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
options.bg_mode = True
|
||||
yellow_print(f'Running qemu on {PYSERIAL_PORT}')
|
||||
global_args['port'] = PYSERIAL_PORT
|
||||
if have_efuse:
|
||||
options.bg_mode = True
|
||||
options.boot_mode = True
|
||||
yellow_print(f'Running qemu on {PYSERIAL_PORT}')
|
||||
global_args['port'] = PYSERIAL_PORT
|
||||
for task in tasks:
|
||||
if fnmatch.fnmatch(task.name, 'efuse-*'):
|
||||
if 'before' in task.action_args.keys():
|
||||
task.action_args['before'] = 'no_reset'
|
||||
|
||||
def _get_project_desc(args: PropertyDict, ctx: Context) -> Any:
|
||||
desc_path = os.path.join(args.build_dir, 'project_description.json')
|
||||
@ -192,7 +213,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
project_desc = json.load(f)
|
||||
return project_desc
|
||||
|
||||
def qemu(action: str, ctx: Context, args: PropertyDict, qemu_extra_args: str, gdb: bool, graphics: bool) -> None:
|
||||
def qemu(action: str, ctx: Context, args: PropertyDict, qemu_extra_args: str, gdb: bool, graphics: bool, efuse_file: str) -> None:
|
||||
project_desc = _get_project_desc(args, ctx)
|
||||
|
||||
# Determine the target and check if we have the necessary QEMU binary
|
||||
@ -209,23 +230,31 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
|
||||
# Generate flash image and efuse image
|
||||
flash_size = get_sdkconfig_value(project_desc['config_file'], 'CONFIG_ESPTOOLPY_FLASHSIZE')
|
||||
bin_path = os.path.join(args.build_dir, 'flash_image.bin')
|
||||
bin_path = os.path.join(args.build_dir, 'qemu_flash.bin')
|
||||
yellow_print(f'Generating flash image: {bin_path}')
|
||||
subprocess.check_call([
|
||||
sys.executable, '-m', 'esptool', f'--chip={target}', 'merge_bin', f'--output={bin_path}',
|
||||
f'--fill-flash-size={flash_size}', '@flash_args'], cwd=args.build_dir)
|
||||
|
||||
efuse_bin_path = os.path.join(args.build_dir, 'qemu_efuse.bin')
|
||||
yellow_print(f'Generating efuse image: {efuse_bin_path}')
|
||||
with open(efuse_bin_path, 'wb') as f:
|
||||
f.write(qemu_target_info.default_efuse)
|
||||
if efuse_file:
|
||||
efuse_bin_path = efuse_file
|
||||
else:
|
||||
efuse_bin_path = os.path.join(args.build_dir, 'qemu_efuse.bin')
|
||||
try:
|
||||
open(efuse_bin_path, 'rb').close()
|
||||
yellow_print(f'Using existing efuse image: {efuse_bin_path}')
|
||||
except FileNotFoundError:
|
||||
yellow_print(f'Generating efuse image: {efuse_bin_path}')
|
||||
with open(efuse_bin_path, 'wb') as f:
|
||||
f.write(qemu_target_info.default_efuse)
|
||||
|
||||
# Prepare QEMU launch arguments
|
||||
qemu_args = [qemu_target_info.qemu_prog]
|
||||
qemu_args += qemu_target_info.qemu_args.split(' ')
|
||||
qemu_args += [
|
||||
'-drive', f'file={bin_path},if=mtd,format=raw',
|
||||
'-drive', f'file={efuse_bin_path},if=none,format=raw,id=efuse', '-global', f'driver=nvram.{target}.efuse,property=drive,value=efuse',
|
||||
'-drive', f'file={efuse_bin_path},if=none,format=raw,id=efuse',
|
||||
'-global', f'driver={qemu_target_info.efuse_device},property=drive,value=efuse',
|
||||
'-global', f'driver=timer.{target}.timg,property=wdt_disable,value=true',
|
||||
]
|
||||
if '-nic' not in qemu_extra_args:
|
||||
@ -242,6 +271,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
else:
|
||||
qemu_args += ['-nographic']
|
||||
|
||||
if options.boot_mode:
|
||||
qemu_args += qemu_target_info.boot_mode_arg.split(' ')
|
||||
|
||||
# Launch QEMU!
|
||||
if not options.bg_mode:
|
||||
qemu_args += ['-serial', 'mon:stdio']
|
||||
@ -300,6 +332,13 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
'help': 'Enable graphical window',
|
||||
'is_flag': True,
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'names': ['--efuse-file'],
|
||||
'help': ('File used to store efuse values. If not specified, qemu_efuse.bin file '
|
||||
'in build directory is used.'),
|
||||
'is_flag': False,
|
||||
'default': '',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@ -23,15 +22,21 @@ class IdfPyQemuTest(unittest.TestCase):
|
||||
logfile_name = os.path.join(os.environ['IDF_PATH'], 'qemu_log.out')
|
||||
with open(logfile_name, 'w+b') as logfile, \
|
||||
pexpect.spawn(sys.executable, args=args, logfile=logfile) as child:
|
||||
child.expect('Executing action: all')
|
||||
child.expect_exact('Executing action: all')
|
||||
logging.info('Waiting for the build to finish...')
|
||||
child.expect('Executing action: qemu', timeout=120)
|
||||
child.expect('Generating flash image:')
|
||||
child.expect('Generating efuse image:')
|
||||
child.expect('Executing action: monitor')
|
||||
child.expect('Hello world!')
|
||||
child.expect('Restarting in 0 seconds', timeout=20)
|
||||
child.expect('Restarting now.')
|
||||
child.expect_exact('Executing action: qemu', timeout=120)
|
||||
child.expect_exact('Generating flash image:')
|
||||
child.expect_exact('Generating efuse image:')
|
||||
child.expect_exact('Executing action: monitor')
|
||||
child.expect_exact('Hello world!')
|
||||
child.expect_exact('Restarting in 0 seconds', timeout=20)
|
||||
child.expect_exact('Restarting now.')
|
||||
|
||||
args = [idf_py, '-C', hello_world_dir, '-B', build_dir, 'qemu', 'efuse-summary', '--format=summary']
|
||||
with open(logfile_name, 'w+b') as logfile, \
|
||||
pexpect.spawn(sys.executable, args=args, logfile=logfile) as child:
|
||||
child.expect_exact('Executing action: efuse-summary')
|
||||
child.expect_exact('WR_DIS (BLOCK0)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user