From 689943c8e54ae222b00e5a2859230f9e1033a28b Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Mon, 18 May 2020 15:10:12 -0400 Subject: [PATCH 1/5] Change mode of .gitignore to no execute --- .gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 .gitignore diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 From d8e66c946b093ea3a0828c2adbcaf387acd6f249 Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Tue, 16 Jun 2020 22:42:18 -0400 Subject: [PATCH 2/5] Add LibVMI VM example with Buildroot Buildroot is used to cross-compile LibVMI with its dependencies and to produce the bzImage and rootfs. The following definitions were added: - `VOLATILITY_PROFILE_PATH` - `REKALL_PROFILE_PATH` - `ENABLE_BUILD_EXAMPLES` To build the LibVMI VM example, one needs to first create a profile of the host kernel with rekall or volatility and provide its path during cmake configuration time. Information to generate a profile can be found in the LibVMI repo or in the DRAKVUF website. e.g.: ``` cmake ../hypervisor -DCACHE_DIR=/tmp/boxy_cache \ -DENABLE_BUILD_EXAMPLES=ON \ -DREKALL_PROFILE_PATH= make ``` The LibVMI process list example can be run as follows: ``` ./prefixes/x86_64-userspace-elf/bin/bfexec \ --bzimage --path prefixes/vms/buildroot/bzImage \ --initrd prefixes/vms/buildroot/rootfs.cpio.gz --uart=0x3F8 \ --verbose --size=0x8000000 \ --cmdline="vmi=/usr/bin/vmi-process-list,-n,dom0" ``` Notes: Vmilinux provides vmi-init, an init app that can start an application with arguments. It does so by parsing the kernel cmdline. vmi-init can also be compiled with statically in order to be the only application running as init. In this case, vmi-init does some pre main work to populate main args and properly calls the linked main, with no extra work needed from the linked application. On Windows, Vagrant can be used to generate the guest VM. To facilitate libvmi development, one can use the following buildroot options: - `BR2_LIBVMI_DEBUG=y` enable libvmi debug definitions - `BR2_LIBVMI_PATH=` use local copy of libvmi directory when set --- CMakeLists.txt | 23 +++ bfsdk/include/bfhypercall.h | 2 + config.cmake | 5 +- examples/buildroot/Config.in | 2 + examples/buildroot/common/post-build.sh | 69 ++++++++ examples/buildroot/configs/vmilinux_defconfig | 21 +++ examples/buildroot/external.desc | 2 + examples/buildroot/external.mk | 1 + examples/buildroot/package/cpio/cpio.mk | 10 ++ examples/buildroot/package/libvmi/Config.in | 16 ++ examples/buildroot/package/libvmi/libvmi.mk | 40 +++++ examples/buildroot/package/vmilinux/Config.in | 7 + .../buildroot/package/vmilinux/vmilinux.mk | 36 ++++ ...-package-json-c-bump-version-to-0.14.patch | 57 +++++++ examples/vmilinux/CMakeLists.txt | 63 +++++++ examples/vmilinux/include/cmdl.h | 131 ++++++++++++++ examples/vmilinux/src/init-vmi.cpp | 160 ++++++++++++++++++ examples/vmilinux/tests/cmdline.txt | 1 + examples/vmilinux/tests/test-init.cpp | 32 ++++ scripts/cmake/config/default.cmake | 26 +++ scripts/cmake/depends/buildroot.cmake | 76 +++++++++ scripts/cmake/utils/copy_no_follow.cmake | 26 +++ scripts/cmake/validate.cmake | 56 ++++++ scripts/util/colors.sh | 30 ++++ 24 files changed, 891 insertions(+), 1 deletion(-) create mode 100644 examples/buildroot/Config.in create mode 100755 examples/buildroot/common/post-build.sh create mode 100644 examples/buildroot/configs/vmilinux_defconfig create mode 100644 examples/buildroot/external.desc create mode 100644 examples/buildroot/external.mk create mode 100644 examples/buildroot/package/cpio/cpio.mk create mode 100644 examples/buildroot/package/libvmi/Config.in create mode 100644 examples/buildroot/package/libvmi/libvmi.mk create mode 100644 examples/buildroot/package/vmilinux/Config.in create mode 100644 examples/buildroot/package/vmilinux/vmilinux.mk create mode 100644 examples/buildroot/patch/0001-package-json-c-bump-version-to-0.14.patch create mode 100644 examples/vmilinux/CMakeLists.txt create mode 100644 examples/vmilinux/include/cmdl.h create mode 100644 examples/vmilinux/src/init-vmi.cpp create mode 100644 examples/vmilinux/tests/cmdline.txt create mode 100644 examples/vmilinux/tests/test-init.cpp create mode 100755 scripts/cmake/depends/buildroot.cmake create mode 100644 scripts/cmake/utils/copy_no_follow.cmake create mode 100644 scripts/cmake/validate.cmake create mode 100644 scripts/util/colors.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 50aa33a..ab33341 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ if(ENABLE_BUILD_GUEST) include_dependency(BOXY_SOURCE_DEPENDS_DIR linux) endif() +if(ENABLE_BUILD_GUEST AND ENABLE_BUILD_EXAMPLES) + include_dependency(BOXY_SOURCE_DEPENDS_DIR buildroot) +endif() + # ------------------------------------------------------------------------------ # VMM # ------------------------------------------------------------------------------ @@ -86,3 +90,22 @@ if(NOT WIN32 AND NOT CYGWIN) DEPENDS bfintrinsics ) endif() + +# ------------------------------------------------------------------------------ +# Examples +# ------------------------------------------------------------------------------ + +if(ENABLE_BUILD_EXAMPLES AND NOT WIN32 AND NOT CYGWIN) + add_subproject( + vmilinux userspace + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/examples/vmilinux + DEPENDS bflinux + ) +endif() + +# ------------------------------------------------------------------------------ +# Validate Build +# ------------------------------------------------------------------------------ + +include(${CMAKE_CURRENT_LIST_DIR}/scripts/cmake/validate.cmake) +validate_build() \ No newline at end of file diff --git a/bfsdk/include/bfhypercall.h b/bfsdk/include/bfhypercall.h index 182fd0a..3cbdded 100644 --- a/bfsdk/include/bfhypercall.h +++ b/bfsdk/include/bfhypercall.h @@ -1930,7 +1930,9 @@ hypercall_uart_ndec_op(uint16_t port, uint64_t val) #define hypercall_enum_domain_op__ldtr_access_rights 0xBF02000000020730 #define hypercall_enum_domain_op__set_ldtr_access_rights 0xBF02000000020731 +#ifndef UART_MAX_BUFFER #define UART_MAX_BUFFER 0x4000 +#endif static inline domainid_t hypercall_domain_op__create_domain(void) diff --git a/config.cmake b/config.cmake index 6760500..74c97d2 100644 --- a/config.cmake +++ b/config.cmake @@ -19,7 +19,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -set(CACHE_DIR ${CMAKE_CURRENT_LIST_DIR}/cache) +if(NOT DEFINED CACHE_DIR) + set(CACHE_DIR ${CMAKE_CURRENT_LIST_DIR}/cache) +endif() + file(MAKE_DIRECTORY ${CACHE_DIR}) set(CMAKE_BUILD_TYPE Release) diff --git a/examples/buildroot/Config.in b/examples/buildroot/Config.in new file mode 100644 index 0000000..6843a99 --- /dev/null +++ b/examples/buildroot/Config.in @@ -0,0 +1,2 @@ +source "$BR2_EXTERNAL_vmilinux_PATH/package/libvmi/Config.in" +source "$BR2_EXTERNAL_vmilinux_PATH/package/vmilinux/Config.in" diff --git a/examples/buildroot/common/post-build.sh b/examples/buildroot/common/post-build.sh new file mode 100755 index 0000000..e65f93a --- /dev/null +++ b/examples/buildroot/common/post-build.sh @@ -0,0 +1,69 @@ +#!/bin/bash -e +# +# Copyright (C) 2020 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# env REKALL_PROFILE_PATH or VOLATILITY_PROFILE_PATH pointing to a json profile + +PROFILE_FILES=(${REKALL_PROFILE_PATH} ${VOLATILITY_PROFILE_PATH}) + +source "${BR2_EXTERNAL_vmilinux_PATH}/../../scripts/util/colors.sh" + +if [[ -z "${REKALL_PROFILE_PATH}" && -z "${VOLATILITY_PROFILE_PATH}" ]]; then + echo -e "${BF_COLOR_RED}Error: a profile environment variable is required.${BF_COLOR_RST}" + echo "Please define REKALL_PROFILE_PATH or VOLATILITY_PROFILE_PATH to a json profile" + exit 1; +fi + +LIBVMI_CONF="dom0 {" + +for JSON_PROFILE in ${PROFILE_FILES}; do + if [ ! -f "${JSON_PROFILE}" ]; then + echo -e "${BF_COLOR_RED}Missing profile file ${JSON_PROFILE}${BF_COLOR_RST}" + echo "" + exit 1; + fi + + [[ $JSON_PROFILE = $REKALL_PROFILE_PATH ]] && JSON_FILE="rekall.json" || JSON_FILE="volatility.json" + + if [ -f "${TARGET_DIR}/root/${JSON_FILE}" ]; then + echo "removing \${TARGET_DIR}/root/${JSON_FILE})" + rm ${TARGET_DIR}/root/${JSON_FILE} + fi + + echo -e "${BF_COLOR_BLU}Copying host profile for libvmi:${BF_COLOR_RST}" + echo "cp \"${JSON_PROFILE}\" \"${TARGET_DIR}/root/${JSON_FILE}\"" + cp "${JSON_PROFILE}" "${TARGET_DIR}/root/${JSON_FILE}" + + case "${JSON_FILE}" in + rekall.json) + LIBVMI_CONF="${LIBVMI_CONF}\n\trekall_profile = \"/root/rekall.json\";" + ;; + volatility.json) + LIBVMI_CONF="${LIBVMI_CONF}\n\tvolatility_ist = \"/root/volatility.json\";" + ;; + *) + ;; + esac +done + +LIBVMI_CONF="${LIBVMI_CONF}\n}" +printf "${LIBVMI_CONF} > \${TARGET_DIR}/etc/libvmi.conf\n" +printf "${LIBVMI_CONF}" > ${TARGET_DIR}/etc/libvmi.conf \ No newline at end of file diff --git a/examples/buildroot/configs/vmilinux_defconfig b/examples/buildroot/configs/vmilinux_defconfig new file mode 100644 index 0000000..cfd4a94 --- /dev/null +++ b/examples/buildroot/configs/vmilinux_defconfig @@ -0,0 +1,21 @@ +BR2_x86_64=y +BR2_x86_core2=y +BR2_TOOLCHAIN_BUILDROOT_GLIBC=y +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_20=y +BR2_PACKAGE_GLIBC_UTILS=y +BR2_TOOLCHAIN_BUILDROOT_CXX=y +BR2_INIT_NONE=y +# BR2_TARGET_ENABLE_ROOT_LOGIN is not set +# BR2_TARGET_GENERIC_GETTY is not set +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://github.com/Bareflank/linux/archive/boxy_1.01.tar.gz" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_vmilinux_PATH}/../../bflinux/config" +# BR2_PACKAGE_BUSYBOX is not set +# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set +BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_vmilinux_PATH}/common/post-build.sh" +BR2_TARGET_ROOTFS_CPIO=y +BR2_TARGET_ROOTFS_CPIO_GZIP=y +# BR2_TARGET_ROOTFS_TAR is not set +BR2_PACKAGE_VMILINUX=y diff --git a/examples/buildroot/external.desc b/examples/buildroot/external.desc new file mode 100644 index 0000000..c43d4c4 --- /dev/null +++ b/examples/buildroot/external.desc @@ -0,0 +1,2 @@ +name: vmilinux +desc: LibVMI examples with Bareflank Boxy diff --git a/examples/buildroot/external.mk b/examples/buildroot/external.mk new file mode 100644 index 0000000..cfc7635 --- /dev/null +++ b/examples/buildroot/external.mk @@ -0,0 +1 @@ +include $(sort $(wildcard $(BR2_EXTERNAL_vmilinux_PATH)/package/*/*.mk)) diff --git a/examples/buildroot/package/cpio/cpio.mk b/examples/buildroot/package/cpio/cpio.mk new file mode 100644 index 0000000..bb05ceb --- /dev/null +++ b/examples/buildroot/package/cpio/cpio.mk @@ -0,0 +1,10 @@ +################################################################################ +# +# cpio to archive target filesystem +# +################################################################################ + +# Overrides rootfs cpio config from fs/cpio/cpio.mk +# to prevent it from placing /init + +ROOTFS_CPIO_PRE_GEN_HOOKS = diff --git a/examples/buildroot/package/libvmi/Config.in b/examples/buildroot/package/libvmi/Config.in new file mode 100644 index 0000000..9f0a693 --- /dev/null +++ b/examples/buildroot/package/libvmi/Config.in @@ -0,0 +1,16 @@ +config BR2_PACKAGE_LIBVMI + bool "libvmi" + depends on BR2_TOOLCHAIN_USES_GLIBC + depends on BR2_USE_WCHAR # glib2 + depends on BR2_TOOLCHAIN_HAS_THREADS # glib2 + depends on BR2_USE_MMU # glib2 + select BR2_PACKAGE_LIBGLIB2 + select BR2_PACKAGE_JSON_C + # BR2_STATIC_LIBS + help + LibVMI is a C library with Python bindings that makes it easy to monitor the + low-level details of a running virtual machine by viewing its memory, trapping + on hardware events, and accessing the vCPU registers. This is called virtual + machine introspection. + + https://github.com/libvmi/libvmi \ No newline at end of file diff --git a/examples/buildroot/package/libvmi/libvmi.mk b/examples/buildroot/package/libvmi/libvmi.mk new file mode 100644 index 0000000..f5123c8 --- /dev/null +++ b/examples/buildroot/package/libvmi/libvmi.mk @@ -0,0 +1,40 @@ +################################################################################ +# +# LibVMI +# +################################################################################ + +ifeq ($(BR2_LIBVMI_PATH),) + LIBVMI_VERSION = 1dd5a2e48e43b70b50d7ff4a01ad1fcb3b8cbba7 + LIBVMI_SITE = $(call github,chp-io,libvmi,$(LIBVMI_VERSION)) + LIBVMI_REPO = https://github.com/chp-io/libvmi +else + LIBVMI_SITE = ${BR2_LIBVMI_PATH} + LIBVMI_SITE_METHOD = local + LIBVMI_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = --exclude '/deps' +endif + +LIBVMI_LICENSE = LGPLv3 +LIBVMI_LICENSE_FILES = COPYING + +LIBVMI_DEPENDENCIES = json-c libglib2 +HOST_LIBVMI_DEPENDENCIES = host-pkgconf host-bison host-flex host-libglib2 +LIBVMI_CONF_OPTS = \ + -DENABLE_XENSTORE=OFF \ + -DENABLE_XEN=OFF \ + -DENABLE_FILE=OFF \ + -DENABLE_VMIFS=OFF \ + -DENABLE_KVM=OFF \ + -DBUILD_EXAMPLES=ON \ + -DENABLE_FREEBSD=OFF \ + -DENABLE_STATIC=OFF \ + -DENABLE_PAGE_CACHE=OFF + +ifeq ($(BR2_LIBVMI_DEBUG),y) + LIBVMI_CONF_OPTS += -DCMAKE_BUILD_TYPE=Debug \ + -DVMI_DEBUG=__VMI_DEBUG_ALL +else + LIBVMI_CONF_OPTS += -DCMAKE_BUILD_TYPE=Release +endif + +$(eval $(cmake-package)) diff --git a/examples/buildroot/package/vmilinux/Config.in b/examples/buildroot/package/vmilinux/Config.in new file mode 100644 index 0000000..9ea9f81 --- /dev/null +++ b/examples/buildroot/package/vmilinux/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_VMILINUX + bool "vmilinux" + depends on BR2_INSTALL_LIBSTDCPP + select BR2_PACKAGE_LIBVMI + help + vmilinux starts a vmi app with populated cmdline parsed from the kernel's + cmdline. \ No newline at end of file diff --git a/examples/buildroot/package/vmilinux/vmilinux.mk b/examples/buildroot/package/vmilinux/vmilinux.mk new file mode 100644 index 0000000..dab1173 --- /dev/null +++ b/examples/buildroot/package/vmilinux/vmilinux.mk @@ -0,0 +1,36 @@ +################################################################################ +# +# vmilinux +# +################################################################################ + +VMILINUX_SITE = ${BR2_EXTERNAL_vmilinux_PATH}/../.. # ${BOXY_SOURCE_ROOT_DIR} +VMILINUX_SITE_METHOD = local +VMILINUX_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = --exclude build --exclude 'build_*' + +VMILINUX_LICENSE = MIT + +# VMILINUX_SUBDIR = hypervisor +VMILINUX_SUPPORTS_IN_SOURCE_BUILD = NO +VMILINUX_CONF_OPTS = -DBUILD_SHARED_LIBS=NO +VMILINUX_MAKE_OPTS = vmilinux_x86_64-userspace-elf + +define VMILINUX_CONFIGURE_CMDS + (cd $(@D); \ + [[ -d buildroot-build ]] || mkdir buildroot-build; \ + cd buildroot-build; \ + $(TARGET_MAKE_ENV) cmake ../hypervisor \ + -DENABLE_BUILD_EXAMPLES=ON \ + -DREKALL_PROFILE_PATH=${REKALL_PROFILE_PATH} \ + -DVOLATILITY_PROFILE_PATH=${VOLATILITY_PROFILE_PATH} \ + -DCACHE_DIR=${CACHE_DIR} \ + ) +endef + +define VMILINUX_INSTALL_TARGET_CMDS + $(INSTALL) -m 0755 \ + -D $(@D)/buildroot-build/prefixes/initrd/sbin/init-vmi \ + $(TARGET_DIR)/sbin/init +endef + +$(eval $(cmake-package)) \ No newline at end of file diff --git a/examples/buildroot/patch/0001-package-json-c-bump-version-to-0.14.patch b/examples/buildroot/patch/0001-package-json-c-bump-version-to-0.14.patch new file mode 100644 index 0000000..944372b --- /dev/null +++ b/examples/buildroot/patch/0001-package-json-c-bump-version-to-0.14.patch @@ -0,0 +1,57 @@ +From e621c74f96c7f4093829d7a4b91f91c589bb3949 Mon Sep 17 00:00:00 2001 +From: Christopher Pelloux +Date: Thu, 4 Jun 2020 21:11:26 -0400 +Subject: [PATCH 1/1] package/json-c: bump version to 0.14 + +Notes: + +- json-c now uses cmake instead of autoconf +- This version also brings support to the much welcomed feature for + parsing uint64_t types + +Signed-off-by: Christopher Pelloux +--- + package/json-c/json-c.hash | 2 +- + package/json-c/json-c.mk | 14 +++----------- + 2 files changed, 4 insertions(+), 12 deletions(-) + +diff --git a/package/json-c/json-c.hash b/package/json-c/json-c.hash +index 0e03007476..93eaff67c3 100644 +--- a/package/json-c/json-c.hash ++++ b/package/json-c/json-c.hash +@@ -1,4 +1,4 @@ + # From https://github.com/json-c/json-c/wiki +-sha256 b87e608d4d3f7bfdd36ef78d56d53c74e66ab278d318b71e6002a369d36f4873 json-c-0.13.1.tar.gz ++sha256 b377de08c9b23ca3b37d9a9828107dff1de5ce208ff4ebb35005a794f30c6870 json-c-0.14.tar.gz + # Locally calculated + sha256 74c1e6ca5eba76b54d0ad00d4815c8315c1b3bc45ff99de61d103dc92486284c COPYING +diff --git a/package/json-c/json-c.mk b/package/json-c/json-c.mk +index 2788fe563b..3e17effdad 100644 +--- a/package/json-c/json-c.mk ++++ b/package/json-c/json-c.mk +@@ -4,19 +4,11 @@ + # + ################################################################################ + +-JSON_C_VERSION = 0.13.1 ++JSON_C_VERSION = 0.14 + JSON_C_SITE = https://s3.amazonaws.com/json-c_releases/releases + JSON_C_INSTALL_STAGING = YES + JSON_C_LICENSE = MIT + JSON_C_LICENSE_FILES = COPYING + +-# update config.h.in timestamp to avoid autoheader run +-define JSON_C_UPDATE_CONFIG_TIMESTAMP +- touch $(@D)/config.h.in +-endef +- +-JSON_C_POST_EXTRACT_HOOKS += JSON_C_UPDATE_CONFIG_TIMESTAMP +-HOST_JSON_C_POST_EXTRACT_HOOKS += JSON_C_UPDATE_CONFIG_TIMESTAMP +- +-$(eval $(autotools-package)) +-$(eval $(host-autotools-package)) ++$(eval $(cmake-package)) ++$(eval $(host-cmake-package)) +-- +2.17.1 + diff --git a/examples/vmilinux/CMakeLists.txt b/examples/vmilinux/CMakeLists.txt new file mode 100644 index 0000000..f4213ac --- /dev/null +++ b/examples/vmilinux/CMakeLists.txt @@ -0,0 +1,63 @@ +# +# Copyright (C) 2019 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.13) +project(vmilinux C CXX) + +init_project(vmilinux INTERFACE) + +target_link_libraries(vmilinux INTERFACE userspace::bflinux) + +if (${SVMI_STATIC_INIT}) + add_executable(init-vmi + ${CMAKE_CURRENT_LIST_DIR}/tests/test-init.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/init-vmi.cpp + ) + target_compile_definitions(init-vmi PUBLIC + SVMI_STATIC_INIT + SVMI_INIT_NO_PROC_MOUNT + SVMI_INIT_NO_PRINTK + SVMI_CMDLINE_PATH="${CMAKE_CURRENT_LIST_DIR}/tests/cmdline.txt" + ) + set(INIT_VMI_BIN init-vmi) +else() + add_executable(init-vmi ${CMAKE_CURRENT_LIST_DIR}/src/init-vmi.cpp) + set(INIT_VMI_BIN init-vmi) +endif() +target_link_options(init-vmi PRIVATE -static) +set_target_properties(init-vmi PROPERTIES CXX_STANDARD 20) +target_include_directories(init-vmi PRIVATE + $ + $ +) +target_link_libraries(${INIT_VMI_BIN} PUBLIC vmilinux) + +add_custom_target( + package ALL + COMMAND ${CMAKE_COMMAND} -E copy ${INIT_VMI_BIN} ${PREFIXES_DIR}/initrd/sbin/ + COMMAND ${CMAKE_COMMAND} -E chdir ${PREFIXES_DIR}/initrd/ bash ${BOXY_SOURCE_ROOT_DIR}/bflinux/build.sh + COMMAND ${CMAKE_COMMAND} -E echo initrd built for vmilinux + VERBATIM +) + +add_dependencies(package init-vmi) + +fini_project() diff --git a/examples/vmilinux/include/cmdl.h b/examples/vmilinux/include/cmdl.h new file mode 100644 index 0000000..4010281 --- /dev/null +++ b/examples/vmilinux/include/cmdl.h @@ -0,0 +1,131 @@ +// +// Copyright (C) 2020 Assured Information Security, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef VMI_CMDL_H +#define VMI_CMDL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace svmi +{ + +class cmdl +{ + +public: + // + cmdl(const std::string &str, const char delim = ',', const std::string &key = "vmi=") + : m_cmdline{str}, m_args{} + { + if (str == "") { + m_argv.push_back(nullptr); + return; + } + + if (int pos = str.find(key); pos != std::string::npos) { + pos += key.size(); + char *tmp; + std::string tmp_str(str); + char *arg = strtok_r(const_cast(tmp_str.data() + pos), &delim, &tmp); + while (arg) { + if (arg[0] == ' ') { + break; + } + m_args.push_back(arg); + arg = strtok_r(NULL, &delim, &tmp); + } + } + else { + return; + } + + m_argv.resize(m_args.size() + 1); + std::transform(m_args.begin(), m_args.end(), m_argv.begin(), [](const std::string & arg) { + return const_cast(arg.data()); + }); + m_argv.back() = nullptr; + } + + char * + file() const noexcept + { + return m_argv[0]; + } + + int + argc() const noexcept + { + return m_args.size(); + } + + char ** + argv() const noexcept + { + return const_cast(m_argv.data()); + } + + std::unique_ptr + to_string() const noexcept + { + auto str = std::make_unique(""); + std::for_each(m_args.begin(), m_args.end(), [&](const std::string & arg) { + *str += (arg + " "); + }); + str->pop_back(); + return str; + } + + const std::string + kernel_cmdline() const noexcept + { + return m_cmdline; + } + + void + add_arg0(const std::string &arg0) + { + m_args.insert(m_args.begin(), arg0); + + // Pointers might now be invalid. Need to recompute argv. + m_argv.resize(m_args.size() + 1); + std::transform(m_args.begin(), m_args.end(), m_argv.begin(), [](const std::string & arg) { + return const_cast(arg.data()); + }); + m_argv.back() = nullptr; + } + +private: + const std::string &m_cmdline; + std::vector m_args; + + std::vector m_argv; +}; + +} + +#endif diff --git a/examples/vmilinux/src/init-vmi.cpp b/examples/vmilinux/src/init-vmi.cpp new file mode 100644 index 0000000..c123eab --- /dev/null +++ b/examples/vmilinux/src/init-vmi.cpp @@ -0,0 +1,160 @@ +// +// Copyright (C) 2020 Assured Information Security, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma GCC diagnostic ignored "-Wunused-result" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +// Notes: +// +// init.cpp parses the kernel cmdline in /proc/cmdline to create argc, argv and passes it to an +// application's main function. Arguements are given by prefixing them with "vmi=" and seperated +// by a comma. e.g. "vmi=arg0,arg1 ..." +// +// init.cpp can be compiled in two ways. With SVMI_STATIC_INIT defined, init.cpp will call a +// statically linked main and a single application will be running on the guest. +// e.g. "vmi=dom0" will call main with argv[0]="init" argv[1]="dom0" +// +// With SVMI_STATIC_INIT not defined, init.cpp will provide a main function that will execute an +// external application. Here, the first agument of "vmi=" needs to be the path of the executable. +// e.g. "vmi=/bin/vmi-module-list,dom0" will call main with argv[0]="/bin/vmi-module-list" and +// argv[1]="dom0". + +// Macros: +// +// SVMI_STATIC_INIT : if defined, call a linked main otherwise provide main and execute an +// external program. + +// Macros (mainly for testing): +// +// SVMI_INIT_NO_PROC_MOUNT : don't mount proc. +// SVMI_INIT_NO_PRINTK : don't redirect output to the kernel ring buffer. +// SVMI_CMDLINE_PATH : define a file path instead of /proc/cmdline for testing. + +#ifndef SVMI_CMDLINE_PATH +#define SVMI_CMDLINE_PATH "/proc/cmdline" +#endif + +int main(int argc, char **argv); + +namespace svmi +{ + +class pre_main +{ +public: + pre_main() + { + +#ifndef SVMI_INIT_NO_PROC_MOUNT + mount("proc", "/proc", "proc", 0, ""); +#endif + +#ifndef SVMI_INIT_NO_PRINTK + freopen("/dev/ttyprintk", "w", stdout); + freopen("/dev/ttyprintk", "w", stderr); +#endif + + printf("Init: reached\n"); + + auto rawtime = time(0); + auto loctime = localtime(&rawtime); + int ret = 0; + + bfn::file f(SVMI_CMDLINE_PATH); + std::string s(f.data(), f.size()); + + // remove line feed + s.pop_back(); + + cmdl cmdl(s); + +#ifdef SVMI_STATIC_INIT + // TODO: Retrieve arg 0 of this process + cmdl.add_arg0("static-init"); +#else + if (cmdl.argc() == 0) { + printf("Init: Error: No vmi args found in kernel cmdline.\n"); + printf("Init: cmdline: %s\n", cmdl.kernel_cmdline().data()); + printf("Init: Please use --cmdline='vmi=,'\n"); + printf("Init: e.g. --cmdline='vmi=/bin/vmi-module-list,dom0'\n"); + goto done; + } +#endif + printf("Init: Starting %s at %s\n", cmdl.to_string()->data(), asctime(loctime)); + ret = main(cmdl.argc(), cmdl.argv()); + +done: + printf("\nInit: return code is %d. Nothing left to do.\n", ret); + // We don't need to sync since we don't have persistent storage yet + reboot(RB_HALT_SYSTEM); + } +}; + +pre_main do_pre_main; + +} + +#ifndef SVMI_STATIC_INIT + +int +main(int argc, char **argv) +{ + pid_t pid; + int status; + int wait_status; + + if ((pid = fork()) == 0) { + if (execve(argv[0], argv, NULL) == -1) { + perror("Init: Error: child process execve failed"); + return -1; + } + } + + wait_status = waitpid(pid, &status, 0); + + if (!WIFEXITED(status)) { + printf( + "Init: %s WEXITSTATUS %d WIFEXITED %d [status %d]\n", + argv[0], + WEXITSTATUS(status), + WIFEXITED(status), + status); + perror("Init: waitpid failed"); + return -1; + } + + return WEXITSTATUS(status); +} + +#endif diff --git a/examples/vmilinux/tests/cmdline.txt b/examples/vmilinux/tests/cmdline.txt new file mode 100644 index 0000000..6736827 --- /dev/null +++ b/examples/vmilinux/tests/cmdline.txt @@ -0,0 +1 @@ +console=uart,io,0x3f8,115200n8 vmi=/usr/bin/vmi-module-list,dom0 diff --git a/examples/vmilinux/tests/test-init.cpp b/examples/vmilinux/tests/test-init.cpp new file mode 100644 index 0000000..e9a78aa --- /dev/null +++ b/examples/vmilinux/tests/test-init.cpp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2020 Assured Information Security, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +int +main(int argc, char **argv) +{ + printf("Test init args: "); + for (int i = 0; i < argc; i++) { + printf("%s ", argv[i]); + } + return argc; +} diff --git a/scripts/cmake/config/default.cmake b/scripts/cmake/config/default.cmake index 8fe4a6e..8d93484 100644 --- a/scripts/cmake/config/default.cmake +++ b/scripts/cmake/config/default.cmake @@ -73,6 +73,11 @@ set(BOXY_SOURCE_BFVMM_DIR ${BOXY_SOURCE_ROOT_DIR}/bfvmm "BOXY bfvmm source dir" ) +set(BOXY_SOURCE_EXAMPLES_DIR ${BOXY_SOURCE_ROOT_DIR}/examples + CACHE INTERNAL + "BOXY example source dir" +) + # ------------------------------------------------------------------------------ # Links # ------------------------------------------------------------------------------ @@ -87,6 +92,20 @@ set(LINUX_URL_MD5 "bbe9d03b76f874444ff2839f74c982e6" "Linux URL MD5 hash" ) +if(NOT DEFINED BUILDROOT_VERSION) + set(BUILDROOT_VERSION "2020.02.2") +endif() + +set(BUILDROOT_URL "https://buildroot.org/downloads/buildroot-${BUILDROOT_VERSION}.tar.gz" + CACHE INTERNAL FORCE + "Buildroot URL" +) + +set(BUILDROOT_URL_MD5 "f288c13b4b51a50e2e06cea4b3869ca0" + CACHE INTERNAL FORCE + "Buildroot URL MD5 hash" +) + # ------------------------------------------------------------------------------ # Boxy guest # ------------------------------------------------------------------------------ @@ -97,3 +116,10 @@ add_config( DEFAULT_VAL ON DESCRIPTION "Build a minimal Linux guest along with the VMM" ) + +add_config( + CONFIG_NAME ENABLE_BUILD_EXAMPLES + CONFIG_TYPE BOOL + DEFAULT_VAL OFF + DESCRIPTION "Build guest examples showcasing Boxy with LibVMI" +) diff --git a/scripts/cmake/depends/buildroot.cmake b/scripts/cmake/depends/buildroot.cmake new file mode 100755 index 0000000..edf82a2 --- /dev/null +++ b/scripts/cmake/depends/buildroot.cmake @@ -0,0 +1,76 @@ +# +# Copyright (C) 2020 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +if(ENABLE_BUILD_EXAMPLES AND ENABLE_BUILD_USERSPACE AND NOT WIN32 AND NOT CYGWIN) + message(STATUS "Including dependency: buildroot") + + set(BUILDROOT_BUILD_DIR ${DEPENDS_DIR}/buildroot/${USERSPACE_PREFIX}/build) + set(BUILDROOT_ENV REKALL_PROFILE_PATH=${REKALL_PROFILE_PATH} VOLATILITY_PROFILE_PATH=${VOLATILITY_PROFILE_PATH} CACHE_DIR=${CACHE_DIR}) + + download_dependency( + buildroot-source + URL ${BUILDROOT_URL} + URL_MD5 ${BUILDROOT_URL_MD5} + PATCH_COMMAND ${CMAKE_COMMAND} -E chdir "${CACHE_DIR}/buildroot-source" git apply -p1 ${BOXY_SOURCE_EXAMPLES_DIR}/buildroot/patch/0001-package-json-c-bump-version-to-0.14.patch + ) + + add_dependency( + buildroot-source userspace + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + ) + + add_dependency_step( + buildroot-source userspace + COMMAND ${CMAKE_COMMAND} -E make_directory ${BUILDROOT_BUILD_DIR} + COMMAND ${CMAKE_COMMAND} -DSRC=${CACHE_DIR}/buildroot-source/ -DDST=${BUILDROOT_BUILD_DIR} + -P ${BOXY_SOURCE_CMAKE_DIR}/utils/copy_no_follow.cmake + COMMAND ${CMAKE_COMMAND} -E chdir ${BUILDROOT_BUILD_DIR} + env ${BUILDROOT_ENV} + make BR2_EXTERNAL=${BOXY_SOURCE_EXAMPLES_DIR}/buildroot vmilinux_defconfig + COMMAND ${CMAKE_COMMAND} -E make_directory ${PREFIXES_DIR}/vms/buildroot + ) + + add_dependency( + buildroot userspace + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "-- skip" + UPDATE_COMMAND ${CMAKE_COMMAND} -E echo "-- checking for updates" + DEPENDS buildroot-source_${USERSPACE_PREFIX} + ) + + add_dependency_step( + buildroot userspace + COMMAND ${CMAKE_COMMAND} -E chdir ${BUILDROOT_BUILD_DIR} + env ${BUILDROOT_ENV} + make -j${BUILD_TARGET_CORES} + COMMAND ${CMAKE_COMMAND} -E copy + ${BUILDROOT_BUILD_DIR}/output/images/bzImage + ${BUILDROOT_BUILD_DIR}/output/images/rootfs.cpio.gz + ${PREFIXES_DIR}/vms/buildroot + ) + + add_dependencies(vms buildroot_${USERSPACE_PREFIX}) +endif() diff --git a/scripts/cmake/utils/copy_no_follow.cmake b/scripts/cmake/utils/copy_no_follow.cmake new file mode 100644 index 0000000..98d95f0 --- /dev/null +++ b/scripts/cmake/utils/copy_no_follow.cmake @@ -0,0 +1,26 @@ +# +# Copyright (C) 2020 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Helper script used instead of cmake -E copy_directory as the latter doesn't +# support symbolic link no follow +# e.g. ExternalProject_Add + +file(COPY "${SRC}/" DESTINATION "${DST}") \ No newline at end of file diff --git a/scripts/cmake/validate.cmake b/scripts/cmake/validate.cmake new file mode 100644 index 0000000..c9a6ffa --- /dev/null +++ b/scripts/cmake/validate.cmake @@ -0,0 +1,56 @@ +# +# Copyright (C) 2020 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# ------------------------------------------------------------------------------ +# Buildroot +# ------------------------------------------------------------------------------ + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "CYGWIN") + if(ENABLE_BUILD_EXAMPLES) + invalid_config("ENABLE_BUILD_EXAMPLES is not supported on Windows. Please use Vagrant.") + endif() +endif() + +if(ENABLE_BUILD_EXAMPLES AND NOT DEFINED REKALL_PROFILE_PATH AND NOT DEFINED VOLATILITY_PROFILE_PATH) + invalid_config("ENABLE_BUILD_EXAMPLES requires REKALL_PROFILE_PATH or VOLATILITY_PROFILE_PATH") +endif() + +if((${REKALL_PROFILE_PATH}) AND NOT EXISTS ${REKALL_PROFILE_PATH}) + message(REKALL_PROFILE_PATH=${REKALL_PROFILE_PATH}) + invalid_config("REKALL_PROFILE_PATH must point to a valid json file") +endif() + +if((${VOLATILITY_PROFILE_PATH}) AND NOT EXISTS ${VOLATILITY_PROFILE_PATH}) + message(REKALL_PROFILE_PATH=${REKALL_PROFILE_PATH}) + invalid_config("VOLATILITY_PROFILE_PATH must point to a valid json file") +endif() + +# add_config( +# CONFIG_NAME REKALL_PROFILE_PATH +# CONFIG_TYPE PATH +# DESCRIPTION "Rekall profile file path for libvmi" +# ) + +# add_config( +# CONFIG_NAME VOLATILITY_PROFILE_PATH +# CONFIG_TYPE PATH +# DESCRIPTION "Volatility profile file path for libvmi" +# ) \ No newline at end of file diff --git a/scripts/util/colors.sh b/scripts/util/colors.sh new file mode 100644 index 0000000..6bdcd4b --- /dev/null +++ b/scripts/util/colors.sh @@ -0,0 +1,30 @@ +# +# Copyright (C) 2020 Assured Information Security, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +BF_Esc='\e' +BF_COLOR_RST="${BF_Esc}[m" +BF_COLOR_RED="${BF_Esc}[91m" +BF_COLOR_GRN="${BF_Esc}[92m" +BF_COLOR_YLW="${BF_Esc}[93m" +BF_COLOR_BLU="${BF_Esc}[94m" +BF_COLOR_MAG="${BF_Esc}[95m" +BF_COLOR_CYN="${BF_Esc}[96m" +BF_COLOR_WHT="${BF_Esc}[97m" From 51839d9def5e70846c38f598f5b4472df31c40ee Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Wed, 15 Jul 2020 10:23:43 -0400 Subject: [PATCH 3/5] MicroV: implement initial spec needed for LibVMI Implement Windows intrinsics for the MicroV specification: - _mv_cpuid - _mv_handle_op_open_handle - _mv_handle_op_close_handle - _mv_vm_properties_op_set_e820 Add initial VMM support for the MicroV specification: - mv_vm_state_op_gva_to_gpa - mv_vm_state_op_map_range - mv_vm_state_op_unmap_range - mv_vp_state_op_reg_val - mv_vp_state_op_msr_val Fixes: - Fix close_handle index - Fix gva_to_gpa to return the correct invalid param - Fix formatting --- bfbuilder/src/platform/linux/intrinsics.S | 135 +++++++- bfbuilder/src/platform/windows/intrinsics.asm | 319 +++++++++++++++--- bfsdk/include/bfhypercall.h | 6 +- bfvmm/include/hve/arch/intel_x64/domain.h | 43 +++ bfvmm/include/hve/arch/intel_x64/vcpu.h | 21 ++ .../hve/arch/intel_x64/vmcall/vm_state_op.h | 8 + .../hve/arch/intel_x64/vmcall/vp_state_op.h | 4 + bfvmm/include/hve/arch/intel_x64/vmexit/msr.h | 9 + bfvmm/src/hve/arch/intel_x64/domain.cpp | 49 +++ bfvmm/src/hve/arch/intel_x64/vcpu.cpp | 16 + .../hve/arch/intel_x64/vmcall/handle_op.cpp | 4 - .../hve/arch/intel_x64/vmcall/vm_state_op.cpp | 183 +++++++++- .../hve/arch/intel_x64/vmcall/vp_state_op.cpp | 215 +++++++++++- bfvmm/src/hve/arch/intel_x64/vmexit/msr.cpp | 16 + .../src/main/arch/intel_x64/vcpu_factory.cpp | 9 +- hypervisor | 2 +- 16 files changed, 967 insertions(+), 72 deletions(-) diff --git a/bfbuilder/src/platform/linux/intrinsics.S b/bfbuilder/src/platform/linux/intrinsics.S index b13f4c0..b30b171 100644 --- a/bfbuilder/src/platform/linux/intrinsics.S +++ b/bfbuilder/src/platform/linux/intrinsics.S @@ -74,7 +74,7 @@ _mv_handle_op_close_handle: mov r10, rdi - mov rax, 0x764D000000010000 + mov rax, 0x764D000000010001 vmcall ret @@ -104,6 +104,139 @@ _mv_vm_properties_op_set_e820: ret .size _mv_vm_properties_op_set_e820, .-_mv_vm_properties_op_set_e820 +/* -------------------------------------------------------------------------- */ +/* _mv_vm_state_op_gva_to_gpa */ +/* -------------------------------------------------------------------------- */ + + .globl _mv_vm_state_op_gva_to_gpa + .type _mv_vm_state_op_gva_to_gpa, @function +_mv_vm_state_op_gva_to_gpa: + + push r12 + push r13 + + mov r10, rdi + mov r11, rsi + mov r12, rdx + mov r13, rcx + + mov rax, 0x764D000000030008 + vmcall + + mov [r8], r10 + mov [r9], r11 + + pop r13 + pop r12 + ret + .size _mv_vm_state_op_gva_to_gpa, .-_mv_vm_state_op_gva_to_gpa + +/* -------------------------------------------------------------------------- */ +/* _mv_vm_state_op_map_range */ +/* -------------------------------------------------------------------------- */ + + .globl _mv_vm_state_op_map_range + .type _mv_vm_state_op_map_range, @function +_mv_vm_state_op_map_range: + + push r12 + push r13 + push r14 + push r15 + + mov r10, rdi + mov r11, rsi + mov r12, rdx + mov r13, rcx + mov r14, r8 + mov r15, r9 + + mov rax, 0x764D000000030009 + vmcall + + pop r15 + pop r14 + pop r13 + pop r12 + ret + .size _mv_vm_state_op_map_range, .-_mv_vm_state_op_map_range + +/* -------------------------------------------------------------------------- */ +/* _mv_vm_state_op_unmap_range */ +/* -------------------------------------------------------------------------- */ + + .globl _mv_vm_state_op_unmap_range + .type _mv_vm_state_op_unmap_range, @function +_mv_vm_state_op_unmap_range: + + push r12 + push r13 + push r14 + push r15 + + mov r10, rdi + mov r11, rsi + mov r12, rdx + mov r13, rcx + mov r14, r8 + mov r15, r9 + + mov rax, 0x764D00000003000A + vmcall + + pop r15 + pop r14 + pop r13 + pop r12 + ret + .size _mv_vm_state_op_unmap_range, .-_mv_vm_state_op_unmap_range + +/* -------------------------------------------------------------------------- */ +/* _mv_vp_state_op_reg_val */ +/* -------------------------------------------------------------------------- */ + + .globl _mv_vp_state_op_reg_val + .type _mv_vp_state_op_reg_val, @function +_mv_vp_state_op_reg_val: + + push r12 + + mov r10, rdi + mov r11, rsi + mov r12, rdx + + mov rax, 0x764D000000070000 + vmcall + + mov [rcx], r10 + + pop r12 + ret + .size _mv_vp_state_op_reg_val, .-_mv_vp_state_op_reg_val + +/* -------------------------------------------------------------------------- */ +/* _mv_vp_state_op_msr_val */ +/* -------------------------------------------------------------------------- */ + + .globl _mv_vp_state_op_msr_val + .type _mv_vp_state_op_msr_val, @function +_mv_vp_state_op_msr_val: + + push r12 + + mov r10, rdi + mov r11, rsi + mov r12, rdx + + mov rax, 0x764D000000070004 + vmcall + + mov [rcx], r10 + + pop r12 + ret + .size _mv_vp_state_op_msr_val, .-_mv_vp_state_op_msr_val + /* -------------------------------------------------------------------------- */ /* !!! WARNING DEPRECATED !!! */ /* -------------------------------------------------------------------------- */ diff --git a/bfbuilder/src/platform/windows/intrinsics.asm b/bfbuilder/src/platform/windows/intrinsics.asm index 4b2b735..c21907a 100644 --- a/bfbuilder/src/platform/windows/intrinsics.asm +++ b/bfbuilder/src/platform/windows/intrinsics.asm @@ -1,53 +1,266 @@ -; -; Copyright (C) 2019 Assured Information Security, Inc. -; -; Permission is hereby granted, free of charge, to any person obtaining a copy -; of this software and associated documentation files (the "Software"), to deal -; in the Software without restriction, including without limitation the rights -; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -; copies of the Software, and to permit persons to whom the Software is -; furnished to do so, subject to the following conditions: -; -; The above copyright notice and this permission notice shall be included in all -; copies or substantial portions of the Software. -; -; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -; SOFTWARE. - -.code - -_cpuid_eax PROC - - push rbx - - mov eax, ecx - mov ecx, 0 - cpuid - - pop rbx - ret - -_cpuid_eax ENDP - -_vmcall PROC - - push rbx - - mov rax, rcx - mov rbx, rdx - mov rcx, r8 - mov rdx, r9 - - vmcall - - pop rbx - ret - -_vmcall ENDP - -end +; +; Copyright (C) 2019 Assured Information Security, Inc. +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in all +; copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +; SOFTWARE. + +.code + +;------------------------------------------------------------------------------- +; _mv_cpuid +;------------------------------------------------------------------------------- + +_mv_cpuid PROC + + push rbx + + mov r10, rcx + mov r11, rdx + + mov eax, [rcx] + cpuid + + mov [r10], eax + mov [r11], ebx + mov [r8], ecx + mov [r9], edx + + pop rbx + ret + +_mv_cpuid ENDP + +;------------------------------------------------------------------------------- +; _mv_handle_op_open_handle +;------------------------------------------------------------------------------- + +_mv_handle_op_open_handle PROC + + mov r11, rcx + + mov rax, 764D000000010000h + vmcall + + mov [rdx], r10 + + ret +_mv_handle_op_open_handle ENDP + +;------------------------------------------------------------------------------- +; _mv_handle_op_close_handle +;------------------------------------------------------------------------------- + +_mv_handle_op_close_handle PROC + + mov r10, rcx + + mov rax, 764D000000010001h + vmcall + + ret + +_mv_handle_op_close_handle ENDP + +;------------------------------------------------------------------------------- +; _mv_vm_properties_op_set_e820 +;------------------------------------------------------------------------------- + +_mv_vm_properties_op_set_e820 PROC + + push r12 + push r13 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + mov r13, r9 + + mov rax, 764D000000020006h + vmcall + + pop r13 + pop r12 + ret + +_mv_vm_properties_op_set_e820 ENDP + +;------------------------------------------------------------------------------- +; _mv_vm_state_op_gva_to_gpa +;------------------------------------------------------------------------------- + +_mv_vm_state_op_gva_to_gpa PROC + + push r12 + push r13 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + mov r13, r9 + + mov rax, 764D000000030008h + vmcall + + mov r8, [rsp+28h+8*2] + mov r9, [rsp+30h+8*2] + + mov [r8], r10 + mov [r9], r11 + + pop r13 + pop r12 + ret + +_mv_vm_state_op_gva_to_gpa ENDP + +;------------------------------------------------------------------------------- +; _mv_vm_state_op_map_range +;------------------------------------------------------------------------------- + +_mv_vm_state_op_map_range PROC + + push r12 + push r13 + push r14 + push r15 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + mov r13, r9 + mov r14, [rsp+28h+8*4] + mov r15, [rsp+30h+8*4] + + mov rax, 764D000000030009h + vmcall + + pop r15 + pop r14 + pop r13 + pop r12 + ret + +_mv_vm_state_op_map_range ENDP + +;------------------------------------------------------------------------------- +; _mv_vm_state_op_unmap_range +;------------------------------------------------------------------------------- + +_mv_vm_state_op_unmap_range PROC + + push r12 + push r13 + push r14 + push r15 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + mov r13, r9 + mov r14, [rsp+28h+8*4] + mov r15, [rsp+30h+8*4] + + mov rax, 764D00000003000Ah + vmcall + + pop r15 + pop r14 + pop r13 + pop r12 + ret + +_mv_vm_state_op_unmap_range ENDP + +;------------------------------------------------------------------------------- +; _mv_vp_state_op_reg_val +;------------------------------------------------------------------------------- + +_mv_vp_state_op_reg_val PROC + + push r12 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + + mov rax, 764D000000070000h + vmcall + + mov [r9], r10 + + pop r12 + ret + +_mv_vp_state_op_reg_val ENDP + +;------------------------------------------------------------------------------- +; _mv_vp_state_op_msr_val +;------------------------------------------------------------------------------- + +_mv_vp_state_op_msr_val PROC + + push r12 + + mov r10, rcx + mov r11, rdx + mov r12, r8 + + mov rax, 764D000000070004h + vmcall + + mov [r9], r10 + + pop r12 + ret + +_mv_vp_state_op_msr_val ENDP + +;------------------------------------------------------------------------------- +; !!! WARNING DEPRECATED !!! +;------------------------------------------------------------------------------- + +_cpuid_eax PROC + + push rbx + + mov eax, ecx + mov ecx, 0 + cpuid + + pop rbx + ret + +_cpuid_eax ENDP + +_vmcall PROC + + push rbx + + mov rax, rcx + mov rbx, rdx + mov rcx, r8 + mov rdx, r9 + + vmcall + + pop rbx + ret + +_vmcall ENDP + +end diff --git a/bfsdk/include/bfhypercall.h b/bfsdk/include/bfhypercall.h index 3cbdded..609b5b2 100644 --- a/bfsdk/include/bfhypercall.h +++ b/bfsdk/include/bfhypercall.h @@ -1059,11 +1059,11 @@ mv_vm_state_op_gva_to_gpa( } if (MV_NULL == gpa) { - return MV_STATUS_INVALID_PARAMS3; + return MV_STATUS_INVALID_PARAMS4; } if (MV_NULL == flags) { - return MV_STATUS_INVALID_PARAMS3; + return MV_STATUS_INVALID_PARAMS5; } return _mv_vm_state_op_gva_to_gpa( @@ -1368,7 +1368,7 @@ mv_vm_management_op_resume_vm( // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// VMID +// VPID // ----------------------------------------------------------------------------- #define MV_VPID_SELF ((mv_uint64_t)0xFFFFFFFFFFFFFFF0) diff --git a/bfvmm/include/hve/arch/intel_x64/domain.h b/bfvmm/include/hve/arch/intel_x64/domain.h index e5e851b..496f2cb 100644 --- a/bfvmm/include/hve/arch/intel_x64/domain.h +++ b/bfvmm/include/hve/arch/intel_x64/domain.h @@ -205,6 +205,46 @@ class domain : public boxy::domain /// void release(uintptr_t gpa); + /// Share Range + /// + /// Share a range of 4k guest physical addresses from foreign mmap using EPT + /// effectivily remapping our guest physical addresses to their host + /// physical addresses. + /// + /// @expects + /// @ensures + /// + /// @param gpa the guest physical address to remap + /// @param foreign_gpa the foreign guest physical address to remap from + /// @param foreign_mmap the foreign mmap to remap from + /// @param size the number of GPA to remap + /// @param attr the map permissions apply to our GPAs being remapped + /// @param cache the memory type for our remapped GPAs + /// + void share_range( + uintptr_t gpa, + uintptr_t foreign_gpa, bfvmm::intel_x64::ept::mmap &foreign_mmap, + uint32_t size, + bfvmm::intel_x64::ept::mmap::attr_type attr, + bfvmm::intel_x64::ept::mmap::memory_type cache); + + /// Unshare Range + /// + /// Unshare a range of 4k guest physical addresses previously assigned with + /// share_range. + /// + /// @expects + /// @ensures + /// + /// @param gpa the guest physical address to restore + /// @param size the number of GPA to restore + /// @param attr the map permissions apply to our GPAs being remapped + /// @param cache the memory type for our remapped GPAs + void unshare_range( + uintptr_t gpa, uint32_t size, + bfvmm::intel_x64::ept::mmap::attr_type attr, + bfvmm::intel_x64::ept::mmap::memory_type cache); + public: /// Set UART @@ -432,6 +472,9 @@ class domain : public boxy::domain mv_mdl_t m_e820_map{}; bfvmm::intel_x64::vcpu_global_state_t m_vcpu_global_state{}; + std::unordered_map m_range_sizes; + std::mutex m_mutex; + uart::port_type m_uart_port{}; uart::port_type m_pt_uart_port{}; uart m_uart_3F8{0x3F8}; diff --git a/bfvmm/include/hve/arch/intel_x64/vcpu.h b/bfvmm/include/hve/arch/intel_x64/vcpu.h index b76b9fa..8c6f007 100644 --- a/bfvmm/include/hve/arch/intel_x64/vcpu.h +++ b/bfvmm/include/hve/arch/intel_x64/vcpu.h @@ -388,6 +388,27 @@ class vcpu : public bfvmm::intel_x64::vcpu /// void halt(const std::string &str = {}) override; +public: + + //-------------------------------------------------------------------------- + // Model-specific registers + //-------------------------------------------------------------------------- + + /// Get MSR Register Value + /// + /// @param msr the msr to get + /// + VIRTUAL uint64_t msr(uint32_t msr) const; + + /// Set MSR Register Value + /// + /// @param msr the msr to set + /// @param val the value to set the msr with + /// + VIRTUAL void set_msr(uint32_t msr, uint64_t val); + + /// @cond + private: void write_dom0_guest_state(domain *domain); diff --git a/bfvmm/include/hve/arch/intel_x64/vmcall/vm_state_op.h b/bfvmm/include/hve/arch/intel_x64/vmcall/vm_state_op.h index 77b46bd..26a9ec2 100644 --- a/bfvmm/include/hve/arch/intel_x64/vmcall/vm_state_op.h +++ b/bfvmm/include/hve/arch/intel_x64/vmcall/vm_state_op.h @@ -54,6 +54,14 @@ class vm_state_op_handler private: + void gva_to_gpa(vcpu *vcpu); + + std::tuple map_range_init(vcpu *vp); + void map_range(vcpu *vcpu); + void unmap_range(vcpu *vcpu); + bool dispatch(vcpu *vcpu); private: diff --git a/bfvmm/include/hve/arch/intel_x64/vmcall/vp_state_op.h b/bfvmm/include/hve/arch/intel_x64/vmcall/vp_state_op.h index f0b499c..b2e3913 100644 --- a/bfvmm/include/hve/arch/intel_x64/vmcall/vp_state_op.h +++ b/bfvmm/include/hve/arch/intel_x64/vmcall/vp_state_op.h @@ -54,6 +54,10 @@ class vp_state_op_handler private: + vcpu *init_target(vcpu *vp); + void reg_val(vcpu *vp); + void msr_val(vcpu *vp); + bool dispatch(vcpu *vcpu); private: diff --git a/bfvmm/include/hve/arch/intel_x64/vmexit/msr.h b/bfvmm/include/hve/arch/intel_x64/vmexit/msr.h index 01aac7b..530c09a 100644 --- a/bfvmm/include/hve/arch/intel_x64/vmexit/msr.h +++ b/bfvmm/include/hve/arch/intel_x64/vmexit/msr.h @@ -94,6 +94,15 @@ class msr_handler /// @endcond +public: + + /// @cond + + uint64_t get_msr(uint32_t msr) const; + void set_msr(uint32_t msr, uint64_t val); + + /// @endcond + private: vcpu *m_vcpu; diff --git a/bfvmm/src/hve/arch/intel_x64/domain.cpp b/bfvmm/src/hve/arch/intel_x64/domain.cpp index fb2b596..c8d548a 100644 --- a/bfvmm/src/hve/arch/intel_x64/domain.cpp +++ b/bfvmm/src/hve/arch/intel_x64/domain.cpp @@ -120,6 +120,55 @@ void domain::release(uintptr_t gpa) { m_ept_map.release(gpa); } +void +domain::share_range( + uintptr_t gpa, + uintptr_t foreign_gpa, bfvmm::intel_x64::ept::mmap &foreign_mmap, + uint32_t size, + bfvmm::intel_x64::ept::mmap::attr_type attr, + bfvmm::intel_x64::ept::mmap::memory_type cache) +{ + std::lock_guard lock(m_mutex); + using namespace ::intel_x64::ept; + + auto &range_size = m_range_sizes[gpa]; + if (range_size != 0) { + throw std::runtime_error("share_range: map range already exists"); + } + range_size = size; + + for (auto _foreign_gpa = foreign_gpa, _gpa = gpa; + _foreign_gpa < (foreign_gpa + (size << pt::from)); + _foreign_gpa += (0x1ULL << pt::from), _gpa += (0x1ULL << pt::from)) { + + m_ept_map.share_4k(_gpa, _foreign_gpa, foreign_mmap, attr, cache); + } +} + +void +domain::unshare_range( + uintptr_t gpa, uint32_t size, + bfvmm::intel_x64::ept::mmap::attr_type attr, + bfvmm::intel_x64::ept::mmap::memory_type cache) + +{ + std::lock_guard lock(m_mutex); + using namespace ::intel_x64::ept; + + if (m_range_sizes[gpa] != size) { + throw std::runtime_error("unshare_range: size is different from original one"); + } + + for (auto _gpa = gpa; + _gpa < (gpa + (size << pt::from)); + _gpa += (0x1ULL << pt::from)) { + + m_ept_map.unshare_4k(_gpa, attr, cache); + } + + m_range_sizes.erase(gpa); +} + void domain::set_uart(uart::port_type uart) noexcept { m_uart_port = uart; } diff --git a/bfvmm/src/hve/arch/intel_x64/vcpu.cpp b/bfvmm/src/hve/arch/intel_x64/vcpu.cpp index bae35b8..0d29e04 100644 --- a/bfvmm/src/hve/arch/intel_x64/vcpu.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vcpu.cpp @@ -305,6 +305,22 @@ vcpu::halt(const std::string &str) } } +//------------------------------------------------------------------------------ +// Model-specific registers +//------------------------------------------------------------------------------ + +uint64_t +vcpu::msr(uint32_t msr) const +{ + return m_msr_handler.get_msr(msr); +} + +void +vcpu::set_msr(uint32_t msr, uint64_t val) +{ + m_msr_handler.set_msr(msr, val); +} + //------------------------------------------------------------------------------ // Setup Functions //------------------------------------------------------------------------------ diff --git a/bfvmm/src/hve/arch/intel_x64/vmcall/handle_op.cpp b/bfvmm/src/hve/arch/intel_x64/vmcall/handle_op.cpp index 33ac7bb..78d8071 100644 --- a/bfvmm/src/hve/arch/intel_x64/vmcall/handle_op.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vmcall/handle_op.cpp @@ -30,10 +30,6 @@ handle_op_handler::handle_op_handler( ) : m_vcpu{vcpu} { - if (vcpu->is_domU()) { - return; - } - vcpu->add_vmcall_handler({&handle_op_handler::dispatch, this}); } diff --git a/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp b/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp index db0c638..c941aa2 100644 --- a/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp @@ -31,11 +31,181 @@ vm_state_op_handler::vm_state_op_handler( ) : m_vcpu{vcpu} { - if (vcpu->is_domU()) { + vcpu->add_vmcall_handler({&vm_state_op_handler::dispatch, this}); +} + + +void +vm_state_op_handler::gva_to_gpa(vcpu *vcpu) +{ + auto vmid{vcpu->r11()}; + auto ptt_gpa{vcpu->r12()}; + auto gva{vcpu->r13()}; + uint64_t gpa = 0; + uint64_t flags = 0; + + bool is_self = vmid == MV_VMID_SELF || vmid == vcpu->domid(); + + if (is_self) { + gpa = vcpu->gva_to_gpa(gva).first; + } + else { + // TODO: translate gva given ptt_gpa for root and guests + throw std::runtime_error("gva_to_gpa: non-self is not yet implemented"); + } + + // TODO handle flags; + + if (!is_self) { + vcpu->load(); + } + + vcpu->set_r10(gpa); + vcpu->set_r11(flags); + + vcpu->set_rax(MV_STATUS_SUCCESS); +} + +inline std::tuple + vm_state_op_handler::map_range_init(vcpu *vp) +{ + auto src_vmid{vp->r11()}; + auto src_gpa{vp->r12()}; + auto dst_vmid{vp->r13()}; + auto dst_gpa{vp->r14()}; + auto flags{vp->r15()}; + auto size{(flags & 0x00000000FFFFFFFF) != 0 ? : 1}; + + using namespace bfvmm::intel_x64::ept; + + mmap::attr_type attr = mmap::attr_type::read_only; + mmap::memory_type cache = mmap::memory_type::write_back; + + domain *src_vm; + domain *dst_vm; + + switch (src_vmid) { + case MV_VMID_ROOT: + src_vm = get_domain(0); + break; + default: + throw std::runtime_error( + "map_range: non-root source is not yet implemented"); + } + + switch (dst_vmid) { + case MV_VMID_SELF: + dst_vm = get_domain(vp->domid()); + break; + default: + throw std::runtime_error( + "map_range: non-self destination is not yet implemented"); + } + + using namespace ::intel_x64::ept; + + // src REVZ must be zero + + if (bfn::lower(src_gpa, pt::from)) { + vp->set_rax(MV_STATUS_INVALID_PARAMS2); + return {}; + } + + // dst REVZ must be zero + + if (bfn::lower(dst_gpa, pt::from)) { + vp->set_rax(MV_STATUS_INVALID_PARAMS4); + return {}; + } + + // TODO donate and zombie flags + + if (flags & MV_GPA_FLAG_DONATE) { + // Only the root VM is allowed to donate + if (vp->is_domU()) { + vp->set_rax(MV_STATUS_INVALID_PARAMS5); + return {}; + } + + vp->set_rax(MV_STATUS_INVALID_PARAMS5); + bfdebug_info(0, "map_range: page donation is not yet implemented"); + return {}; + } + + // Memory access type + + switch ((flags >> 32) & 0x3LLU) { + case 0: break; + case 1: attr = mmap::attr_type::read_only; break; + case 2: attr = mmap::attr_type::write_only; break; + case 3: attr = mmap::attr_type::read_write; break; + case 4: attr = mmap::attr_type::execute_only; break; + case 5: attr = mmap::attr_type::read_execute; break; + case 6: attr = mmap::attr_type::read_write_execute; break; + default: + vp->set_rax(MV_STATUS_INVALID_PARAMS5); + return {}; + } + + // Cacheability + + switch ((flags >> 35) & 0x7LLU) { + case 0x00: break; + case 0x01: cache = mmap::memory_type::uncacheable; break; + // TODO: case 0x02 uncacheable_minus + case 0x04: cache = mmap::memory_type::write_combining; break; + // TODO: case 0x08 write_combining_plus + case 0x10: cache = mmap::memory_type::write_through; break; + case 0x20: cache = mmap::memory_type::write_back; break; + case 0x40: cache = mmap::memory_type::write_protected; break; + default: + vp->set_rax(MV_STATUS_INVALID_PARAMS5); + return {}; + } + + return {src_vm, src_gpa, dst_vm, dst_gpa, size, attr, cache}; +} + +void +vm_state_op_handler::map_range(vcpu *vp) +{ + auto [src_vm, src_gpa, dst_vm, dst_gpa, size, attr, cache] = + map_range_init(vp); + + if ((vp->rax() >> 48) == 0xDEAD) { return; } - vcpu->add_vmcall_handler({&vm_state_op_handler::dispatch, this}); + try { + dst_vm->share_range(dst_gpa, src_gpa, src_vm->ept(), size, attr, cache); + } + catchall({ + throw std::runtime_error("map_range failed"); + }) + + vp->set_rax(MV_STATUS_SUCCESS); +} + +void +vm_state_op_handler::unmap_range(vcpu *vp) +{ + auto [src_vm, src_gpa, dst_vm, dst_gpa, size, attr, cache] = + map_range_init(vp); + + if ((vp->rax() >> 48) == 0xDEAD) { + return; + } + + try { + dst_vm->unshare_range(dst_gpa, size, attr, cache); + } + catchall({ + throw std::runtime_error("map_range failed"); + }) + + vp->set_rax(MV_STATUS_SUCCESS); } bool @@ -48,6 +218,15 @@ vm_state_op_handler::dispatch(vcpu *vcpu) // TODO: Validate the handle switch (mv_hypercall_index(vcpu->rax())) { + case MV_VM_STATE_OP_GVA_TO_GPA_IDX_VAL: + this->gva_to_gpa(vcpu); + return true; + case MV_VM_STATE_OP_MAP_RANGE_IDX_VAL: + this->map_range(vcpu); + return true; + case MV_VM_STATE_OP_UNMAP_RANGE_IDX_VAL: + this->unmap_range(vcpu); + return true; default: break; }; diff --git a/bfvmm/src/hve/arch/intel_x64/vmcall/vp_state_op.cpp b/bfvmm/src/hve/arch/intel_x64/vmcall/vp_state_op.cpp index 4d361cd..0c9c851 100644 --- a/bfvmm/src/hve/arch/intel_x64/vmcall/vp_state_op.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vmcall/vp_state_op.cpp @@ -31,28 +31,231 @@ vp_state_op_handler::vp_state_op_handler( ) : m_vcpu{vcpu} { - if (vcpu->is_domU()) { + vcpu->add_vmcall_handler({&vp_state_op_handler::dispatch, this}); +} + +#define case_reg(name) \ + case mv_reg_t_ ## name: \ + reg_val = target->name(); \ + break + +#define case_reg2(name0, name1) \ + case mv_reg_t_ ## name0: \ + reg_val = target->name1(); \ + break + +#define case_reg_vmcs(name) \ + case mv_reg_t_ ## name: \ + target->load(); \ + reg_val = target->name(); \ + vp->load(); \ + break + +#define case_reg_vmcs2(name0, name1) \ + case mv_reg_t_ ## name0: \ + target->load(); \ + reg_val = target->name1(); \ + vp->load(); \ + break + +void +vp_state_op_handler::reg_val(vcpu *vp) +{ + mv_reg_t reg{static_cast(vp->r12())}; + uint64_t reg_val{}; + vcpu *target{init_target(vp)}; + + if (target == nullptr) { return; } - vcpu->add_vmcall_handler({&vp_state_op_handler::dispatch, this}); + switch (reg) { + case_reg(rax); + case_reg(rbx); + case_reg(rcx); + case_reg(rdx); + case_reg(rdi); + case_reg(rsi); + case_reg2(r8, r08); + case_reg2(r9, r09); + case_reg(r10); + case_reg(r11); + case_reg(r12); + case_reg(r13); + case_reg(r14); + case_reg(r15); + case_reg(rbp); + case_reg(rsp); + case_reg(rip); + case_reg_vmcs(cr0); + case_reg(cr2); + case_reg_vmcs(cr3); + case_reg_vmcs(cr4); + case_reg(cr8); + case_reg(dr0); + case_reg(dr1); + case_reg(dr2); + case_reg(dr3); + // FIXME mv_reg_t_dr4 + // FIXME mv_reg_t_dr5 + case_reg(dr6); + case_reg_vmcs(dr7); + case_reg_vmcs(rflags); + case_reg_vmcs2(es, es_selector); + case_reg_vmcs2(es_base_addr, es_base); + case_reg_vmcs(es_limit); + case_reg_vmcs2(es_attributes, es_access_rights); + case_reg_vmcs2(cs, cs_selector); + case_reg_vmcs2(cs_base_addr, cs_base); + case_reg_vmcs(cs_limit); + case_reg_vmcs2(cs_attributes, cs_access_rights); + case_reg_vmcs2(ss, ss_selector); + case_reg_vmcs2(ss_base_addr, ss_base); + case_reg_vmcs(ss_limit); + case_reg_vmcs2(ss_attributes, ss_access_rights); + case_reg_vmcs2(ds, ds_selector); + case_reg_vmcs2(ds_base_addr, ds_base); + case_reg_vmcs(ds_limit); + case_reg_vmcs2(ds_attributes, ds_access_rights); + case_reg_vmcs2(fs, fs_selector); + case_reg_vmcs2(fs_base_addr, fs_base); + case_reg_vmcs(fs_limit); + case_reg_vmcs2(fs_attributes, fs_access_rights); + case_reg_vmcs2(gs, gs_selector); + case_reg_vmcs2(gs_base_addr, gs_base); + case_reg_vmcs(gs_limit); + case_reg_vmcs2(gs_attributes, gs_access_rights); + case_reg_vmcs2(ldtr, ldtr_selector); + case_reg_vmcs2(ldtr_base_addr, ldtr_base); + case_reg_vmcs(ldtr_limit); + case_reg_vmcs2(ldtr_attributes, ldtr_access_rights); + case_reg_vmcs2(tr, tr_selector); + case_reg_vmcs2(tr_base_addr, tr_base); + case_reg_vmcs(tr_limit); + case_reg_vmcs2(tr_attributes, tr_access_rights); + // FIXME: mv_reg_t_gdtr + case_reg_vmcs2(gdtr_base_addr, gdt_base); + case_reg_vmcs2(gdtr_limit, gdt_limit); + // FIXME: mv_reg_t_gdtr_attributes + // FIXME: mv_reg_t_idtr + case_reg_vmcs2(idtr_base_addr, idt_base); + case_reg_vmcs2(idtr_limit, idt_limit); + // FIXME: mv_reg_t_idtr_attributes + + default: + vp->set_rax(MV_STATUS_INVALID_PARAMS2); + return; + } + + vp->set_r10(reg_val); + vp->set_rax(MV_STATUS_SUCCESS); +} + +#define case_msr_vmcs(msr, name) \ + case msr: \ + target->load(); \ + msr_val = vmcs_n::guest_## name ::get(); \ + vp->load(); \ + break + +void +vp_state_op_handler::msr_val(vcpu *vp) +{ + uint32_t msr{gsl::narrow_cast(vp->r12())}; + uint64_t msr_val{}; + vcpu *target{init_target(vp)}; + + if (target == nullptr) { + return; + } + + switch (msr) { + case_msr_vmcs(::x64::msrs::ia32_pat::addr, ia32_pat); + case_msr_vmcs(::intel_x64::msrs::ia32_efer::addr, ia32_efer); + case_msr_vmcs(::intel_x64::msrs::ia32_fs_base::addr, fs_base); + case_msr_vmcs(::intel_x64::msrs::ia32_gs_base::addr, gs_base); + case_msr_vmcs(::intel_x64::msrs::ia32_sysenter_cs::addr, ia32_sysenter_cs); + case_msr_vmcs(::intel_x64::msrs::ia32_sysenter_eip::addr, ia32_sysenter_eip); + case_msr_vmcs(::intel_x64::msrs::ia32_sysenter_esp::addr, ia32_sysenter_esp); + + default: + try { + msr_val = target->msr(msr); + } + catchall({ + bferror_nhex(0, "vp_state_op unhandled msr:", msr); + vp->set_rax(MV_STATUS_INVALID_PARAMS2); + return; + }) + } + + vp->set_r10(msr_val); + vp->set_rax(MV_STATUS_SUCCESS); +} + +vcpu * +vp_state_op_handler::init_target(vcpu *vp) +{ + vcpu *target{nullptr}; + + // For now we only allow a domU to target its parent + // either via MV_VPID_PARENT or with its parent vp id + switch (vp->r11()) { + case MV_VPID_SELF: + vp->set_rax(MV_STATUS_INVALID_VPID_UNSUPPORTED_SELF); + break; + case MV_VPID_PARENT: + if (vp->is_dom0()) { + vp->set_rax(MV_STATUS_INVALID_VPID_UNSUPPORTED_PARENT); + break; + } + target = vp->parent_vcpu(); + break; + case MV_VPID_ANY: + vp->set_rax(MV_STATUS_INVALID_VPID_UNSUPPORTED_ANY); + break; + default: + if (vp->is_dom0()) { + // TODO check if is child of current vp + } + else if (vp->parent_vcpu()->id() != vp->r11()) { + vp->set_rax(MV_STATUS_FAILURE_UNSUPPORTED_HYPERCALL); + break; + } + + try { + target = get_vcpu(vp->r11()); + } + catchall({ + vp->set_rax(MV_STATUS_INVALID_VPID_UNKNOWN); + break; + }) + } + + return target; } bool -vp_state_op_handler::dispatch(vcpu *vcpu) +vp_state_op_handler::dispatch(vcpu *vp) { - if (mv_hypercall_opcode(vcpu->rax()) != MV_VP_STATE_OP_VAL) { + if (mv_hypercall_opcode(vp->rax()) != MV_VP_STATE_OP_VAL) { return false; } // TODO: Validate the handle - switch (mv_hypercall_index(vcpu->rax())) { + switch (mv_hypercall_index(vp->rax())) { + case MV_VP_STATE_OP_REG_VAL_IDX_VAL: + this->reg_val(vp); + return true; + case MV_VP_STATE_OP_MSR_VAL_IDX_VAL: + this->msr_val(vp); + return true; default: break; }; - vcpu->set_rax(MV_STATUS_FAILURE_UNKNOWN_HYPERCALL); + vp->set_rax(MV_STATUS_FAILURE_UNKNOWN_HYPERCALL); return true; } diff --git a/bfvmm/src/hve/arch/intel_x64/vmexit/msr.cpp b/bfvmm/src/hve/arch/intel_x64/vmexit/msr.cpp index 3cf8961..edc502b 100644 --- a/bfvmm/src/hve/arch/intel_x64/vmexit/msr.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vmexit/msr.cpp @@ -285,4 +285,20 @@ msr_handler::handle_wrmsr_0x0000064E( return false; } +// ----------------------------------------------------------------------------- +// Accessor / Mutator +// ----------------------------------------------------------------------------- + +uint64_t +msr_handler::get_msr(uint32_t msr) const +{ + return m_msrs.at(msr); +} + +void +msr_handler::set_msr(uint32_t msr, uint64_t val) +{ + m_msrs[msr] = val; +} + } diff --git a/bfvmm/src/main/arch/intel_x64/vcpu_factory.cpp b/bfvmm/src/main/arch/intel_x64/vcpu_factory.cpp index 278758c..3def1a0 100644 --- a/bfvmm/src/main/arch/intel_x64/vcpu_factory.cpp +++ b/bfvmm/src/main/arch/intel_x64/vcpu_factory.cpp @@ -21,6 +21,7 @@ #include #include +#include namespace bfvmm { @@ -29,12 +30,16 @@ std::unique_ptr vcpu_factory::make(vcpuid::type vcpuid, void *data) { using namespace boxy::intel_x64; - static domain dom0{0}; + static bfn::once_flag flag; if (vcpuid::is_host_vcpu(vcpuid)) { + bfn::call_once(flag, [&] { + g_dm->create(0, nullptr); + }); + return std::make_unique( - vcpuid, &dom0 + vcpuid, get_domain(0) ); } else { diff --git a/hypervisor b/hypervisor index 7c1dce3..9358b54 160000 --- a/hypervisor +++ b/hypervisor @@ -1 +1 @@ -Subproject commit 7c1dce35e9e54601de1c4655565fde803ab446f0 +Subproject commit 9358b546244a71512d92e7983638048733f8abbb From 2b3c614e91f4e10c11b4f5b693e01e74a0812524 Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Fri, 28 Aug 2020 22:43:56 -0400 Subject: [PATCH 4/5] Buildroot: Update LibVMI version --- examples/buildroot/package/libvmi/libvmi.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/buildroot/package/libvmi/libvmi.mk b/examples/buildroot/package/libvmi/libvmi.mk index f5123c8..2d6ab45 100644 --- a/examples/buildroot/package/libvmi/libvmi.mk +++ b/examples/buildroot/package/libvmi/libvmi.mk @@ -5,7 +5,7 @@ ################################################################################ ifeq ($(BR2_LIBVMI_PATH),) - LIBVMI_VERSION = 1dd5a2e48e43b70b50d7ff4a01ad1fcb3b8cbba7 + LIBVMI_VERSION = bebefc7c4564b48801baf97084ff21f26d24631c LIBVMI_SITE = $(call github,chp-io,libvmi,$(LIBVMI_VERSION)) LIBVMI_REPO = https://github.com/chp-io/libvmi else From 53a6934256da21faeac13e5c4797903ab726efa7 Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Thu, 10 Sep 2020 16:21:36 -0400 Subject: [PATCH 5/5] MicroV: invept on map_range and unmap_range --- bfvmm/include/hve/arch/intel_x64/domain.h | 6 ++++++ bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/bfvmm/include/hve/arch/intel_x64/domain.h b/bfvmm/include/hve/arch/intel_x64/domain.h index 496f2cb..3af121f 100644 --- a/bfvmm/include/hve/arch/intel_x64/domain.h +++ b/bfvmm/include/hve/arch/intel_x64/domain.h @@ -211,6 +211,9 @@ class domain : public boxy::domain /// effectivily remapping our guest physical addresses to their host /// physical addresses. /// + /// The caller must invalidate the translations derived from EPT with + /// invept. + /// /// @expects /// @ensures /// @@ -233,6 +236,9 @@ class domain : public boxy::domain /// Unshare a range of 4k guest physical addresses previously assigned with /// share_range. /// + /// The caller must invalidate the translations derived from EPT with + /// invept. + /// /// @expects /// @ensures /// diff --git a/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp b/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp index c941aa2..c397162 100644 --- a/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp +++ b/bfvmm/src/hve/arch/intel_x64/vmcall/vm_state_op.cpp @@ -180,6 +180,7 @@ vm_state_op_handler::map_range(vcpu *vp) try { dst_vm->share_range(dst_gpa, src_gpa, src_vm->ept(), size, attr, cache); + vp->invept(); } catchall({ throw std::runtime_error("map_range failed"); @@ -200,6 +201,7 @@ vm_state_op_handler::unmap_range(vcpu *vp) try { dst_vm->unshare_range(dst_gpa, size, attr, cache); + vp->invept(); } catchall({ throw std::runtime_error("map_range failed");