1# SPDX-License-Identifier: Apache-2.0 2 3if("${ARCH}" STREQUAL "x86") 4 set_ifndef(QEMU_binary_suffix i386) 5elseif("${ARCH}" STREQUAL "mips") 6 if(CONFIG_BIG_ENDIAN) 7 set_ifndef(QEMU_binary_suffix mips) 8 else() 9 set_ifndef(QEMU_binary_suffix mipsel) 10 endif() 11elseif(DEFINED QEMU_ARCH) 12 set_ifndef(QEMU_binary_suffix ${QEMU_ARCH}) 13else() 14 set_ifndef(QEMU_binary_suffix ${ARCH}) 15endif() 16 17set(qemu_alternate_path $ENV{QEMU_BIN_PATH}) 18if(qemu_alternate_path) 19find_program( 20 QEMU 21 PATHS ${qemu_alternate_path} 22 NO_DEFAULT_PATH 23 NAMES qemu-system-${QEMU_binary_suffix} 24 ) 25else() 26find_program( 27 QEMU 28 qemu-system-${QEMU_binary_suffix} 29 ) 30endif() 31 32# We need to set up uefi-run and OVMF environment 33# for testing UEFI method on qemu platforms 34if(CONFIG_QEMU_UEFI_BOOT) 35 find_program(UEFI NAMES uefi-run REQUIRED) 36 if(DEFINED ENV{OVMF_FD_PATH}) 37 set(OVMF_FD_PATH $ENV{OVMF_FD_PATH}) 38 else() 39 message(FATAL_ERROR "Couldn't find an valid OVMF_FD_PATH.") 40 endif() 41 list(APPEND UEFI -b ${OVMF_FD_PATH} -q ${QEMU}) 42 set(QEMU ${UEFI}) 43endif() 44 45set(qemu_targets 46 run_qemu 47 debugserver_qemu 48 ) 49 50set(QEMU_FLAGS -pidfile) 51if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") 52 list(APPEND QEMU_FLAGS qemu\${QEMU_INSTANCE}.pid) 53else() 54 list(APPEND QEMU_FLAGS qemu${QEMU_INSTANCE}.pid) 55endif() 56 57# If running with sysbuild, we need to ensure this variable is populated 58zephyr_get(QEMU_PIPE) 59# Set up chardev for console. 60if(QEMU_PTY) 61 # Redirect console to a pseudo-tty, used for running automated tests. 62 list(APPEND QEMU_FLAGS -chardev pty,id=con,mux=on) 63elseif(QEMU_PIPE) 64 # Redirect console to a pipe, used for running automated tests. 65 list(APPEND QEMU_FLAGS -chardev pipe,id=con,mux=on,path=${QEMU_PIPE}) 66 # Create the pipe file before passing the path to QEMU. 67 foreach(target ${qemu_targets}) 68 list(APPEND PRE_QEMU_COMMANDS_FOR_${target} COMMAND ${CMAKE_COMMAND} -E touch ${QEMU_PIPE}) 69 endforeach() 70else() 71 # Redirect console to stdio, used for manual debugging. 72 list(APPEND QEMU_FLAGS -chardev stdio,id=con,mux=on) 73endif() 74 75# Connect main serial port to the console chardev. 76list(APPEND QEMU_FLAGS -serial chardev:con) 77 78# Connect semihosting console to the console chardev if configured. 79if(CONFIG_SEMIHOST) 80 list(APPEND QEMU_FLAGS 81 -semihosting-config enable=on,target=auto,chardev=con 82 ) 83endif() 84 85# Connect monitor to the console chardev. 86list(APPEND QEMU_FLAGS -mon chardev=con,mode=readline) 87 88if(CONFIG_QEMU_ICOUNT) 89 if(CONFIG_QEMU_ICOUNT_SLEEP) 90 list(APPEND QEMU_FLAGS 91 -icount shift=${CONFIG_QEMU_ICOUNT_SHIFT},align=off,sleep=on 92 -rtc clock=vm) 93 else() 94 list(APPEND QEMU_FLAGS 95 -icount shift=${CONFIG_QEMU_ICOUNT_SHIFT},align=off,sleep=off 96 -rtc clock=vm) 97 endif() 98endif() 99 100# Add a BT serial device when building for bluetooth, unless the 101# application explicitly opts out with NO_QEMU_SERIAL_BT_SERVER. 102if(CONFIG_BT) 103 if(NOT CONFIG_BT_UART) 104 set(NO_QEMU_SERIAL_BT_SERVER 1) 105 endif() 106 if(NOT NO_QEMU_SERIAL_BT_SERVER) 107 list(APPEND QEMU_FLAGS -serial unix:/tmp/bt-server-bredr) 108 endif() 109endif() 110 111# If we are running a networking application in QEMU, then set proper 112# QEMU variables. This also allows two QEMUs to be hooked together and 113# pass data between them. The QEMU flags are not set for standalone 114# tests defined by CONFIG_NET_TEST. For PPP, the serial port file is 115# not available if we run unit tests which define CONFIG_NET_TEST. 116if(CONFIG_NETWORKING) 117 if(CONFIG_NET_QEMU_SLIP) 118 if((CONFIG_NET_SLIP_TAP) OR (CONFIG_IEEE802154_UPIPE)) 119 set(QEMU_NET_STACK 1) 120 endif() 121 elseif((CONFIG_NET_QEMU_PPP) AND NOT (CONFIG_NET_TEST)) 122 set(QEMU_NET_STACK 1) 123 endif() 124endif() 125 126# TO create independent pipes for each QEMU application set QEMU_PIPE_STACK 127if(QEMU_PIPE_STACK) 128 list(APPEND qemu_targets 129 node 130 ) 131 132 if(NOT QEMU_PIPE_ID) 133 set(QEMU_PIPE_ID 1) 134 endif() 135 136 list(APPEND QEMU_FLAGS 137 -serial none 138 ) 139 140 list(APPEND MORE_FLAGS_FOR_node 141 -serial pipe:/tmp/hub/ip-stack-node${QEMU_PIPE_ID} 142 -pidfile qemu-node${QEMU_PIPE_ID}.pid 143 ) 144 145 set(PIPE_NODE_IN /tmp/hub/ip-stack-node${QEMU_PIPE_ID}.in) 146 set(PIPE_NODE_OUT /tmp/hub/ip-stack-node${QEMU_PIPE_ID}.out) 147 148 set(pipes 149 ${PIPE_NODE_IN} 150 ${PIPE_NODE_OUT} 151 ) 152 153 set(destroy_pipe_commands 154 COMMAND ${CMAKE_COMMAND} -E remove -f ${pipes} 155 ) 156 157 set(create_pipe_commands 158 COMMAND ${CMAKE_COMMAND} -E make_directory /tmp/hub 159 COMMAND mkfifo ${PIPE_NODE_IN} 160 COMMAND mkfifo ${PIPE_NODE_OUT} 161 ) 162 163 set(PRE_QEMU_COMMANDS_FOR_node 164 ${destroy_pipe_commands} 165 ${create_pipe_commands} 166 ) 167 168elseif(QEMU_NET_STACK) 169 list(APPEND qemu_targets 170 client 171 server 172 ) 173 174 foreach(target ${qemu_targets}) 175 if((${target} STREQUAL client) OR (${target} STREQUAL server)) 176 list(APPEND MORE_FLAGS_FOR_${target} 177 -serial pipe:/tmp/ip-stack-${target} 178 -pidfile qemu-${target}.pid 179 ) 180 else() 181 # QEMU_INSTANCE is a command line argument to *make* (not cmake). By 182 # appending the instance name to the pid file we can easily run more 183 # instances of the same sample. 184 185 if(CONFIG_NET_QEMU_PPP) 186 if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") 187 set(ppp_path unix:/tmp/ppp\${QEMU_INSTANCE}) 188 else() 189 set(ppp_path unix:/tmp/ppp${QEMU_INSTANCE}) 190 endif() 191 192 list(APPEND MORE_FLAGS_FOR_${target} 193 -serial ${ppp_path} 194 ) 195 else() 196 if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") 197 set(tmp_file unix:/tmp/slip.sock\${QEMU_INSTANCE}) 198 else() 199 set(tmp_file unix:/tmp/slip.sock${QEMU_INSTANCE}) 200 endif() 201 202 list(APPEND MORE_FLAGS_FOR_${target} 203 -serial ${tmp_file} 204 ) 205 endif() 206 207 endif() 208 endforeach() 209 210 211 set(PIPE_SERVER_IN /tmp/ip-stack-server.in) 212 set(PIPE_SERVER_OUT /tmp/ip-stack-server.out) 213 set(PIPE_CLIENT_IN /tmp/ip-stack-client.in) 214 set(PIPE_CLIENT_OUT /tmp/ip-stack-client.out) 215 216 set(pipes 217 ${PIPE_SERVER_IN} 218 ${PIPE_SERVER_OUT} 219 ${PIPE_CLIENT_IN} 220 ${PIPE_CLIENT_OUT} 221 ) 222 223 set(destroy_pipe_commands 224 COMMAND ${CMAKE_COMMAND} -E remove -f ${pipes} 225 ) 226 227 # TODO: Port to Windows. Perhaps using python? Or removing the 228 # need for mkfifo and create_symlink somehow. 229 set(create_pipe_commands 230 COMMAND mkfifo ${PIPE_SERVER_IN} 231 COMMAND mkfifo ${PIPE_SERVER_OUT} 232 ) 233 if(PCAP) 234 list(APPEND create_pipe_commands 235 COMMAND mkfifo ${PIPE_CLIENT_IN} 236 COMMAND mkfifo ${PIPE_CLIENT_OUT} 237 ) 238 else() 239 list(APPEND create_pipe_commands 240 COMMAND ${CMAKE_COMMAND} -E create_symlink ${PIPE_SERVER_IN} ${PIPE_CLIENT_OUT} 241 COMMAND ${CMAKE_COMMAND} -E create_symlink ${PIPE_SERVER_OUT} ${PIPE_CLIENT_IN} 242 ) 243 endif() 244 245 set(PRE_QEMU_COMMANDS_FOR_server 246 ${destroy_pipe_commands} 247 ${create_pipe_commands} 248 ) 249 if(PCAP) 250 # Start a monitor application to capture traffic 251 # 252 # Assumes; 253 # PCAP has been set to the file where traffic should be captured 254 # NET_TOOLS has been set to the net-tools repo path 255 # net-tools/monitor_15_4 has been built beforehand 256 257 set_ifndef(NET_TOOLS ${ZEPHYR_BASE}/../tools/net-tools) # Default if not set 258 259 list(APPEND PRE_QEMU_COMMANDS_FOR_server 260 #Disable Ctrl-C to ensure that users won't accidentally exit 261 #w/o killing the monitor. 262 COMMAND stty intr ^d 263 264 #This command is run in the background using '&'. This prevents 265 #chaining other commands with '&&'. The command is enclosed in '{}' 266 #to fix this. 267 COMMAND { 268 ${NET_TOOLS}/monitor_15_4 269 ${PCAP} 270 /tmp/ip-stack-server 271 /tmp/ip-stack-client 272 > /dev/null & 273 } 274 ) 275 set(POST_QEMU_COMMANDS_FOR_server 276 # Re-enable Ctrl-C. 277 COMMAND stty intr ^c 278 279 # Kill the monitor_15_4 sub-process 280 COMMAND pkill -P $$$$ 281 ) 282 endif() 283endif(QEMU_PIPE_STACK) 284 285if(CONFIG_CAN AND NOT (CONFIG_NIOS2 OR CONFIG_SOC_LEON3)) 286 # Add CAN bus 0 287 list(APPEND QEMU_FLAGS -object can-bus,id=canbus0) 288 289 if(NOT "${CONFIG_CAN_QEMU_IFACE_NAME}" STREQUAL "") 290 # Connect CAN bus 0 to host SocketCAN interface 291 list(APPEND QEMU_FLAGS 292 -object can-host-socketcan,id=canhost0,if=${CONFIG_CAN_QEMU_IFACE_NAME},canbus=canbus0) 293 endif() 294 295 if(CONFIG_CAN_KVASER_PCI) 296 # Emulate a single-channel Kvaser PCIcan card connected to CAN bus 0 297 list(APPEND QEMU_FLAGS -device kvaser_pci,canbus=canbus0) 298 endif() 299endif() 300 301if(CONFIG_X86_64 AND NOT CONFIG_QEMU_UEFI_BOOT) 302 # QEMU doesn't like 64-bit ELF files. Since we don't use any >4GB 303 # addresses, converting it to 32-bit is safe enough for emulation. 304 add_custom_target(qemu_image_target 305 COMMAND 306 ${CMAKE_OBJCOPY} 307 -O elf32-i386 308 $<TARGET_FILE:${logical_target_for_zephyr_elf}> 309 ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf 310 DEPENDS ${logical_target_for_zephyr_elf} 311 ) 312 313 # Split the 'locore' and 'main' memory regions into separate executable 314 # images and specify the 'locore' as the boot kernel, in order to prevent 315 # the QEMU direct multiboot kernel loader from overwriting the BIOS and 316 # option ROM areas located in between the two memory regions. 317 # (for more details, refer to the issue zephyrproject-rtos/sdk-ng#168) 318 add_custom_target(qemu_locore_image_target 319 COMMAND 320 ${CMAKE_OBJCOPY} 321 -j .locore 322 ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf 323 ${ZEPHYR_BINARY_DIR}/zephyr-qemu-locore.elf 324 2>&1 | grep -iv \"empty loadable segment detected\" || true 325 DEPENDS qemu_image_target 326 ) 327 328 add_custom_target(qemu_main_image_target 329 COMMAND 330 ${CMAKE_OBJCOPY} 331 -R .locore 332 ${ZEPHYR_BINARY_DIR}/zephyr-qemu.elf 333 ${ZEPHYR_BINARY_DIR}/zephyr-qemu-main.elf 334 2>&1 | grep -iv \"empty loadable segment detected\" || true 335 DEPENDS qemu_image_target 336 ) 337 338 add_custom_target( 339 qemu_kernel_target 340 DEPENDS qemu_locore_image_target qemu_main_image_target 341 ) 342 343 set(QEMU_KERNEL_FILE "${ZEPHYR_BINARY_DIR}/zephyr-qemu-locore.elf") 344 345 list(APPEND QEMU_EXTRA_FLAGS 346 "-device;loader,file=${ZEPHYR_BINARY_DIR}/zephyr-qemu-main.elf" 347 ) 348endif() 349 350if(CONFIG_IVSHMEM) 351 if(CONFIG_IVSHMEM_DOORBELL) 352 list(APPEND QEMU_FLAGS 353 -device ivshmem-doorbell,vectors=${CONFIG_IVSHMEM_MSI_X_VECTORS},chardev=ivshmem 354 -chardev socket,path=/tmp/ivshmem_socket,id=ivshmem 355 ) 356 else() 357 list(APPEND QEMU_FLAGS 358 -device ivshmem-plain,memdev=hostmem 359 -object memory-backend-file,size=${CONFIG_QEMU_IVSHMEM_PLAIN_MEM_SIZE}M,share,mem-path=/dev/shm/ivshmem,id=hostmem 360 ) 361 endif() 362endif() 363 364if(CONFIG_NVME) 365 if(qemu_alternate_path) 366 find_program( 367 QEMU_IMG 368 PATHS ${qemu_alternate_path} 369 NO_DEFAULT_PATH 370 NAMES qemu-img 371 ) 372 else() 373 find_program( 374 QEMU_IMG 375 qemu-img 376 ) 377 endif() 378 379 list(APPEND QEMU_EXTRA_FLAGS 380 -drive file=${ZEPHYR_BINARY_DIR}/nvme_disk.img,if=none,id=nvm1 381 -device nvme,serial=deadbeef,drive=nvm1 382 ) 383 384 add_custom_target(qemu_nvme_disk 385 COMMAND 386 ${QEMU_IMG} 387 create 388 ${ZEPHYR_BINARY_DIR}/nvme_disk.img 389 1M 390 ) 391else() 392 add_custom_target(qemu_nvme_disk) 393endif() 394 395if(NOT QEMU_PIPE) 396 set(QEMU_PIPE_COMMENT "\nTo exit from QEMU enter: 'CTRL+a, x'\n") 397endif() 398 399# Don't just test CONFIG_SMP, there is at least one test of the lower 400# level multiprocessor API that wants an auxiliary CPU but doesn't 401# want SMP using it. 402if(NOT CONFIG_MP_MAX_NUM_CPUS MATCHES "1") 403 list(APPEND QEMU_SMP_FLAGS -smp cpus=${CONFIG_MP_MAX_NUM_CPUS}) 404endif() 405 406# Use flags passed in from the environment 407set(env_qemu $ENV{QEMU_EXTRA_FLAGS}) 408separate_arguments(env_qemu) 409list(APPEND QEMU_EXTRA_FLAGS ${env_qemu}) 410 411# Also append QEMU flags from config 412if(NOT CONFIG_QEMU_EXTRA_FLAGS STREQUAL "") 413 set(config_qemu_flags ${CONFIG_QEMU_EXTRA_FLAGS}) 414 separate_arguments(config_qemu_flags) 415 list(APPEND QEMU_EXTRA_FLAGS "${config_qemu_flags}") 416endif() 417 418list(APPEND MORE_FLAGS_FOR_debugserver_qemu -S) 419 420if(NOT CONFIG_QEMU_GDBSERVER_LISTEN_DEV STREQUAL "") 421 list(APPEND MORE_FLAGS_FOR_debugserver_qemu -gdb "${CONFIG_QEMU_GDBSERVER_LISTEN_DEV}") 422endif() 423 424# Architectures can define QEMU_KERNEL_FILE to use a specific output 425# file to pass to qemu (and a "qemu_kernel_target" target to generate 426# it), or set QEMU_KERNEL_OPTION if they want to replace the "-kernel 427# ..." option entirely. 428if(CONFIG_QEMU_UEFI_BOOT) 429 set(QEMU_UEFI_OPTION ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.efi) 430 list(APPEND QEMU_UEFI_OPTION --) 431elseif(DEFINED QEMU_KERNEL_FILE) 432 set(QEMU_KERNEL_OPTION "-kernel;${QEMU_KERNEL_FILE}") 433elseif(NOT DEFINED QEMU_KERNEL_OPTION) 434 set(QEMU_KERNEL_OPTION "-kernel;$<TARGET_FILE:${logical_target_for_zephyr_elf}>") 435elseif(DEFINED QEMU_KERNEL_OPTION) 436 string(CONFIGURE "${QEMU_KERNEL_OPTION}" QEMU_KERNEL_OPTION) 437endif() 438 439foreach(target ${qemu_targets}) 440 add_custom_target(${target} 441 ${PRE_QEMU_COMMANDS} 442 ${PRE_QEMU_COMMANDS_FOR_${target}} 443 COMMAND 444 ${QEMU} 445 ${QEMU_UEFI_OPTION} 446 ${QEMU_FLAGS_${ARCH}} 447 ${QEMU_FLAGS} 448 ${QEMU_EXTRA_FLAGS} 449 ${MORE_FLAGS_FOR_${target}} 450 ${QEMU_SMP_FLAGS} 451 ${QEMU_KERNEL_OPTION} 452 ${POST_QEMU_COMMANDS_FOR_${target}} 453 DEPENDS ${logical_target_for_zephyr_elf} 454 WORKING_DIRECTORY ${APPLICATION_BINARY_DIR} 455 COMMENT "${QEMU_PIPE_COMMENT}[QEMU] CPU: ${QEMU_CPU_TYPE_${ARCH}}" 456 USES_TERMINAL 457 ) 458 if(DEFINED QEMU_KERNEL_FILE) 459 add_dependencies(${target} qemu_nvme_disk qemu_kernel_target) 460 endif() 461endforeach() 462