zephyr/cmake/emu/qemu.cmake
Andy Ross b69d0da82d arch/x86_64: New architecture added
This patch adds a x86_64 architecture and qemu_x86_64 board to Zephyr.
Only the basic architecture support needed to run 64 bit code is
added; no drivers are added, though a low-level console exists and is
wired to printk().

The support is built on top of a "X86 underkernel" layer, which can be
built in isolation as a unit test on a Linux host.

Limitations:

+ Right now the SDK lacks an x86_64 toolchain.  The build will fall
  back to a host toolchain if it finds no cross compiler defined,
  which is tested to work on gcc 8.2.1 right now.

+ No x87/SSE/AVX usage is allowed.  This is a stronger limitation than
  other architectures where the instructions work from one thread even
  if the context switch code doesn't support it.  We are passing
  -no-sse to prevent gcc from automatically generating SSE
  instructions for non-floating-point purposes, which has the side
  effect of changing the ABI.  Future work to handle the FPU registers
  will need to be combined with an "application" ABI distinct from the
  kernel one (or just to require USERSPACE).

+ Paging is enabled (it has to be in long mode), but is a 1:1 mapping
  of all memory.  No MMU/USERSPACE support yet.

+ We are building with -mno-red-zone for stack size reasons, but this
  is a valuable optimization.  Enabling it requires automatic stack
  switching, which requires a TSS, which means it has to happen after
  MMU support.

+ The OS runs in 64 bit mode, but for compatibility reasons is
  compiled to the 32 bit "X32" ABI.  So while the full 64 bit
  registers and instruction set are available, C pointers are 32 bits
  long and Zephyr is constrained to run in the bottom 4G of memory.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-01-11 15:18:52 -05:00

267 lines
7.0 KiB
CMake

if("${ARCH}" STREQUAL "x86")
set_ifndef(QEMU_binary_suffix i386)
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()
set(qemu_targets
run
debugserver
)
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()
# We can set "default" value for QEMU_PTY & QEMU_PIPE on cmake invocation.
if(QEMU_PTY)
# Send console output to a pseudo-tty, used for running automated tests
set(CMAKE_QEMU_SERIAL0 pty)
else()
if(QEMU_PIPE)
# Send console output to a pipe, used for running automated tests
set(CMAKE_QEMU_SERIAL0 pipe:${QEMU_PIPE})
else()
set(CMAKE_QEMU_SERIAL0 mon:stdio)
endif()
endif()
# But also can set QEMU_PTY & QEMU_PIPE on *make* (not cmake) invocation,
# like it was before cmake.
if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
list(APPEND QEMU_FLAGS
-serial
\${if \${QEMU_PTY}, pty, \${if \${QEMU_PIPE}, pipe:\${QEMU_PIPE}, ${CMAKE_QEMU_SERIAL0}}}
# NB: \$ is not supported by Ninja
)
else()
list(APPEND QEMU_FLAGS
-serial
${CMAKE_QEMU_SERIAL0}
)
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(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.
if(CONFIG_NETWORKING)
if(CONFIG_NET_QEMU_SLIP)
if((CONFIG_NET_SLIP_TAP) OR (CONFIG_IEEE802154_UPIPE))
set(QEMU_NET_STACK 1)
endif()
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(${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()
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 ${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_X86_IAMCU)
list(APPEND PRE_QEMU_COMMANDS
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/qemu-machine-hack.py
$<TARGET_FILE:${logical_target_for_zephyr_elf}>
)
endif()
if(CONFIG_X86_64)
set(QEMU_KERNEL_FILE "${CMAKE_BINARY_DIR}/zephyr-qemu.elf")
endif()
if(NOT QEMU_PIPE)
set(QEMU_PIPE_COMMENT "\nTo exit from QEMU enter: 'CTRL+a, x'\n")
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})
list(APPEND MORE_FLAGS_FOR_debugserver -s -S)
# 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(DEFINED QEMU_KERNEL_FILE)
set(QEMU_KERNEL_OPTION "-kernel;${QEMU_KERNEL_FILE}")
elseif(NOT DEFINED QEMU_KERNEL_OPTION)
set(QEMU_KERNEL_OPTION "-kernel;$<TARGET_FILE:${logical_target_for_zephyr_elf}>")
endif()
foreach(target ${qemu_targets})
add_custom_target(${target}
${PRE_QEMU_COMMANDS}
${PRE_QEMU_COMMANDS_FOR_${target}}
COMMAND
${QEMU}
${QEMU_FLAGS_${ARCH}}
${QEMU_FLAGS}
${QEMU_EXTRA_FLAGS}
${MORE_FLAGS_FOR_${target}}
${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_kernel_target)
endif()
endforeach()