# SPDX-License-Identifier: Apache-2.0 if("${ARCH}" STREQUAL "x86") set_ifndef(QEMU_binary_suffix i386) elseif("${ARCH}" STREQUAL "mips") if(CONFIG_BIG_ENDIAN) set_ifndef(QEMU_binary_suffix mips) else() set_ifndef(QEMU_binary_suffix mipsel) endif() elseif(DEFINED QEMU_ARCH) set_ifndef(QEMU_binary_suffix ${QEMU_ARCH}) else() set_ifndef(QEMU_binary_suffix ${ARCH}) endif() set(qemu_alternate_path $ENV{QEMU_BIN_PATH}) if(qemu_alternate_path) find_program( QEMU PATHS ${qemu_alternate_path} NO_DEFAULT_PATH NAMES qemu-system-${QEMU_binary_suffix} ) else() find_program( QEMU qemu-system-${QEMU_binary_suffix} ) endif() # We need to set up uefi-run and OVMF environment # for testing UEFI method on qemu platforms if(CONFIG_QEMU_UEFI_BOOT) find_program(UEFI NAMES uefi-run REQUIRED) if(DEFINED ENV{OVMF_FD_PATH}) set(OVMF_FD_PATH $ENV{OVMF_FD_PATH}) else() message(FATAL_ERROR "Couldn't find an valid OVMF_FD_PATH.") endif() list(APPEND UEFI -b ${OVMF_FD_PATH} -q ${QEMU}) set(QEMU ${UEFI}) endif() set(qemu_targets run_qemu debugserver_qemu ) set(QEMU_FLAGS -pidfile) if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") list(APPEND QEMU_FLAGS qemu\${QEMU_INSTANCE}.pid) else() list(APPEND QEMU_FLAGS qemu${QEMU_INSTANCE}.pid) endif() # If running with sysbuild, we need to ensure this variable is populated zephyr_get(QEMU_PIPE) # Set up chardev for console. if(QEMU_PTY) # Redirect console to a pseudo-tty, used for running automated tests. list(APPEND QEMU_FLAGS -chardev pty,id=con,mux=on) elseif(QEMU_PIPE) # Redirect console to a pipe, used for running automated tests. list(APPEND QEMU_FLAGS -chardev pipe,id=con,mux=on,path=${QEMU_PIPE}) # Create the pipe file before passing the path to QEMU. foreach(target ${qemu_targets}) list(APPEND PRE_QEMU_COMMANDS_FOR_${target} COMMAND ${CMAKE_COMMAND} -E touch ${QEMU_PIPE}) endforeach() else() # Redirect console to stdio, used for manual debugging. list(APPEND QEMU_FLAGS -chardev stdio,id=con,mux=on) endif() # Connect main serial port to the console chardev. list(APPEND QEMU_FLAGS -serial chardev:con) # Connect semihosting console to the console chardev if configured. if(CONFIG_SEMIHOST) list(APPEND QEMU_FLAGS -semihosting-config enable=on,target=auto,chardev=con ) endif() # Connect monitor to the console chardev. list(APPEND QEMU_FLAGS -mon chardev=con,mode=readline) if(CONFIG_QEMU_ICOUNT) if(CONFIG_QEMU_ICOUNT_SLEEP) list(APPEND QEMU_FLAGS -icount shift=${CONFIG_QEMU_ICOUNT_SHIFT},align=off,sleep=on -rtc clock=vm) else() list(APPEND QEMU_FLAGS -icount shift=${CONFIG_QEMU_ICOUNT_SHIFT},align=off,sleep=off -rtc clock=vm) endif() endif() # Add a BT serial device when building for bluetooth, unless the # application explicitly opts out with NO_QEMU_SERIAL_BT_SERVER. if(CONFIG_BT) if(CONFIG_BT_NO_DRIVER) set(NO_QEMU_SERIAL_BT_SERVER 1) endif() if(NOT NO_QEMU_SERIAL_BT_SERVER) list(APPEND QEMU_FLAGS -serial unix:/tmp/bt-server-bredr) endif() endif() # If we are running a networking application in QEMU, then set proper # QEMU variables. This also allows two QEMUs to be hooked together and # pass data between them. The QEMU flags are not set for standalone # tests defined by CONFIG_NET_TEST. For PPP, the serial port file is # not available if we run unit tests which define CONFIG_NET_TEST. if(CONFIG_NETWORKING) if(CONFIG_NET_QEMU_SLIP) if((CONFIG_NET_SLIP_TAP) OR (CONFIG_IEEE802154_UPIPE)) set(QEMU_NET_STACK 1) endif() elseif((CONFIG_NET_QEMU_PPP) AND NOT (CONFIG_NET_TEST)) set(QEMU_NET_STACK 1) endif() endif() # TO create independent pipes for each QEMU application set QEMU_PIPE_STACK if(QEMU_PIPE_STACK) list(APPEND qemu_targets node ) if(NOT QEMU_PIPE_ID) set(QEMU_PIPE_ID 1) endif() list(APPEND QEMU_FLAGS -serial none ) list(APPEND MORE_FLAGS_FOR_node -serial pipe:/tmp/hub/ip-stack-node${QEMU_PIPE_ID} -pidfile qemu-node${QEMU_PIPE_ID}.pid ) set(PIPE_NODE_IN /tmp/hub/ip-stack-node${QEMU_PIPE_ID}.in) set(PIPE_NODE_OUT /tmp/hub/ip-stack-node${QEMU_PIPE_ID}.out) set(pipes ${PIPE_NODE_IN} ${PIPE_NODE_OUT} ) set(destroy_pipe_commands COMMAND ${CMAKE_COMMAND} -E remove -f ${pipes} ) set(create_pipe_commands COMMAND ${CMAKE_COMMAND} -E make_directory /tmp/hub COMMAND mkfifo ${PIPE_NODE_IN} COMMAND mkfifo ${PIPE_NODE_OUT} ) set(PRE_QEMU_COMMANDS_FOR_node ${destroy_pipe_commands} ${create_pipe_commands} ) elseif(QEMU_NET_STACK) list(APPEND qemu_targets client server ) foreach(target ${qemu_targets}) if((${target} STREQUAL client) OR (${target} STREQUAL server)) list(APPEND MORE_FLAGS_FOR_${target} -serial pipe:/tmp/ip-stack-${target} -pidfile qemu-${target}.pid ) else() # QEMU_INSTANCE is a command line argument to *make* (not cmake). By # appending the instance name to the pid file we can easily run more # instances of the same sample. if(CONFIG_NET_QEMU_PPP) if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") set(ppp_path unix:/tmp/ppp\${QEMU_INSTANCE}) else() set(ppp_path unix:/tmp/ppp${QEMU_INSTANCE}) endif() list(APPEND MORE_FLAGS_FOR_${target} -serial ${ppp_path} ) else() if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") set(tmp_file unix:/tmp/slip.sock\${QEMU_INSTANCE}) else() set(tmp_file unix:/tmp/slip.sock${QEMU_INSTANCE}) endif() list(APPEND MORE_FLAGS_FOR_${target} -serial ${tmp_file} ) endif() endif() endforeach() set(PIPE_SERVER_IN /tmp/ip-stack-server.in) set(PIPE_SERVER_OUT /tmp/ip-stack-server.out) set(PIPE_CLIENT_IN /tmp/ip-stack-client.in) set(PIPE_CLIENT_OUT /tmp/ip-stack-client.out) set(pipes ${PIPE_SERVER_IN} ${PIPE_SERVER_OUT} ${PIPE_CLIENT_IN} ${PIPE_CLIENT_OUT} ) set(destroy_pipe_commands COMMAND ${CMAKE_COMMAND} -E remove -f ${pipes} ) # TODO: Port to Windows. Perhaps using python? Or removing the # need for mkfifo and create_symlink somehow. set(create_pipe_commands COMMAND mkfifo ${PIPE_SERVER_IN} COMMAND mkfifo ${PIPE_SERVER_OUT} ) if(PCAP) list(APPEND create_pipe_commands COMMAND mkfifo ${PIPE_CLIENT_IN} COMMAND mkfifo ${PIPE_CLIENT_OUT} ) else() list(APPEND create_pipe_commands COMMAND ${CMAKE_COMMAND} -E create_symlink ${PIPE_SERVER_IN} ${PIPE_CLIENT_OUT} COMMAND ${CMAKE_COMMAND} -E create_symlink ${PIPE_SERVER_OUT} ${PIPE_CLIENT_IN} ) endif() set(PRE_QEMU_COMMANDS_FOR_server ${destroy_pipe_commands} ${create_pipe_commands} ) if(PCAP) # Start a monitor application to capture traffic # # Assumes; # PCAP has been set to the file where traffic should be captured # NET_TOOLS has been set to the net-tools repo path # net-tools/monitor_15_4 has been built beforehand set_ifndef(NET_TOOLS ${ZEPHYR_BASE}/../net-tools) # Default if not set list(APPEND PRE_QEMU_COMMANDS_FOR_server COMMAND #This command is run in the background using '&'. This prevents #chaining other commands with '&&'. The command is enclosed in '{}' #to fix this. { ${NET_TOOLS}/monitor_15_4 ${PCAP} /tmp/ip-stack-server /tmp/ip-stack-client > /dev/null & } # TODO: Support cleanup of the monitor_15_4 process ) endif() endif(QEMU_PIPE_STACK) if(CONFIG_CAN AND NOT (CONFIG_NIOS2 OR CONFIG_SOC_LEON3)) # Add CAN bus 0 list(APPEND QEMU_FLAGS -object can-bus,id=canbus0) if(NOT "${CONFIG_CAN_QEMU_IFACE_NAME}" STREQUAL "") # Connect CAN bus 0 to host SocketCAN interface list(APPEND QEMU_FLAGS -object can-host-socketcan,id=canhost0,if=${CONFIG_CAN_QEMU_IFACE_NAME},canbus=canbus0) endif() if(CONFIG_CAN_KVASER_PCI) # Emulate a single-channel Kvaser PCIcan card connected to CAN bus 0 list(APPEND QEMU_FLAGS -device kvaser_pci,canbus=canbus0) endif() endif() if(CONFIG_X86_64 AND NOT CONFIG_QEMU_UEFI_BOOT) # QEMU doesn't like 64-bit ELF files. Since we don't use any >4GB # addresses, converting it to 32-bit is safe enough for emulation. add_custom_target(qemu_image_target COMMAND ${CMAKE_OBJCOPY} -O elf32-i386 $ ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf DEPENDS ${logical_target_for_zephyr_elf} ) # Split the 'locore' and 'main' memory regions into separate executable # images and specify the 'locore' as the boot kernel, in order to prevent # the QEMU direct multiboot kernel loader from overwriting the BIOS and # option ROM areas located in between the two memory regions. # (for more details, refer to the issue zephyrproject-rtos/sdk-ng#168) add_custom_target(qemu_locore_image_target COMMAND ${CMAKE_OBJCOPY} -j .locore ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf ${ZEPHYR_BINARY_DIR}/zephyr-qemu-locore.elf 2>&1 | grep -iv \"empty loadable segment detected\" || true DEPENDS qemu_image_target ) add_custom_target(qemu_main_image_target COMMAND ${CMAKE_OBJCOPY} -R .locore ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf ${ZEPHYR_BINARY_DIR}/zephyr-qemu-main.elf 2>&1 | grep -iv \"empty loadable segment detected\" || true DEPENDS qemu_image_target ) add_custom_target( qemu_kernel_target DEPENDS qemu_locore_image_target qemu_main_image_target ) set(QEMU_KERNEL_FILE "${ZEPHYR_BINARY_DIR}/zephyr-qemu-locore.elf") list(APPEND QEMU_EXTRA_FLAGS "-device;loader,file=${ZEPHYR_BINARY_DIR}/zephyr-qemu-main.elf" ) endif() if(CONFIG_IVSHMEM) if(CONFIG_IVSHMEM_DOORBELL) list(APPEND QEMU_FLAGS -device ivshmem-doorbell,vectors=${CONFIG_IVSHMEM_MSI_X_VECTORS},chardev=ivshmem -chardev socket,path=/tmp/ivshmem_socket,id=ivshmem ) else() list(APPEND QEMU_FLAGS -device ivshmem-plain,memdev=hostmem -object memory-backend-file,size=${CONFIG_QEMU_IVSHMEM_PLAIN_MEM_SIZE}M,share,mem-path=/dev/shm/ivshmem,id=hostmem ) endif() endif() if(CONFIG_NVME) if(qemu_alternate_path) find_program( QEMU_IMG PATHS ${qemu_alternate_path} NO_DEFAULT_PATH NAMES qemu-img ) else() find_program( QEMU_IMG qemu-img ) endif() list(APPEND QEMU_EXTRA_FLAGS -drive file=${ZEPHYR_BINARY_DIR}/nvme_disk.img,if=none,id=nvm1 -device nvme,serial=deadbeef,drive=nvm1 ) add_custom_target(qemu_nvme_disk COMMAND ${QEMU_IMG} create ${ZEPHYR_BINARY_DIR}/nvme_disk.img 1M ) else() add_custom_target(qemu_nvme_disk) endif() if(NOT QEMU_PIPE) set(QEMU_PIPE_COMMENT "\nTo exit from QEMU enter: 'CTRL+a, x'\n") endif() # Don't just test CONFIG_SMP, there is at least one test of the lower # level multiprocessor API that wants an auxiliary CPU but doesn't # want SMP using it. if(NOT CONFIG_MP_MAX_NUM_CPUS MATCHES "1") list(APPEND QEMU_SMP_FLAGS -smp cpus=${CONFIG_MP_MAX_NUM_CPUS}) endif() # Use flags passed in from the environment set(env_qemu $ENV{QEMU_EXTRA_FLAGS}) separate_arguments(env_qemu) list(APPEND QEMU_EXTRA_FLAGS ${env_qemu}) # Also append QEMU flags from config if(NOT CONFIG_QEMU_EXTRA_FLAGS STREQUAL "") set(config_qemu_flags ${CONFIG_QEMU_EXTRA_FLAGS}) separate_arguments(config_qemu_flags) list(APPEND QEMU_EXTRA_FLAGS "${config_qemu_flags}") endif() list(APPEND MORE_FLAGS_FOR_debugserver_qemu -S) if(NOT CONFIG_QEMU_GDBSERVER_LISTEN_DEV STREQUAL "") list(APPEND MORE_FLAGS_FOR_debugserver_qemu -gdb "${CONFIG_QEMU_GDBSERVER_LISTEN_DEV}") endif() # Architectures can define QEMU_KERNEL_FILE to use a specific output # file to pass to qemu (and a "qemu_kernel_target" target to generate # it), or set QEMU_KERNEL_OPTION if they want to replace the "-kernel # ..." option entirely. if(CONFIG_QEMU_UEFI_BOOT) set(QEMU_UEFI_OPTION ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.efi) list(APPEND QEMU_UEFI_OPTION --) elseif(DEFINED QEMU_KERNEL_FILE) set(QEMU_KERNEL_OPTION "-kernel;${QEMU_KERNEL_FILE}") elseif(NOT DEFINED QEMU_KERNEL_OPTION) set(QEMU_KERNEL_OPTION "-kernel;$") elseif(DEFINED QEMU_KERNEL_OPTION) string(CONFIGURE "${QEMU_KERNEL_OPTION}" QEMU_KERNEL_OPTION) endif() foreach(target ${qemu_targets}) add_custom_target(${target} ${PRE_QEMU_COMMANDS} ${PRE_QEMU_COMMANDS_FOR_${target}} COMMAND ${QEMU} ${QEMU_UEFI_OPTION} ${QEMU_FLAGS_${ARCH}} ${QEMU_FLAGS} ${QEMU_EXTRA_FLAGS} ${MORE_FLAGS_FOR_${target}} ${QEMU_SMP_FLAGS} ${QEMU_KERNEL_OPTION} DEPENDS ${logical_target_for_zephyr_elf} WORKING_DIRECTORY ${APPLICATION_BINARY_DIR} COMMENT "${QEMU_PIPE_COMMENT}[QEMU] CPU: ${QEMU_CPU_TYPE_${ARCH}}" USES_TERMINAL ) if(DEFINED QEMU_KERNEL_FILE) add_dependencies(${target} qemu_nvme_disk qemu_kernel_target) endif() endforeach()