From 00743917f55ab63c18e2b0e8cdc91a5a00aa727c Mon Sep 17 00:00:00 2001 From: Pavan Date: Fri, 22 Oct 2021 04:27:34 +0000 Subject: [PATCH 1/9] MSRS Get/Set API Changes --- hypercall/include/mv_rdl_t.h | 4 + shim/include/handle_system_kvm_get_msrs.h | 4 +- shim/include/handle_vcpu_kvm_get_msrs.h | 7 +- shim/include/handle_vcpu_kvm_set_msrs.h | 7 +- shim/include/kvm_msr_entry.h | 61 ++++++++++ shim/include/kvm_msr_entry.hpp | 53 +++++++++ shim/include/kvm_msrs.h | 14 ++- shim/include/kvm_msrs.hpp | 60 ++++++++++ shim/integration/CMakeLists.txt | 2 + shim/integration/kvm_get_msrs.cpp | 110 ++++++++++++++++++ shim/integration/kvm_set_msrs.cpp | 110 ++++++++++++++++++ .../shim_platform_interface.h | 8 +- .../shim_platform_interface.hpp | 22 +++- shim/linux/src/entry.c | 54 +++++++-- shim/src/handle_vcpu_kvm_get_msrs.c | 51 +++++++- shim/src/handle_vcpu_kvm_set_msrs.c | 43 ++++++- .../src/test_handle_vcpu_kvm_get_msrs.cpp | 94 ++++++++++++++- .../src/test_handle_vcpu_kvm_set_msrs.cpp | 49 +++++++- 18 files changed, 713 insertions(+), 40 deletions(-) create mode 100644 shim/include/kvm_msr_entry.h create mode 100644 shim/include/kvm_msr_entry.hpp create mode 100644 shim/include/kvm_msrs.hpp create mode 100644 shim/integration/kvm_get_msrs.cpp create mode 100644 shim/integration/kvm_set_msrs.cpp diff --git a/hypercall/include/mv_rdl_t.h b/hypercall/include/mv_rdl_t.h index fe70cebb9..3aebe4021 100644 --- a/hypercall/include/mv_rdl_t.h +++ b/hypercall/include/mv_rdl_t.h @@ -30,6 +30,10 @@ #include // IWYU pragma: export #include +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + #ifdef __cplusplus extern "C" { diff --git a/shim/include/handle_system_kvm_get_msrs.h b/shim/include/handle_system_kvm_get_msrs.h index 94d5ad2cc..7c53278b0 100644 --- a/shim/include/handle_system_kvm_get_msrs.h +++ b/shim/include/handle_system_kvm_get_msrs.h @@ -40,10 +40,10 @@ extern "C" * @brief Handles the execution of kvm_check_extension. * * - * @param pmut_ioctl_args the arguments provided by userspace + * @param pmut_userargs the arguments provided by userspace * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. */ - NODISCARD int64_t handle_system_kvm_get_msrs(struct kvm_msrs *const pmut_ioctl_args) NOEXCEPT; + NODISCARD int64_t handle_system_kvm_get_msrs(struct kvm_msrs *const pmut_userargs) NOEXCEPT; #ifdef __cplusplus } diff --git a/shim/include/handle_vcpu_kvm_get_msrs.h b/shim/include/handle_vcpu_kvm_get_msrs.h index f0df091c9..04f8ed8e9 100644 --- a/shim/include/handle_vcpu_kvm_get_msrs.h +++ b/shim/include/handle_vcpu_kvm_get_msrs.h @@ -29,6 +29,7 @@ #include #include +#include #ifdef __cplusplus extern "C" @@ -40,10 +41,12 @@ extern "C" * @brief Handles the execution of kvm_get_msrs. * * - * @param pmut_ioctl_args the arguments provided by userspace + * @param vcpu to get vsid to pass to hypercall + * @param pmut_args the arguments provided by userspace * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. */ - NODISCARD int64_t handle_vcpu_kvm_get_msrs(struct kvm_msrs *const pmut_ioctl_args) NOEXCEPT; + NODISCARD int64_t handle_vcpu_kvm_get_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs *const pmut_args) NOEXCEPT; #ifdef __cplusplus } diff --git a/shim/include/handle_vcpu_kvm_set_msrs.h b/shim/include/handle_vcpu_kvm_set_msrs.h index 9bc49c0b7..374732537 100644 --- a/shim/include/handle_vcpu_kvm_set_msrs.h +++ b/shim/include/handle_vcpu_kvm_set_msrs.h @@ -29,6 +29,7 @@ #include #include +#include #ifdef __cplusplus extern "C" @@ -40,10 +41,12 @@ extern "C" * @brief Handles the execution of kvm_set_msrs. * * - * @param pmut_ioctl_args the arguments provided by userspace + * @param vcpu arguments received from private data + * @param args the arguments provided by userspace * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. */ - NODISCARD int64_t handle_vcpu_kvm_set_msrs(struct kvm_msrs *const pmut_ioctl_args) NOEXCEPT; + NODISCARD int64_t handle_vcpu_kvm_set_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs const *const args) NOEXCEPT; #ifdef __cplusplus } diff --git a/shim/include/kvm_msr_entry.h b/shim/include/kvm_msr_entry.h new file mode 100644 index 000000000..b5db31b74 --- /dev/null +++ b/shim/include/kvm_msr_entry.h @@ -0,0 +1,61 @@ +/** + * @copyright + * Copyright (C) 2020 Assured Information Security, Inc. + * + * @copyright + * 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: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * 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 KVM_MSR_ENTRY_H +#define KVM_MSR_ENTRY_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#pragma pack(push, 1) + + /** + * @struct kvm_msr_entry + * + * + * @brief see /include/uapi/linux/kvm.h in Linux for more details. + */ + struct kvm_msr_entry + { + /** @brief defines index for msr entries */ + uint32_t index; + /** @brief defines reserved for msr entries */ + uint32_t reserved; + /** @brief defines data for msr entries*/ + uint64_t data; + }; + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shim/include/kvm_msr_entry.hpp b/shim/include/kvm_msr_entry.hpp new file mode 100644 index 000000000..38bdc7591 --- /dev/null +++ b/shim/include/kvm_msr_entry.hpp @@ -0,0 +1,53 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 KVM_MSR_ENTRY_HPP +#define KVM_MSR_ENTRY_HPP + +#include + +#pragma pack(push, 1) + +namespace shim +{ + /// @struct kvm_msr_entry + /// + /// + /// @brief see /include/uapi/linux/kvm.h in Linux for more details. + /// + struct kvm_msr_entry final + { + /** @brief defines index for msr entries */ + bsl::uint32 index; + /** @brief defines reserved for msr entries */ + bsl::uint32 reserved; + /** @brief defines data for msr entries*/ + bsl::uint64 data; + }; + +} + +#pragma pack(pop) + +#endif diff --git a/shim/include/kvm_msrs.h b/shim/include/kvm_msrs.h index 308c8b86b..bf7743235 100644 --- a/shim/include/kvm_msrs.h +++ b/shim/include/kvm_msrs.h @@ -27,8 +27,14 @@ #ifndef KVM_MSRS_H #define KVM_MSRS_H +#include +#include #include +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + #ifdef __cplusplus extern "C" { @@ -44,8 +50,12 @@ extern "C" */ struct kvm_msrs { - /** @brief replace me with contents from KVM API */ - int32_t dummy; + /** @brief number of msrs in entries */ + uint32_t nmsrs; + /** @brief number of pad in entries */ + uint32_t pad; + /** @brief defines array of entries*/ + struct kvm_msr_entry entries[MV_RDL_MAX_ENTRIES]; }; #pragma pack(pop) diff --git a/shim/include/kvm_msrs.hpp b/shim/include/kvm_msrs.hpp new file mode 100644 index 000000000..99862c277 --- /dev/null +++ b/shim/include/kvm_msrs.hpp @@ -0,0 +1,60 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 KVM_MSRS_HPP +#define KVM_MSRS_HPP + +#include + +#include +#include +#include + +#pragma pack(push, 1) + +namespace shim +{ + /// @brief defines the max number of entires in the RDL + constexpr auto MV_RDL_MAX_ENTRIES{250_u64}; + + /// @struct kvm_msrs + /// + /// + /// @brief see /include/uapi/linux/kvm.h in Linux for more details. + /// + struct kvm_msrs final + { + /** @brief number of msrs in entries */ + bsl::uint32 nmsrs; + /** @brief number of pad in entries */ + bsl::uint32 pad; + /// @brief stores each entry in the RDL + bsl::array entries; + }; + +} + +#pragma pack(pop) + +#endif diff --git a/shim/integration/CMakeLists.txt b/shim/integration/CMakeLists.txt index 3433c3c6c..e95a2f049 100644 --- a/shim/integration/CMakeLists.txt +++ b/shim/integration/CMakeLists.txt @@ -46,3 +46,5 @@ microv_add_shim_integration(kvm_get_mp_state HEADERS) microv_add_shim_integration(kvm_set_mp_state HEADERS) microv_add_shim_integration(kvm_get_tsc_khz HEADERS) microv_add_shim_integration(kvm_get_msr_index_list HEADERS) +microv_add_shim_integration(kvm_get_msrs HEADERS) +microv_add_shim_integration(kvm_set_msrs HEADERS) diff --git a/shim/integration/kvm_get_msrs.cpp b/shim/integration/kvm_get_msrs.cpp new file mode 100644 index 000000000..064df9221 --- /dev/null +++ b/shim/integration/kvm_get_msrs.cpp @@ -0,0 +1,110 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + /// @brief defines the number of MSRS we expect + constexpr auto EXPECTED_NMSRS{0x01_u32}; + /// @brief defines the PAD we expect + constexpr auto EXPECTED_PAD{0x00_u32}; + /// @brief defines the size for entries in RDL + constexpr auto MYSIZE_ENTRIES{1_u64}; + /// @brief defines the register index we expect + constexpr auto EXPECTED_INDEX{0x00_u32}; + /// @brief defines the register data we expect + constexpr auto EXPECTED_DATA{0x42_u64}; +} + +/// +/// @brief Provides the main entry point for this application. +/// +/// +/// @return bsl::exit_success on success, bsl::exit_failure otherwise. +/// +[[nodiscard]] auto +main() noexcept -> bsl::exit_code +{ + shim::kvm_msrs mut_msrs{}; + + mut_msrs.nmsrs = EXPECTED_NMSRS.get(); + mut_msrs.pad = EXPECTED_PAD.get(); + + for (bsl::safe_idx mut_i{}; mut_i < MYSIZE_ENTRIES.get(); ++mut_i) { + mut_msrs.entries.at_if(mut_i)->index = EXPECTED_INDEX.get(); + mut_msrs.entries.at_if(mut_i)->data = EXPECTED_DATA.get(); + } + + bsl::enable_color(); + integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; + + /// Verify that get/set works + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + auto const vcpufd{mut_vm.send(shim::KVM_CREATE_VCPU)}; + integration::ioctl_t mut_vcpu{bsl::to_i32(vcpufd)}; + + integration::verify(mut_vcpu.write(shim::KVM_SET_MSRS, &mut_msrs).is_zero()); + mut_msrs = {}; + auto const ret{bsl::to_u32(mut_vcpu.read(shim::KVM_GET_MSRS, &mut_msrs))}; + + integration::verify(ret == EXPECTED_NMSRS.get()); + integration::verify(EXPECTED_NMSRS == mut_msrs.nmsrs); + integration::verify(EXPECTED_PAD == mut_msrs.pad); + for (bsl::safe_idx mut_i{}; mut_i < MYSIZE_ENTRIES.get(); ++mut_i) { + mut_msrs.entries.at_if(mut_i)->index = EXPECTED_INDEX.get(); + mut_msrs.entries.at_if(mut_i)->data = EXPECTED_DATA.get(); + } + } + // Try a bunch of times + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + auto const vcpufd{mut_vm.send(shim::KVM_CREATE_VCPU)}; + integration::ioctl_t mut_vcpu{bsl::to_i32(vcpufd)}; + + constexpr auto num_loops{0x1000_umx}; + for (bsl::safe_idx mut_i{}; mut_i < num_loops; ++mut_i) { + auto const ret{bsl::to_u32(mut_vcpu.read(shim::KVM_GET_MSRS, &mut_msrs))}; + integration::verify(ret == EXPECTED_NMSRS.get()); + } + } + + return bsl::exit_success; +} diff --git a/shim/integration/kvm_set_msrs.cpp b/shim/integration/kvm_set_msrs.cpp new file mode 100644 index 000000000..eb11427a3 --- /dev/null +++ b/shim/integration/kvm_set_msrs.cpp @@ -0,0 +1,110 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + /// @brief defines the number of MSRS we expect + constexpr auto EXPECTED_NMSRS{0x01_u32}; + /// @brief defines the PAD we expect + constexpr auto EXPECTED_PAD{0x00_u32}; + /// @brief defines the size for entries in RDL + constexpr auto MYSIZE_ENTRIES{1_u64}; + /// @brief defines the register index we expect + constexpr auto EXPECTED_INDEX{0x00_u32}; + /// @brief defines the register data we expect + constexpr auto EXPECTED_DATA{0x42_u64}; +} + +/// +/// @brief Provides the main entry point for this application. +/// +/// +/// @return bsl::exit_success on success, bsl::exit_failure otherwise. +/// +[[nodiscard]] auto +main() noexcept -> bsl::exit_code +{ + shim::kvm_msrs mut_msrs{}; + + mut_msrs.nmsrs = EXPECTED_NMSRS.get(); + mut_msrs.pad = EXPECTED_PAD.get(); + + for (bsl::safe_idx mut_i{}; mut_i < MYSIZE_ENTRIES.get(); ++mut_i) { + mut_msrs.entries.at_if(mut_i)->index = EXPECTED_INDEX.get(); + mut_msrs.entries.at_if(mut_i)->data = EXPECTED_DATA.get(); + } + + bsl::enable_color(); + integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; + + /// Verify that get/set works + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + auto const vcpufd{mut_vm.send(shim::KVM_CREATE_VCPU)}; + integration::ioctl_t mut_vcpu{bsl::to_i32(vcpufd)}; + + integration::verify(mut_vcpu.write(shim::KVM_SET_MSRS, &mut_msrs).is_zero()); + mut_msrs = {}; + auto const ret{bsl::to_u32(mut_vcpu.read(shim::KVM_GET_MSRS, &mut_msrs))}; + + integration::verify(ret == EXPECTED_NMSRS.get()); + integration::verify(EXPECTED_NMSRS == mut_msrs.nmsrs); + integration::verify(EXPECTED_PAD == mut_msrs.pad); + for (bsl::safe_idx mut_i{}; mut_i < MYSIZE_ENTRIES.get(); ++mut_i) { + mut_msrs.entries.at_if(mut_i)->index = EXPECTED_INDEX.get(); + mut_msrs.entries.at_if(mut_i)->data = EXPECTED_DATA.get(); + } + } + + // Try a bunch of times + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + auto const vcpufd{mut_vm.send(shim::KVM_CREATE_VCPU)}; + integration::ioctl_t mut_vcpu{bsl::to_i32(vcpufd)}; + + constexpr auto num_loops{0x1000_umx}; + for (bsl::safe_idx mut_i{}; mut_i < num_loops; ++mut_i) { + integration::verify(mut_vcpu.write(shim::KVM_SET_MSRS, &mut_msrs).is_zero()); + } + } + + return bsl::exit_success; +} diff --git a/shim/linux/include/platform_interface/shim_platform_interface.h b/shim/linux/include/platform_interface/shim_platform_interface.h index 03e73db7b..fd2ed2f1c 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.h +++ b/shim/linux/include/platform_interface/shim_platform_interface.h @@ -78,6 +78,9 @@ /** @brief defines the /dev name of the shim */ #define SHIM_DEVICE_NAME "/dev/microv_shim" +#define _IOWR_LIST(type, nr, size, sub_size) \ + _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), sizeof(size) - sizeof(sub_size)) + /** @brief defines KVM's KVM_GET_API_VERSION IOCTL */ #define KVM_GET_API_VERSION _IO(SHIMIO, 0x00) /** @brief defines KVM's KVM_CREATE_VM IOCTL */ @@ -109,8 +112,11 @@ /** @brief defines KVM's KVM_INTERRUPT IOCTL */ #define KVM_INTERRUPT _IOW(SHIMIO, 0x86, struct kvm_interrupt) /** @brief defines KVM's KVM_GET_MSRS IOCTL */ -#define KVM_GET_MSRS _IOWR(SHIMIO, 0x88, struct kvm_msrs) +#define KVM_GET_MSRS \ + _IOWR_LIST(SHIMIO, 0x88, struct kvm_msrs, struct kvm_msr_entry[MV_RDL_MAX_ENTRIES]) +//#define KVM_GET_MSRS _IOWR(SHIMIO, 0x88, struct kvm_msrs) /** @brief defines KVM's KVM_SET_MSRS IOCTL */ +//#define KVM_SET_MSRS _IOWR_LIST(SHIMIO, 0x89, struct kvm_msrs, struct kvm_msr_entry[MV_RDL_MAX_ENTRIES]) #define KVM_SET_MSRS _IOW(SHIMIO, 0x89, struct kvm_msrs) /** @brief defines KVM's KVM_SET_CPUID IOCTL */ #define KVM_SET_CPUID _IOW(SHIMIO, 0x8a, struct kvm_cpuid) diff --git a/shim/linux/include/platform_interface/shim_platform_interface.hpp b/shim/linux/include/platform_interface/shim_platform_interface.hpp index 74321c259..b7f062e95 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.hpp +++ b/shim/linux/include/platform_interface/shim_platform_interface.hpp @@ -30,7 +30,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -78,6 +80,16 @@ // #include // #include // #include +/** + * @brief Hack for defining ioctl commands that require structs + * with zero-length arrays. This is usually for ioctls that return + * a list. + * + * It is just like _IOWR, except it subtracts the size of a pointer + * from the size of the struct passed. + */ +#define _IOWR_LIST(type, nr, size, sub_size) \ + _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), sizeof(size) - sizeof(sub_size)) namespace shim { @@ -125,10 +137,12 @@ namespace shim // constexpr bsl::safe_umx KVM_TRANSLATE{static_cast(_IOWR(SHIMIO.get(), 0x85, struct kvm_translation))}; // /// @brief defines KVM's KVM_INTERRUPT IOCTL // constexpr bsl::safe_umx KVM_INTERRUPT{static_cast(_IOW(SHIMIO.get(), 0x86, struct kvm_interrupt))}; - // /// @brief defines KVM's KVM_GET_MSRS IOCTL - // constexpr bsl::safe_umx KVM_GET_MSRS{static_cast(_IOWR(SHIMIO.get(), 0x88, struct kvm_msrs))}; - // /// @brief defines KVM's KVM_SET_MSRS IOCTL - // constexpr bsl::safe_umx KVM_SET_MSRS{static_cast(_IOW(SHIMIO.get(), 0x89, struct kvm_msrs))}; + /// @brief defines KVM's KVM_GET_MSRS IOCTL + constexpr bsl::safe_umx KVM_GET_MSRS{static_cast(_IOWR_LIST( + SHIMIO.get(), 0x88, struct kvm_msrs, struct kvm_msr_entry[MV_RDL_MAX_ENTRIES.get()]))}; + /// @brief defines KVM's KVM_SET_MSRS IOCTL + constexpr bsl::safe_umx KVM_SET_MSRS{ + static_cast(_IOW(SHIMIO.get(), 0x89, struct kvm_msrs))}; // /// @brief defines KVM's KVM_SET_CPUID IOCTL // constexpr bsl::safe_umx KVM_SET_CPUID{static_cast(_IOW(SHIMIO.get(), 0x8a, struct kvm_cpuid))}; // /// @brief defines KVM's KVM_GET_CPUID2 IOCTL diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index a848e23b2..6c17842d8 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -32,15 +32,18 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -951,10 +954,23 @@ dispatch_vcpu_kvm_get_mp_state( } static long -dispatch_vcpu_kvm_get_msrs(struct kvm_msrs *const ioctl_args) +dispatch_vcpu_kvm_get_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs *const user_args) { - (void)ioctl_args; - return -EINVAL; + struct kvm_msrs mut_args; + uint64_t const size = sizeof(mut_args); + + if (handle_vcpu_kvm_get_msrs(vcpu, &mut_args)) { + bferror("handle_vcpu_kvm_get_msrs failed"); + return -EINVAL; + } + + if (platform_copy_to_user(user_args, &mut_args, size)) { + bferror("platform_copy_to_user failed"); + return -EINVAL; + } + + return (long)mut_args.nmsrs; } static long @@ -1155,10 +1171,29 @@ dispatch_vcpu_kvm_set_mp_state( } static long -dispatch_vcpu_kvm_set_msrs(struct kvm_msrs *const ioctl_args) +dispatch_vcpu_kvm_set_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs *const user_args) { - (void)ioctl_args; - return -EINVAL; + + struct kvm_msrs mut_args; + uint64_t const size = sizeof(mut_args); + + if (NULL == user_args) { + bferror("user_args are null"); + return -EINVAL; + } + + if (platform_copy_from_user(&mut_args, user_args, size)) { + bferror("platform_copy_from_user failed"); + return -EINVAL; + } + + if (handle_vcpu_kvm_set_msrs(vcpu, &mut_args)) { + bferror("handle_vcpu_kvm_set_msrs failed"); + return -EINVAL; + } + + return 0; } static long @@ -1314,7 +1349,8 @@ dev_unlocked_ioctl_vcpu( } case KVM_GET_MSRS: { - return dispatch_vcpu_kvm_get_msrs((struct kvm_msrs *)ioctl_args); + return dispatch_vcpu_kvm_get_msrs( + pmut_mut_vcpu, (struct kvm_msrs *)ioctl_args); } case KVM_GET_NESTED_STATE: { @@ -1390,7 +1426,6 @@ dev_unlocked_ioctl_vcpu( } case KVM_SET_FPU: { - bferror("in KVM_set_fpu"); return dispatch_vcpu_kvm_set_fpu( pmut_mut_vcpu, (struct kvm_fpu *)ioctl_args); } @@ -1411,7 +1446,8 @@ dev_unlocked_ioctl_vcpu( } case KVM_SET_MSRS: { - return dispatch_vcpu_kvm_set_msrs((struct kvm_msrs *)ioctl_args); + return dispatch_vcpu_kvm_set_msrs( + pmut_mut_vcpu, (struct kvm_msrs *)ioctl_args); } case KVM_SET_NESTED_STATE: { diff --git a/shim/src/handle_vcpu_kvm_get_msrs.c b/shim/src/handle_vcpu_kvm_get_msrs.c index 3e9b2d298..4b63a6155 100644 --- a/shim/src/handle_vcpu_kvm_get_msrs.c +++ b/shim/src/handle_vcpu_kvm_get_msrs.c @@ -24,20 +24,63 @@ * SOFTWARE. */ +#include +#include +#include +#include #include +#include +#include +#include #include - +#include +#include +#include /** * * @brief Handles the execution of kvm_get_msrs. * * - * @param pmut_ioctl_args the arguments provided by userspace + * @param vcpu arguments received from private data + * @param pmut_args the arguments provided by userspace * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. */ NODISCARD int64_t -handle_vcpu_kvm_get_msrs(struct kvm_msrs *const pmut_ioctl_args) NOEXCEPT +handle_vcpu_kvm_get_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs *const pmut_args) NOEXCEPT { - (void)pmut_ioctl_args; + uint64_t mut_i; + platform_expects(MV_INVALID_HANDLE != g_mut_hndl); + platform_expects(NULL != vcpu); + platform_expects(NULL != pmut_args); + + if (detect_hypervisor()) { + bferror("The shim is not running in a VM. Did you forget to start MicorV?"); + return SHIM_FAILURE; + } + + struct mv_rdl_t *const pmut_rdl = (struct mv_rdl_t *)shared_page_for_current_pp(); + platform_expects(NULL != pmut_rdl); + + pmut_rdl->num_entries = (uint64_t)pmut_args->nmsrs; + + for (mut_i = ((uint64_t)0); mut_i < pmut_rdl->num_entries; ++mut_i) { + pmut_rdl->entries[mut_i].reg = (uint64_t)pmut_args->entries[mut_i].index; + } + + if (mv_vs_op_msr_get_list(g_mut_hndl, vcpu->vsid)) { + bferror("mv_vs_op_msr_get_list failed"); + return SHIM_FAILURE; + } + + if (pmut_rdl->num_entries != (uint64_t)pmut_args->nmsrs) { + bferror("The RDL's num_entries is no longer valid"); + return SHIM_FAILURE; + } + + for (mut_i = ((uint64_t)0); mut_i < pmut_rdl->num_entries; ++mut_i) { + pmut_args->entries[mut_i].data = pmut_rdl->entries[mut_i].val; + } + return SHIM_SUCCESS; } diff --git a/shim/src/handle_vcpu_kvm_set_msrs.c b/shim/src/handle_vcpu_kvm_set_msrs.c index 6f2bc0380..e719c5fea 100644 --- a/shim/src/handle_vcpu_kvm_set_msrs.c +++ b/shim/src/handle_vcpu_kvm_set_msrs.c @@ -23,21 +23,56 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include /** * * @brief Handles the execution of kvm_set_msrs. * * - * @param pmut_ioctl_args the arguments provided by userspace + * @param args the arguments provided by userspace + * @param vcpu arguments received from private data * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. */ NODISCARD int64_t -handle_vcpu_kvm_set_msrs(struct kvm_msrs *const pmut_ioctl_args) NOEXCEPT +handle_vcpu_kvm_set_msrs( + struct shim_vcpu_t const *const vcpu, struct kvm_msrs const *const args) NOEXCEPT { - (void)pmut_ioctl_args; + uint64_t mut_i; + platform_expects(MV_INVALID_HANDLE != g_mut_hndl); + platform_expects(NULL != vcpu); + platform_expects(NULL != args); + + if (detect_hypervisor()) { + bferror("The shim is not running in a VM. Did you forget to start MicroV?"); + return SHIM_FAILURE; + } + + struct mv_rdl_t *const pmut_rdl = (struct mv_rdl_t *)shared_page_for_current_pp(); + platform_expects(NULL != pmut_rdl); + + pmut_rdl->num_entries = (uint64_t)args->nmsrs; + + for (mut_i = ((uint64_t)0); mut_i < pmut_rdl->num_entries; ++mut_i) { + pmut_rdl->entries[mut_i].reg = (uint64_t)args->entries[mut_i].index; + pmut_rdl->entries[mut_i].val = args->entries[mut_i].data; + } + + if (mv_vs_op_msr_set_list(g_mut_hndl, vcpu->vsid)) { + bferror("mv_vs_op_msr_set_list failed"); + return SHIM_FAILURE; + } + return SHIM_SUCCESS; } diff --git a/shim/tests/src/test_handle_vcpu_kvm_get_msrs.cpp b/shim/tests/src/test_handle_vcpu_kvm_get_msrs.cpp index 6b9fca788..6b2107682 100644 --- a/shim/tests/src/test_handle_vcpu_kvm_get_msrs.cpp +++ b/shim/tests/src/test_handle_vcpu_kvm_get_msrs.cpp @@ -24,13 +24,18 @@ #include "../../include/handle_vcpu_kvm_get_msrs.h" +#include #include -#include +#include +#include +#include #include namespace shim { + constexpr auto VAL32{2_u32}; + /// /// @brief Used to execute the actual checks. We put the checks in this /// function so that we can validate the tests both at compile-time @@ -43,18 +48,97 @@ namespace shim [[nodiscard]] constexpr auto tests() noexcept -> bsl::exit_code { - bsl::ut_scenario{"description"} = []() noexcept { + init_tests(); + constexpr auto handle{&handle_vcpu_kvm_get_msrs}; + + bsl::ut_scenario{"success"} = []() noexcept { bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; kvm_msrs mut_args{}; bsl::ut_when{} = [&]() noexcept { + mut_args.nmsrs = VAL32.get(); bsl::ut_then{} = [&]() noexcept { - bsl::ut_check(SHIM_SUCCESS == handle_vcpu_kvm_get_msrs(&mut_args)); + bsl::ut_check(SHIM_SUCCESS == handle(&vcpu, &mut_args)); }; }; }; }; - - return bsl::ut_success(); + bsl::ut_scenario{"hypervisor not detected"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_hypervisor_detected = false; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_hypervisor_detected = true; + }; + }; + }; + }; + bsl::ut_scenario{"mv_vs_op_msr_get_list fails"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = MV_STATUS_FAILURE_UNKNOWN; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = {}; + }; + }; + }; + }; + bsl::ut_scenario{"mv_vs_op_msr_get_list adds 0 register"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = MV_STATUS_FAILURE_INC_NUM_ENTRIES; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = {}; + }; + }; + }; + }; + bsl::ut_scenario{"mv_vs_op_msr_get_list adds unknown"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = MV_STATUS_FAILURE_ADD_UNKNOWN; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = {}; + }; + }; + }; + }; + bsl::ut_scenario{"g_mut_mv_vs_op_msr_get_list corrupts num_entries"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = MV_STATUS_FAILURE_CORRUPT_NUM_ENTRIES; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_vs_op_msr_get_list = {}; + }; + }; + }; + }; + return fini_tests(); } } diff --git a/shim/tests/src/test_handle_vcpu_kvm_set_msrs.cpp b/shim/tests/src/test_handle_vcpu_kvm_set_msrs.cpp index 25e08be69..44e084acd 100644 --- a/shim/tests/src/test_handle_vcpu_kvm_set_msrs.cpp +++ b/shim/tests/src/test_handle_vcpu_kvm_set_msrs.cpp @@ -24,13 +24,18 @@ #include "../../include/handle_vcpu_kvm_set_msrs.h" +#include #include -#include +#include +#include +#include #include namespace shim { + constexpr auto VAL32{2_u32}; + /// /// @brief Used to execute the actual checks. We put the checks in this /// function so that we can validate the tests both at compile-time @@ -43,18 +48,52 @@ namespace shim [[nodiscard]] constexpr auto tests() noexcept -> bsl::exit_code { - bsl::ut_scenario{"description"} = []() noexcept { + init_tests(); + constexpr auto handle{&handle_vcpu_kvm_set_msrs}; + + bsl::ut_scenario{"success"} = []() noexcept { bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; kvm_msrs mut_args{}; bsl::ut_when{} = [&]() noexcept { + mut_args.nmsrs = VAL32.get(); bsl::ut_then{} = [&]() noexcept { - bsl::ut_check(SHIM_SUCCESS == handle_vcpu_kvm_set_msrs(&mut_args)); + bsl::ut_check(SHIM_SUCCESS == handle(&vcpu, &mut_args)); }; }; }; }; - - return bsl::ut_success(); + bsl::ut_scenario{"hypervisor not detected"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_hypervisor_detected = false; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_hypervisor_detected = true; + }; + }; + }; + }; + bsl::ut_scenario{"mv_vs_op_msr_set_list fails"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + shim_vcpu_t const vcpu{}; + kvm_msrs mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_vs_op_msr_set_list = MV_STATUS_FAILURE_UNKNOWN; + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&vcpu, &mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_vs_op_msr_set_list = {}; + }; + }; + }; + }; + return fini_tests(); } } From 37d4f9e408bc847f67c7e67665a10cf3d865d53c Mon Sep 17 00:00:00 2001 From: Brendan Kerrigan Date: Thu, 21 Oct 2021 08:01:32 -0400 Subject: [PATCH 2/9] io emulation: add support for port IO emulation on Intel Signed-off-by: Brendan Kerrigan --- vmm/src/x64/intel/dispatch_vmexit_io.hpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/vmm/src/x64/intel/dispatch_vmexit_io.hpp b/vmm/src/x64/intel/dispatch_vmexit_io.hpp index fdaa2dd4e..d04bcd11b 100644 --- a/vmm/src/x64/intel/dispatch_vmexit_io.hpp +++ b/vmm/src/x64/intel/dispatch_vmexit_io.hpp @@ -73,11 +73,6 @@ namespace microv vs_pool_t &mut_vs_pool, bsl::safe_u16 const &vsid) noexcept -> bsl::errc_type { - /// TODO: - /// - Need to properly handle string instructions (INS/OUTS) - /// - Need to properly handle IN instructions - /// - bsl::expects(!mut_sys.is_the_active_vm_the_root_vm()); bsl::discard(gs); @@ -131,8 +126,7 @@ namespace microv mut_exit_io->type = hypercall::MV_EXIT_IO_OUT.get(); } else { - bsl::error() << "MV_EXIT_IO_IN not implemented\n" << bsl::here(); - return bsl::errc_failure; + mut_exit_io->type = hypercall::MV_EXIT_IO_IN.get(); } constexpr auto bytes1{0_u64}; From 921d7db5ae27f0ac2387da301de7881d16a1cc88 Mon Sep 17 00:00:00 2001 From: Brendan Kerrigan Date: Fri, 22 Oct 2021 06:12:45 -0400 Subject: [PATCH 3/9] set_user_memory_region: add flag support for kvm set user memory region Signed-off-by: Brendan Kerrigan --- .../handle_vm_kvm_set_user_memory_region.h | 5 -- shim/include/kvm_userspace_memory_region.h | 3 + .../kvm_set_user_memory_region.cpp | 78 +++++++++++++++++++ .../handle_vm_kvm_set_user_memory_region.c | 63 +++++++++++---- vmm/src/x64/emulated_mmio_t.hpp | 5 +- 5 files changed, 129 insertions(+), 25 deletions(-) diff --git a/shim/include/handle_vm_kvm_set_user_memory_region.h b/shim/include/handle_vm_kvm_set_user_memory_region.h index aae722c3b..4d501e20f 100644 --- a/shim/include/handle_vm_kvm_set_user_memory_region.h +++ b/shim/include/handle_vm_kvm_set_user_memory_region.h @@ -35,11 +35,6 @@ extern "C" { #endif -/** @brief TBD */ -#define KVM_MEM_LOG_DIRTY_PAGES (((uint64_t)1) << ((uint64_t)0)) -/** @brief allows a slot to be read-only */ -#define KVM_MEM_READONLY (((uint64_t)1) << ((uint64_t)1)) - /** * * @brief Handles the execution of kvm_set_user_memory_region. diff --git a/shim/include/kvm_userspace_memory_region.h b/shim/include/kvm_userspace_memory_region.h index 2a29a446b..f4da48e6c 100644 --- a/shim/include/kvm_userspace_memory_region.h +++ b/shim/include/kvm_userspace_memory_region.h @@ -36,6 +36,9 @@ extern "C" #pragma pack(push, 1) +#define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) +#define KVM_MEM_READONLY (1UL << 1) + /** * @struct kvm_userspace_memory_region * diff --git a/shim/integration/kvm_set_user_memory_region.cpp b/shim/integration/kvm_set_user_memory_region.cpp index b6954c9d9..d8f61311d 100644 --- a/shim/integration/kvm_set_user_memory_region.cpp +++ b/shim/integration/kvm_set_user_memory_region.cpp @@ -201,6 +201,84 @@ main() noexcept -> bsl::exit_code mut_vm.close(); } + // non-canonical address below the canonical boundary + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + constexpr auto non_canonical_address{0xFFFF7FFFFFFFFFFF_u64}; + shim::kvm_userspace_memory_region mut_region{}; + mut_region.slot = {}; + mut_region.flags = {}; + mut_region.guest_phys_addr = {}; + mut_region.memory_size = vm_image.size().get(); + mut_region.userspace_addr = reinterpret_cast(non_canonical_address.get()); + + auto const ret{mut_vm.write(shim::KVM_SET_USER_MEMORY_REGION, &mut_region)}; + integration::verify(ret.is_neg()); + + mut_vm.close(); + } + + // non-canonical address above the canonical boundary + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + constexpr auto non_canonical_address{0x0008000000000000_u64}; + shim::kvm_userspace_memory_region mut_region{}; + mut_region.slot = {}; + mut_region.flags = {}; + mut_region.guest_phys_addr = {}; + mut_region.memory_size = vm_image.size().get(); + mut_region.userspace_addr = reinterpret_cast(non_canonical_address.get()); + + auto const ret{mut_vm.write(shim::KVM_SET_USER_MEMORY_REGION, &mut_region)}; + integration::verify(ret.is_neg()); + + mut_vm.close(); + } + + // canonical address on the higher canonical boundary + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + constexpr auto non_canonical_address{0xFFFF800000000000_u64}; + shim::kvm_userspace_memory_region mut_region{}; + mut_region.slot = {}; + mut_region.flags = {}; + mut_region.guest_phys_addr = {}; + mut_region.memory_size = vm_image.size().get(); + mut_region.userspace_addr = reinterpret_cast(non_canonical_address.get()); + + auto const ret{mut_vm.write(shim::KVM_SET_USER_MEMORY_REGION, &mut_region)}; + // This fails on platform_virt_to_phys, but none of the error paths distinguish + // between error type. This test should verify ret.is_zero(). Might have to add + // flags for the shim to short circuit. + integration::verify(ret.is_neg()); + + mut_vm.close(); + } + + // canonical address on the lower canonical boundary + { + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + constexpr auto non_canonical_address{0x0007FFFFFFFFFFFF_u64}; + shim::kvm_userspace_memory_region mut_region{}; + mut_region.slot = {}; + mut_region.flags = {}; + mut_region.guest_phys_addr = {}; + mut_region.memory_size = vm_image.size().get(); + mut_region.userspace_addr = reinterpret_cast(non_canonical_address.get()); + + auto const ret{mut_vm.write(shim::KVM_SET_USER_MEMORY_REGION, &mut_region)}; + // This fails on platform_virt_to_phys, but none of the error paths distinguish + // between error type. This test should verify ret.is_zero(). Might have to add + // flags for the shim to short circuit. + integration::verify(ret.is_neg()); + + mut_vm.close(); + } + // The shim has a limited number of slots { auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; diff --git a/shim/src/handle_vm_kvm_set_user_memory_region.c b/shim/src/handle_vm_kvm_set_user_memory_region.c index b96fcbf11..bdf525212 100644 --- a/shim/src/handle_vm_kvm_set_user_memory_region.c +++ b/shim/src/handle_vm_kvm_set_user_memory_region.c @@ -70,6 +70,30 @@ get_slot_as(uint32_t const slot) NOEXCEPT return slot & as_mask; } +/** + * + * @brief Translates KVM_MEM_* flags to MV_MAP_FLAG* flags + * + * + * @param flags KVM memory flag in the set_user_memory_region + * @return Returns the microv equivalent flag for the kvm flag + */ +NODISCARD static inline uint64_t +kvm_to_mv_page_flags(uint32_t const flags) NOEXCEPT +{ + uint64_t mut_flags = ((uint64_t)0); + if (flags & ((uint32_t)KVM_MEM_READONLY)) { + // Anticipate that we'll have to provide RE access here + // in reality + mut_flags |= MV_MAP_FLAG_READ_ACCESS; + } + else { + mv_touch(); + } + + return mut_flags; +} + /** * * @brief Handles the execution of kvm_set_user_memory_region. @@ -83,6 +107,8 @@ NODISCARD int64_t handle_vm_kvm_set_user_memory_region( struct kvm_userspace_memory_region const *const args, struct shim_vm_t *const pmut_vm) NOEXCEPT { + uint64_t const high_canonical_boundary = ((uint64_t)0xFFFF800000000000ULL); + uint64_t const low_canonical_boundary = ((uint64_t)0x00007FFFFFFFFFFFULL); struct mv_mdl_t *pmut_mut_mdl; int64_t mut_i; @@ -92,6 +118,7 @@ handle_vm_kvm_set_user_memory_region( uint32_t mut_slot_as; uint64_t mut_dst; uint64_t mut_src; + uint64_t const flags = kvm_to_mv_page_flags(args->flags); platform_expects(NULL != args); platform_expects(NULL != pmut_vm); @@ -124,7 +151,7 @@ handle_vm_kvm_set_user_memory_region( } if (((uint64_t)0) == args->memory_size) { - bferror("deleting an existing slot is currently not implemeneted"); + bferror("deleting an existing slot is currently not implemented"); return SHIM_FAILURE; } @@ -148,18 +175,26 @@ handle_vm_kvm_set_user_memory_region( return SHIM_FAILURE; } - /// TODO: - /// - Check to make sure that the userspace address that was provided - /// is canonical. Otherwise MicroV will get mad. - /// + if (high_canonical_boundary < args->userspace_addr) { + if (low_canonical_boundary <= args->userspace_addr) { + mv_touch(); + } + else { + bferror("args->userspace_addr is not a canonical address"); + return SHIM_FAILURE; + } + } + else { + mv_touch(); + } - /// TODO: - /// - Check to make sure that the provided flags are supported by MicroV - /// and then construct the MicroV flags as required. - /// + // Don't support KVM_MEM_LOG_DIRTY_PAGES right now + if (args->flags & ~((uint32_t)KVM_MEM_READONLY)) { + return SHIM_FAILURE; + } /// TODO: - /// - Check to make sure that non of the slots overlap. This is not + /// - Check to make sure that none of the slots overlap. This is not /// allowed by the KVM API, and even if it were, MicroV would get /// mad as it doesn't allow this either. /// @@ -231,15 +266,9 @@ handle_vm_kvm_set_user_memory_region( pmut_mut_mdl->entries[pmut_mut_mdl->num_entries].dst = dst; pmut_mut_mdl->entries[pmut_mut_mdl->num_entries].src = src; pmut_mut_mdl->entries[pmut_mut_mdl->num_entries].bytes = HYPERVISOR_PAGE_SIZE; + pmut_mut_mdl->entries[pmut_mut_mdl->num_entries].flags = flags; ++pmut_mut_mdl->num_entries; - /// TODO: - /// - Need to add support for memory flags. Right now, MicroV ignores - /// the flags field and always sets the memory to RWE. This needs - /// to be fixed, and then we will need to translate the KVM flags - /// to MicroV flags here and send them up properly. - /// - /// TODO: /// - Right now MicroV assumes that every entry is 4k in size. /// Instead, it should be modified to handle any page aligned diff --git a/vmm/src/x64/emulated_mmio_t.hpp b/vmm/src/x64/emulated_mmio_t.hpp index 4025aa44c..5126a0670 100644 --- a/vmm/src/x64/emulated_mmio_t.hpp +++ b/vmm/src/x64/emulated_mmio_t.hpp @@ -247,7 +247,7 @@ namespace microv auto const gpa{bsl::to_u64(entry->dst)}; auto const spa{this->gpa_to_spa(mut_sys, bsl::to_u64(entry->src))}; - + auto const flags{bsl::to_u64(entry->flags)}; /// TODO: /// - Add support for entries that have a size greater than /// 4k. For now we only support 4k pages. @@ -258,8 +258,7 @@ namespace microv /// because guest software will not attempt to undo a /// failed map operation. /// - auto const ret{ - m_slpt.map(tls, mut_page_pool, gpa, spa, MAP_PAGE_RWE, false, mut_sys)}; + auto const ret{m_slpt.map(tls, mut_page_pool, gpa, spa, flags, false, mut_sys)}; if (bsl::unlikely(ret == bsl::errc_already_exists)) { bsl::error() << "mdl entry " // -- From c67cd353d6f92539a8412fb3a7abcd87218a6b74 Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Fri, 22 Oct 2021 10:19:37 -0400 Subject: [PATCH 4/9] shim/linux: use normal array in kvm_msr_list Instead of using a pointer for the indices entry in kvm_msr_list, use a normal array and define the KVM_MSR_GET_INDEX_LIST ioctl using the _IOWR_LIST macro (see "shim/linux: add _IOWR_LIST macro"). Define MSR_LIST_MAX_INDICES to be 128, as this leaves some room to keep the stack frame < 1024 bytes. This may be too low of a max, but is plenty for our current QEMU and integration testing. Signed-off-by: Nick Rosbrook --- shim/include/kvm_msr_list.h | 4 +- shim/include/kvm_msr_list.hpp | 8 +++- shim/integration/kvm_get_msr_index_list.cpp | 9 ++--- .../shim_platform_interface.h | 3 +- .../shim_platform_interface.hpp | 4 +- shim/linux/src/entry.c | 39 +++++-------------- .../handle_system_kvm_get_msr_index_list.c | 5 --- ...t_handle_system_kvm_get_msr_index_list.cpp | 24 ++---------- 8 files changed, 29 insertions(+), 67 deletions(-) diff --git a/shim/include/kvm_msr_list.h b/shim/include/kvm_msr_list.h index 60ce5b87a..668eed1be 100644 --- a/shim/include/kvm_msr_list.h +++ b/shim/include/kvm_msr_list.h @@ -34,6 +34,8 @@ extern "C" { #endif +#define MSR_LIST_MAX_INDICES 128 + #pragma pack(push, 1) /** @@ -48,7 +50,7 @@ extern "C" uint32_t nmsrs; /** @brief array containing the indices of supported MSRs */ - uint32_t *indices; + uint32_t indices[MSR_LIST_MAX_INDICES]; }; #pragma pack(pop) diff --git a/shim/include/kvm_msr_list.hpp b/shim/include/kvm_msr_list.hpp index 421f7589a..9895a9677 100644 --- a/shim/include/kvm_msr_list.hpp +++ b/shim/include/kvm_msr_list.hpp @@ -27,7 +27,11 @@ #ifndef KVM_MSR_LIST_HPP #define KVM_MSR_LIST_HPP -#include +#include +#include +#include + +constexpr auto MSR_LIST_MAX_INDICES{128_u32}; namespace shim { @@ -45,7 +49,7 @@ namespace shim bsl::uint32 nmsrs; /** @brief array containing the indices of supported MSRs */ - bsl::uint32 *indices; + bsl::array indices; }; } diff --git a/shim/integration/kvm_get_msr_index_list.cpp b/shim/integration/kvm_get_msr_index_list.cpp index 723167779..4d62448c3 100644 --- a/shim/integration/kvm_get_msr_index_list.cpp +++ b/shim/integration/kvm_get_msr_index_list.cpp @@ -51,13 +51,11 @@ main() noexcept -> bsl::exit_code bsl::enable_color(); integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; - bsl::array mut_msr_indices{}; // nmsrs is too big { mut_msr_list.nmsrs = HYPERVISOR_PAGE_SIZE.get(); mut_msr_list.nmsrs++; - mut_msr_list.indices = mut_msr_indices.front_if(); integration::verify( mut_system_ctl.write(shim::KVM_GET_MSR_INDEX_LIST, &mut_msr_list).is_neg()); @@ -65,7 +63,6 @@ main() noexcept -> bsl::exit_code { mut_msr_list.nmsrs = init_nmsrs.get(); - mut_msr_list.indices = mut_msr_indices.front_if(); integration::verify( mut_system_ctl.write(shim::KVM_GET_MSR_INDEX_LIST, &mut_msr_list).is_zero()); @@ -82,13 +79,13 @@ main() noexcept -> bsl::exit_code auto mut_nmsrs{bsl::to_idx(mut_msr_list.nmsrs)}; for (bsl::safe_idx mut_i{}; mut_i < mut_nmsrs; ++mut_i) { - if (star_val == mut_msr_list.indices[mut_i.get()]) { + if (star_val == *mut_msr_list.indices.at_if(mut_i)) { mut_found_star = true; } - else if (pat_val == mut_msr_list.indices[mut_i.get()]) { + else if (pat_val == *mut_msr_list.indices.at_if(mut_i)) { mut_found_pat = true; } - else if (apic_base_val == mut_msr_list.indices[mut_i.get()]) { + else if (apic_base_val == *mut_msr_list.indices.at_if(mut_i)) { mut_found_apic_base = true; } } diff --git a/shim/linux/include/platform_interface/shim_platform_interface.h b/shim/linux/include/platform_interface/shim_platform_interface.h index fd2ed2f1c..639ba8d44 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.h +++ b/shim/linux/include/platform_interface/shim_platform_interface.h @@ -86,7 +86,8 @@ /** @brief defines KVM's KVM_CREATE_VM IOCTL */ #define KVM_CREATE_VM _IO(SHIMIO, 0x01) /** @brief defines KVM's KVM_GET_MSR_INDEX_LIST IOCTL */ -#define KVM_GET_MSR_INDEX_LIST _IOWR(SHIMIO, 0x02, struct kvm_msr_list) +#define KVM_GET_MSR_INDEX_LIST \ + _IOWR_LIST(SHIMIO, 0x02, struct kvm_msr_list, uint32_t[MSR_LIST_MAX_INDICES]) /** @brief defines KVM's KVM_GET_MSR_FEATURE_INDEX_LIST IOCTL */ #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(SHIMIO, 0x0a, struct kvm_msr_list) /** @brief defines KVM's KVM_CHECK_EXTENSION IOCTL */ diff --git a/shim/linux/include/platform_interface/shim_platform_interface.hpp b/shim/linux/include/platform_interface/shim_platform_interface.hpp index b7f062e95..70c1e27e8 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.hpp +++ b/shim/linux/include/platform_interface/shim_platform_interface.hpp @@ -106,8 +106,8 @@ namespace shim /// @brief defines KVM's KVM_CREATE_VM IOCTL constexpr bsl::safe_umx KVM_CREATE_VM{static_cast(_IO(SHIMIO.get(), 0x01))}; /// @brief defines KVM's KVM_GET_MSR_INDEX_LIST IOCTL - constexpr bsl::safe_umx KVM_GET_MSR_INDEX_LIST{ - static_cast(_IOWR(SHIMIO.get(), 0x02, struct kvm_msr_list))}; + constexpr bsl::safe_umx KVM_GET_MSR_INDEX_LIST{static_cast( + _IOWR_LIST(SHIMIO.get(), 0x02, struct kvm_msr_list, uint32_t[MSR_LIST_MAX_INDICES.get()]))}; // /// @brief defines KVM's KVM_GET_MSR_FEATURE_INDEX_LIST IOCTL // constexpr bsl::safe_umx KVM_GET_MSR_FEATURE_INDEX_LIST{static_cast(_IOWR(SHIMIO.get(), 0x0a, struct kvm_msr_list))}; /// @brief defines KVM's KVM_CHECK_EXTENSION IOCTL diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index 6c17842d8..66e198a91 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -236,54 +236,33 @@ dispatch_system_kvm_get_msr_index_list( struct kvm_msr_list __user *const user_args) { struct kvm_msr_list mut_args; - uint32_t __user *pmut_mut_user_indices; int64_t mut_ret; - uint64_t mut_alloc_size; if (platform_copy_from_user(&mut_args, user_args, sizeof(mut_args))) { bferror("platform_copy_from_user failed"); return -EINVAL; } - mut_alloc_size = mut_args.nmsrs * sizeof(*mut_args.indices); - if (mut_alloc_size > HYPERVISOR_PAGE_SIZE) { - bferror("requested nmsrs too big"); + if (mut_args.nmsrs > MSR_LIST_MAX_INDICES) { + bferror("caller nmsrs exceeds MSR_LIST_MAX_INDICES"); return -ENOMEM; } - pmut_mut_user_indices = mut_args.indices; - mut_args.indices = vzalloc(mut_alloc_size); - - if (NULL == mut_args.indices) { - bferror("vzalloc failed"); - return -ENOMEM; - } - - mut_ret = -EINVAL; if (handle_system_kvm_get_msr_index_list(&mut_args)) { bferror("handle_system_kvm_get_msr_index_list failed"); - goto out; - } - - if (platform_copy_to_user(user_args, &mut_args, sizeof(mut_args.nmsrs))) { - bferror("platform_copy_to_user nmsrs failed"); - goto out; + return -EINVAL; } if (platform_copy_to_user( - pmut_mut_user_indices, - mut_args.indices, - mut_args.nmsrs * sizeof(*mut_args.indices))) { + user_args, + &mut_args, + sizeof(mut_args.nmsrs) + + mut_args.nmsrs * sizeof(*mut_args.indices))) { bferror("platform_copy_to_user indices failed"); - goto out; + return -EINVAL; } - mut_ret = 0; -out: - if (mut_args.indices) - vfree(mut_args.indices); - - return mut_ret; + return 0; } static long diff --git a/shim/src/handle_system_kvm_get_msr_index_list.c b/shim/src/handle_system_kvm_get_msr_index_list.c index 00df95a34..61c35e67a 100644 --- a/shim/src/handle_system_kvm_get_msr_index_list.c +++ b/shim/src/handle_system_kvm_get_msr_index_list.c @@ -57,11 +57,6 @@ handle_system_kvm_get_msr_index_list(struct kvm_msr_list *const pmut_ioctl_args) return SHIM_FAILURE; } - if (NULL == pmut_ioctl_args->indices) { - bferror("indices not allocated"); - return SHIM_FAILURE; - } - platform_expects(MV_INVALID_HANDLE != g_mut_hndl); do { diff --git a/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp b/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp index cffb43e2c..da11efd70 100644 --- a/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp +++ b/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -36,7 +35,6 @@ namespace shim { constexpr auto VAL64{42_u64}; constexpr auto INIT_NMSRS{0x10_u32}; - bsl::array g_mut_msr_indices{}; /// /// @brief Used to execute the actual checks. We put the checks in this @@ -57,7 +55,6 @@ namespace shim bsl::ut_given{} = [&]() noexcept { kvm_msr_list mut_args{}; bsl::ut_when{} = [&]() noexcept { - mut_args.indices = g_mut_msr_indices.front_if(); mut_args.nmsrs = INIT_NMSRS.get(); g_mut_val = bsl::safe_u64::magic_2().get(); bsl::ut_then{} = [&]() noexcept { @@ -71,7 +68,6 @@ namespace shim bsl::ut_given{} = [&]() noexcept { kvm_msr_list mut_args{}; bsl::ut_when{} = [&]() noexcept { - mut_args.indices = g_mut_msr_indices.front_if(); mut_args.nmsrs = INIT_NMSRS.get(); g_mut_val = bsl::safe_u64::magic_2().get(); g_mut_mv_pp_op_msr_get_supported_list = MV_STATUS_FAILURE_SET_RDL_REG1; @@ -86,25 +82,12 @@ namespace shim }; }; - bsl::ut_scenario{"indices not allocated"} = []() noexcept { - bsl::ut_given{} = [&]() noexcept { - kvm_msr_list mut_args{}; - bsl::ut_when{} = [&]() noexcept { - mut_args.nmsrs = INIT_NMSRS.get(); - bsl::ut_then{} = [&]() noexcept { - bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); - }; - }; - }; - }; - bsl::ut_scenario{"hypervisor not detected"} = []() noexcept { bsl::ut_given{} = [&]() noexcept { kvm_msr_list mut_args{}; bsl::ut_when{} = [&]() noexcept { g_mut_hypervisor_detected = false; mut_args.nmsrs = INIT_NMSRS.get(); - mut_args.indices = g_mut_msr_indices.front_if(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); }; @@ -119,10 +102,14 @@ namespace shim bsl::ut_given{} = [&]() noexcept { kvm_msr_list mut_args{}; bsl::ut_when{} = [&]() noexcept { + g_mut_val = VAL64.get(); mut_args.nmsrs = bsl::safe_u32::magic_0().get(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_val = {}; + }; }; }; }; @@ -133,7 +120,6 @@ namespace shim bsl::ut_when{} = [&]() noexcept { g_mut_mv_pp_op_msr_get_supported_list = MV_STATUS_FAILURE_CORRUPT_NUM_ENTRIES; mut_args.nmsrs = INIT_NMSRS.get(); - mut_args.indices = g_mut_msr_indices.front_if(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); }; @@ -150,7 +136,6 @@ namespace shim bsl::ut_when{} = [&]() noexcept { g_mut_mv_pp_op_msr_get_supported_list = VAL64.get(); mut_args.nmsrs = INIT_NMSRS.get(); - mut_args.indices = g_mut_msr_indices.front_if(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); }; @@ -166,7 +151,6 @@ namespace shim kvm_msr_list mut_args{}; bsl::ut_when{} = [&]() noexcept { mut_args.nmsrs = INIT_NMSRS.get(); - mut_args.indices = g_mut_msr_indices.front_if(); g_mut_val = VAL64.get(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); From 6a653c9e166b0828315bafdd35a79a78ce58429f Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Fri, 22 Oct 2021 10:20:46 -0400 Subject: [PATCH 5/9] shim/linux: fix E2BIG behavior in KVM_GET_MSR_INDEX_LIST The KVM API states that when the number of MSRs is greater than the value set in nmsrs by the caller, the kernel will set the correct value in nmsrs and return -E2BIG. This behavior was excluded from the first implementation of KVM_GET_MSR_INDEX_LIST. Define SHIM_2BIG so that other shim functions can implement this behavior, which is a common pattern in ioctls. Signed-off-by: Nick Rosbrook --- shim/integration/kvm_get_msr_index_list.cpp | 10 +++++-- shim/linux/include/types.h | 5 ++++ shim/linux/src/entry.c | 12 +++++++- .../handle_system_kvm_get_msr_index_list.c | 30 ++++++++++++++----- shim/tests/include/types.h | 6 ++++ ...t_handle_system_kvm_get_msr_index_list.cpp | 18 +---------- 6 files changed, 52 insertions(+), 29 deletions(-) diff --git a/shim/integration/kvm_get_msr_index_list.cpp b/shim/integration/kvm_get_msr_index_list.cpp index 4d62448c3..eb086ac00 100644 --- a/shim/integration/kvm_get_msr_index_list.cpp +++ b/shim/integration/kvm_get_msr_index_list.cpp @@ -52,13 +52,15 @@ main() noexcept -> bsl::exit_code bsl::enable_color(); integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; - // nmsrs is too big + // nmsrs is too small { - mut_msr_list.nmsrs = HYPERVISOR_PAGE_SIZE.get(); - mut_msr_list.nmsrs++; + mut_msr_list.nmsrs = bsl::safe_u32::magic_0().get(); integration::verify( mut_system_ctl.write(shim::KVM_GET_MSR_INDEX_LIST, &mut_msr_list).is_neg()); + + auto mut_nmsrs{bsl::to_u32(mut_msr_list.nmsrs)}; + integration::verify(mut_nmsrs > bsl::safe_u32::magic_0()); } { @@ -97,6 +99,8 @@ main() noexcept -> bsl::exit_code // Try a bunch of times { + mut_msr_list.nmsrs = init_nmsrs.get(); + constexpr auto num_loops{0x1000_umx}; for (bsl::safe_idx mut_i{}; mut_i < num_loops; ++mut_i) { integration::verify( diff --git a/shim/linux/include/types.h b/shim/linux/include/types.h index ea5b19990..92fc2dd4f 100644 --- a/shim/linux/include/types.h +++ b/shim/linux/include/types.h @@ -50,4 +50,9 @@ */ #define SHIM_INTERRUPTED ((int64_t)-EINTR) +/** + * @brief Returned by a shim function when a provided data structure + * is not large enough to hold the return data. + */ +#define SHIM_2BIG ((int64_t)-E2BIG) #endif diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index 66e198a91..045cd64e4 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -248,7 +248,17 @@ dispatch_system_kvm_get_msr_index_list( return -ENOMEM; } - if (handle_system_kvm_get_msr_index_list(&mut_args)) { + mut_ret = handle_system_kvm_get_msr_index_list(&mut_args); + if (SHIM_2BIG == mut_ret) { + if (platform_copy_to_user( + user_args, &mut_args, sizeof(mut_args.nmsrs))) { + bferror("platform_copy_to_user nmsrs failed"); + return -EINVAL; + } + + return -E2BIG; + } + else if (mut_ret) { bferror("handle_system_kvm_get_msr_index_list failed"); return -EINVAL; } diff --git a/shim/src/handle_system_kvm_get_msr_index_list.c b/shim/src/handle_system_kvm_get_msr_index_list.c index 61c35e67a..0b156a87c 100644 --- a/shim/src/handle_system_kvm_get_msr_index_list.c +++ b/shim/src/handle_system_kvm_get_msr_index_list.c @@ -41,11 +41,14 @@ * * * @param pmut_ioctl_args the arguments provided by userspace - * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. + * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure, and SHIM_2BIG when + * the number of MSRs is greater than what was set in nmsrs. When SHIM_2BIG is + * returned, the correct number of MSRs is set in the nmsrs field. */ NODISCARD int64_t handle_system_kvm_get_msr_index_list(struct kvm_msr_list *const pmut_ioctl_args) NOEXCEPT { + int64_t mut_ret; int64_t mut_i; uint32_t mut_nmsrs = ((uint32_t)0); @@ -73,18 +76,29 @@ handle_system_kvm_get_msr_index_list(struct kvm_msr_list *const pmut_ioctl_args) return SHIM_FAILURE; } + /** + * If the provided buffer is not large enough to fit all the MSRs, we still + * need to calculate the total number, and set the nmsrs field correctly. + */ if (pmut_rdl->num_entries + ((uint64_t)mut_nmsrs) > ((uint64_t)pmut_ioctl_args->nmsrs)) { - bferror("number of MSRs is larger than kvm_msr_list indices"); - return SHIM_FAILURE; + mut_nmsrs += pmut_rdl->num_entries; } - - for (mut_i = ((int64_t)0); mut_i < ((int64_t)pmut_rdl->num_entries); ++mut_i) { - pmut_ioctl_args->indices[mut_nmsrs] = ((uint32_t)pmut_rdl->entries[mut_i].reg); - ++mut_nmsrs; + else { + for (mut_i = ((int64_t)0); mut_i < ((int64_t)pmut_rdl->num_entries); ++mut_i) { + pmut_ioctl_args->indices[mut_nmsrs] = ((uint32_t)pmut_rdl->entries[mut_i].reg); + ++mut_nmsrs; + } } } while (((uint64_t)0) != pmut_rdl->reg1); + if (mut_nmsrs > pmut_ioctl_args->nmsrs) { + mut_ret = SHIM_2BIG; + } + else { + mut_ret = SHIM_SUCCESS; + } + pmut_ioctl_args->nmsrs = mut_nmsrs; - return SHIM_SUCCESS; + return mut_ret; } diff --git a/shim/tests/include/types.h b/shim/tests/include/types.h index 138613e9c..128331921 100644 --- a/shim/tests/include/types.h +++ b/shim/tests/include/types.h @@ -49,4 +49,10 @@ */ #define SHIM_INTERRUPTED ((int64_t)-2) +/** + * @brief Returned by a shim function when a provided data structure + * is not large enough to hold the return data. + */ +#define SHIM_2BIG ((int64_t)-3) + #endif diff --git a/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp b/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp index da11efd70..bdbea6f4d 100644 --- a/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp +++ b/shim/tests/src/test_handle_system_kvm_get_msr_index_list.cpp @@ -98,22 +98,6 @@ namespace shim }; }; - bsl::ut_scenario{"indices too small"} = []() noexcept { - bsl::ut_given{} = [&]() noexcept { - kvm_msr_list mut_args{}; - bsl::ut_when{} = [&]() noexcept { - g_mut_val = VAL64.get(); - mut_args.nmsrs = bsl::safe_u32::magic_0().get(); - bsl::ut_then{} = [&]() noexcept { - bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); - }; - bsl::ut_cleanup{} = [&]() noexcept { - g_mut_val = {}; - }; - }; - }; - }; - bsl::ut_scenario{"mv_pp_op_msr_get_supported_list corrupts num_entries"} = []() noexcept { bsl::ut_given{} = [&]() noexcept { kvm_msr_list mut_args{}; @@ -153,7 +137,7 @@ namespace shim mut_args.nmsrs = INIT_NMSRS.get(); g_mut_val = VAL64.get(); bsl::ut_then{} = [&]() noexcept { - bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); + bsl::ut_check(SHIM_2BIG == handle(&mut_args)); }; bsl::ut_cleanup{} = [&]() noexcept { g_mut_val = {}; From b6b8a016cf2456bbf557f04cc347e3dd27ef70d3 Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Tue, 19 Oct 2021 12:23:30 -0400 Subject: [PATCH 6/9] hypercall: fix definition of enum mv_cpuid_flag_t The enum mv_cpuid_flag_t is incorrectly defined as mv_cpuid_flag (missing the _t suffix) in the C header file, which causes the following build errors: /home/nr/bareflank/microv/hypercall/include/mv_cdl_entry_t.h:51:9: error: User-defined types must have the same name as the header file they are defined in. Either name the Enum 'mv_cdl_entry_t', or name the header 'mv_cpuid_flag_t' [bsl-user-defined-type-names-match-header-name,-warnings-as-errors] enum mv_cpuid_flag_t flags; ^ /home/nr/bareflank/microv/hypercall/include/mv_cdl_entry_t.h:51:14: error: Enum 'mv_cpuid_flag_t' is missing documentation. Are you missing the '@brief' command? [bsl-documentation,-warnings-as-errors] enum mv_cpuid_flag_t flags; ^ /home/nr/bareflank/microv/hypercall/include/mv_cdl_entry_t.h:51:30: error: field has incomplete type 'enum mv_cpuid_flag_t' [clang-diagnostic-error] enum mv_cpuid_flag_t flags; ^ /home/nr/bareflank/microv/hypercall/include/mv_cdl_entry_t.h:51:14: note: forward declaration of 'enum mv_cpuid_flag_t' enum mv_cpuid_flag_t flags; ^ /home/nr/bareflank/microv/hypercall/include/mv_cpuid_flag_t.h:46:1: error: User-defined types must have the same name as the header file they are defined in. Either name the Enum 'mv_cpuid_flag_t', or name the header 'mv_cpuid_flag' [bsl-user-defined-type-names-match-header-name,-warnings-as-errors] enum mv_cpuid_flag ^ Fix this by adding the _t suffix in the C header file. Signed-off-by: Nick Rosbrook --- hypercall/include/mv_cpuid_flag_t.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hypercall/include/mv_cpuid_flag_t.h b/hypercall/include/mv_cpuid_flag_t.h index e08ae0f2f..809b3a1ae 100644 --- a/hypercall/include/mv_cpuid_flag_t.h +++ b/hypercall/include/mv_cpuid_flag_t.h @@ -37,13 +37,13 @@ extern "C" * * @brief Defines CPUID flags */ - enum mv_cpuid_flag : int32_t + enum mv_cpuid_flag_t : int32_t #else /** * * @brief Defines CPUID flags */ -enum mv_cpuid_flag +enum mv_cpuid_flag_t #endif { /** @brief reserved */ From 964ee648a9f42e667906b60790ad15771efd64ad Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Fri, 15 Oct 2021 09:21:38 -0400 Subject: [PATCH 7/9] shim/linux: implement GET_SUPPORTED_CPUID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implment support in the shim for the GET_SUPPORTED_CPUID ioctl. This has been tested against QEMU to confirm execution advances passed these ioctl calls. Note that the current CPUID2_MAX_ENTRIES causes stack frames to exceed 1024 bytes: /home/nr/bareflank/microv/shim/linux/src/entry.c: In function ‘dispatch_system_kvm_get_supported_cpuid’: /home/nr/bareflank/microv/shim/linux/src/entry.c:325:1: warning: the frame size of 1616 bytes is larger than 1024 bytes [-Wframe-larger-than=] 325 | } | ^ Currently the number of entries we return is 30, and reducing the max much lower could cause QEMU to fail. Signed-off-by: Nick Rosbrook --- hypercall/mocks/mv_hypercall.h | 26 +++++ hypercall/tests/mocks/mv_hypercall.cpp | 3 + shim/include/kvm_cpuid2.h | 11 +- shim/include/kvm_cpuid2.hpp | 61 ++++++++++ shim/include/kvm_cpuid_entry2.h | 84 ++++++++++++++ shim/include/kvm_cpuid_entry2.hpp | 80 +++++++++++++ shim/integration/CMakeLists.txt | 1 + shim/integration/kvm_get_supported_cpuid.cpp | 95 ++++++++++++++++ .../shim_platform_interface.h | 2 +- .../shim_platform_interface.hpp | 7 +- shim/linux/src/entry.c | 52 +++++++-- .../handle_system_kvm_get_supported_cpuid.c | 92 ++++++++++++++- ..._handle_system_kvm_get_supported_cpuid.cpp | 105 +++++++++++++++++- 13 files changed, 600 insertions(+), 19 deletions(-) create mode 100644 shim/include/kvm_cpuid2.hpp create mode 100644 shim/include/kvm_cpuid_entry2.h create mode 100644 shim/include/kvm_cpuid_entry2.hpp create mode 100644 shim/integration/kvm_get_supported_cpuid.cpp diff --git a/hypercall/mocks/mv_hypercall.h b/hypercall/mocks/mv_hypercall.h index 1884aea71..5786383ed 100644 --- a/hypercall/mocks/mv_hypercall.h +++ b/hypercall/mocks/mv_hypercall.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +247,8 @@ extern "C" NODISCARD static inline mv_status_t mv_pp_op_cpuid_get_supported_list(uint64_t const hndl) NOEXCEPT { + uint64_t mut_i; + struct mv_cdl_entry_t mut_cdl_entry; #ifdef __cplusplus bsl::expects(MV_INVALID_HANDLE != hndl); bsl::expects(hndl > ((uint64_t)0)); @@ -253,7 +256,30 @@ extern "C" platform_expects(MV_INVALID_HANDLE != hndl); platform_expects(hndl > ((uint64_t)0)); #endif + struct mv_cdl_t *const pmut_cdl = (struct mv_cdl_t *)g_mut_shared_pages[0]; +#ifdef __cplusplus + bsl::expects(nullptr != pmut_cdl); + bsl::expects(pmut_cdl->num_entries < MV_CDL_MAX_ENTRIES); +#else + platform_expects(NULL != pmut_cdl); + platform_expects(pmut_cdl->num_entries < MV_CDL_MAX_ENTRIES); +#endif + if (MV_STATUS_FAILURE_CORRUPT_NUM_ENTRIES == g_mut_mv_pp_op_msr_get_supported_list) { + pmut_cdl->num_entries = GARBAGE; + return MV_STATUS_SUCCESS; + } + + pmut_cdl->num_entries = g_mut_val; + for (mut_i = ((uint64_t)0); mut_i < pmut_cdl->num_entries; ++mut_i) { + mut_cdl_entry.fun = ((uint32_t)g_mut_val); + mut_cdl_entry.idx = ((uint32_t)g_mut_val); + mut_cdl_entry.eax = ((uint32_t)g_mut_val); + mut_cdl_entry.ebx = ((uint32_t)g_mut_val); + mut_cdl_entry.ecx = ((uint32_t)g_mut_val); + mut_cdl_entry.edx = ((uint32_t)g_mut_val); + pmut_cdl->entries[mut_i] = mut_cdl_entry; + } return g_mut_mv_pp_op_cpuid_get_supported_list; } diff --git a/hypercall/tests/mocks/mv_hypercall.cpp b/hypercall/tests/mocks/mv_hypercall.cpp index fe49332a9..023916ae2 100644 --- a/hypercall/tests/mocks/mv_hypercall.cpp +++ b/hypercall/tests/mocks/mv_hypercall.cpp @@ -194,7 +194,10 @@ namespace shim bsl::ut_given{} = [&]() noexcept { constexpr auto hypercall{&mv_pp_op_cpuid_get_supported_list}; constexpr auto expected{42_u64}; + mv_cdl_t mut_cdl{}; bsl::ut_when{} = [&]() noexcept { + mut_cdl.num_entries = bsl::safe_u64::magic_2().get(); + g_mut_shared_pages[0] = &mut_cdl; g_mut_mv_pp_op_cpuid_get_supported_list = expected.get(); bsl::ut_then{} = [&]() noexcept { bsl::ut_check(expected == hypercall(hndl)); diff --git a/shim/include/kvm_cpuid2.h b/shim/include/kvm_cpuid2.h index aa74d264b..84a323ca0 100644 --- a/shim/include/kvm_cpuid2.h +++ b/shim/include/kvm_cpuid2.h @@ -27,6 +27,7 @@ #ifndef KVM_CPUID2_H #define KVM_CPUID2_H +#include #include #ifdef __cplusplus @@ -34,6 +35,8 @@ extern "C" { #endif +#define CPUID2_MAX_ENTRIES 40 + #pragma pack(push, 1) /** @@ -44,8 +47,12 @@ extern "C" */ struct kvm_cpuid2 { - /** @brief replace me with contents from KVM API */ - int32_t dummy; + /** @brief number of entries */ + uint32_t nent; + /** @brief padding for alignment */ + uint32_t padding; + /** @brief CPUID entries */ + struct kvm_cpuid_entry2 entries[CPUID2_MAX_ENTRIES]; }; #pragma pack(pop) diff --git a/shim/include/kvm_cpuid2.hpp b/shim/include/kvm_cpuid2.hpp new file mode 100644 index 000000000..134a4bfc1 --- /dev/null +++ b/shim/include/kvm_cpuid2.hpp @@ -0,0 +1,61 @@ +/** + * @copyright + * Copyright (C) 2020 Assured Information Security, Inc. + * + * @copyright + * 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: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * 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 KVM_CPUID2_HPP +#define KVM_CPUID2_HPP + +#include + +#include +#include +#include + +constexpr auto CPUID2_MAX_ENTRIES{40_u32}; + +namespace shim +{ +#pragma pack(push, 1) + + /** + * @struct kvm_cpuid2 + * + * + * @brief see /include/uapi/linux/kvm.h in Linux for more details. + */ + struct kvm_cpuid2 + { + /** @brief number of entries */ + bsl::uint32 nent; + /** @brief padding for alignment */ + bsl::uint32 padding; + /** @brief CPUID entries */ + bsl::array entries; + }; +} + +#pragma pack(pop) + +#endif diff --git a/shim/include/kvm_cpuid_entry2.h b/shim/include/kvm_cpuid_entry2.h new file mode 100644 index 000000000..4efdcab9c --- /dev/null +++ b/shim/include/kvm_cpuid_entry2.h @@ -0,0 +1,84 @@ +/** + * @copyright + * Copyright (C) 2021 Assured Information Security, Inc. + * + * @copyright + * 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: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * 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 KVM_CPUID_ENTRY2_H +#define KVM_CPUID_ENTRY2_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX (1 << 0) +#define KVM_CPUID_FLAG_STATEFUL_FUNC (1 << 1) +#define KVM_CPUID_FLAG_STATE_READ_NEXT (1 << 2) + +#pragma pack(push, 1) + + /** + * @struct kvm_cpuid_entry2 + * + * + * @brief see /include/uapi/linux/kvm.h in Linux for more details. + */ + struct kvm_cpuid_entry2 + { + /** @brief the eax value used to obtain the entry */ + uint32_t function; + /** @brief the ecx value used to obtain the entry (for entries that are affected by ecx) */ + uint32_t index; + /** @brief an OR of zero or more of the following: + * KVM_CPUID_FLAG_SIGNIFCANT_INDEX: if the index field is valid + * KVM_CPUID_FLAG_STATEFUL_FUNC: if cpuid for this function + * returns different values for successive invocations; there will + * be several entries with the same function, all with this flag + * set + * KVM_CPUID_FLAG_STATE_READ_NEXT: for + * KVM_CPUID_FLAG_STATEFUL_FUNC entries, set if this entry if the + * first entry to be read by a cpu + */ + uint32_t flags; + /** @brief the eax value returned by the cpuid instruction for this function/index combination */ + uint32_t eax; + /** @brief the ebx value returned by the cpuid instruction for this function/index combination */ + uint32_t ebx; + /** @brief the ecx value returned by the cpuid instruction for this function/index combination */ + uint32_t ecx; + /** @brief the edx value returned by the cpuid instruction for this function/index combination */ + uint32_t edx; + /** @brief padding for alignment */ + uint32_t padding[3]; + }; + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shim/include/kvm_cpuid_entry2.hpp b/shim/include/kvm_cpuid_entry2.hpp new file mode 100644 index 000000000..ffb44040c --- /dev/null +++ b/shim/include/kvm_cpuid_entry2.hpp @@ -0,0 +1,80 @@ +/** + * @copyright + * Copyright (C) 2021 Assured Information Security, Inc. + * + * @copyright + * 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: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * 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 KVM_CPUID_ENTRY2_HPP +#define KVM_CPUID_ENTRY2_HPP + +#include +#include +#include + +namespace shim +{ + constexpr auto KVM_CPUID_FLAG_SIGNIFCANT_INDEX{0x1_u32}; + constexpr auto KVM_CPUID_FLAG_STATEFUL_FUNC{0x10_u32}; + constexpr auto KVM_CPUID_FLAG_STATE_READ_NEXT{0x100_u32}; + +#pragma pack(push, 1) + + /** + * @struct kvm_cpuid_entry2 + * + * + * @brief see /include/uapi/linux/kvm.h in Linux for more details. + */ + struct kvm_cpuid_entry2 + { + /** @brief the eax value used to obtain the entry */ + bsl::uint32 function; + /** @breif the ecx value used to obtain the entry (for entries that are affected by ecx) */ + bsl::uint32 index; + /** @brief an OR of zero or more of the following: + * KVM_CPUID_FLAG_SIGNIFCANT_INDEX: if the index field is valid + * KVM_CPUID_FLAG_STATEFUL_FUNC: if cpuid for this function + * returns different values for successive invocations; there will + * be several entries with the same function, all with this flag + * set + * KVM_CPUID_FLAG_STATE_READ_NEXT: for + * KVM_CPUID_FLAG_STATEFUL_FUNC entries, set if this entry if the + * first entry to be read by a cpu + */ + bsl::uint32 flags; + /** @brief the eax value returned by the cpuid instruction for this function/index combination */ + bsl::uint32 eax; + /** @brief the ebx value returned by the cpuid instruction for this function/index combination */ + bsl::uint32 ebx; + /** @brief the ecx value returned by the cpuid instruction for this function/index combination */ + bsl::uint32 ecx; + /** @brief the edx value returned by the cpuid instruction for this function/index combination */ + bsl::uint32 edx; + /** @brief padding for alignment */ + bsl::array padding; + }; +} + +#pragma pack(pop) + +#endif diff --git a/shim/integration/CMakeLists.txt b/shim/integration/CMakeLists.txt index e95a2f049..6eba716b6 100644 --- a/shim/integration/CMakeLists.txt +++ b/shim/integration/CMakeLists.txt @@ -48,3 +48,4 @@ microv_add_shim_integration(kvm_get_tsc_khz HEADERS) microv_add_shim_integration(kvm_get_msr_index_list HEADERS) microv_add_shim_integration(kvm_get_msrs HEADERS) microv_add_shim_integration(kvm_set_msrs HEADERS) +microv_add_shim_integration(kvm_get_supported_cpuid HEADERS) diff --git a/shim/integration/kvm_get_supported_cpuid.cpp b/shim/integration/kvm_get_supported_cpuid.cpp new file mode 100644 index 000000000..92fba2b2d --- /dev/null +++ b/shim/integration/kvm_get_supported_cpuid.cpp @@ -0,0 +1,95 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/// +/// @brief Provides the main entry point for this application. +/// +/// +/// @return bsl::exit_success on success, bsl::exit_failure otherwise. +/// +[[nodiscard]] auto +main() noexcept -> bsl::exit_code +{ + constexpr auto init_nent{0x20_u32}; + constexpr auto cpuid_fn0000_0001{0x00000001_u32}; + shim::kvm_cpuid2 mut_cpuid2{}; + + bsl::enable_color(); + integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; + + { + mut_cpuid2.nent = init_nent.get(); + + integration::verify( + mut_system_ctl.write(shim::KVM_GET_SUPPORTED_CPUID, &mut_cpuid2).is_zero()); + + auto nent{bsl::to_u32(mut_cpuid2.nent)}; + integration::verify(nent > bsl::safe_u32::magic_0()); + } + + // Valid registers should be present + { + // Fn0000_0001h[0][EDX][ 5]: RDMSR and WRMSR support + bool found_rdmsr_support{}; + constexpr auto rdmsr_bit{0x20_u32}; + constexpr shim::kvm_cpuid_entry2 rdmsr_support{ + .function = cpuid_fn0000_0001.get(), .index = 0U, .edx = rdmsr_bit.get()}; + auto mut_nent{bsl::to_idx(mut_cpuid2.nent)}; + shim::kvm_cpuid_entry2 mut_entry{}; + + for (bsl::safe_idx mut_i{}; mut_i < mut_nent; ++mut_i) { + mut_entry = *mut_cpuid2.entries.at_if(mut_i); + if ((mut_entry.function == rdmsr_support.function) && // NOLINT + (mut_entry.index == rdmsr_support.index) && // NOLINT + ((mut_entry.edx & rdmsr_support.edx) == rdmsr_support.edx)) { // NOLINT + found_rdmsr_support = true; + } + } + integration::verify(found_rdmsr_support); + } + + // Try a bunch of times + { + constexpr auto num_loops{0x1000_umx}; + for (bsl::safe_idx mut_i{}; mut_i < num_loops; ++mut_i) { + integration::verify( + mut_system_ctl.write(shim::KVM_GET_SUPPORTED_CPUID, &mut_cpuid2).is_zero()); + } + } + + return bsl::exit_success; +} diff --git a/shim/linux/include/platform_interface/shim_platform_interface.h b/shim/linux/include/platform_interface/shim_platform_interface.h index 639ba8d44..15a78ed6b 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.h +++ b/shim/linux/include/platform_interface/shim_platform_interface.h @@ -176,7 +176,7 @@ /** @brief defines KVM's KVM_SET_XCRS IOCTL */ #define KVM_SET_XCRS _IOW(SHIMIO, 0xa7, struct kvm_xcrs) /** @brief defines KVM's KVM_GET_SUPPORTED_CPUID IOCTL */ -#define KVM_GET_SUPPORTED_CPUID _IOWR(SHIMIO, 0x05, struct kvm_cpuid2) +#define KVM_GET_SUPPORTED_CPUID _IOWR_LIST(SHIMIO, 0x05, struct kvm_cpuid2, struct kvm_cpuid_entry2[CPUID2_MAX_ENTRIES]) /** @brief defines KVM's KVM_SET_GSI_ROUTING IOCTL */ #define KVM_SET_GSI_ROUTING _IOW(SHIMIO, 0x6a, struct kvm_irq_routing) /** @brief defines KVM's KVM_GET_TSC_KHZ IOCTL */ diff --git a/shim/linux/include/platform_interface/shim_platform_interface.hpp b/shim/linux/include/platform_interface/shim_platform_interface.hpp index 70c1e27e8..a7d1d37ca 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.hpp +++ b/shim/linux/include/platform_interface/shim_platform_interface.hpp @@ -28,6 +28,7 @@ #define SHIM_PLATFORM_INTERFACE_HPP #include +#include #include #include #include @@ -45,7 +46,6 @@ // #include // #include // #include -// #include // #include // #include // #include @@ -205,8 +205,9 @@ namespace shim // constexpr bsl::safe_umx KVM_GET_XCRS{static_cast(_IOR(SHIMIO.get(), 0xa6, struct kvm_xcrs))}; // /// @brief defines KVM's KVM_SET_XCRS IOCTL // constexpr bsl::safe_umx KVM_SET_XCRS{static_cast(_IOW(SHIMIO.get(), 0xa7, struct kvm_xcrs))}; - // /// @brief defines KVM's KVM_GET_SUPPORTED_CPUID IOCTL - // constexpr bsl::safe_umx KVM_GET_SUPPORTED_CPUID{static_cast(_IOWR(SHIMIO.get(), 0x05, struct kvm_cpuid2))}; + /// @brief defines KVM's KVM_GET_SUPPORTED_CPUID IOCTL + constexpr bsl::safe_umx KVM_GET_SUPPORTED_CPUID{ + static_cast(_IOWR_LIST(SHIMIO.get(), 0x05, struct kvm_cpuid2, struct kvm_cpuid_entry2[CPUID2_MAX_ENTRIES.get()]))}; // /// @brief defines KVM's KVM_SET_GSI_ROUTING IOCTL // constexpr bsl::safe_umx KVM_SET_GSI_ROUTING{static_cast(_IOW(SHIMIO.get(), 0x6a, struct kvm_irq_routing))}; /// @brief defines KVM's KVM_GET_TSC_KHZ IOCTL diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index 045cd64e4..b524b32a6 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -233,12 +234,12 @@ dispatch_system_kvm_get_msr_feature_index_list( static long dispatch_system_kvm_get_msr_index_list( - struct kvm_msr_list __user *const user_args) + struct kvm_msr_list __user *const pmut_user_args) { struct kvm_msr_list mut_args; int64_t mut_ret; - if (platform_copy_from_user(&mut_args, user_args, sizeof(mut_args))) { + if (platform_copy_from_user(&mut_args, pmut_user_args, sizeof(mut_args))) { bferror("platform_copy_from_user failed"); return -EINVAL; } @@ -251,7 +252,7 @@ dispatch_system_kvm_get_msr_index_list( mut_ret = handle_system_kvm_get_msr_index_list(&mut_args); if (SHIM_2BIG == mut_ret) { if (platform_copy_to_user( - user_args, &mut_args, sizeof(mut_args.nmsrs))) { + pmut_user_args, &mut_args, sizeof(mut_args.nmsrs))) { bferror("platform_copy_to_user nmsrs failed"); return -EINVAL; } @@ -264,7 +265,7 @@ dispatch_system_kvm_get_msr_index_list( } if (platform_copy_to_user( - user_args, + pmut_user_args, &mut_args, sizeof(mut_args.nmsrs) + mut_args.nmsrs * sizeof(*mut_args.indices))) { @@ -283,10 +284,47 @@ dispatch_system_kvm_get_msrs(struct kvm_msrs *const ioctl_args) } static long -dispatch_system_kvm_get_supported_cpuid(struct kvm_cpuid2 *const ioctl_args) +dispatch_system_kvm_get_supported_cpuid( + struct kvm_cpuid2 __user *const pmut_user_args) { - (void)ioctl_args; - return -EINVAL; + struct kvm_cpuid2 mut_args; + int64_t mut_ret; + + if (platform_copy_from_user(&mut_args, pmut_user_args, sizeof(mut_args))) { + bferror("platform_copy_from_user failed"); + return -EINVAL; + } + + if (mut_args.nent > CPUID2_MAX_ENTRIES) { + bferror("caller nent exceeds CPUID2_MAX_ENTRIES"); + return -ENOMEM; + } + + mut_ret = handle_system_kvm_get_supported_cpuid(&mut_args); + if (SHIM_2BIG == mut_ret) { + if (platform_copy_to_user( + pmut_user_args, &mut_args, sizeof(mut_args.nent))) { + bferror("platform_copy_to_user nent failed"); + return -EINVAL; + } + + return -E2BIG; + } + else if (mut_ret) { + bferror("handle_system_kvm_get_msr_index_list failed"); + return -EINVAL; + } + + if (platform_copy_to_user( + pmut_user_args, + &mut_args, + sizeof(mut_args.nent) + + mut_args.nent * sizeof(*mut_args.entries))) { + bferror("platform_copy_to_user failed"); + return -EINVAL; + } + + return 0; } static long diff --git a/shim/src/handle_system_kvm_get_supported_cpuid.c b/shim/src/handle_system_kvm_get_supported_cpuid.c index e5c945d2c..16bde371c 100644 --- a/shim/src/handle_system_kvm_get_supported_cpuid.c +++ b/shim/src/handle_system_kvm_get_supported_cpuid.c @@ -24,8 +24,17 @@ * SOFTWARE. */ +#include +#include +#include #include +#include +#include +#include +#include #include +#include +#include /** * @@ -33,11 +42,90 @@ * * * @param pmut_ioctl_args the arguments provided by userspace - * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure. + * @return SHIM_SUCCESS on success, SHIM_FAILURE on failure, and SHIM_2BIG when + * the number of CPUID entries is greater than what was set in nent. When SHIM_2BIG is + * returned, the correct number of CPUID entries is set in the nent field. */ NODISCARD int64_t handle_system_kvm_get_supported_cpuid(struct kvm_cpuid2 *const pmut_ioctl_args) NOEXCEPT { - (void)pmut_ioctl_args; + uint32_t const init_fun = ((uint32_t)0x00000000); + uint32_t const init_xfun = ((uint32_t)0x80000000); + uint32_t mut_fun = init_fun; + uint32_t mut_xfun = init_xfun; + uint32_t mut_fun_max; + uint32_t mut_xfun_max; + uint64_t mut_i; + struct mv_cdl_entry_t mut_cdl_entry; + + struct mv_cdl_t *const pmut_cdl = (struct mv_cdl_t *)shared_page_for_current_pp(); + platform_expects(NULL != pmut_cdl); + + if (detect_hypervisor()) { + bferror("The shim is not running in a VM. Did you forget to start MicroV?"); + return SHIM_FAILURE; + } + + if (!pmut_ioctl_args->entries) { + bferror("entries array not allocated"); + return SHIM_FAILURE; + } + + /* Start by getting the largest function and largest extended function */ + pmut_cdl->num_entries = ((uint64_t)2); + pmut_cdl->entries[0].fun = mut_fun; + pmut_cdl->entries[1].fun = mut_xfun; + + platform_expects(MV_INVALID_HANDLE != g_mut_hndl); + if (mv_pp_op_cpuid_get_supported_list(g_mut_hndl)) { + bferror("mv_pp_op_cpuid_get_supported_list failed"); + return SHIM_FAILURE; + } + + /* Calculate the new num_entries */ + mut_fun_max = pmut_cdl->entries[0].eax; + mut_xfun_max = pmut_cdl->entries[1].eax; + pmut_cdl->num_entries = ((uint64_t)(mut_fun_max + mut_xfun_max - init_xfun)); + + if (pmut_cdl->num_entries >= MV_CDL_MAX_ENTRIES) { + bferror("num_entries exceeds MV_CDL_MAX_ENTRIES"); + return SHIM_FAILURE; + } + + if (pmut_cdl->num_entries > ((uint64_t)pmut_ioctl_args->nent)) { + bferror("CDL entries is larger than kvm_cpuid2 entries"); + pmut_ioctl_args->nent = ((uint32_t)pmut_cdl->num_entries); + return SHIM_2BIG; + } + + mut_i = ((uint64_t)0); + for (; mut_fun < mut_fun_max; ++mut_fun) { + pmut_cdl->entries[mut_i].fun = mut_fun; + ++mut_i; + } + + for (; mut_xfun < mut_xfun_max; ++mut_xfun) { + pmut_cdl->entries[mut_i].fun = mut_xfun; + ++mut_i; + } + + platform_expects(MV_INVALID_HANDLE != g_mut_hndl); + if (mv_pp_op_cpuid_get_supported_list(g_mut_hndl)) { + bferror("mv_pp_op_cpuid_get_supported_list failed"); + return SHIM_FAILURE; + } + + for (mut_i = ((uint64_t)0); mut_i < ((uint64_t)pmut_cdl->num_entries); ++mut_i) { + mut_cdl_entry = pmut_cdl->entries[mut_i]; + pmut_ioctl_args->entries[mut_i].function = mut_cdl_entry.fun; + pmut_ioctl_args->entries[mut_i].index = mut_cdl_entry.idx; + pmut_ioctl_args->entries[mut_i].flags = mut_cdl_entry.flags; + pmut_ioctl_args->entries[mut_i].eax = mut_cdl_entry.eax; + pmut_ioctl_args->entries[mut_i].ebx = mut_cdl_entry.ebx; + pmut_ioctl_args->entries[mut_i].ecx = mut_cdl_entry.ecx; + pmut_ioctl_args->entries[mut_i].edx = mut_cdl_entry.edx; + } + pmut_ioctl_args->nent = ((uint32_t)pmut_cdl->num_entries); + return SHIM_SUCCESS; } diff --git a/shim/tests/src/test_handle_system_kvm_get_supported_cpuid.cpp b/shim/tests/src/test_handle_system_kvm_get_supported_cpuid.cpp index 8d668a7df..bb7fd52e9 100644 --- a/shim/tests/src/test_handle_system_kvm_get_supported_cpuid.cpp +++ b/shim/tests/src/test_handle_system_kvm_get_supported_cpuid.cpp @@ -24,13 +24,19 @@ #include "../../include/handle_system_kvm_get_supported_cpuid.h" +#include #include #include +#include +#include +#include #include namespace shim { + constexpr auto VAL64{42_u64}; + constexpr auto INIT_NENT{0x20_u32}; /// /// @brief Used to execute the actual checks. We put the checks in this /// function so that we can validate the tests both at compile-time @@ -43,19 +49,110 @@ namespace shim [[nodiscard]] constexpr auto tests() noexcept -> bsl::exit_code { - bsl::ut_scenario{"description"} = []() noexcept { + constexpr auto handle{&handle_system_kvm_get_supported_cpuid}; + init_tests(); + + bsl::ut_scenario{"success"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + mut_args.nent = INIT_NENT.get(); + g_mut_val = bsl::safe_u64::magic_2().get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_SUCCESS == handle(&mut_args)); + }; + }; + }; + }; + + bsl::ut_scenario{"entries not allocated"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + mut_args.nent = INIT_NENT.get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); + }; + }; + }; + }; + + bsl::ut_scenario{"hypervisor not detected"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_hypervisor_detected = false; + mut_args.nent = INIT_NENT.get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_hypervisor_detected = true; + }; + }; + }; + }; + + bsl::ut_scenario{"entries too small"} = []() noexcept { bsl::ut_given{} = [&]() noexcept { kvm_cpuid2 mut_args{}; bsl::ut_when{} = [&]() noexcept { + mut_args.nent = bsl::safe_u32::magic_0().get(); bsl::ut_then{} = [&]() noexcept { - bsl::ut_check( - SHIM_SUCCESS == handle_system_kvm_get_supported_cpuid(&mut_args)); + bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); }; }; }; }; - return bsl::ut_success(); + bsl::ut_scenario{"mv_pp_op_cpuid_get_supported_list corrupts num_entries"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_pp_op_cpuid_get_supported_list = MV_STATUS_FAILURE_CORRUPT_NUM_ENTRIES; + mut_args.nent = INIT_NENT.get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_pp_op_cpuid_get_supported_list = {}; + }; + }; + }; + }; + + bsl::ut_scenario{"mv_pp_op_cpuid_get_supported_list fails"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + g_mut_mv_pp_op_cpuid_get_supported_list = VAL64.get(); + mut_args.nent = INIT_NENT.get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_FAILURE == handle(&mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_mv_pp_op_cpuid_get_supported_list = {}; + }; + }; + }; + }; + + bsl::ut_scenario{"number of MSRs is larger than kvm_cpuid2 entries"} = []() noexcept { + bsl::ut_given{} = [&]() noexcept { + kvm_cpuid2 mut_args{}; + bsl::ut_when{} = [&]() noexcept { + mut_args.nent = INIT_NENT.get(); + g_mut_val = VAL64.get(); + bsl::ut_then{} = [&]() noexcept { + bsl::ut_check(SHIM_2BIG == handle(&mut_args)); + }; + bsl::ut_cleanup{} = [&]() noexcept { + g_mut_val = {}; + }; + }; + }; + }; + return fini_tests(); } } From c1c17609e7dba62bf3d74d520d30beb02f636338 Mon Sep 17 00:00:00 2001 From: Brendan Kerrigan Date: Tue, 26 Oct 2021 09:08:29 -0400 Subject: [PATCH 8/9] shim: implement KVM_GET_CLOCK ioctl Add KVM_GET_CLOCK vm ioctl to the shim Signed-off-by: Brendan Kerrigan --- shim/include/kvm_clock_data.h | 12 +++- shim/include/kvm_clock_data.hpp | 60 +++++++++++++++++++ shim/integration/CMakeLists.txt | 1 + shim/integration/kvm_get_clock.cpp | 55 +++++++++++++++++ .../shim_platform_interface.hpp | 5 +- shim/linux/src/entry.c | 18 +++++- shim/src/handle_vm_kvm_get_clock.c | 7 ++- 7 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 shim/include/kvm_clock_data.hpp create mode 100644 shim/integration/kvm_get_clock.cpp diff --git a/shim/include/kvm_clock_data.h b/shim/include/kvm_clock_data.h index 39ecec2d3..f4a23907e 100644 --- a/shim/include/kvm_clock_data.h +++ b/shim/include/kvm_clock_data.h @@ -34,6 +34,8 @@ extern "C" { #endif +#define KVM_CLOCK_TSC_STABLE 2 + #pragma pack(push, 1) /** @@ -44,8 +46,14 @@ extern "C" */ struct kvm_clock_data { - /** @brief replace me with contents from KVM API */ - int32_t dummy; + /** @brief value of the clock */ + uint64_t clock; + + /** @brief clock flags */ + uint32_t flags; + + /** @brief unused padding for alignment */ + uint32_t pad[9]; }; #pragma pack(pop) diff --git a/shim/include/kvm_clock_data.hpp b/shim/include/kvm_clock_data.hpp new file mode 100644 index 000000000..76c87a862 --- /dev/null +++ b/shim/include/kvm_clock_data.hpp @@ -0,0 +1,60 @@ +/** + * @copyright + * Copyright (C) 2020 Assured Information Security, Inc. + * + * @copyright + * 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: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * 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 KVM_CLOCK_DATA_HPP +#define KVM_CLOCK_DATA_HPP + +#include +#include +#include + +constexpr auto CLOCK_TSC_STABLE{2_u32}; +constexpr auto PADDING{9_u32}; + +namespace shim +{ +#pragma pack(push, 1) + + /** + * @struct kvm_clock_data + * + * + * @brief see /include/uapi/linux/kvm.h in Linux for more details. + */ + struct kvm_clock_data + { + /** @brief value of the clock data */ + bsl::uint64 clock; + /** @brief clock flags */ + bsl::uint32 flags; + /** @brief CPUID entries */ + bsl::array pad; + }; +} + +#pragma pack(pop) + +#endif diff --git a/shim/integration/CMakeLists.txt b/shim/integration/CMakeLists.txt index 6eba716b6..80c3be60a 100644 --- a/shim/integration/CMakeLists.txt +++ b/shim/integration/CMakeLists.txt @@ -37,6 +37,7 @@ microv_add_shim_integration(kvm_set_regs HEADERS) microv_add_shim_integration(kvm_set_sregs HEADERS) microv_add_shim_integration(kvm_set_user_memory_region HEADERS) microv_add_shim_integration(kvm_get_api_version HEADERS) +microv_add_shim_integration(kvm_get_clock HEADERS) microv_add_shim_integration(kvm_set_fpu HEADERS) microv_add_shim_integration(kvm_get_fpu HEADERS) microv_add_shim_integration(kvm_set_tss_addr HEADERS) diff --git a/shim/integration/kvm_get_clock.cpp b/shim/integration/kvm_get_clock.cpp new file mode 100644 index 000000000..87ceccc73 --- /dev/null +++ b/shim/integration/kvm_get_clock.cpp @@ -0,0 +1,55 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 +#include +#include +#include + +#include +#include +#include +#include + +/// +/// @brief Provides the main entry point for this application. +/// +/// +/// @return bsl::exit_success on success, bsl::exit_failure otherwise. +/// +[[nodiscard]] auto +main() noexcept -> bsl::exit_code +{ + bsl::enable_color(); + integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + shim::kvm_clock_data mut_clock_data; + integration::verify(mut_vm.write(shim::KVM_GET_CLOCK, &mut_clock_data).is_zero()); + + bsl::print() << " clock: " << bsl::hex(mut_clock_data.clock) << bsl::endl; + + return bsl::exit_success; +} diff --git a/shim/linux/include/platform_interface/shim_platform_interface.hpp b/shim/linux/include/platform_interface/shim_platform_interface.hpp index a7d1d37ca..05ac00ca2 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.hpp +++ b/shim/linux/include/platform_interface/shim_platform_interface.hpp @@ -28,6 +28,7 @@ #define SHIM_PLATFORM_INTERFACE_HPP #include +#include #include #include #include @@ -167,8 +168,8 @@ namespace shim // constexpr bsl::safe_umx KVM_SET_IRQCHIP{static_cast(_IOR(SHIMIO.get(), 0x63, struct kvm_irqchip))}; // /// @brief defines KVM's KVM_XEN_HVM_CONFIG IOCTL // constexpr bsl::safe_umx KVM_XEN_HVM_CONFIG{static_cast(_IOW(SHIMIO.get(), 0x7a, struct kvm_xen_hvm_config))}; - // /// @brief defines KVM's KVM_GET_CLOCK IOCTL - // constexpr bsl::safe_umx KVM_GET_CLOCK{static_cast(_IOR(SHIMIO.get(), 0x7c, struct kvm_clock_data))}; + /// @brief defines KVM's KVM_GET_CLOCK IOCTL + constexpr bsl::safe_umx KVM_GET_CLOCK{static_cast(_IOR(SHIMIO.get(), 0x7c, struct kvm_clock_data))}; // /// @brief defines KVM's KVM_SET_CLOCK IOCTL // constexpr bsl::safe_umx KVM_SET_CLOCK{static_cast(_IOW(SHIMIO.get(), 0x7b, struct kvm_clock_data))}; // /// @brief defines KVM's KVM_GET_VCPU_EVENTS IOCTL diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index b524b32a6..1193752fa 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -530,8 +531,19 @@ dispatch_vm_kvm_create_vcpu(struct shim_vm_t *const pmut_vm) static long dispatch_vm_kvm_get_clock(struct kvm_clock_data *const ioctl_args) { - (void)ioctl_args; - return -EINVAL; + struct kvm_clock_data mut_args; + + if (handle_vm_kvm_get_clock(&mut_args)) { + bferror("handle_vm_kvm_get_clock failed"); + return -EINVAL; + } + + if (platform_copy_to_user(ioctl_args, &mut_args, sizeof(*ioctl_args))) { + bferror("platform_copy_from_user failed"); + return -EINVAL; + } + + return 0; } static long @@ -1328,7 +1340,7 @@ static long dispatch_vcpu_kvm_x86_set_mce(struct kvm_x86_mce *const ioctl_args) { (void)ioctl_args; - return -EINVAL; + return -EINVAL; } static long diff --git a/shim/src/handle_vm_kvm_get_clock.c b/shim/src/handle_vm_kvm_get_clock.c index 283e64da2..f3b1364ab 100644 --- a/shim/src/handle_vm_kvm_get_clock.c +++ b/shim/src/handle_vm_kvm_get_clock.c @@ -25,6 +25,7 @@ */ #include +#include #include /** @@ -38,6 +39,10 @@ NODISCARD int64_t handle_vm_kvm_get_clock(struct kvm_clock_data *const pmut_ioctl_args) NOEXCEPT { - (void)pmut_ioctl_args; + platform_expects(NULL != pmut_ioctl_args); + + pmut_ioctl_args->clock = 0xDEADBEEF; + pmut_ioctl_args->flags = 0; + return SHIM_SUCCESS; } From 73cce5a9447734787735e1ecd9cc716aa051333b Mon Sep 17 00:00:00 2001 From: Brendan Kerrigan Date: Tue, 26 Oct 2021 09:28:15 -0400 Subject: [PATCH 9/9] shim: implement KVM_SET_CLOCK ioctl Add KVM_SET_CLOCK vm ioctl to the shim Signed-off-by: Brendan Kerrigan --- shim/integration/CMakeLists.txt | 1 + shim/integration/kvm_set_clock.cpp | 55 +++++++++++++++++++ .../shim_platform_interface.hpp | 4 +- shim/linux/src/entry.c | 16 +++++- shim/src/handle_vm_kvm_set_clock.c | 7 ++- 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 shim/integration/kvm_set_clock.cpp diff --git a/shim/integration/CMakeLists.txt b/shim/integration/CMakeLists.txt index 80c3be60a..59e37ac8b 100644 --- a/shim/integration/CMakeLists.txt +++ b/shim/integration/CMakeLists.txt @@ -38,6 +38,7 @@ microv_add_shim_integration(kvm_set_sregs HEADERS) microv_add_shim_integration(kvm_set_user_memory_region HEADERS) microv_add_shim_integration(kvm_get_api_version HEADERS) microv_add_shim_integration(kvm_get_clock HEADERS) +microv_add_shim_integration(kvm_set_clock HEADERS) microv_add_shim_integration(kvm_set_fpu HEADERS) microv_add_shim_integration(kvm_get_fpu HEADERS) microv_add_shim_integration(kvm_set_tss_addr HEADERS) diff --git a/shim/integration/kvm_set_clock.cpp b/shim/integration/kvm_set_clock.cpp new file mode 100644 index 000000000..0534ece2d --- /dev/null +++ b/shim/integration/kvm_set_clock.cpp @@ -0,0 +1,55 @@ +/// @copyright +/// Copyright (C) 2020 Assured Information Security, Inc. +/// +/// @copyright +/// 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: +/// +/// @copyright +/// The above copyright notice and this permission notice shall be included in +/// all copies or substantial portions of the Software. +/// +/// @copyright +/// 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 +#include +#include +#include + +#include +#include +#include +#include + +/// +/// @brief Provides the main entry point for this application. +/// +/// +/// @return bsl::exit_success on success, bsl::exit_failure otherwise. +/// +[[nodiscard]] auto +main() noexcept -> bsl::exit_code +{ + bsl::enable_color(); + integration::ioctl_t mut_system_ctl{shim::DEVICE_NAME}; + auto const vmfd{mut_system_ctl.send(shim::KVM_CREATE_VM)}; + integration::ioctl_t mut_vm{bsl::to_i32(vmfd)}; + + shim::kvm_clock_data mut_clock_data; + mut_clock_data.clock = 0xfeedbeef; + mut_clock_data.flags = 2; + integration::verify(mut_vm.write(shim::KVM_SET_CLOCK, &mut_clock_data).is_zero()); + + return bsl::exit_success; +} diff --git a/shim/linux/include/platform_interface/shim_platform_interface.hpp b/shim/linux/include/platform_interface/shim_platform_interface.hpp index 05ac00ca2..c0e7aa821 100644 --- a/shim/linux/include/platform_interface/shim_platform_interface.hpp +++ b/shim/linux/include/platform_interface/shim_platform_interface.hpp @@ -170,8 +170,8 @@ namespace shim // constexpr bsl::safe_umx KVM_XEN_HVM_CONFIG{static_cast(_IOW(SHIMIO.get(), 0x7a, struct kvm_xen_hvm_config))}; /// @brief defines KVM's KVM_GET_CLOCK IOCTL constexpr bsl::safe_umx KVM_GET_CLOCK{static_cast(_IOR(SHIMIO.get(), 0x7c, struct kvm_clock_data))}; - // /// @brief defines KVM's KVM_SET_CLOCK IOCTL - // constexpr bsl::safe_umx KVM_SET_CLOCK{static_cast(_IOW(SHIMIO.get(), 0x7b, struct kvm_clock_data))}; + /// @brief defines KVM's KVM_SET_CLOCK IOCTL + constexpr bsl::safe_umx KVM_SET_CLOCK{static_cast(_IOW(SHIMIO.get(), 0x7b, struct kvm_clock_data))}; // /// @brief defines KVM's KVM_GET_VCPU_EVENTS IOCTL // constexpr bsl::safe_umx KVM_GET_VCPU_EVENTS{static_cast(_IOR(SHIMIO.get(), 0x9f, struct kvm_vcpu_events))}; // /// @brief defines KVM's KVM_SET_VCPU_EVENTS IOCTL diff --git a/shim/linux/src/entry.c b/shim/linux/src/entry.c index 1193752fa..afe1a2bd2 100644 --- a/shim/linux/src/entry.c +++ b/shim/linux/src/entry.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -639,8 +640,19 @@ dispatch_vm_kvm_set_boot_cpu_id(void) static long dispatch_vm_kvm_set_clock(struct kvm_clock_data *const ioctl_args) { - (void)ioctl_args; - return -EINVAL; + struct kvm_clock_data mut_args; + + if (platform_copy_from_user(&mut_args, ioctl_args, sizeof(*ioctl_args))) { + bferror("platform_copy_from_user failed"); + return -EINVAL; + } + + if (handle_vm_kvm_set_clock(&mut_args)) { + bferror("handle_vm_kvm_get_clock failed"); + return -EINVAL; + } + + return 0; } static long diff --git a/shim/src/handle_vm_kvm_set_clock.c b/shim/src/handle_vm_kvm_set_clock.c index 894b0c4c4..a8665a5cd 100644 --- a/shim/src/handle_vm_kvm_set_clock.c +++ b/shim/src/handle_vm_kvm_set_clock.c @@ -24,8 +24,10 @@ * SOFTWARE. */ +#include #include #include +#include /** * @@ -38,6 +40,9 @@ NODISCARD int64_t handle_vm_kvm_set_clock(struct kvm_clock_data *const pmut_ioctl_args) NOEXCEPT { - (void)pmut_ioctl_args; + platform_expects(NULL != pmut_ioctl_args); + + bferror_x64("clock value: ", pmut_ioctl_args->clock); + return SHIM_SUCCESS; }