Skip to content
Ivan Grokhotkov edited this page Aug 3, 2022 · 17 revisions

About this repository

This repository contains a fork of QEMU with patches for Espressif chips support. We hope that these patches will eventually be mature enough to become part of upstream QEMU project.

At the moment, Espressif does not provide support for QEMU. We appreciate issue reports, but keep in mind that our response may be delayed. We will likely not be able to help with issues which require extensive troubleshooting or don't have a straightforward way to reproduce them. We will also likely not be able to help with particular use cases which aren't supported yet (e.g. due to missing emulation of some peripherals).

The main branch of this repository is esp-develop. This branch will often be rebased on top of the upstream master branch, and force-pushed. If you wish to submit a non-trivial PR, please open an issue first so that we can avoid making conflicting changes.

Disclaimer

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

Usage

The rest of this page provides some ESP-specific instructions for running QEMU. Refer to the official documentation for general QEMU usage questions: https://www.qemu.org/documentation/.

Prerequisites

Check QEMU documentation regarding build prerequisites.

In addition, make sure that libgcrypt is installed (libgcrypt-devel on Ubuntu, libgcrypt on Arch, libgcrypt on macOS Homebrew).

Configure

./configure --target-list=xtensa-softmmu \
    --enable-gcrypt \
    --enable-debug --enable-sanitizers \
    --disable-strip --disable-user \
    --disable-capstone --disable-vnc \
    --disable-sdl --disable-gtk

(feel free to add more --disable options, to reduce the first compilation time)

Build

ninja -C build

A program build/qemu-system-xtensa should be built.

Build test app

Build an ESP-IDF application.

Create a flash image, combining the bootloader, partition table, and the application. This can be done using esptool.py merge_bin command, supported in esptool.py 3.1 or later.

(cd build; esptool.py --chip esp32 merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args)

Here flash_args is a file generated by ESP-IDF build system in the build directory, it contains the list of names of binary files and corresponding flash addresses. merge_bin command takes this list and creates the binary image of the whole flash. --fill-flash-size 4MB argument specifies the total flash size.

Notes

  • For "Secure Boot" feature in ESP-IDF, we recommend separate command to flash bootloader and hence flash_args file do not have corresponding entry. However, you may modify flash_args file to add entry for bootloader.bin as per below:
0x1000 bootloader/bootloader.bin
  • It is also possible to use esptool.py to "flash" the application into Qemu, but Qemu needs to be started with correct strapping mode. See below.

Run QEMU

Start QEMU without attaching GDB:

build/qemu-system-xtensa -nographic \
    -machine esp32 \
    -drive file=flash_image.bin,if=mtd,format=raw

Start QEMU with GDB server, waiting for connection:

build/qemu-system-xtensa -nographic -s -S \
    -machine esp32 \
    -drive file=flash_image.bin,if=mtd,format=raw

To connect using GDB, use the following:

xtensa-esp32-elf-gdb build/app-name.elf \
    -ex "target remote :1234" \
    -ex "monitor system_reset" \
    -ex "tb app_main" -ex "c"

(or put the same commands into a gdbinit script)

GDB compatibility (for esp-2021r1 and earlier toolchain releases)

If GDB reports such errors when connecting to QEMU:

Remote 'g' packet reply is too long (expected 420 bytes, got 628 bytes):

then you need to set the following environment variable:

export QEMU_XTENSA_CORE_REGS_ONLY=1

When this environment variable is set, QEMU will only send the values of non-privileged registers to GDB.

In future Xtensa toolchain releases (esp-2021r2 and later), GDB will work without setting this variable.

Hardware crypto support

Starting from IDF 4.1, the following hardware crypto features are enabled by default: AES, SHA, RSA.

At the time of writing, only the SHA and RSA engines are implemented in QEMU.

Note, SHA emulation currently doesn't support concurrent operations with different SHA types.

You can disable the missing hardware crypto features in menuconfig (under Component config, mbedTLS).

Enabling Ethernet support

Support for Opencores Ethernet MAC in ESP-IDF is added in https://github.com/espressif/esp-idf/commit/31dac92e5f0daac98190fd603df213a0a25a3807.

  • When running protocols examples, enable CONFIG_EXAMPLE_CONNECT_ETHERNET and CONFIG_EXAMPLE_USE_OPENETH.
  • When running a custom app, enable CONFIG_ETH_USE_OPENETH and initialize the Ethernet driver as it is done in examples/common_components/protocol_examples_common/connect.c (look for esp_eth_mac_new_openeth).

When starting QEMU, use open_eth network device.

User mode networking

For example, to start networking in user mode (TCP/UDP only, emulated device is behind NAT), add the following option to the QEMU command line:

-nic user,model=open_eth

Some ESP project (specifically running tcp listeners) might need port forwarding to be setup,

-nic user,model=open_eth,id=lo0,hostfwd=tcp:127.0.0.1:PORT_HOST-:PORT_GUEST

(e.g. asio-echo-server sets up a server on 2222 by default, so hostfwd=tcp:127.0.0.1:2222-:2222 enables to nc localhost 2222 from host machine)

Bridge/tap networking

Specifying bootstrapping mode

Add extra argument to the command line to specify the desired strapping mode:

-global driver=esp32.gpio,property=strap_mode,value=0x0f

This sets the value of GPIO_STRAP register.

  • Use 0x12 for flash boot mode (default)
  • Use 0x0f for UART-only download mode (since the SDIO part is not implemented)

Specifying eFuse storage

Add extra arguments to the command line:

-drive file=qemu_efuse.bin,if=none,format=raw,id=efuse
-global driver=nvram.esp32.efuse,property=drive,value=efuse

The first argument creates creates a block device backed by qemu_efuse.bin file, with identifier efuse. The second line configures nvram.esp32.efuse device to use this block device for storage.

The file must be created prior to starting qemu:

dd if=/dev/zero bs=1 count=124 of=/tmp/qemu_efuse.bin

124 bytes is the total size of ESP32 eFuse blocks.

Note

Specifying eFuse storage is mandatory to test out any platform security features like "Secure Boot" or "Flash Encryption".

Disabling the watchdogs

By default, Timer Group watchdog timers are emulated, and TG0 WDT is enabled at reset. It is sometimes useful to disable these watchdog timers. This can be done by adding the following to the command line:

-global driver=timer.esp32.timg,property=wdt_disable,value=true

This disables emulation of TG watchdog timers. Even if the application configures them, they will not fire.

RTC watchdog timer is not emulated yet, so doesn't need to be disabled.

Using esptool.py and espefuse.py to interact with Qemu

  1. Start Qemu:

    build/qemu-system-xtensa -nographic \
        -machine esp32 \
        -drive file=flash_image.bin,if=mtd,format=raw \
        -global driver=esp32.gpio,property=strap_mode,value=0x0f \
        -drive file=qemu_efuse.bin,if=none,format=raw,id=efuse \
        -global driver=nvram.esp32.efuse,property=drive,value=efuse \
        -serial tcp::5555,server,nowait
    

    The final line redirects the emulated UART to TCP port 5555 (Qemu acts as a server).

    Type q and press Enter at any time to quit.

  2. Run esptool.py:

    esptool.py -p socket://localhost:5555 flash_id
    

    Flashing with idf.py also works:

    export ESPPORT=socket://localhost:5555
    idf.py flash
    
  3. Or, run espefuse.py:

    espefuse.py --port socket://localhost:5555 --do-not-confirm burn_custom_mac 00:11:22:33:44:55
    

Note: esptool can not reset the emulated chip using the RTS signal, because the state of RTS is not transmitted over TCP to Qemu. To reset the emulated chip, run system_reset command in Qemu console (started at step 1).

Specifying ROM ELF file

If -kernel and -bios arguments are not given, ESP32 (rev. 3) ROM code will be loaded. This ROM code binary is included in the repository. To specify the ROM code ELF file to load, pass the filename with a -bios <filename> argument.

Emulating ESP32 ECO3

In order for the application to detect the emulated chip as ESP32 ECO3, the following virtual efuses must be set:

  • CHIP_VER_REV1
  • CHIP_VER_REV2

Here is the corresponding efuse file (in hexadecimal, produced using xxd -p):

000000000000000000000000008000000000000000001000000000000000
000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
00000000

To convert this (efuse.hex) back to binary, run xxd -r -p efuse.hex qemu_efuse.bin.

Alternatively, these bits can be set using espefuse:

espefuse.py --port=socket://localhost:5555 burn_efuse CHIP_VER_REV1
espefuse.py --port=socket://localhost:5555 burn_efuse CHIP_VER_REV2

Using flash encryption

Instructions to be added.

Adding PSRAM

QEMU "memory size" option can be used to enable PSRAM emulation. By default, no PSRAM is added to the machine. You can add 2MB or 4MB PSRAM using -m 2M or -m 4M command line options, respectively.

Note that PSRAM MMU is not emulated yet, so things like bank switching (himem in IDF) do not work.

Using SD cards

QEMU emulates SD/MMC host controller used in ESP32. To add an SD card to the system, create an image and pass it to QEMU.

  1. Create a raw image file, for example 64 MB:
    $ dd if=/dev/zero bs=$((1024*1024)) count=64 of=sd_image.bin
    
  2. Add the following argument when running QEMU:
    -drive file=sd_image.bin,if=sd,format=raw
    

If you need to create a large SD card image, it is recommended to use sparse cqow2 images instead of raw ones. Consult QEMU manual about qemu-img tool for details.

Only one SD card is supported at a time. You can use either slot 0 or slot 1 of the SD/MMC controller in application code.

Clone this wiki locally