mirror of
https://github.com/Bareflank/hypervisor
synced 2026-08-17 06:23:04 -04:00
Fix fail handler logic, TLB flushing, Windows support
This patch: - Fixes some bugs with Windows. It is working again - Fixes a long-time issue with Linux on some Intel CPUs that would cause any attempt to load the driver to crash due to the GDT being marked as read only, and the Linux APIs for this will triple fault your system if these APIs are not used very quickly (a bug that probably should be reported to Linux). - Fixes issues with how the fail logic worked. You can not fail inside the fail handler. New integration tests were added to ensure that this case works as expected. In general, the hypervisor has less assembly, and should be more robust as a result. - Fixes how TLB flushing is done, and adds TLB flushing APIs. A future PR will use this code to implement free_page and free_huge. - Continued work on removing the heap logic that is no longer supported.
This commit is contained in:
parent
ace605a80b
commit
fd7e6bee3c
187 changed files with 14326 additions and 6013 deletions
|
|
@ -377,6 +377,22 @@ bf_add_config(
|
|||
SKIP_VALIDATION
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_FAIL_STACK_ADDR
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL "0x0000318000000000"
|
||||
DESCRIPTION "Defines an extension's fail stack address"
|
||||
OPTIONS 0x0000318000000000
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_FAIL_STACK_SIZE
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL "0x8000"
|
||||
DESCRIPTION "Defines an extension's stack size in bytes"
|
||||
SKIP_VALIDATION
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_CODE_ADDR
|
||||
CONFIG_TYPE STRING
|
||||
|
|
@ -412,9 +428,9 @@ bf_add_config(
|
|||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_PAGE_POOL_ADDR
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL ${HYPERVISOR_EXT_DIRECT_MAP_ADDR}
|
||||
DEFAULT_VAL "0x0000200000000000"
|
||||
DESCRIPTION "Defines an extension's default page pool address"
|
||||
OPTIONS ${HYPERVISOR_EXT_DIRECT_MAP_ADDR}
|
||||
OPTIONS 0x0000200000000000
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
|
|
@ -428,9 +444,9 @@ bf_add_config(
|
|||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_HUGE_POOL_ADDR
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL ${HYPERVISOR_EXT_DIRECT_MAP_ADDR}
|
||||
DEFAULT_VAL ${HYPERVISOR_EXT_PAGE_POOL_ADDR}
|
||||
DESCRIPTION "Defines an extension's default huge pool address"
|
||||
OPTIONS ${HYPERVISOR_EXT_DIRECT_MAP_ADDR}
|
||||
OPTIONS ${HYPERVISOR_EXT_PAGE_POOL_ADDR}
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
|
|
@ -441,20 +457,4 @@ bf_add_config(
|
|||
OPTIONS ${HYPERVISOR_MK_HUGE_POOL_SIZE}
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_HEAP_POOL_ADDR
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL "0x0000348000000000"
|
||||
DESCRIPTION "Defines an extension's default heap pool address in bytes"
|
||||
OPTIONS 0x0000348000000000
|
||||
)
|
||||
|
||||
bf_add_config(
|
||||
CONFIG_NAME HYPERVISOR_EXT_HEAP_POOL_SIZE
|
||||
CONFIG_TYPE STRING
|
||||
DEFAULT_VAL ${HYPERVISOR_MK_PAGE_POOL_SIZE}
|
||||
DESCRIPTION "Defines an extension's default heap pool max size"
|
||||
OPTIONS ${HYPERVISOR_MK_PAGE_POOL_SIZE}
|
||||
)
|
||||
|
||||
bf_find_program(HYPERVISOR_VERIFY_LLD "${HYPERVISOR_CXX_LINKER}" "https://github.com/Bareflank/hypervisor#build-requirements")
|
||||
|
|
|
|||
|
|
@ -28,23 +28,8 @@ add_subdirectory(../../loader loader)
|
|||
add_subdirectory(../../runtime runtime)
|
||||
add_subdirectory(../../syscall syscall)
|
||||
add_subdirectory(../../lib lib)
|
||||
# add_subdirectory(../../kernel/integration integration)
|
||||
add_subdirectory(../../kernel/integration integration)
|
||||
|
||||
if(DEFINED HYPERVISOR_EXTENSIONS_DIR)
|
||||
add_subdirectory(${HYPERVISOR_EXTENSIONS_DIR} extension)
|
||||
endif()
|
||||
|
||||
# get_filename_component(HYPERVISOR_EXTENSIONS_REALPATH
|
||||
# "${HYPERVISOR_EXTENSIONS_DIR}" REALPATH BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
# get_filename_component(HYPERVISOR_DEFAULT_REALPATH
|
||||
# "../../example/default" REALPATH BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
# get_filename_component(HYPERVISOR_NESTED_PAGING_REALPATH
|
||||
# "../../example/nested_paging" REALPATH BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
# if(NOT HYPERVISOR_DEFAULT_REALPATH STREQUAL "${HYPERVISOR_EXTENSIONS_REALPATH}")
|
||||
# add_subdirectory(../../example/default example_default)
|
||||
# endif()
|
||||
|
||||
# if(NOT HYPERVISOR_NESTED_PAGING_REALPATH STREQUAL "${HYPERVISOR_EXTENSIONS_REALPATH}")
|
||||
# add_subdirectory(../../example/nested_paging example_nested_paging)
|
||||
# endif()
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ macro(hypervisor_add_cmake_args)
|
|||
-DHYPERVISOR_EXT_DIRECT_MAP_SIZE=${HYPERVISOR_EXT_DIRECT_MAP_SIZE}
|
||||
-DHYPERVISOR_EXT_STACK_ADDR=${HYPERVISOR_EXT_STACK_ADDR}
|
||||
-DHYPERVISOR_EXT_STACK_SIZE=${HYPERVISOR_EXT_STACK_SIZE}
|
||||
-DHYPERVISOR_EXT_FAIL_STACK_ADDR=${HYPERVISOR_EXT_FAIL_STACK_ADDR}
|
||||
-DHYPERVISOR_EXT_FAIL_STACK_SIZE=${HYPERVISOR_EXT_FAIL_STACK_SIZE}
|
||||
-DHYPERVISOR_EXT_CODE_ADDR=${HYPERVISOR_EXT_CODE_ADDR}
|
||||
-DHYPERVISOR_EXT_CODE_SIZE=${HYPERVISOR_EXT_CODE_SIZE}
|
||||
-DHYPERVISOR_EXT_TLS_ADDR=${HYPERVISOR_EXT_TLS_ADDR}
|
||||
|
|
@ -100,7 +102,5 @@ macro(hypervisor_add_cmake_args)
|
|||
-DHYPERVISOR_EXT_PAGE_POOL_SIZE=${HYPERVISOR_EXT_PAGE_POOL_SIZE}
|
||||
-DHYPERVISOR_EXT_HUGE_POOL_ADDR=${HYPERVISOR_EXT_HUGE_POOL_ADDR}
|
||||
-DHYPERVISOR_EXT_HUGE_POOL_SIZE=${HYPERVISOR_EXT_HUGE_POOL_SIZE}
|
||||
-DHYPERVISOR_EXT_HEAP_POOL_ADDR=${HYPERVISOR_EXT_HEAP_POOL_ADDR}
|
||||
-DHYPERVISOR_EXT_HEAP_POOL_SIZE=${HYPERVISOR_EXT_HEAP_POOL_SIZE}
|
||||
)
|
||||
endmacro(hypervisor_add_cmake_args)
|
||||
|
|
|
|||
|
|
@ -263,6 +263,16 @@ macro(hypervisor_add_info)
|
|||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${BF_COLOR_YLW} HYPERVISOR_EXT_FAIL_STACK_ADDR ${BF_COLOR_CYN}${HYPERVISOR_EXT_FAIL_STACK_ADDR}${BF_COLOR_RST}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${BF_COLOR_YLW} HYPERVISOR_EXT_FAIL_STACK_SIZE ${BF_COLOR_CYN}${HYPERVISOR_EXT_FAIL_STACK_SIZE}${BF_COLOR_RST}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${BF_COLOR_YLW} HYPERVISOR_EXT_CODE_ADDR ${BF_COLOR_CYN}${HYPERVISOR_EXT_CODE_ADDR}${BF_COLOR_RST}"
|
||||
VERBATIM
|
||||
|
|
@ -303,16 +313,6 @@ macro(hypervisor_add_info)
|
|||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${BF_COLOR_YLW} HYPERVISOR_EXT_HEAP_POOL_ADDR ${BF_COLOR_CYN}${HYPERVISOR_EXT_HEAP_POOL_ADDR}${BF_COLOR_RST}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${BF_COLOR_YLW} HYPERVISOR_EXT_HEAP_POOL_SIZE ${BF_COLOR_CYN}${HYPERVISOR_EXT_HEAP_POOL_SIZE}${BF_COLOR_RST}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET info
|
||||
COMMAND ${CMAKE_COMMAND} -E echo " "
|
||||
VERBATIM
|
||||
|
|
|
|||
|
|
@ -26,25 +26,33 @@ macro(hypervisor_add_integration NAME HEADERS)
|
|||
|
||||
target_include_directories(integration_${NAME} PRIVATE
|
||||
support
|
||||
support/include
|
||||
support/src
|
||||
)
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD")
|
||||
target_include_directories(integration_${NAME} PRIVATE
|
||||
support/x64
|
||||
support/x64/amd
|
||||
support/include/x64
|
||||
support/include/x64/amd
|
||||
support/src/x64
|
||||
support/src/x64/amd
|
||||
)
|
||||
|
||||
target_sources(integration_${NAME} PRIVATE
|
||||
support/src/x64/intrinsic_cpuid_impl.S
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "GenuineIntel")
|
||||
target_include_directories(integration_${NAME} PRIVATE
|
||||
support/x64
|
||||
support/x64/intel
|
||||
support/include/x64
|
||||
support/include/x64/intel
|
||||
support/src/x64
|
||||
support/src/x64/intel
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
target_include_directories(integration_${NAME} PRIVATE
|
||||
support/arm/aarch64
|
||||
target_sources(integration_${NAME} PRIVATE
|
||||
support/src/x64/intrinsic_cpuid_impl.S
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
@ -61,11 +69,10 @@ macro(hypervisor_add_integration NAME HEADERS)
|
|||
bsl
|
||||
loader
|
||||
syscall
|
||||
lib
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL RELEASE OR CMAKE_BUILD_TYPE STREQUAL MINSIZEREL)
|
||||
add_custom_command(TARGET integration_${NAME} POST_BUILD COMMAND ${CMAKE_STRIP} integration_${NAME})
|
||||
endif()
|
||||
|
||||
install(TARGETS integration_${NAME} DESTINATION bin)
|
||||
endmacro(hypervisor_add_integration)
|
||||
|
|
|
|||
|
|
@ -27,24 +27,24 @@ macro(hypervisor_add_integration_target NAME)
|
|||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_custom_target(integration_${NAME}
|
||||
COMMAND sync
|
||||
COMMAND sudo vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/mk_cross_compile/bin/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/bin/integration_${NAME} | true
|
||||
COMMAND sudo vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/build/integration/integration_${NAME} | true
|
||||
COMMAND sudo vmmctl/vmmctl dump
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(${NAME}
|
||||
COMMAND sync
|
||||
COMMAND sudo vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/mk_cross_compile/bin/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/bin/integration_${NAME} | true
|
||||
COMMAND sudo vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/build/integration/integration_${NAME} | true
|
||||
COMMAND sudo vmmctl/vmmctl dump
|
||||
VERBATIM
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_custom_target(integration_${NAME}
|
||||
COMMAND vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/mk_cross_compile/bin/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/bin/integration_${NAME} | true
|
||||
COMMAND vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/build/integration/integration_${NAME} | true
|
||||
COMMAND vmmctl/vmmctl dump
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(${NAME}
|
||||
COMMAND vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/mk_cross_compile/bin/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/bin/integration_${NAME} | true
|
||||
COMMAND vmmctl/vmmctl start ${CMAKE_BINARY_DIR}/kernel ${CMAKE_BINARY_DIR}/ext_cross_compile/build/integration/integration_${NAME} | true
|
||||
COMMAND vmmctl/vmmctl dump
|
||||
VERBATIM
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ target_compile_definitions(hypervisor INTERFACE
|
|||
HYPERVISOR_EXT_DIRECT_MAP_SIZE=${HYPERVISOR_EXT_DIRECT_MAP_SIZE}_umx
|
||||
HYPERVISOR_EXT_STACK_ADDR=${HYPERVISOR_EXT_STACK_ADDR}_umx
|
||||
HYPERVISOR_EXT_STACK_SIZE=${HYPERVISOR_EXT_STACK_SIZE}_umx
|
||||
HYPERVISOR_EXT_FAIL_STACK_ADDR=${HYPERVISOR_EXT_FAIL_STACK_ADDR}_umx
|
||||
HYPERVISOR_EXT_FAIL_STACK_SIZE=${HYPERVISOR_EXT_FAIL_STACK_SIZE}_umx
|
||||
HYPERVISOR_EXT_CODE_ADDR=${HYPERVISOR_EXT_CODE_ADDR}_umx
|
||||
HYPERVISOR_EXT_CODE_SIZE=${HYPERVISOR_EXT_CODE_SIZE}_umx
|
||||
HYPERVISOR_EXT_TLS_ADDR=${HYPERVISOR_EXT_TLS_ADDR}_umx
|
||||
|
|
@ -66,6 +68,4 @@ target_compile_definitions(hypervisor INTERFACE
|
|||
HYPERVISOR_EXT_PAGE_POOL_SIZE=${HYPERVISOR_EXT_PAGE_POOL_SIZE}_umx
|
||||
HYPERVISOR_EXT_HUGE_POOL_ADDR=${HYPERVISOR_EXT_HUGE_POOL_ADDR}_umx
|
||||
HYPERVISOR_EXT_HUGE_POOL_SIZE=${HYPERVISOR_EXT_HUGE_POOL_SIZE}_umx
|
||||
HYPERVISOR_EXT_HEAP_POOL_ADDR=${HYPERVISOR_EXT_HEAP_POOL_ADDR}_umx
|
||||
HYPERVISOR_EXT_HEAP_POOL_SIZE=${HYPERVISOR_EXT_HEAP_POOL_SIZE}_umx
|
||||
)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ hypervisor_silence(HYPERVISOR_EXT_DIRECT_MAP_ADDR)
|
|||
hypervisor_silence(HYPERVISOR_EXT_DIRECT_MAP_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_STACK_ADDR)
|
||||
hypervisor_silence(HYPERVISOR_EXT_STACK_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_FAIL_STACK_ADDR)
|
||||
hypervisor_silence(HYPERVISOR_EXT_FAIL_STACK_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_CODE_ADDR)
|
||||
hypervisor_silence(HYPERVISOR_EXT_CODE_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_TLS_ADDR)
|
||||
|
|
@ -89,5 +91,3 @@ hypervisor_silence(HYPERVISOR_EXT_PAGE_POOL_ADDR)
|
|||
hypervisor_silence(HYPERVISOR_EXT_PAGE_POOL_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_HUGE_POOL_ADDR)
|
||||
hypervisor_silence(HYPERVISOR_EXT_HUGE_POOL_SIZE)
|
||||
hypervisor_silence(HYPERVISOR_EXT_HEAP_POOL_ADDR)
|
||||
hypervisor_silence(HYPERVISOR_EXT_HEAP_POOL_SIZE)
|
||||
|
|
|
|||
|
|
@ -22,22 +22,22 @@
|
|||
if(HYPERVISOR_BUILD_LOADER AND NOT HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_custom_target(loader_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux make CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux make CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND sync
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux make CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux make CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND sync
|
||||
VERBATIM
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_custom_target(loader_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows MSBuild.exe /p:Configuration=Debug /p:Platform=x64 /p:Arch=${HYPERVISOR_TARGET_ARCH} /p:CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows MSBuild.exe /p:Configuration=Debug /p:Platform=x64 /p:Arch=${HYPERVISOR_TARGET_ARCH} /p:CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows MSBuild.exe /p:Configuration=Debug /p:Platform=x64 /p:Arch=${HYPERVISOR_TARGET_ARCH} /p:CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows MSBuild.exe /p:Configuration=Debug /p:Platform=x64 /p:Arch=${HYPERVISOR_TARGET_ARCH} /p:CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -22,28 +22,22 @@
|
|||
if(HYPERVISOR_BUILD_LOADER AND NOT HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_custom_target(loader_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux make clean CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux make clean CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND sync
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux make clean CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux make clean CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND sync
|
||||
VERBATIM
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_custom_target(loader_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows/x64
|
||||
COMMAND sync
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${hypervisor_SOURCE_DIR}/loader/windows/x64
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows/x64
|
||||
COMMAND sync
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${hypervisor_SOURCE_DIR}/loader/windows/x64
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -22,30 +22,26 @@
|
|||
if(HYPERVISOR_BUILD_LOADER AND NOT HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_custom_target(loader_load
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux sudo make load CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux sudo make load CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_load
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux sudo make load CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux sudo make load CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
VERBATIM
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_custom_target(loader_load
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine root
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine trustedpublisher
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon install x64/Debug/loader/loader.inf ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine root
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine trustedpublisher
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon install x64/Debug/loader/loader.inf ROOT\\loader
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_load
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine root
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine trustedpublisher
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon install x64/Debug/loader/loader.inf ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine root
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows certmgr /add x64/Debug/loader.cer /s /r localMachine trustedpublisher
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon install x64/Debug/loader/loader.inf ROOT\\loader
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -21,13 +21,17 @@
|
|||
|
||||
if(HYPERVISOR_BUILD_LOADER AND NOT HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
add_custom_target(loader_quick
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target loader_load
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target loader_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target loader_clean
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target loader_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target loader_load
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_quick
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_clean
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target driver_load
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target driver_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target driver_clean
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target driver_build
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_COMMAND} --build . --target driver_load
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -23,21 +23,21 @@ if(HYPERVISOR_BUILD_LOADER AND NOT HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
|||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_custom_target(loader_unload
|
||||
COMMAND sync
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux sudo make unload CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux sudo make unload CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_unload
|
||||
COMMAND sync
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/linux sudo make unload CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/linux sudo make unload CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}'
|
||||
VERBATIM
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_custom_target(loader_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon remove ROOT\\loader
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(driver_unload
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_LIST_DIR}/../../loader/windows devcon remove ROOT\\loader
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir ${hypervisor_SOURCE_DIR}/loader/windows devcon remove ROOT\\loader
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/constants.h)
|
|||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_DIRECT_MAP_SIZE ((uint64_t)(${HYPERVISOR_EXT_DIRECT_MAP_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_STACK_ADDR ((uint64_t)(${HYPERVISOR_EXT_STACK_ADDR}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_STACK_SIZE ((uint64_t)(${HYPERVISOR_EXT_STACK_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_FAIL_STACK_ADDR ((uint64_t)(${HYPERVISOR_EXT_FAIL_STACK_ADDR}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_FAIL_STACK_SIZE ((uint64_t)(${HYPERVISOR_EXT_FAIL_STACK_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_CODE_ADDR ((uint64_t)(${HYPERVISOR_EXT_CODE_ADDR}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_CODE_SIZE ((uint64_t)(${HYPERVISOR_EXT_CODE_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_TLS_ADDR ((uint64_t)(${HYPERVISOR_EXT_TLS_ADDR}))\n")
|
||||
|
|
@ -76,8 +78,6 @@ if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/constants.h)
|
|||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_PAGE_POOL_SIZE ((uint64_t)(${HYPERVISOR_EXT_PAGE_POOL_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_HUGE_POOL_ADDR ((uint64_t)(${HYPERVISOR_EXT_HUGE_POOL_ADDR}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_HUGE_POOL_SIZE ((uint64_t)(${HYPERVISOR_EXT_HUGE_POOL_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_HEAP_POOL_ADDR ((uint64_t)(${HYPERVISOR_EXT_HEAP_POOL_ADDR}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#define HYPERVISOR_EXT_HEAP_POOL_SIZE ((uint64_t)(${HYPERVISOR_EXT_HEAP_POOL_SIZE}))\n")
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "\n")
|
||||
|
||||
file(APPEND ${HYPERVISOR_CONSTANTS} "#endif\n")
|
||||
|
|
|
|||
102
docs/Memory Layout.md
Normal file
102
docs/Memory Layout.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
## Table of Contents <!-- omit in toc -->
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
The question asked to most on the project by beginners is always, "I need to access memory from my application in the hypervisor, but when I dereference it, bad things happen", or at least something to that effect. This document aims to explain why, and how memory is layed out in Bareflank.
|
||||
|
||||
# 2. Address Translation
|
||||
|
||||
We will use x86 as our primary CPU architecture here, mainly because it is one of the most complicated, at least when it comes to translation, while ARM probably wins with respect to cache management, although I bet some would argue otherwise.
|
||||
|
||||
On x86, you have several different modes, and how an address is translated depends on the mode. Intel has many different names for an address including:
|
||||
- logical
|
||||
- effective
|
||||
- virtual
|
||||
- linear
|
||||
- physical
|
||||
|
||||
To keep this simple, we are going to stick to 64bit mode, in which case, the following gross generalization will help tame the beast:
|
||||
|
||||
```
|
||||
virtual --> linear --> physical
|
||||
| |
|
||||
| |
|
||||
segmentation paging
|
||||
```
|
||||
|
||||
To be clear, it is more complicated than this, and if you need to understand why something might no be working right, you will need to read and learn the different modes of operation including:
|
||||
- 16bit real mode
|
||||
- V8086 mode
|
||||
- 32bit protected mode
|
||||
- 32bit protected mode with paging
|
||||
- 32bit compatibility mode
|
||||
- 64bit long mode
|
||||
|
||||
And how each of these change how address translation works. The best way to learn this is to simply read Intel and AMD's manuals. Read both as they both share different interpretations which helps. Also, keep in mind that 64bit mode does NOT remove segmentation, contrary to what you will read online. GS, FS and the TSS all require segmentation.
|
||||
|
||||
So lets look at this simple example:
|
||||
|
||||
```
|
||||
thread_local int my_var{};
|
||||
auto my_ptr = &my_var;
|
||||
```
|
||||
|
||||
The goal with this section is to answer the following questions:
|
||||
- what is the value of my_ptr?
|
||||
- how do I access my_ptr from an extension in Bareflank?
|
||||
|
||||
There is a ton more to talk about, but honestly we could write a book on nothing but this topic, so the goal is to keep this practical.
|
||||
|
||||
Under the hood, TLS on x86 is implemented using both segmentation and paging. Every userspace process has a consistent linear view of memory from 0 - MAX. This is done using paging, which is why we call these addresses linear. Paging allows an operating to create this linear view of memory using physical pages (i.e., physical addresses) that may not be contiguous. Meaning, address 0 might point to physical address 0x2000, address 0x1000 might point to physical address 0x4000 and address 0x2000 might point to physical address 0x1000. The operating system is free to reorder memory however it wants using paging to ensure that every userspace process sees a linear view of memory from 0 - MAX no matter how it is actually laid out in physical memory.
|
||||
|
||||
Thread local storage is a special case. Each thread is given it's own copy of a variable depending on which thread is executing. So reading the value of my_var on thread 1 will return a different result than reading the value of my_var on thread 2. But how does the program know which memory address to read? On x86, this is done using segmentation. When you access a TLS variable, the code under the hood reads and offset from FS. So for example:
|
||||
|
||||
```
|
||||
mov rax, fs:[0x10]
|
||||
```
|
||||
|
||||
When a thread changes, the OS changes the value of FS, which changes which memory location my_var is actually pointing to. From a hardware point of view, the CPU takes the value of FS and adds 0x10 giving it a linear address (again, skipping some details here). It then takes this linear address, and converts it to a physical address by walking the page tables. Or in other words, it performs the virtual -> linear -> physical address translation, which tells the CPU where to get the actual value from memory. And again, the TLS calculations above are another gross generalization because in reality, how TLS is done is based on your ABI (SysV or MS64), and there are entire specs dedicated to how this is done, including dynamic linking, etc...
|
||||
|
||||
To answer question 2, we must bring two new concepts into the picture. First, it is important to never forget that the segmentation values and page tables change all the time, and they depend on your current context. This is the #1 mistake that everyone makes. They think that a virtual address in their application can somehow be read from another context like the kernel or a hypervisor, but this is not the case. Kernels and hypervisors have their own segmentation register values and page tables. So when you are in the hypervisor, you need ensure the hypervisor's segmentation and page tables are set up to be capable of reading a virtual address from another context.
|
||||
|
||||
The second thing we need to introduce is something called second level paging, or second level address translation (SLAT). Second level paging adds a second level of page tables for guest VMs. What this means is that when the hardware converts a linear address to a physical address, what it has really done is convert a guest linear address (GLA) to a guest physical address (GPA). A second level of paging is required to translate from a GPA to a system physical address (SPA) which is the actual memory. Second level paging allows a hypervisor to rearrange memory a second time. Think of it this way. The OS has to provide userspace with a linear view of memory so that an application can run thinking that it owns addresses 0 - MAX no matter how this memory is actually laid out in physical memory. The OS will do this by using paging to map it's linear view of physical memory, in whatever order is needs to, to provide userspace with it's own linear view of memory. A hypervisor must do the same thing for the OS's view of physical memory. The hypervisor is going to have a linear view of system memory, and this view will be broken up into pages, and the order of these pages will likely be mixed up and somewhat random. The hypervisor must provide the OS with a linear view of physical memory, and second level paging provides this.
|
||||
|
||||
Ok, so to answer the second question, here we go. We have a TLS variable in a userspace application, from a guest VM, and we want to access this memory from our extension in Bareflank. Keep in mind that the guest VM could be the root VM. It doesn't matter.
|
||||
- Start by converting the virtual address of my_var to a linear address. How this is done depends on what mode the CPU is in. For now, we will only talk about 32bit mode and 64bit mode. In 32bit mode, we would read the FS segment, which would tell us which GDT entry to use, and then use the value of the base address as the offset into the application's linear address space. If we are in 64bit, we would simply read the FS base MSR as the base address for FS in the GDT is ignored.
|
||||
- From there we need to know what the offset of my_var is in the FS segment. The best way to get this is to simply decode the instruction for reading my_var. Either that or we would need to know the layout of the TLS variables, which is up to the compiler and where it wants to put each TLS variable.
|
||||
- With the base address of FS, and the offset into FS, we can no calculate our linear address. We could have also cheated, and had the userspace application use the lea instruction to perform this translation for us, and depending on the VMExit, the VMCS/VMCB might already contain this translation. Either way, we now have a guest linear address.
|
||||
- With the guest linear address, we need to use paging to determine what the guest physical address is. To do this we need to figure out where the userspace application's page tables are located. This is NOT as simple as reading CR3 from the guest VMCS/VMCB. For example, did your VMExit originate from the userspace process or the kernel? Thanks to Meltdown, the kernel has more than one set of page tables, and the currently located CR3 might not be pointing to a set of page tables that correctly maps the linear address space of the userspace application. Even if the VMExit originates from the userspace application itself, the kernel may be providing userspace with more than one set of page tables and swapping these page tables on-demand. The point is, you need to understand from what POV are you trying to access this memory, and you need to locate the CR3 value that is used to map this POV. This question is asked a lot, and there is no simple answer. For MicroV, we generally are only interested in the emulation of an instruction, so whatever the state is at the time, is all that we care about. For introspection, this is a lot more complicated. One way to get this CR3 value is to simply trap on all writes to CR3, record each write, and try to make sense of each CR3 value you see, which will likely require parsing kernel structures.
|
||||
- Once you have the CR3 value that maps the guest linear address space associated with the POV you care about, you can then walk the page tables to translate the guest linear address to a guest physical address.
|
||||
- Oh..., but here is where it gets interesting. To explain why it all goes to hell here, let's start with the first page table. In 64bit, CR3 points to the PML4. Our task is to find the PDPT. To do that, we figure out which entry in the PML4 we care about by grabbing the index into the table from the linear address (bits 39-48). Once we know which entry, we can read the PML4 entry to get the guest physical address of the PDPT. The problem is, the value of CR3 is a guest physical address. But we are in an extension in Bareflank. An extension runs from the host (i.e., VMX-root) in userspace. So it has it's own linear address space which is defined by the microkernel.
|
||||
|
||||
So how do we read the PML4, if all we have is a GPA?
|
||||
- Step 1 is to convert the GPA to an SPA. To do this, we need to walk our second level page tables. How hard can that be? To do this we need to start by getting the pointer to our top level table for second level paging. On Intel, this is NOT our EPTP. It is the virtual address that points to the EPML4. EPTP is the physical address of our EPML4 with some added configuration bits. Typically, the virtual address to the EPML4 will be saved in some structure that also contains other information about the VM you are working with (might just be the root VM), but either way, this pointer is usually just stored somewhere that you can easily get to (it's your extension after all).
|
||||
- From here, we need to read the EPDPT. To get the address of the EPDPT, we need to figure out which entry in the EPML4 points to the EPDPT that we care about. Like the PML4 -> PDPT, we use bits 39-48, but instead of using the guest linear address, we use the guest physical address. Simply read this entry, and grab the system physical address of the EPDPT.
|
||||
- The problem is now we have an SPA of the EPDPT. You cannot read SPAs from a Bareflank extension either. Again, your extension has it's own linear address space which is defined by the microkernel. To read the EPDPT, we need a virtual address in the extension that we can read, which means that the microkernel needs to map this SPA into our extension's linear address space for us.
|
||||
|
||||
Ok, from here we really need to talk about the memory layout of Bareflank for any of the rest of this to make any sense. All of the major CPU architectures have more linear address bits than physical address bits. For example, on Intel, most systems support up to 42bits of physical memory and 48bits of linear memory (not including 5-level paging here, and really these number can change wildly). The reason they do this is because you can have several different views of ALL of physical memory. Like every OS, Bareflank takes advantage of this. If physical memory is mapped into one of these views using a contiguous layout we call this a direct map. A direct map allows us to take a physical address, add a constant offset, to get our linear address. Direct maps do not always mean that you can then simply dereference the resulting linear address and read memory, it just means that the linear address is a constant offset from the physical address. In Bareflank, you need to ask the microkernel to map this memory before you can read it. Bareflank has more than one direct map.
|
||||
- bit 48 == 1, bit 47 == 0: direct map and page/huge pool for microkernel
|
||||
- bit 48 == 1, bit 47 == 1: direct map for extension
|
||||
- bit 48 == 0, bit 47 == 1: page/huge pool for extension
|
||||
- bit 48 == 0, bit 47 == 0: code, stack and TLS for kernel and extension
|
||||
|
||||
Oh, but it gets worse, but for a good reason. Lets say we map SPA 0x1000 into the direct map of the extension. This map only applies to the active VM. When the active VM changes, the extension's direct map changes too. The page/huge pool for the extension, and the direct map and page/huge pool for the microkernel do not. The reason the direct map for the extension changes is because of Spectre/Meltdown and L1TF. Specifically, each VM has it's own set of page tables dedicated to the direct map in the microkernel. Any attempt to read physical memory from an extension must come from the direct map, but when the active VM changes, this direct map will change as well. This prevents the memory needed by the hypervisor for one VM from being mapped while any other VM is active, preventing a number of Spectre specific attacks. To get a complete view of how memory is laid out, use `make info` as all of the addresses are there.
|
||||
|
||||
The Microkernel has has bf_vm_op_map_direct that allows you to map an SPA, and it returns a linear address that you can use to read the SPA. Again, this map is only valid from the VM that is active. If the active VM changes, this linear address is no longer usable. But, that's not what we are going to do here to access our EPDPT. Since the extension created the second level page tables, the address of the EPDPT is already mapped into the extension. We just need to figure out what it is. When the extension created the second level page tables, each table that it added would have created by allocating a page using bf_mem_op_alloc_page. The pages that are allocated are mapped into the linear address space of the extension into it's page/huge direct map. So if we were to use bf_vm_op_map_direct, we would have ended up with a linear address to the EPDPT in both the direct map, and the page/huge pool of the extension. Instead, the reason that page and huge allocations are mapped into one of the three direct maps is so that you can calculate the linear address of an SPA using a simple offset (meaning virt to phys and phys to virt translations for page/huge allocations is a simply offset calculation). So, to figure out what the linear address is of the EPDPT given an SPA, we simply add HYPERVISOR_EXT_PAGE_POOL_ADDR to the SPA, because that is the address that the microkernel would have returned when the EPDPT was originally allocated.
|
||||
|
||||
Ok, so now we can read our EPDPT. Back to our list of steps:
|
||||
- Now that we have a means to read our EPDPT, we can continue to walk our page tables until we get to the EPT. This will give use the SPA of the PML4.
|
||||
- So now we have the SPA of the PML4, which we want to read. Again, we cannot read an SPA from the extension. Unlike our second level page tables, the page tables that we are parsing so that we can read memory from userspace of an application in the guest is not controlled by us. It is controlled by the OS of the guest VM. So this means that we do not have the PML4 mapped into any of our direct maps. To be able to read this memory, we will need to use bf_vm_op_map_direct. We only need this memory for a short time, so when we are done with all of this we should use bf_vm_op_unmap_direct so that our direct map is put back to normal before we are done.
|
||||
- bf_vm_op_map_direct will give us a linear address that we can use to access the PML4. We can now locate the entry in the PML4, and get the guest physical address of the PDPT.
|
||||
- Repeat this process, by getting the physical address of the PDPT, walking the second level page tables to get an SPA, mapping this SPA and then reading the next table, until you end up with finally, the PTE containing the guest physical address of "my_var".
|
||||
- The rest of this should be obvious at this point. We then take the guest physical address of my_var, walk the second level page tables again, which will give us an SPA of my_var, and then use bf_vm_op_map_direct to get a linear address in our extension that we can use to actually read my_var.
|
||||
|
||||
If you are familiar with Bareflank v2.1, you will notice that this process is different. Bareflank v2.1 was monolithic, so your extension ran in ring 0. So instead of calling the microkernel to map memory, you would simply call an API which would map memory directly into the page tables of the hypervisor, and return a linear address that could be used to read/write SPAs. Unlike Bareflank v3.0 however, Bareflank v2.1 did not have any direct maps, so virt to phys and phys to virt translations were slow, because you would have to walk the hypervisor's page tables as well, so the search above was not O(N^2), but actually O(N^3). The addition of the direct maps in Bareflank v3.0 dramatically improve performance. Sadly, this performance is not as good as it would be with something like Linux or Xen, where all of RAM is mapped into the direct map all the time, and the direct map never changes based on which VM is loaded at the time (in the case of Xen at least). These additional security measures do reduce overall performance, but in general this is a non issue.
|
||||
|
||||
Extensions like MicroV do not actually do these types of translations often. They are required. For example, if you want to emulate the LAPIC, you need to decode instructions that operate on the LAPIC, and the process above is what is required. But usually, you will do this a couple of times, cache the results, and never do it again. Extensions that focus on introspection can implement their own on-demand paging using the fail handler. Specifically, just access the direct map as if the SPA you want to read is mapped in. If it is not, which will be the case for the first access, a page fault will occur. The microkernel will see this and execute the fail handler, and provide a page fault fail reason, as well as the linear address that created the page fault. From there, the extension can map the address in using bf_vm_op_map_direct, and then return success. This memory access will then continue, and the extension will continue it's execution. All future reads from this address will no longer produce a page fault. Extensions that use this trick can do this on-demand, or preload the direct map on boot using bf_vm_op_map_direct. Either way, once the direct map has every address you might ever care about, the performance of the extension will be the same as Linux or Xen, but unlike Linux or Xen, the extension has the choice, and is capable of choosing what it cares about more.
|
||||
|
||||
For more information about all of this stuff, please read the code and the Intel/AMD manuals.
|
||||
|
||||
# 3. Stack
|
||||
|
||||
TBD - Details about the stack, IST, etc...
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
- [2.9. Control Syscalls](#29-control-syscalls)
|
||||
- [2.9.1. bf_control_op_exit, OP=0x0, IDX=0x0](#291-bf_control_op_exit-op0x0-idx0x0)
|
||||
- [2.9.2. bf_control_op_wait, OP=0x0, IDX=0x1](#292-bf_control_op_wait-op0x0-idx0x1)
|
||||
- [2.9.2. bf_control_op_again, OP=0x0, IDX=0x2](#292-bf_control_op_again-op0x0-idx0x2)
|
||||
- [2.10. Handle Syscalls](#210-handle-syscalls)
|
||||
- [2.10.1. bf_handle_op_open_handle, OP=0x1, IDX=0x0](#2101-bf_handle_op_open_handle-op0x1-idx0x0)
|
||||
- [2.10.2. bf_handle_op_close_handle, OP=0x1, IDX=0x1](#2102-bf_handle_op_close_handle-op0x1-idx0x1)
|
||||
|
|
@ -64,6 +65,7 @@
|
|||
- [2.13.3. bf_vm_op_map_direct, OP=0x4, IDX=0x2](#2133-bf_vm_op_map_direct-op0x4-idx0x2)
|
||||
- [2.13.3. bf_vm_op_unmap_direct, OP=0x4, IDX=0x3](#2133-bf_vm_op_unmap_direct-op0x4-idx0x3)
|
||||
- [2.13.3. bf_vm_op_unmap_direct_broadcast, OP=0x4, IDX=0x4](#2133-bf_vm_op_unmap_direct_broadcast-op0x4-idx0x4)
|
||||
- [2.15.3. bf_vm_op_tlb_flush, OP=0x4, IDX=0x5](#2153-bf_vm_op_tlb_flush-op0x4-idx0x5)
|
||||
- [2.14. Virtual Processor Syscalls](#214-virtual-processor-syscalls)
|
||||
- [2.14.2. bf_vp_op_create_vp, OP=0x5, IDX=0x0](#2142-bf_vp_op_create_vp-op0x5-idx0x0)
|
||||
- [2.14.3. bf_vp_op_destroy_vp, OP=0x5, IDX=0x1](#2143-bf_vp_op_destroy_vp-op0x5-idx0x1)
|
||||
|
|
@ -73,27 +75,24 @@
|
|||
- [2.14.9. bf_vs_op_init_as_root, OP=0x6, IDX=0x2](#2149-bf_vs_op_init_as_root-op0x6-idx0x2)
|
||||
- [2.14.10. bf_vs_op_read, OP=0x6, IDX=0x3](#21410-bf_vs_op_read-op0x6-idx0x3)
|
||||
- [2.14.11. bf_vs_op_write, OP=0x6, IDX=0x4](#21411-bf_vs_op_write-op0x6-idx0x4)
|
||||
- [2.14.12. bf_vs_op_run, OP=0x5, IDX=0x5](#21412-bf_vs_op_run-op0x5-idx0x5)
|
||||
- [2.14.13. bf_vs_op_run_current, OP=0x5, IDX=0x6](#21413-bf_vs_op_run_current-op0x5-idx0x6)
|
||||
- [2.14.14. bf_vs_op_advance_ip_and_run_impl, OP=0x5, IDX=0x7](#21414-bf_vs_op_advance_ip_and_run_impl-op0x5-idx0x7)
|
||||
- [2.14.15. bf_vs_op_advance_ip_and_run_current, OP=0x5, IDX=0x8](#21415-bf_vs_op_advance_ip_and_run_current-op0x5-idx0x8)
|
||||
- [2.14.16. bf_vs_op_promote, OP=0x5, IDX=0x9](#21416-bf_vs_op_promote-op0x5-idx0x9)
|
||||
- [2.14.17. bf_vs_op_clear, OP=0x5, IDX=0xA](#21417-bf_vs_op_clear-op0x5-idx0xa)
|
||||
- [2.14.17. bf_vs_op_migrate, OP=0x5, IDX=0xB](#21417-bf_vs_op_migrate-op0x5-idx0xb)
|
||||
- [2.14.17. bf_vs_op_set_active, OP=0x5, IDX=0xC](#21417-bf_vs_op_set_active-op0x5-idx0xc)
|
||||
- [2.14.17. bf_vs_op_advance_ip_and_set_active, OP=0x5, IDX=0xD](#21417-bf_vs_op_advance_ip_and_set_active-op0x5-idx0xd)
|
||||
- [2.14.12. bf_vs_op_run, OP=0x6, IDX=0x5](#21412-bf_vs_op_run-op0x6-idx0x5)
|
||||
- [2.14.13. bf_vs_op_run_current, OP=0x6, IDX=0x6](#21413-bf_vs_op_run_current-op0x6-idx0x6)
|
||||
- [2.14.14. bf_vs_op_advance_ip_and_run_impl, OP=0x6, IDX=0x7](#21414-bf_vs_op_advance_ip_and_run_impl-op0x6-idx0x7)
|
||||
- [2.14.15. bf_vs_op_advance_ip_and_run_current, OP=0x6, IDX=0x8](#21415-bf_vs_op_advance_ip_and_run_current-op0x6-idx0x8)
|
||||
- [2.14.16. bf_vs_op_promote, OP=0x6, IDX=0x9](#21416-bf_vs_op_promote-op0x6-idx0x9)
|
||||
- [2.14.17. bf_vs_op_clear, OP=0x6, IDX=0xA](#21417-bf_vs_op_clear-op0x6-idx0xa)
|
||||
- [2.14.17. bf_vs_op_migrate, OP=0x6, IDX=0xB](#21417-bf_vs_op_migrate-op0x6-idx0xb)
|
||||
- [2.14.17. bf_vs_op_set_active, OP=0x6, IDX=0xC](#21417-bf_vs_op_set_active-op0x6-idx0xc)
|
||||
- [2.14.17. bf_vs_op_advance_ip_and_set_active, OP=0x6, IDX=0xD](#21417-bf_vs_op_advance_ip_and_set_active-op0x6-idx0xd)
|
||||
- [2.15.3. bf_vs_op_tlb_flush, OP=0x6, IDX=0xE](#2153-bf_vs_op_tlb_flush-op0x6-idx0xe)
|
||||
- [2.15. Intrinsic Syscalls](#215-intrinsic-syscalls)
|
||||
- [2.15.1. bf_intrinsic_op_rdmsr, OP=0x7, IDX=0x0](#2151-bf_intrinsic_op_rdmsr-op0x7-idx0x0)
|
||||
- [2.15.2. bf_intrinsic_op_wrmsr, OP=0x7, IDX=0x1](#2152-bf_intrinsic_op_wrmsr-op0x7-idx0x1)
|
||||
- [2.15.3. bf_intrinsic_op_invlpga, OP=0x7, IDX=0x2](#2153-bf_intrinsic_op_invlpga-op0x7-idx0x2)
|
||||
- [2.15.4. bf_intrinsic_op_invept, OP=0x7, IDX=0x3](#2154-bf_intrinsic_op_invept-op0x7-idx0x3)
|
||||
- [2.15.5. bf_intrinsic_op_invvpid, OP=0x7, IDX=0x4](#2155-bf_intrinsic_op_invvpid-op0x7-idx0x4)
|
||||
- [2.16. Mem Syscalls](#216-mem-syscalls)
|
||||
- [2.16.1. bf_mem_op_alloc_page, OP=0x7, IDX=0x0](#2161-bf_mem_op_alloc_page-op0x7-idx0x0)
|
||||
- [2.16.2. bf_mem_op_free_page, OP=0x7, IDX=0x1](#2162-bf_mem_op_free_page-op0x7-idx0x1)
|
||||
- [2.16.3. bf_mem_op_alloc_huge, OP=0x7, IDX=0x2](#2163-bf_mem_op_alloc_huge-op0x7-idx0x2)
|
||||
- [2.16.4. bf_mem_op_free_huge, OP=0x7, IDX=0x3](#2164-bf_mem_op_free_huge-op0x7-idx0x3)
|
||||
- [2.16.5. bf_mem_op_alloc_heap, OP=0x7, IDX=0x4](#2165-bf_mem_op_alloc_heap-op0x7-idx0x4)
|
||||
- [2.16.1. bf_mem_op_alloc_page, OP=0x8, IDX=0x0](#2161-bf_mem_op_alloc_page-op0x8-idx0x0)
|
||||
- [2.16.2. bf_mem_op_free_page, OP=0x8, IDX=0x1](#2162-bf_mem_op_free_page-op0x8-idx0x1)
|
||||
- [2.16.3. bf_mem_op_alloc_huge, OP=0x8, IDX=0x2](#2163-bf_mem_op_alloc_huge-op0x8-idx0x2)
|
||||
- [2.16.4. bf_mem_op_free_huge, OP=0x8, IDX=0x3](#2164-bf_mem_op_free_huge-op0x8-idx0x3)
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
|
|
@ -182,7 +181,7 @@ Defines the signature of the VM exit callback handler
|
|||
|
||||
Defines the signature of the fast fail callback handler
|
||||
|
||||
**typedef, void(*bf_callback_handler_fail_t)(bf_status_t)**
|
||||
**typedef, void(*bf_callback_handler_fail_t)(uint64_t, uint64_t)**
|
||||
|
||||
## 1.5. ID Constants
|
||||
|
||||
|
|
@ -615,6 +614,15 @@ This syscall tells the microkernel that the extension would like to wait for a c
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000001 | Defines the index for bf_control_op_wait |
|
||||
|
||||
### 2.9.2. bf_control_op_again, OP=0x0, IDX=0x2
|
||||
|
||||
This syscall tells the microkernel that the extension would like to try again from a fast fail callback. This syscall is a blocking syscall that never returns and should be used to return from the fail_entry function.
|
||||
|
||||
**const, uint64_t: BF_CONTROL_OP_AGAIN_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000002 | Defines the index for bf_control_op_again |
|
||||
|
||||
## 2.10. Handle Syscalls
|
||||
|
||||
### 2.10.1. bf_handle_op_open_handle, OP=0x1, IDX=0x0
|
||||
|
|
@ -923,7 +931,7 @@ This syscall tells the microkernel to unmap a previously mapped virtual address
|
|||
|
||||
### 2.13.3. bf_vm_op_unmap_direct_broadcast, OP=0x4, IDX=0x4
|
||||
|
||||
This syscall tells the microkernel to unmap a previously mapped virtual address in the direct map. Unlike bf_vm_op_unmap_direct, this syscall performs a broadcast TLB flush which means it can be safely used on all direct mapped addresses. The downside of using this function is that it can be a lot slower than bf_vm_op_unmap_direct, especially on systems with a lot of cores.
|
||||
This syscall tells the microkernel to unmap a previously mapped virtual address in the direct map. Unlike bf_vm_op_unmap_direct, this syscall performs a broadcast TLB flush which means it can be safely used on all direct mapped addresses. The downside of using this function is that it can be a lot slower than bf_vm_op_unmap_direct, especially on systems with a lot of PPs.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
|
|
@ -939,9 +947,25 @@ This syscall tells the microkernel to unmap a previously mapped virtual address
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000004 | Defines the index for bf_vm_op_unmap_direct_broadcast |
|
||||
|
||||
### 2.15.3. bf_vm_op_tlb_flush, OP=0x4, IDX=0x5
|
||||
|
||||
Given the ID of a VM, invalidates the VM's TLB on the PP that this is executed on.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 15:0 | The ID of the VM to invalidate |
|
||||
| REG1 | 63:16 | REVI |
|
||||
|
||||
**const, uint64_t: BF_VM_OP_TLB_FLUSH_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000005 | Defines the index for bf_vm_op_tlb_flush |
|
||||
|
||||
## 2.14. Virtual Processor Syscalls
|
||||
|
||||
A Virtual Processor or VP virtually represents a logical core. Although the microkernel has an internal representation of a VP, it doesn't understand what a VP is outside of resource management, and it is up to the extension to define what a VM is and how it should operate.
|
||||
A Virtual Processor or VP virtually represents a PP. Although the microkernel has an internal representation of a VP, it doesn't understand what a VP is outside of resource management, and it is up to the extension to define what a VM is and how it should operate.
|
||||
|
||||
Once a VP is run, it is assigned to the VM it was run on, and cannot be run on any other VM for the remainder of it's lifetime. A VP is also assigned to a specific PP (physical processor). Unlike the assigned VM, the assigned PP can be changed by migrating the VP to another PP.
|
||||
|
||||
|
|
@ -1083,7 +1107,7 @@ Writes to a CPU register in the VS given a bf_reg_t and the value to write. Note
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000004 | Defines the index for bf_vs_op_write |
|
||||
|
||||
### 2.14.12. bf_vs_op_run, OP=0x5, IDX=0x5
|
||||
### 2.14.12. bf_vs_op_run, OP=0x6, IDX=0x5
|
||||
|
||||
TODO
|
||||
|
||||
|
|
@ -1103,7 +1127,7 @@ TODO
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000005 | Defines the index for bf_vs_op_run |
|
||||
|
||||
### 2.14.13. bf_vs_op_run_current, OP=0x5, IDX=0x6
|
||||
### 2.14.13. bf_vs_op_run_current, OP=0x6, IDX=0x6
|
||||
|
||||
bf_vs_op_run_current tells the microkernel to execute the currently active VS, VP and VM.
|
||||
|
||||
|
|
@ -1117,7 +1141,7 @@ bf_vs_op_run_current tells the microkernel to execute the currently active VS, V
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000006 | Defines the index for bf_vs_op_run_current |
|
||||
|
||||
### 2.14.14. bf_vs_op_advance_ip_and_run_impl, OP=0x5, IDX=0x7
|
||||
### 2.14.14. bf_vs_op_advance_ip_and_run_impl, OP=0x6, IDX=0x7
|
||||
|
||||
TODO
|
||||
|
||||
|
|
@ -1133,7 +1157,7 @@ TODO
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000007 | Defines the index for bf_vs_op_advance_ip_and_run_impl |
|
||||
|
||||
### 2.14.15. bf_vs_op_advance_ip_and_run_current, OP=0x5, IDX=0x8
|
||||
### 2.14.15. bf_vs_op_advance_ip_and_run_current, OP=0x6, IDX=0x8
|
||||
|
||||
TODO
|
||||
|
||||
|
|
@ -1147,7 +1171,7 @@ TODO
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000008 | Defines the index for bf_vs_op_advance_ip_and_run_current |
|
||||
|
||||
### 2.14.16. bf_vs_op_promote, OP=0x5, IDX=0x9
|
||||
### 2.14.16. bf_vs_op_promote, OP=0x6, IDX=0x9
|
||||
|
||||
bf_vs_op_promote tells the microkernel to promote the requested VS. bf_vs_op_promote will stop the hypervisor on the physical processor and replace its state with the state in the given VS. Note that this syscall only returns on error.
|
||||
|
||||
|
|
@ -1163,7 +1187,7 @@ bf_vs_op_promote tells the microkernel to promote the requested VS. bf_vs_op_pro
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000009 | Defines the index for bf_vs_op_promote |
|
||||
|
||||
### 2.14.17. bf_vs_op_clear, OP=0x5, IDX=0xA
|
||||
### 2.14.17. bf_vs_op_clear, OP=0x6, IDX=0xA
|
||||
|
||||
bf_vs_op_clear tells the microkernel to clear the VS's hardware cache, if one exists. How this is used depends entirely on the hardware and is associated with AMD's VMCB Clean Bits, and Intel's VMClear instruction. See the associated documentation for more details. On AMD, this ABI clears the entire VMCB. For more fine grained control, use the write ABIs to manually modify the VMCB.
|
||||
|
||||
|
|
@ -1179,7 +1203,7 @@ bf_vs_op_clear tells the microkernel to clear the VS's hardware cache, if one ex
|
|||
| :---- | :---------- |
|
||||
| 0x000000000000000A | Defines the index for bf_vs_op_clear |
|
||||
|
||||
### 2.14.17. bf_vs_op_migrate, OP=0x5, IDX=0xB
|
||||
### 2.14.17. bf_vs_op_migrate, OP=0x6, IDX=0xB
|
||||
|
||||
TODO
|
||||
|
||||
|
|
@ -1197,7 +1221,7 @@ TODO
|
|||
| :---- | :---------- |
|
||||
| 0x000000000000000B | Defines the index for bf_vs_op_migrate |
|
||||
|
||||
### 2.14.17. bf_vs_op_set_active, OP=0x5, IDX=0xC
|
||||
### 2.14.17. bf_vs_op_set_active, OP=0x6, IDX=0xC
|
||||
|
||||
Sets the active VM, VP and VS to the provided VM, VP and VS.
|
||||
|
||||
|
|
@ -1217,7 +1241,7 @@ Sets the active VM, VP and VS to the provided VM, VP and VS.
|
|||
| :---- | :---------- |
|
||||
| 0x000000000000000C | Defines the index for bf_vs_op_set_active |
|
||||
|
||||
### 2.14.17. bf_vs_op_advance_ip_and_set_active, OP=0x5, IDX=0xD
|
||||
### 2.14.17. bf_vs_op_advance_ip_and_set_active, OP=0x6, IDX=0xD
|
||||
|
||||
Advances the IP of the current VS and then sets the active VM, VP and VS to the provided VM, VP and VS.
|
||||
|
||||
|
|
@ -1237,6 +1261,23 @@ Advances the IP of the current VS and then sets the active VM, VP and VS to the
|
|||
| :---- | :---------- |
|
||||
| 0x000000000000000D | Defines the index for bf_vs_op_advance_ip_and_set_active |
|
||||
|
||||
### 2.15.3. bf_vs_op_tlb_flush, OP=0x6, IDX=0xE
|
||||
|
||||
Given the ID of a VS, invalidates a TLB entry for a given GLA on the PP that this is executed on.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 15:0 | The ID of the VS to invalidate |
|
||||
| REG1 | 63:16 | REVI |
|
||||
| REG2 | 63:0 | The GLA to invalidate |
|
||||
|
||||
**const, uint64_t: BF_VS_OP_TLB_FLUSH_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x000000000000000E | Defines the index for bf_vs_op_tlb_flush |
|
||||
|
||||
## 2.15. Intrinsic Syscalls
|
||||
|
||||
### 2.15.1. bf_intrinsic_op_rdmsr, OP=0x7, IDX=0x0
|
||||
|
|
@ -1277,56 +1318,6 @@ Writes to an MSR directly from the CPU given the address of the MSR to write and
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000001 | Defines the index for bf_intrinsic_op_wrmsr |
|
||||
|
||||
### 2.15.3. bf_intrinsic_op_invlpga, OP=0x7, IDX=0x2
|
||||
|
||||
Invalidates the TLB mapping for a given virtual page and a given ASID. Note that this is specific to AMD only.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 63:0 | The address to invalidate |
|
||||
| REG2 | 63:0 | The ASID to invalidate |
|
||||
|
||||
**const, uint64_t: BF_INTRINSIC_OP_INVLPGA_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000002 | Defines the index for bf_intrinsic_op_invlpga |
|
||||
|
||||
### 2.15.4. bf_intrinsic_op_invept, OP=0x7, IDX=0x3
|
||||
|
||||
Invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches that were derived from extended page tables (EPT). Note that this is specific to Intel only.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 63:0 | The EPTP to invalidate |
|
||||
| REG2 | 63:0 | The INVEPT type (see the Intel SDM for details) |
|
||||
|
||||
**const, uint64_t: BF_INTRINSIC_OP_INVEPT_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000003 | Defines the index for bf_intrinsic_op_invept |
|
||||
|
||||
### 2.15.5. bf_intrinsic_op_invvpid, OP=0x7, IDX=0x4
|
||||
|
||||
Invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on virtual-processor identifier (VPID). Note that this is specific to Intel only.
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 63:0 | The address to invalidate |
|
||||
| REG2 | 15:0 | The VPID to invalidate |
|
||||
| REG2 | 63:16 | REVI |
|
||||
| REG3 | 63:0 | The INVVPID type (see the Intel SDM for details) |
|
||||
|
||||
**const, uint64_t: BF_INTRINSIC_OP_INVVPID_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000004 | Defines the index for bf_intrinsic_op_invvpid |
|
||||
|
||||
## 2.16. Mem Syscalls
|
||||
|
||||
Each extension has access to several different memory pools:
|
||||
|
|
@ -1347,7 +1338,7 @@ Thread-Local Storage (TLS) memory (typically allocated using `thread_local`) pro
|
|||
|
||||
The direct map provides an extension with a means to access any physical address by accessing the direct map region of the virtual address space (depends on the hypervisor's configuration). By default, on Intel/AMD with 4-level paging, this region starts at 0x0000600000000000, but it can be changed using CMake. An extension can access any physical address by simply adding 0x0000600000000000 to the physical address and dereferencing the resulting value. When a VM is destroyed, all physical memory maps associated with that VM will be removed. The direct map is also where page and huge page allocations are mapped, providing an extension with a simple means for performing a virtual address to physical address (and vice versa) translations.
|
||||
|
||||
### 2.16.1. bf_mem_op_alloc_page, OP=0x7, IDX=0x0
|
||||
### 2.16.1. bf_mem_op_alloc_page, OP=0x8, IDX=0x0
|
||||
|
||||
bf_mem_op_alloc_page allocates a page, and maps this page into the direct map of the VM.
|
||||
|
||||
|
|
@ -1367,7 +1358,7 @@ bf_mem_op_alloc_page allocates a page, and maps this page into the direct map of
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000000 | Defines the index for bf_mem_op_alloc_page |
|
||||
|
||||
### 2.16.2. bf_mem_op_free_page, OP=0x7, IDX=0x1
|
||||
### 2.16.2. bf_mem_op_free_page, OP=0x8, IDX=0x1
|
||||
|
||||
Frees a page previously allocated by bf_mem_op_alloc_page. This operation is optional and not all microkernels may implement it.
|
||||
|
||||
|
|
@ -1387,7 +1378,7 @@ Frees a page previously allocated by bf_mem_op_alloc_page. This operation is opt
|
|||
| 0x0000000000000001 | Defines the index for bf_mem_op_free_page |
|
||||
|
||||
|
||||
### 2.16.3. bf_mem_op_alloc_huge, OP=0x7, IDX=0x2
|
||||
### 2.16.3. bf_mem_op_alloc_huge, OP=0x8, IDX=0x2
|
||||
|
||||
bf_mem_op_alloc_huge allocates a physically contiguous block of memory. When allocating a page, the extension should keep in mind the following:
|
||||
- The total memory available to allocate from this pool is extremely limited. This should only be used when absolutely needed, and extensions should not expect more than 1 MB (might be less) of total memory available.
|
||||
|
|
@ -1410,7 +1401,7 @@ bf_mem_op_alloc_huge allocates a physically contiguous block of memory. When all
|
|||
| :---- | :---------- |
|
||||
| 0x0000000000000002 | Defines the index for bf_mem_op_alloc_huge |
|
||||
|
||||
### 2.16.4. bf_mem_op_free_huge, OP=0x7, IDX=0x3
|
||||
### 2.16.4. bf_mem_op_free_huge, OP=0x8, IDX=0x3
|
||||
|
||||
Frees memory previously allocated by bf_mem_op_alloc_huge. This operation is optional and not all microkernels may implement it.
|
||||
|
||||
|
|
@ -1428,28 +1419,3 @@ Frees memory previously allocated by bf_mem_op_alloc_huge. This operation is opt
|
|||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000003 | Defines the index for bf_mem_op_free_huge |
|
||||
|
||||
### 2.16.5. bf_mem_op_alloc_heap, OP=0x7, IDX=0x4
|
||||
|
||||
bf_mem_op_alloc_heap allocates heap memory. When allocating heap memory, the extension should keep in mind the following:
|
||||
- This ABI is designed to work similar to sbrk() to support malloc/free implementations common with existing open source libraries.
|
||||
- Calling this ABI with with a size of 0 will return the current heap location.
|
||||
- Calling this ABI with a size (in bytes) will result in return the previous heap location. The current heap location will be set to the previous location, plus the provide size, rounded to the nearest page size.
|
||||
- The heap is not mapped into the direct map, so virtual to physical (and vice versa) translations are not possible.
|
||||
- There is no ability to free heap memory
|
||||
|
||||
**Input:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | Set to the result of bf_handle_op_open_handle |
|
||||
| REG1 | 63:0 | The number of bytes to increase the heap by |
|
||||
|
||||
**Output:**
|
||||
| Register Name | Bits | Description |
|
||||
| :------------ | :--- | :---------- |
|
||||
| REG0 | 63:0 | The virtual address of the previous heap location |
|
||||
|
||||
**const, uint64_t: BF_MEM_OP_ALLOC_HEAP_IDX_VAL**
|
||||
| Value | Description |
|
||||
| :---- | :---------- |
|
||||
| 0x0000000000000004 | Defines the index for bf_mem_op_alloc_heap |
|
||||
|
|
|
|||
|
|
@ -50,8 +50,13 @@ namespace example
|
|||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
|
|
@ -63,11 +68,11 @@ namespace example
|
|||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t const &vp_pool,
|
||||
vs_pool_t const &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &fail_reason) noexcept -> bsl::errc_type
|
||||
bsl::safe_u64 const &errc,
|
||||
bsl::safe_u64 const &addr) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::expects(vsid.is_valid_and_checked());
|
||||
bsl::expects(fail_reason.is_valid_and_checked());
|
||||
bsl::expects(errc.is_valid_and_checked());
|
||||
bsl::expects(addr.is_valid_and_checked());
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(sys);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
|
|
@ -50,8 +51,13 @@ namespace example
|
|||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
|
|
@ -63,11 +69,11 @@ namespace example
|
|||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t const &vp_pool,
|
||||
vs_pool_t const &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &fail_reason) noexcept -> bsl::errc_type
|
||||
bsl::safe_u64 const &errc,
|
||||
bsl::safe_u64 const &addr) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::expects(vsid.is_valid_and_checked());
|
||||
bsl::expects(fail_reason.is_valid_and_checked());
|
||||
bsl::expects(errc.is_valid_and_checked());
|
||||
bsl::expects(addr.is_valid_and_checked());
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
|
@ -91,6 +97,7 @@ namespace example
|
|||
/// fault system works properly during testing.
|
||||
///
|
||||
|
||||
bsl::alert() << "this extension does not support handling fast fail events\n";
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,12 +142,16 @@ namespace example
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
|
|
@ -163,8 +167,8 @@ namespace example
|
|||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(fail_reason))};
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
|
|
|
|||
|
|
@ -68,11 +68,16 @@ namespace example
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void fail_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const fail_reason) noexcept;
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept;
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ namespace example
|
|||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::uint64 const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,57 +64,7 @@ target_include_directories(kernel PRIVATE
|
|||
# Headers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/call_ext.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/get_current_tls.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/promote.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/return_to_mk.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/return_to_vmexit_loop.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/serial_write_c.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/serial_write_hex.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/vmexit_loop_entry.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_ehdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_phdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_shdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/debug_ring_write.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_esr_page_fault.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_callback_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_control_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_debug_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_handle_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_mem_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_vm_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_vp_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_vs_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/ext_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/ext_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fast_fail.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/huge_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/mk_main_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/root_page_table_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/serial_write.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vm_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vm_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vmexit_loop.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vp_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vp_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vs_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/bsl/cstdio.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/bsl/details/print_thread_id.hpp
|
||||
)
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD" OR HYPERVISOR_TARGET_ARCH STREQUAL "GenuineIntel")
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/general_purpose_regs_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/tls_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/vmexit_log_pp_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/vmexit_log_record_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/dispatch_esr.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/dispatch_syscall_intrinsic_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/vmexit_log_t.hpp
|
||||
)
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD")
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/amd/vmcb_t.hpp
|
||||
|
|
@ -134,57 +84,103 @@ if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD" OR HYPERVISOR_TARGET_ARCH STRE
|
|||
${CMAKE_CURRENT_LIST_DIR}/src/x64/intel/vs_t.hpp
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/general_purpose_regs_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l0t_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l0te_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l1t_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l1te_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l2t_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l2te_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l3t_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/l3te_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/tls_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/vmcb_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/vmexit_log_pp_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/arm/aarch64/vmexit_log_record_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/dispatch_esr.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/dispatch_syscall_intrinsic_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/intrinsic_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/root_page_table_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/vmexit_log_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/arm/aarch64/vs_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/general_purpose_regs_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/l0e_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/l1e_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/l2e_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/l3e_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/missing_registers_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/tls_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/vmexit_log_pp_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/x64/vmexit_log_record_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/dispatch_esr.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/dispatch_syscall_bf_intrinsic_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/x64/vmexit_log_t.hpp
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/allocated_status_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/alloc_huge_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/alloc_page_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_ehdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_phdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/bfelf/elf64_shdr_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/call_ext.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/errc_types.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/ext_tcb_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/get_current_tls.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/map_page_flags.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/page_4k_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/page_aligned_bytes_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/pause.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/promote.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/return_to_mk.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/return_to_vmexit_loop.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/running_status_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/serial_write_c.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/serial_write_hex.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/vmexit_loop_entry.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/bsl/cstdio.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/bsl/cstdlib.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/bsl/details/print_thread_id.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/debug_ring_write.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_callback_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_control_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_debug_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_handle_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_mem_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_vm_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_vp_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_bf_vs_op.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall_helpers.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/dispatch_syscall.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/ext_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/ext_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/huge_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lock_guard_helpers.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lock_guard_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/mk_main_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/page_pool_helpers.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/page_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/root_page_table_helpers.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/root_page_table_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/serial_write.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/spinlock_helpers.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/spinlock_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vmexit_loop.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vm_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vm_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vp_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vp_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/vs_pool_t.hpp
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_allocated_status_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_alloc_huge_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_alloc_page_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_entries_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_entry_status_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_map_page_flags.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_page_1g_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_page_2m_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_page_4k_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_page_pool_node_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_page_table_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_running_status_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/include/basic_tlb_flush_type_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/src/basic_lock_guard_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/src/basic_page_pool_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/src/basic_root_page_table_t.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../lib/src/basic_spinlock_t.hpp
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sources
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
hypervisor_target_source(kernel src/main.cpp ${HEADERS})
|
||||
hypervisor_target_source(kernel src/msg_halt.cpp ${HEADERS})
|
||||
hypervisor_target_source(kernel src/msg_stack_chk_fail.cpp ${HEADERS})
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD" OR HYPERVISOR_TARGET_ARCH STREQUAL "GenuineIntel")
|
||||
hypervisor_target_source(kernel src/x64/__stack_chk_fail.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/call_ext.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/dispatch_esr_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/dispatch_syscall_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/fast_fail_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/get_current_tls.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/mk_main_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/pause.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/return_to_current_fast_fail.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/return_to_mk.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/return_to_vmexit_loop.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/serial_write_c.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/serial_write_hex.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/set_esr.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/vmexit_loop_entry.S ${HEADERS})
|
||||
|
||||
if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD")
|
||||
hypervisor_target_source(kernel src/x64/amd/intrinsic_t.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/amd/promote.S ${HEADERS})
|
||||
|
|
@ -194,26 +190,23 @@ if(HYPERVISOR_TARGET_ARCH STREQUAL "AuthenticAMD" OR HYPERVISOR_TARGET_ARCH STRE
|
|||
hypervisor_target_source(kernel src/x64/intel/intrinsic_t.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/intel/promote.S ${HEADERS})
|
||||
endif()
|
||||
|
||||
hypervisor_target_source(kernel src/x64/__stack_chk_fail.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/call_ext.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/dispatch_esr_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/dispatch_syscall_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/get_current_tls.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/mk_main_entry.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/pause.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/return_to_mk.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/serial_write_c.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/serial_write_hex.S ${HEADERS})
|
||||
hypervisor_target_source(kernel src/x64/set_esr.S ${HEADERS})
|
||||
endif()
|
||||
|
||||
# if(HYPERVISOR_TARGET_ARCH STREQUAL "aarch64")
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/__stack_chk_fail.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/call_ext.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/dispatch_esr_entry.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/dispatch_syscall_entry.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/fast_fail_entry.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/get_current_tls.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/intrinsic_t.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/mk_main_entry.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/pause.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/promote.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/return_to_current_fast_fail.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/return_to_mk.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/return_to_vmexit_loop.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/serial_write_c.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/serial_write_hex.S ${HEADERS})
|
||||
# hypervisor_target_source(kernel src/arm/aarch64/vmexit_loop_entry.S ${HEADERS})
|
||||
# endif()
|
||||
hypervisor_target_source(kernel src/main.cpp ${HEADERS})
|
||||
hypervisor_target_source(kernel src/msg_halt.cpp ${HEADERS})
|
||||
hypervisor_target_source(kernel src/msg_stack_chk_fail.cpp ${HEADERS})
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Libraries
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace mk
|
|||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ip the instruction pointer to call into
|
||||
/// @param sp the stack pointer to use when calling the ext
|
||||
/// @param sp the stack pointer to use
|
||||
/// @param arg0 the first argument to pass the extension
|
||||
/// @param arg1 the second argument to pass the extension
|
||||
/// @return returns the exit status from the extension
|
||||
|
|
|
|||
|
|
@ -22,20 +22,17 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef GS_T_HPP
|
||||
#define GS_T_HPP
|
||||
#ifndef ERRC_TYPES_HPP
|
||||
#define ERRC_TYPES_HPP
|
||||
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace mk
|
||||
{
|
||||
/// @class integration::gs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Global Storage (GS).
|
||||
///
|
||||
struct gs_t final
|
||||
{};
|
||||
/// @brief Returned when a VMExit is a success
|
||||
// NOLINTNEXTLINE(bsl-name-case)
|
||||
constexpr bsl::errc_type vmexit_success{1001};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -70,144 +70,144 @@ namespace mk
|
|||
/// --------------------------------------------------------------------
|
||||
|
||||
/// @brief stores the value of rbx for the microkernel (0x000)
|
||||
bsl::uintmx mk_rbx;
|
||||
bsl::uint64 mk_rbx;
|
||||
/// @brief stores the value of rbp for the microkernel (0x008)
|
||||
bsl::uintmx mk_rbp;
|
||||
bsl::uint64 mk_rbp;
|
||||
/// @brief stores the value of r12 for the microkernel (0x010)
|
||||
bsl::uintmx mk_r12;
|
||||
bsl::uint64 mk_r12;
|
||||
/// @brief stores the value of r13 for the microkernel (0x018)
|
||||
bsl::uintmx mk_r13;
|
||||
bsl::uint64 mk_r13;
|
||||
/// @brief stores the value of r14 for the microkernel (0x020)
|
||||
bsl::uintmx mk_r14;
|
||||
bsl::uint64 mk_r14;
|
||||
/// @brief stores the value of r15 for the microkernel (0x028)
|
||||
bsl::uintmx mk_r15;
|
||||
bsl::uint64 mk_r15;
|
||||
|
||||
/// --------------------------------------------------------------------
|
||||
/// Extension State
|
||||
/// --------------------------------------------------------------------
|
||||
|
||||
/// @brief RAX, stores the extension's syscall (0x030)
|
||||
bsl::uintmx ext_syscall;
|
||||
bsl::uint64 ext_syscall;
|
||||
/// @brief RBX, reserved (0x038)
|
||||
bsl::uintmx reserved_reg1;
|
||||
bsl::uint64 reserved_rbx;
|
||||
/// @brief RCX, reserved (0x040)
|
||||
bsl::uintmx reserved_reg2;
|
||||
bsl::uint64 reserved_rcx;
|
||||
/// @brief RDX, stores the value of REG2 for the extension (0x048)
|
||||
bsl::uintmx ext_reg2;
|
||||
bsl::uint64 ext_reg2;
|
||||
/// @brief RBP, reserved (0x050)
|
||||
bsl::uintmx reserved_reg3;
|
||||
bsl::uint64 reserved_rbp;
|
||||
/// @brief RSI, stores the value of REG1 for the extension (0x058)
|
||||
bsl::uintmx ext_reg1;
|
||||
bsl::uint64 ext_reg1;
|
||||
/// @brief RDI, stores the value of REG0 for the extension (0x060)
|
||||
bsl::uintmx ext_reg0;
|
||||
bsl::uint64 ext_reg0;
|
||||
/// @brief R8, stores the value of REG4 for the extension (0x068)
|
||||
bsl::uintmx ext_reg4;
|
||||
bsl::uint64 ext_reg4;
|
||||
/// @brief R9, stores the value of REG5 for the extension (0x070)
|
||||
bsl::uintmx ext_reg5;
|
||||
bsl::uint64 ext_reg5;
|
||||
/// @brief R10, stores the value of REG3 for the extension (0x078)
|
||||
bsl::uintmx ext_reg3;
|
||||
bsl::uint64 ext_reg3;
|
||||
/// @brief R11, reserved (0x080)
|
||||
bsl::uintmx reserved_reg4;
|
||||
bsl::uint64 reserved_r11;
|
||||
/// @brief R12, reserved (0x088)
|
||||
bsl::uintmx reserved_reg5;
|
||||
bsl::uint64 reserved_r12;
|
||||
/// @brief R13, reserved (0x090)
|
||||
bsl::uintmx reserved_reg6;
|
||||
bsl::uint64 reserved_r13;
|
||||
/// @brief R14, reserved (0x098)
|
||||
bsl::uintmx reserved_reg7;
|
||||
bsl::uint64 reserved_r14;
|
||||
/// @brief R15, reserved (0x0A0)
|
||||
bsl::uintmx reserved_reg8;
|
||||
bsl::uint64 reserved_r15;
|
||||
/// @brief RSP, reserved (0x0A8)
|
||||
bsl::uintmx reserved_reg9;
|
||||
bsl::uint64 ext_sp;
|
||||
|
||||
/// --------------------------------------------------------------------
|
||||
/// ESR State
|
||||
/// --------------------------------------------------------------------
|
||||
|
||||
/// @brief stores the value of rax for the ESR (0x0B0)
|
||||
bsl::uintmx esr_rax;
|
||||
bsl::uint64 esr_rax;
|
||||
/// @brief stores the value of rbx for the ESR (0x0B8)
|
||||
bsl::uintmx esr_rbx;
|
||||
bsl::uint64 esr_rbx;
|
||||
/// @brief stores the value of rcx for the ESR (0x0C0)
|
||||
bsl::uintmx esr_rcx;
|
||||
bsl::uint64 esr_rcx;
|
||||
/// @brief stores the value of rdx for the ESR (0x0C8)
|
||||
bsl::uintmx esr_rdx;
|
||||
bsl::uint64 esr_rdx;
|
||||
/// @brief stores the value of rbp for the ESR (0x0D0)
|
||||
bsl::uintmx esr_rbp;
|
||||
bsl::uint64 esr_rbp;
|
||||
/// @brief stores the value of rsi for the ESR (0x0D8)
|
||||
bsl::uintmx esr_rsi;
|
||||
bsl::uint64 esr_rsi;
|
||||
/// @brief stores the value of rdi for the ESR (0x0E0)
|
||||
bsl::uintmx esr_rdi;
|
||||
bsl::uint64 esr_rdi;
|
||||
/// @brief stores the value of r8 for the ESR (0x0E8)
|
||||
bsl::uintmx esr_r8;
|
||||
bsl::uint64 esr_r8;
|
||||
/// @brief stores the value of r9 for the ESR (0x0F0)
|
||||
bsl::uintmx esr_r9;
|
||||
bsl::uint64 esr_r9;
|
||||
/// @brief stores the value of r10 for the ESR (0x0F8)
|
||||
bsl::uintmx esr_r10;
|
||||
bsl::uint64 esr_r10;
|
||||
/// @brief stores the value of r11 for the ESR (0x100)
|
||||
bsl::uintmx esr_r11;
|
||||
bsl::uint64 esr_r11;
|
||||
/// @brief stores the value of r12 for the ESR (0x108)
|
||||
bsl::uintmx esr_r12;
|
||||
bsl::uint64 esr_r12;
|
||||
/// @brief stores the value of r13 for the ESR (0x110)
|
||||
bsl::uintmx esr_r13;
|
||||
bsl::uint64 esr_r13;
|
||||
/// @brief stores the value of r14 for the ESR (0x118)
|
||||
bsl::uintmx esr_r14;
|
||||
bsl::uint64 esr_r14;
|
||||
/// @brief stores the value of r15 for the ESR (0x120)
|
||||
bsl::uintmx esr_r15;
|
||||
bsl::uint64 esr_r15;
|
||||
/// @brief stores the value of rip for the ESR (0x128)
|
||||
bsl::uintmx esr_ip;
|
||||
bsl::uint64 esr_ip;
|
||||
/// @brief stores the value of rsp for the ESR (0x130)
|
||||
bsl::uintmx esr_sp;
|
||||
bsl::uint64 esr_sp;
|
||||
|
||||
/// @brief stores the value of the ESR vector (0x138)
|
||||
bsl::uintmx esr_vector;
|
||||
bsl::uint64 esr_vector;
|
||||
/// @brief stores the value of the ESR error code (0x140)
|
||||
bsl::uintmx esr_error_code;
|
||||
bsl::uint64 esr_error_code;
|
||||
|
||||
/// @brief stores the value of cr0 for the ESR (0x148)
|
||||
bsl::uintmx esr_cr0;
|
||||
bsl::uint64 esr_cr0;
|
||||
/// @brief stores the value of cr2 for the ESR (0x150)
|
||||
bsl::uintmx esr_pf_addr;
|
||||
bsl::uint64 esr_pf_addr;
|
||||
/// @brief stores the value of cr3 for the ESR (0x158)
|
||||
bsl::uintmx esr_cr3;
|
||||
bsl::uint64 esr_cr3;
|
||||
/// @brief stores the value of cr4 for the ESR (0x160)
|
||||
bsl::uintmx esr_cr4;
|
||||
bsl::uint64 esr_cr4;
|
||||
|
||||
/// @brief stores the value of cs for the ESR (0x168)
|
||||
bsl::uintmx esr_cs;
|
||||
bsl::uint64 esr_cs;
|
||||
/// @brief stores the value of ss for the ESR (0x170)
|
||||
bsl::uintmx esr_ss;
|
||||
bsl::uint64 esr_ss;
|
||||
|
||||
/// @brief stores the value of ss for the ESR (0x178)
|
||||
bsl::uintmx esr_rflags;
|
||||
bsl::uint64 esr_rflags;
|
||||
|
||||
/// --------------------------------------------------------------------
|
||||
/// Fast Fail Information
|
||||
/// Fail Handler States
|
||||
/// --------------------------------------------------------------------
|
||||
|
||||
/// @brief stores the current fast fail address (0x180)
|
||||
bsl::uintmx current_fast_fail_ip;
|
||||
/// @brief stores the current fast fail stack (0x188)
|
||||
bsl::uintmx current_fast_fail_sp;
|
||||
/// @brief stores the value of rsp for the MK (0x180)
|
||||
bsl::uint64 mk_sp;
|
||||
/// @brief stores the value of rsp for the MK when calling fail (0x188)
|
||||
bsl::uint64 mk_handling_esr;
|
||||
|
||||
/// @brief stores the mk_main fast fail address (0x190)
|
||||
bsl::uintmx mk_main_fast_fail_ip;
|
||||
/// @brief stores the mk_main fast fail stack (0x198)
|
||||
bsl::uintmx mk_main_fast_fail_sp;
|
||||
/// @brief stores the value of rsp for the MK when failing (0x190)
|
||||
bsl::uint64 mk_fail_sp;
|
||||
/// @brief stores the fail sp used by extensions for callbacks (0x198)
|
||||
bsl::uint64 ext_fail_sp;
|
||||
|
||||
/// @brief stores the call_ext fast fail address (0x1A0)
|
||||
bsl::uintmx call_ext_fast_fail_ip;
|
||||
/// @brief stores the call_ext fast fail stack (0x1A8)
|
||||
bsl::uintmx call_ext_fast_fail_sp;
|
||||
/// @brief reserved (0x1A0)
|
||||
bsl::uint64 reserved_tmp4;
|
||||
/// @brief reserved (0x1A8)
|
||||
bsl::uint64 reserved_tmp5;
|
||||
|
||||
/// @brief stores the dispatch_syscall fast fail address (0x1B0)
|
||||
bsl::uintmx dispatch_syscall_fast_fail_ip;
|
||||
/// @brief stores the dispatch_syscall fast fail stack (0x1B8)
|
||||
bsl::uintmx dispatch_syscall_fast_fail_sp;
|
||||
/// @brief reserved (0x1B0)
|
||||
bsl::uint64 reserved_tmp6;
|
||||
/// @brief reserved (0x1B8)
|
||||
bsl::uint64 reserved_tmp7;
|
||||
|
||||
/// @brief stores the vmexit loop address (0x1C0)
|
||||
bsl::uintmx vmexit_loop_ip;
|
||||
/// @brief stores the vmexit loop stack (0x1C8)
|
||||
bsl::uintmx vmexit_loop_sp;
|
||||
/// @brief reserved (0x1C0)
|
||||
bsl::uint64 reserved_tmp8;
|
||||
/// @brief reserved (0x1C8)
|
||||
bsl::uint64 reserved_tmp9;
|
||||
|
||||
/// @brief reserve the rest of the TLS block for later use.
|
||||
bsl::array<bsl::uint8, TLS_T_RESERVED1_SIZE.get()> reserved1;
|
||||
|
|
@ -250,20 +250,20 @@ namespace mk
|
|||
bsl::uint16 active_vsid;
|
||||
|
||||
/// @brief stores the sp used by extensions for callbacks (0x240)
|
||||
bsl::uintmx sp;
|
||||
bsl::uint64 sp;
|
||||
/// @brief stores the tps used by extensions for callbacks (0x248)
|
||||
bsl::uintmx tp;
|
||||
bsl::uint64 tp;
|
||||
|
||||
/// @brief used to store a return address for unsafe ops (0x250)
|
||||
bsl::uintmx unsafe_rip;
|
||||
bsl::uint64 unsafe_rip;
|
||||
|
||||
/// @brief used to signal NMIs are not safe (0x258)
|
||||
bsl::uintmx nmi_lock;
|
||||
bsl::uint64 nmi_lock;
|
||||
/// @brief used to singal an NMI has fired (0x260)
|
||||
bsl::uintmx nmi_pending;
|
||||
bsl::uint64 nmi_pending;
|
||||
|
||||
/// @brief stores whether or not the first launch succeeded (0x268)
|
||||
bsl::uintmx first_launch_succeeded;
|
||||
bsl::uint64 first_launch_succeeded;
|
||||
|
||||
/// @brief stores the currently active root page table (0x270)
|
||||
void *active_rpt;
|
||||
|
|
|
|||
|
|
@ -22,24 +22,31 @@
|
|||
include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/function/hypervisor_add_integration.cmake)
|
||||
|
||||
list(APPEND HEADERS
|
||||
${CMAKE_CURRENT_LIST_DIR}/integration_utils.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/support/integration_utils.hpp
|
||||
)
|
||||
|
||||
# hypervisor_add_integration(bf_callback_op_register_bootstrap HEADERS)
|
||||
# hypervisor_add_integration(bf_callback_op_register_fail HEADERS)
|
||||
# hypervisor_add_integration(bf_callback_op_register_vmexit HEADERS)
|
||||
# hypervisor_add_integration(bf_handle_op_close_handle HEADERS)
|
||||
# hypervisor_add_integration(bf_handle_op_open_handle HEADERS)
|
||||
# hypervisor_add_integration(bf_vm_op_create_vm HEADERS)
|
||||
# hypervisor_add_integration(bf_vm_op_destroy_vm HEADERS)
|
||||
# hypervisor_add_integration(bf_vp_op_create_vp HEADERS)
|
||||
# hypervisor_add_integration(bf_vp_op_destroy_vp HEADERS)
|
||||
# hypervisor_add_integration(bf_vs_op_create_vs HEADERS)
|
||||
# hypervisor_add_integration(bf_vs_op_destroy_vs HEADERS)
|
||||
hypervisor_add_integration(bf_callback_op_register_bootstrap HEADERS)
|
||||
hypervisor_add_integration(bf_callback_op_register_fail HEADERS)
|
||||
hypervisor_add_integration(bf_callback_op_register_vmexit HEADERS)
|
||||
hypervisor_add_integration(bf_handle_op_close_handle HEADERS)
|
||||
hypervisor_add_integration(bf_handle_op_open_handle HEADERS)
|
||||
hypervisor_add_integration(bf_vm_op_create_vm HEADERS)
|
||||
hypervisor_add_integration(bf_vm_op_destroy_vm HEADERS)
|
||||
hypervisor_add_integration(bf_vm_op_map_direct HEADERS)
|
||||
hypervisor_add_integration(bf_vm_op_unmap_direct HEADERS)
|
||||
#hypervisor_add_integration(bf_vm_op_unmap_direct_broadcast HEADERS)
|
||||
hypervisor_add_integration(bf_vp_op_create_vp HEADERS)
|
||||
hypervisor_add_integration(bf_vp_op_destroy_vp HEADERS)
|
||||
hypervisor_add_integration(bf_vs_op_create_vs HEADERS)
|
||||
hypervisor_add_integration(bf_vs_op_destroy_vs HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_bootstrap_with_no_syscall HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_bootstrap_with_segfault HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_bootstrap_with_wait HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_bootstrap HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_fail_with_no_syscall HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_fail_with_segfault HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_fail_with_wait HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_fail HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_main_with_no_syscall HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_main_with_segfault HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_main HEADERS)
|
||||
|
|
@ -47,6 +54,8 @@ hypervisor_add_integration(fast_fail_exit_from_vmexit_with_no_syscall HEADERS)
|
|||
hypervisor_add_integration(fast_fail_exit_from_vmexit_with_segfault HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_vmexit_with_wait HEADERS)
|
||||
hypervisor_add_integration(fast_fail_exit_from_vmexit HEADERS)
|
||||
hypervisor_add_integration(fast_fail_recover_from_assert HEADERS)
|
||||
hypervisor_add_integration(fast_fail_recover_from_page_fault HEADERS)
|
||||
hypervisor_add_integration(fast_fail_wait_no_bootstrap HEADERS)
|
||||
hypervisor_add_integration(fast_fail_wait_no_fail HEADERS)
|
||||
hypervisor_add_integration(fast_fail_wait_no_vmexit HEADERS)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ hypervisor_add_integration_target(bf_handle_op_close_handle)
|
|||
hypervisor_add_integration_target(bf_handle_op_open_handle)
|
||||
hypervisor_add_integration_target(bf_vm_op_create_vm)
|
||||
hypervisor_add_integration_target(bf_vm_op_destroy_vm)
|
||||
hypervisor_add_integration_target(bf_vm_op_map_direct)
|
||||
hypervisor_add_integration_target(bf_vm_op_unmap_direct)
|
||||
hypervisor_add_integration_target(bf_vm_op_unmap_direct_broadcast)
|
||||
hypervisor_add_integration_target(bf_vp_op_create_vp)
|
||||
hypervisor_add_integration_target(bf_vp_op_destroy_vp)
|
||||
hypervisor_add_integration_target(bf_vs_op_create_vs)
|
||||
|
|
@ -36,6 +39,10 @@ hypervisor_add_integration_target(fast_fail_exit_from_bootstrap_with_no_syscall)
|
|||
hypervisor_add_integration_target(fast_fail_exit_from_bootstrap_with_segfault)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_bootstrap_with_wait)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_bootstrap)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_fail_with_no_syscall)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_fail_with_segfault)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_fail_with_wait)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_fail)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_main_with_no_syscall)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_main_with_segfault)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_main)
|
||||
|
|
@ -43,6 +50,9 @@ hypervisor_add_integration_target(fast_fail_exit_from_vmexit_with_no_syscall)
|
|||
hypervisor_add_integration_target(fast_fail_exit_from_vmexit_with_segfault)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_vmexit_with_wait)
|
||||
hypervisor_add_integration_target(fast_fail_exit_from_vmexit)
|
||||
hypervisor_add_integration_target(fast_fail_from_fail_handler)
|
||||
hypervisor_add_integration_target(fast_fail_recover_from_assert)
|
||||
hypervisor_add_integration_target(fast_fail_recover_from_page_fault)
|
||||
hypervisor_add_integration_target(fast_fail_wait_no_bootstrap)
|
||||
hypervisor_add_integration_target(fast_fail_wait_no_fail)
|
||||
hypervisor_add_integration_target(fast_fail_wait_no_vmexit)
|
||||
|
|
|
|||
|
|
@ -22,36 +22,216 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -61,25 +241,28 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::safe_umx hndl{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, nullptr);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
// register nullptr
|
||||
ret = bf_callback_op_register_bootstrap_impl(hndl.get(), nullptr);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// register twice
|
||||
ret = bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
return integration::early();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,36 +22,216 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -61,31 +241,28 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::safe_umx hndl{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, nullptr);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
// register nullptr
|
||||
ret = bf_callback_op_register_fail_impl(hndl.get(), nullptr);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// register twice
|
||||
ret = bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
/// TODO:
|
||||
/// - When multiple extensions are supported, an extra test needs
|
||||
/// to be added for when two extensions try to register for this
|
||||
/// callback as that is not allowed
|
||||
///
|
||||
|
||||
return integration::early();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,39 +22,216 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -64,31 +241,28 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::safe_umx hndl{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, nullptr);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
// register nullptr
|
||||
ret = bf_callback_op_register_vmexit_impl(hndl.get(), nullptr);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// register twice
|
||||
ret = bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
/// TODO:
|
||||
/// - When multiple extensions are supported, an extra test needs
|
||||
/// to be added for when two extensions try to register for this
|
||||
/// callback as that is not allowed
|
||||
///
|
||||
|
||||
return integration::early();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,20 +22,216 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -45,26 +241,27 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
syscall::bf_handle_t handle{};
|
||||
bsl::safe_umx hndl{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_close_handle(handle);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
// close without open
|
||||
ret = bf_handle_op_close_handle_impl(hndl.get());
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, handle);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// close twice
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_close_handle_impl(hndl.get());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_close_handle_impl(hndl.get());
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_handle_op_close_handle(handle);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
|
||||
ret = syscall::bf_handle_op_close_handle(handle);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
return integration::early();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,20 +22,216 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -45,24 +241,25 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
syscall::bf_handle_t handle{};
|
||||
bsl::safe_umx hndl{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
constexpr auto invalid_spec_id{0x42_u32};
|
||||
ret = syscall::bf_handle_op_open_handle(invalid_spec_id, handle);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
// open invalid version
|
||||
ret = bf_handle_op_open_handle_impl({}, hndl.data());
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, handle);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// open twice
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(version, handle);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
return integration::early();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,91 +22,211 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
bf_status_t const ret{bf_vm_op_create_vm_impl(hndl.get(), mut_id.data())};
|
||||
integration::require(ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
{
|
||||
for (bsl::safe_idx i{bsl::safe_idx::magic_1()}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
auto const vmid{g_mut_sys.bf_vm_op_create_vm()};
|
||||
integration::require(vmid.is_valid());
|
||||
}
|
||||
|
||||
auto const vmid{g_mut_sys.bf_vm_op_create_vm()};
|
||||
integration::require(vmid.is_invalid());
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
|
||||
bsl::errc_type ret{};
|
||||
bsl::safe_u16 vmid{};
|
||||
|
||||
/// NOTE:
|
||||
/// - The max number of VMs an extension can create is one less than
|
||||
/// the provided max because the microkernel creates the Root VM
|
||||
/// automatically for the extension as it cannot be deleted.
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
constexpr auto one{1_umx};
|
||||
auto const max_vms{HYPERVISOR_MAX_VMS - one};
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
// create with invalid handle
|
||||
ret = syscall::bf_vm_op_create_vm({}, vmid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
for (bsl::safe_idx i{}; i < max_vms; ++i) {
|
||||
ret = syscall::bf_vm_op_create_vm(g_handle, vmid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_vm_op_create_vm(g_handle, vmid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -116,25 +236,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,174 +22,243 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <arch_support.hpp>
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
mut_ret = bf_vm_op_destroy_vm_impl(hndl.get(), {});
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vmid
|
||||
{
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vm_op_destroy_vm_impl({}, BF_INVALID_ID.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VMS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_destroy_vm_impl({}, oor.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VMS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_destroy_vm_impl({}, nyc.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create all and and then make sure we can destroy them all
|
||||
{
|
||||
for (bsl::safe_idx i{bsl::safe_idx::magic_1()}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
auto const vmid{g_mut_sys.bf_vm_op_create_vm()};
|
||||
integration::require(vmid.is_valid());
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{bsl::safe_idx::magic_1()}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vm_op_destroy_vm(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
// do it again to make sure that after destroy, create still works
|
||||
{
|
||||
for (bsl::safe_idx i{bsl::safe_idx::magic_1()}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
auto const vmid{g_mut_sys.bf_vm_op_create_vm()};
|
||||
integration::require(vmid.is_valid());
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{bsl::safe_idx::magic_1()}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vm_op_destroy_vm(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// VMExit Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy assigned VM (turns VM into zombie)
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, syscall::bf_tls_vmid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy zombie
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, syscall::bf_tls_vmid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Done
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::safe_u16 vmid{};
|
||||
bsl::safe_u16 vpid{};
|
||||
bsl::safe_u16 vsid{};
|
||||
|
||||
/// NOTE:
|
||||
/// - The max number of VMs an extension can create is one less than
|
||||
/// the provided max because the microkernel creates the Root VM
|
||||
/// automatically for the extension as it cannot be deleted.
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Setup
|
||||
// ---------------------------------------------------------------------
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
ret = syscall::bf_vm_op_create_vm(g_handle, vmid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Bootstrap Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy with invalid handle
|
||||
ret = syscall::bf_vm_op_destroy_vm({}, vmid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy root VM
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, syscall::BF_ROOT_VMID);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with invalid ID
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, syscall::BF_INVALID_ID);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with ID that is greater than or equal to MAX_VMS
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, bsl::to_u16(HYPERVISOR_MAX_VMS));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with VM that has not been created
|
||||
constexpr auto invalid_vmid{2_u16};
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, invalid_vmid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy success
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, vmid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
|
||||
// destroy same VM twice (double free bug)
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, vmid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all, then destroy all, prove that we can still create
|
||||
constexpr auto one{1_umx};
|
||||
for (bsl::safe_idx i{one}; i < HYPERVISOR_MAX_VMS; ++i) {
|
||||
ret = syscall::bf_vm_op_create_vm(g_handle, vmid);
|
||||
integration::require_success(ret);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{HYPERVISOR_MAX_VMS}; i > one; --i) {
|
||||
ret = syscall::bf_vm_op_destroy_vm(g_handle, bsl::to_u16(i - one));
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
}
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
ret = syscall::bf_vm_op_create_vm(g_handle, vmid);
|
||||
integration::require_success(ret);
|
||||
integration::require(vmid == bsl::to_u16(one));
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// The following is needed to setup the remaining tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, vmid, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_init_as_root(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = init_vs(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_run(g_handle, vmid, vpid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -199,25 +268,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
378
kernel/integration/bf_vm_op_map_direct.cpp
Normal file
378
kernel/integration/bf_vm_op_map_direct.cpp
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <page_4k_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
constexpr auto phys{HYPERVISOR_PAGE_SIZE};
|
||||
void *pmut_mut_p{};
|
||||
|
||||
mut_ret = bf_vm_op_map_direct_impl(hndl.get(), {}, phys.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vmid
|
||||
{
|
||||
constexpr auto phys{HYPERVISOR_PAGE_SIZE};
|
||||
void *pmut_mut_p{};
|
||||
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, BF_INVALID_ID.get(), phys.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VMS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, oor.get(), phys.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VMS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, nyc.get(), phys.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid phys
|
||||
{
|
||||
void *pmut_mut_p{};
|
||||
|
||||
// nullptr
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, {}, {}, &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{0xFFFFFFFFFFFFF000_u64};
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, {}, oor.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// unaligned address
|
||||
auto const uaa{0x42_u64};
|
||||
mut_ret = bf_vm_op_map_direct_impl({}, {}, uaa.get(), &pmut_mut_p);
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
/// TODO:
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_MK_STACK_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_MK_CODE_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_MK_PAGE_POOL_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_MK_HUGE_POOL_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_DIRECT_MAP_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_STACK_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_FAIL_STACK_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_CODE_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_TLS_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_PAGE_POOL_ADDR
|
||||
/// - Need to add a check for a phys mapped to HYPERVISOR_EXT_HUGE_POOL_ADDR
|
||||
///
|
||||
/// The best way to handle these is that once we have a single
|
||||
/// physically contiguous buffer from UEFI to map everything,
|
||||
/// we can simply check to see if this phys is in that buffer
|
||||
/// which is not too bad since these values can be a CMake
|
||||
/// variable. See the Security.md for more details.
|
||||
///
|
||||
}
|
||||
|
||||
// map the same address twice
|
||||
{
|
||||
constexpr auto phys{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
auto const *const ptr1{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr1);
|
||||
|
||||
bsl::print() << "data at spa 0x1000: " << bsl::hex(ptr1->data.front()) << bsl::endl;
|
||||
|
||||
auto const *const ptr2{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr == ptr2);
|
||||
|
||||
auto const ret{g_mut_sys.bf_vm_op_unmap_direct<page_4k_t>({}, ptr1)};
|
||||
integration::require(ret);
|
||||
}
|
||||
|
||||
// Map a bunch of addresses
|
||||
{
|
||||
bsl::uint8 data{};
|
||||
constexpr auto num_pages{0x5000_umx};
|
||||
|
||||
for (bsl::safe_idx mut_i{bsl::safe_idx::magic_1()}; mut_i < num_pages; ++mut_i) {
|
||||
auto const phys{(HYPERVISOR_PAGE_SIZE * bsl::to_umx(mut_i)).checked()};
|
||||
|
||||
auto const *const ptr{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr);
|
||||
|
||||
data += ptr->data.front(); // NOLINT
|
||||
}
|
||||
|
||||
bsl::print() << "data: " << bsl::hex(data) << bsl::endl;
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
435
kernel/integration/bf_vm_op_unmap_direct.cpp
Normal file
435
kernel/integration/bf_vm_op_unmap_direct.cpp
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <page_4k_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
constexpr auto virt{HYPERVISOR_EXT_DIRECT_MAP_ADDR};
|
||||
|
||||
mut_ret = bf_vm_op_unmap_direct_impl(hndl.get(), {}, virt.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vmid
|
||||
{
|
||||
constexpr auto virt{HYPERVISOR_EXT_DIRECT_MAP_ADDR};
|
||||
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, BF_INVALID_ID.get(), virt.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VMS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, oor.get(), virt.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VMS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, nyc.get(), virt.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid virt
|
||||
{
|
||||
// nullptr
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, {});
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{0xFFFFFFFFFFFFF000_u64};
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, oor.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// unaligned address
|
||||
auto const uaa{0x42_u64};
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, uaa.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_MK_STACK_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_MK_STACK_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_MK_CODE_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_MK_CODE_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_MK_PAGE_POOL_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_MK_PAGE_POOL_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_MK_HUGE_POOL_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_MK_HUGE_POOL_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_STACK_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_STACK_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_FAIL_STACK_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_FAIL_STACK_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_CODE_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_CODE_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_TLS_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_TLS_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// nullptr (any address == HYPERVISOR_EXT_DIRECT_MAP_ADDR is a null phys)
|
||||
{
|
||||
// HYPERVISOR_EXT_DIRECT_MAP_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_DIRECT_MAP_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_PAGE_POOL_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_PAGE_POOL_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// HYPERVISOR_EXT_HUGE_POOL_ADDR
|
||||
mut_ret = bf_vm_op_unmap_direct_impl({}, {}, HYPERVISOR_EXT_HUGE_POOL_ADDR.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// never mapped
|
||||
{
|
||||
auto const phys{(HYPERVISOR_EXT_DIRECT_MAP_ADDR + HYPERVISOR_PAGE_SIZE).checked()};
|
||||
auto const *const virt{reinterpret_cast<page_4k_t const *>(phys.get())}; // NOLINT
|
||||
|
||||
auto const ret{g_mut_sys.bf_vm_op_unmap_direct({}, virt)};
|
||||
integration::require(!ret);
|
||||
}
|
||||
|
||||
// unmap the same address twice
|
||||
{
|
||||
constexpr auto phys{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
auto const *const ptr{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr);
|
||||
|
||||
auto const ret1{g_mut_sys.bf_vm_op_unmap_direct({}, ptr)};
|
||||
integration::require(ret1);
|
||||
|
||||
auto const ret2{g_mut_sys.bf_vm_op_unmap_direct({}, ptr)};
|
||||
integration::require(!ret2);
|
||||
}
|
||||
|
||||
// Map and then unmap a bunch of addresses
|
||||
{
|
||||
constexpr auto num_pages{0x5000_umx};
|
||||
|
||||
for (bsl::safe_idx mut_i{bsl::safe_idx::magic_1()}; mut_i < num_pages; ++mut_i) {
|
||||
auto const phys{(HYPERVISOR_PAGE_SIZE * bsl::to_umx(mut_i)).checked()};
|
||||
|
||||
auto const *const ptr{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr);
|
||||
|
||||
auto const ret{g_mut_sys.bf_vm_op_unmap_direct({}, ptr)};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
// Do it again to make sure we can remap everything again
|
||||
{
|
||||
constexpr auto num_pages{0x5000_umx};
|
||||
|
||||
for (bsl::safe_idx mut_i{bsl::safe_idx::magic_1()}; mut_i < num_pages; ++mut_i) {
|
||||
auto const phys{(HYPERVISOR_PAGE_SIZE * bsl::to_umx(mut_i)).checked()};
|
||||
|
||||
auto const *const ptr{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr);
|
||||
|
||||
auto const ret{g_mut_sys.bf_vm_op_unmap_direct({}, ptr)};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
// If the unmap is working, attempting to access a freed pointer should
|
||||
// segfault, which is how we will end this test.
|
||||
{
|
||||
bsl::debug() << "success. remaining fault is expected\n" << bsl::here();
|
||||
constexpr auto phys{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
auto const *const ptr{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
integration::require(nullptr != ptr);
|
||||
|
||||
auto const ret{g_mut_sys.bf_vm_op_unmap_direct({}, ptr)};
|
||||
integration::require(ret);
|
||||
|
||||
bsl::print() << "data at spa 0x1000: " << bsl::hex(ptr->data.front()) << bsl::endl;
|
||||
}
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,102 +22,231 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
mut_ret = bf_vp_op_create_vp_impl(hndl.get(), {}, mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vmid
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vp_op_create_vp_impl({}, BF_INVALID_ID.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VMS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vp_op_create_vp_impl({}, oor.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VMS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vp_op_create_vp_impl({}, nyc.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_valid());
|
||||
}
|
||||
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_invalid());
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::safe_u16 vpid{};
|
||||
|
||||
// create with invalid handle
|
||||
ret = syscall::bf_vp_op_create_vp({}, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with invalid vmid
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_INVALID_ID, ppid, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with vmid that has not been created
|
||||
constexpr auto invalid_vpid{2_u16};
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, invalid_vpid, ppid, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with invalid ppid
|
||||
ret = syscall::bf_vp_op_create_vp(
|
||||
g_handle, syscall::BF_ROOT_VMID, syscall::BF_INVALID_ID, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with ppid that is create than the total number of online pps
|
||||
ret = syscall::bf_vp_op_create_vp(
|
||||
g_handle, syscall::BF_ROOT_VMID, syscall::bf_tls_online_pps(g_handle), vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VS; ++i) {
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -127,25 +256,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,165 +22,243 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <arch_support.hpp>
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VPExit entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VPExit
|
||||
/// @param exit_reason the exit reason associated with the VPExit
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// VMExit Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy assigned VP (turns VP into zombie)
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::bf_tls_vpid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy zombie
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::bf_tls_vpid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Done
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
bsl::safe_u16 vpid{};
|
||||
bsl::safe_u16 vsid{};
|
||||
// create with invalid handle
|
||||
{
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
/// NOTE:
|
||||
/// - The max number of VPs an extension can create is one less than
|
||||
/// the provided max because the microkernel creates the Root VP
|
||||
/// automatically for the extension as it cannot be deleted.
|
||||
///
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Setup
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Bootstrap Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy with invalid handle
|
||||
ret = syscall::bf_vp_op_destroy_vp({}, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with invalid ID
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::BF_INVALID_ID);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with ID that is greater than or equal to MAX_VS
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, bsl::to_u16(HYPERVISOR_MAX_VS));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with VP that has not been created
|
||||
constexpr auto invalid_vpid{2_u16};
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, invalid_vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy success
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, vpid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
|
||||
// destroy same VP twice (double free bug)
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all, then destroy all, prove that we can still create
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VS; ++i) {
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
mut_ret = bf_vp_op_destroy_vp_impl(hndl.get(), {});
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VS; ++i) {
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, bsl::to_u16(i));
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
// create with invalid vpid
|
||||
{
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vp_op_destroy_vp_impl({}, BF_INVALID_ID.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VPS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vp_op_destroy_vp_impl({}, oor.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VPS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vp_op_destroy_vp_impl({}, nyc.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
integration::require(vpid.is_zero());
|
||||
// create all and and then make sure we can destroy them all
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_valid());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// The following is needed to setup the remaining tests
|
||||
// ---------------------------------------------------------------------
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vp_op_destroy_vp(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::require_success(ret);
|
||||
// do it again to make sure that after destroy, create still works
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_valid());
|
||||
}
|
||||
|
||||
ret = syscall::bf_vs_op_init_as_root(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vp_op_destroy_vp(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = init_vs(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_run(g_handle, vpid, vpid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
syscall::bf_control_op_exit();
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
|
|
@ -190,25 +268,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,164 +0,0 @@
|
|||
/// @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 "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::safe_u16 vpid{};
|
||||
bsl::safe_u16 vsid{};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Setup
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// create with invalid handle
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Bootstrap Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// create with invalid handle
|
||||
ret = syscall::bf_vs_op_create_vs({}, vpid, ppid, vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with invalid vmid
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, syscall::BF_INVALID_ID, ppid, vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with vmid that has not been created
|
||||
constexpr auto invalid_vsid{2_u16};
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, invalid_vsid, ppid, vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with invalid ppid
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, syscall::BF_INVALID_ID, vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create with ppid that is create than the total number of online pps
|
||||
ret =
|
||||
syscall::bf_vs_op_create_vs(g_handle, vpid, syscall::bf_tls_online_pps(g_handle), vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VSS; ++i) {
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
}
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
/// @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 "integration_utils.hpp"
|
||||
|
||||
#include <arch_support.hpp>
|
||||
#include <bf_constants.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/exit_code.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VPExit entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VPExit
|
||||
/// @param exit_reason the exit reason associated with the VPExit
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
vmexit_entry(bsl::uint16 const vsid, bsl::uint64 const exit_reason) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// VMExit Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy assigned VP (turns VP into zombie)
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::bf_tls_vpid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy zombie
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::bf_tls_vpid(g_handle));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Done
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
bootstrap_entry(bsl::uint16 const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::safe_u16 vpid{};
|
||||
bsl::safe_u16 vsid{};
|
||||
|
||||
/// NOTE:
|
||||
/// - The max number of VPs an extension can create is one less than
|
||||
/// the provided max because the microkernel creates the Root VP
|
||||
/// automatically for the extension as it cannot be deleted.
|
||||
///
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Setup
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Bootstrap Tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy with invalid handle
|
||||
ret = syscall::bf_vp_op_destroy_vp({}, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with invalid ID
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, syscall::BF_INVALID_ID);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with ID that is greater than or equal to MAX_VS
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, bsl::to_u16(HYPERVISOR_MAX_VS));
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy with VP that has not been created
|
||||
constexpr auto invalid_vpid{2_u16};
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, invalid_vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// destroy success
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, vpid);
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
|
||||
// destroy same VP twice (double free bug)
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, vpid);
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
|
||||
// create all, then destroy all, prove that we can still create
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VS; ++i) {
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VS; ++i) {
|
||||
ret = syscall::bf_vp_op_destroy_vp(g_handle, bsl::to_u16(i));
|
||||
integration::verify(bsl::errc_success == ret);
|
||||
}
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
integration::require(vpid.is_zero());
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// The following is needed to setup the remaining tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_init_as_root(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = init_vs(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_run(g_handle, vpid, vpid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this integration
|
||||
/// test
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
336
kernel/integration/bf_vs_op_create_vs.cpp
Normal file
336
kernel/integration/bf_vs_op_create_vs.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_valid());
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
mut_ret = bf_vs_op_create_vs_impl(hndl.get(), {}, {}, mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vpid
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, BF_INVALID_ID.get(), {}, mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VPS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, oor.get(), {}, mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VPS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, nyc.get(), {}, mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid ppid
|
||||
{
|
||||
bsl::safe_u16 mut_id{};
|
||||
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, {}, BF_INVALID_ID.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_PPS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, {}, oor.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_PPS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_create_vs_impl({}, {}, nyc.get(), mut_id.data());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create all and prove that creating one more will fail
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VSS; ++i) {
|
||||
auto const vsid{g_mut_sys.bf_vs_op_create_vs({}, {})};
|
||||
integration::require(vsid.is_valid());
|
||||
}
|
||||
|
||||
auto const vsid{g_mut_sys.bf_vs_op_create_vs({}, {})};
|
||||
integration::require(vsid.is_invalid());
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
329
kernel/integration/bf_vs_op_destroy_vs.cpp
Normal file
329
kernel/integration/bf_vs_op_destroy_vs.cpp
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bf_status_t mut_ret{};
|
||||
|
||||
auto const vpid{g_mut_sys.bf_vp_op_create_vp({})};
|
||||
integration::require(vpid.is_valid());
|
||||
|
||||
// create with invalid handle
|
||||
{
|
||||
constexpr auto hndl{BF_INVALID_HANDLE};
|
||||
|
||||
mut_ret = bf_vs_op_destroy_vs_impl(hndl.get(), {});
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create with invalid vsid
|
||||
{
|
||||
// BF_INVALID_ID
|
||||
mut_ret = bf_vs_op_destroy_vs_impl({}, BF_INVALID_ID.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// out of range
|
||||
auto const oor{bsl::to_u16(HYPERVISOR_MAX_VPS + bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_destroy_vs_impl({}, oor.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
|
||||
// not yet created
|
||||
auto const nyc{bsl::to_u16(HYPERVISOR_MAX_VPS - bsl::safe_u64::magic_1()).checked()};
|
||||
mut_ret = bf_vs_op_destroy_vs_impl({}, nyc.get());
|
||||
integration::require(mut_ret != BF_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// create all and and then make sure we can destroy them all
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const vsid{g_mut_sys.bf_vs_op_create_vs({}, {})};
|
||||
integration::require(vsid.is_valid());
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vs_op_destroy_vs(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
// do it again to make sure that after destroy, create still works
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const vsid{g_mut_sys.bf_vs_op_create_vs({}, {})};
|
||||
integration::require(vsid.is_valid());
|
||||
}
|
||||
|
||||
for (bsl::safe_idx i{}; i < HYPERVISOR_MAX_VPS; ++i) {
|
||||
auto const ret{g_mut_sys.bf_vs_op_destroy_vs(bsl::to_u16(i))};
|
||||
integration::require(ret);
|
||||
}
|
||||
}
|
||||
|
||||
bsl::debug() << "success. remaining backtrace is expected\n" << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
namespace integration
|
||||
{
|
||||
/// @brief stores the handle the extension will use
|
||||
constinit inline syscall::bf_handle_t g_handle{};
|
||||
constinit inline bf_handle_t g_handle{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VPExit entry function.
|
||||
|
|
@ -58,18 +58,18 @@ namespace integration
|
|||
// ---------------------------------------------------------------------
|
||||
|
||||
// destroy assigned VP (turns VP into zombie)
|
||||
ret = syscall::bf_vs_op_destroy_vs(g_handle, syscall::bf_tls_vsid());
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
ret = bf_vs_op_destroy_vs(g_handle, bf_tls_vsid());
|
||||
integration::require(bsl::errc_failure == ret);
|
||||
|
||||
// destroy zombie
|
||||
ret = syscall::bf_vs_op_destroy_vs(g_handle, syscall::bf_tls_vsid());
|
||||
integration::verify(bsl::errc_failure == ret);
|
||||
ret = bf_vs_op_destroy_vs(g_handle, bf_tls_vsid());
|
||||
integration::require(bsl::errc_failure == ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Done
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -80,10 +80,10 @@ namespace integration
|
|||
///
|
||||
void
|
||||
// NOLINTNEXTLINE(bsl-non-safe-integral-types-are-forbidden)
|
||||
fail_entry(syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::uint64 const fail_reason) noexcept
|
||||
{
|
||||
bsl::discard(fail_reason);
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -105,10 +105,10 @@ namespace integration
|
|||
// Setup
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vp_op_create_vp(g_handle, syscall::BF_ROOT_VMID, ppid, vpid);
|
||||
ret = bf_vp_op_create_vp(g_handle, BF_ROOT_VMID, ppid, vpid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
ret = bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
@ -116,31 +116,31 @@ namespace integration
|
|||
// ---------------------------------------------------------------------
|
||||
|
||||
// init with invalid handle
|
||||
ret = syscall::bf_vs_op_init_as_root({}, vsid);
|
||||
ret = bf_vs_op_init_as_root({}, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
// destroy with invalid ID
|
||||
ret = syscall::bf_vs_op_init_as_root({}, syscall::BF_INVALID_ID);
|
||||
ret = bf_vs_op_init_as_root({}, BF_INVALID_ID);
|
||||
integration::require_success(ret);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// The following is needed to setup the remaining tests
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
ret = syscall::bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
ret = bf_vs_op_create_vs(g_handle, vpid, ppid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_init_as_root(g_handle, vsid);
|
||||
ret = bf_vs_op_init_as_root(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = init_vs(g_handle, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_vs_op_run(g_handle, syscall::BF_ROOT_VMID, vpid, vsid);
|
||||
ret = bf_vs_op_run(g_handle, BF_ROOT_VMID, vpid, vsid);
|
||||
integration::require_success(ret);
|
||||
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -157,23 +157,23 @@ namespace integration
|
|||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(version))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(version))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle(syscall::BF_SPEC_ID1_VAL, g_handle);
|
||||
ret = bf_handle_op_open_handle(BF_SPEC_ID1_VAL, g_handle);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
ret = bf_callback_op_register_bootstrap(g_handle, &bootstrap_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
ret = bf_callback_op_register_vmexit(g_handle, &vmexit_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
ret = bf_callback_op_register_fail(g_handle, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
|
||||
syscall::bf_control_op_wait();
|
||||
bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,17 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,12 +104,12 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
|
||||
bsl::error() << "extension purposely exiting early\n";
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -55,18 +117,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -79,13 +170,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -99,9 +215,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,7 +104,7 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
bsl::error() << "extension purposely not calling exit syscall. fault expected\n";
|
||||
|
|
@ -53,18 +115,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -77,13 +168,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -97,9 +213,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,7 +104,7 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
|
||||
|
|
@ -58,18 +120,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -82,13 +173,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -102,9 +218,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,12 +104,12 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
|
||||
bsl::error() << "extension purposely exiting early using the wrong ABI\n";
|
||||
syscall::bf_control_op_wait();
|
||||
bf_control_op_wait();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -55,18 +117,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -79,13 +170,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -99,9 +215,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
282
kernel/integration/fast_fail_exit_from_fail.cpp
Normal file
282
kernel/integration/fast_fail_exit_from_fail.cpp
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(errc);
|
||||
bsl::discard(addr);
|
||||
|
||||
bsl::assert("fail from the fail handler", bsl::here());
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
bsl::assert("force the fail entry to be called", bsl::here());
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
282
kernel/integration/fast_fail_exit_from_fail_with_no_syscall.cpp
Normal file
282
kernel/integration/fast_fail_exit_from_fail_with_no_syscall.cpp
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(errc);
|
||||
bsl::discard(addr);
|
||||
|
||||
bsl::error() << "extension purposely not calling exit syscall. fault expected\n";
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
bsl::assert("force the fail entry to be called", bsl::here());
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
286
kernel/integration/fast_fail_exit_from_fail_with_segfault.cpp
Normal file
286
kernel/integration/fast_fail_exit_from_fail_with_segfault.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(errc);
|
||||
bsl::discard(addr);
|
||||
|
||||
bsl::error() << "extension purposely dereferencing nullptr. fault expected\n";
|
||||
bool *i{};
|
||||
// This is intentional as it is what we are testing.
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
|
||||
*i = true;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
bsl::assert("force the fail entry to be called", bsl::here());
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
283
kernel/integration/fast_fail_exit_from_fail_with_wait.cpp
Normal file
283
kernel/integration/fast_fail_exit_from_fail_with_wait.cpp
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(errc);
|
||||
bsl::discard(addr);
|
||||
|
||||
bsl::error() << "extension purposely exiting early using the wrong ABI\n";
|
||||
bf_control_op_wait();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
bsl::assert("force the fail entry to be called", bsl::here());
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,17 +22,80 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,10 +105,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -53,18 +143,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -77,13 +196,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -97,10 +241,10 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
integration::require(g_mut_sys.initialize(
|
||||
bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
|
||||
bsl::error() << "extension purposely exiting early\n";
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,80 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,10 +105,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -53,18 +143,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -77,13 +196,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -97,9 +241,9 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
integration::require(g_mut_sys.initialize(
|
||||
bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
|
||||
bsl::error() << "extension purposely not calling exit syscall. fault expected\n";
|
||||
bsl::error() << "extension purposely not calling exit syscall\n";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,80 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -42,10 +105,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -53,18 +143,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -77,13 +196,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -97,8 +241,8 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
integration::require_success(
|
||||
g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
integration::require(g_mut_sys.initialize(
|
||||
bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry));
|
||||
|
||||
bsl::error() << "extension purposely dereferencing nullptr. fault expected\n";
|
||||
bool *i{};
|
||||
|
|
|
|||
|
|
@ -22,38 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <bootstrap_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
constinit intrinsic_t g_intrinsic{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
constinit vp_pool_t g_vp_pool{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
constinit vs_pool_t g_vs_pool{};
|
||||
|
||||
/// @brief stores the bootstrap_t that this code will use
|
||||
constinit bootstrap_t g_bootstrap{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
constinit gs_t g_gs{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
thread_local tls_t g_tls{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -63,25 +104,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
ret = g_bootstrap.dispatch( // --
|
||||
g_gs, // --
|
||||
g_tls, // --
|
||||
g_sys, // --
|
||||
g_intrinsic, // --
|
||||
g_vp_pool, // --
|
||||
g_vs_pool, // --
|
||||
bsl::to_u16(ppid));
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -89,18 +142,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -113,14 +195,13 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
bsl::error() << "extension purposely exiting early\n";
|
||||
syscall::bf_control_op_exit();
|
||||
bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -134,23 +215,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
ret = g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
ret = g_intrinsic.initialize(g_gs, g_tls);
|
||||
integration::require_success(ret);
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
ret = g_vp_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_vs_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_bootstrap.initialize(g_gs, g_tls, g_sys, g_intrinsic, g_vp_pool, g_vs_pool);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,38 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <bootstrap_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
constinit intrinsic_t g_intrinsic{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
constinit vp_pool_t g_vp_pool{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
constinit vs_pool_t g_vs_pool{};
|
||||
|
||||
/// @brief stores the bootstrap_t that this code will use
|
||||
constinit bootstrap_t g_bootstrap{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
constinit gs_t g_gs{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
thread_local tls_t g_tls{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -63,25 +104,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
ret = g_bootstrap.dispatch( // --
|
||||
g_gs, // --
|
||||
g_tls, // --
|
||||
g_sys, // --
|
||||
g_intrinsic, // --
|
||||
g_vp_pool, // --
|
||||
g_vs_pool, // --
|
||||
bsl::to_u16(ppid));
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -89,18 +142,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -113,8 +195,7 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
|
@ -133,23 +214,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
ret = g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
ret = g_intrinsic.initialize(g_gs, g_tls);
|
||||
integration::require_success(ret);
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
ret = g_vp_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_vs_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_bootstrap.initialize(g_gs, g_tls, g_sys, g_intrinsic, g_vp_pool, g_vs_pool);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,38 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <bootstrap_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
constinit intrinsic_t g_intrinsic{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
constinit vp_pool_t g_vp_pool{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
constinit vs_pool_t g_vs_pool{};
|
||||
|
||||
/// @brief stores the bootstrap_t that this code will use
|
||||
constinit bootstrap_t g_bootstrap{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
constinit gs_t g_gs{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
thread_local tls_t g_tls{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -63,25 +104,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
ret = g_bootstrap.dispatch( // --
|
||||
g_gs, // --
|
||||
g_tls, // --
|
||||
g_sys, // --
|
||||
g_intrinsic, // --
|
||||
g_vp_pool, // --
|
||||
g_vs_pool, // --
|
||||
bsl::to_u16(ppid));
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -89,18 +142,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -113,8 +195,7 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
|
@ -137,23 +218,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
ret = g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
ret = g_intrinsic.initialize(g_gs, g_tls);
|
||||
integration::require_success(ret);
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
ret = g_vp_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_vs_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_bootstrap.initialize(g_gs, g_tls, g_sys, g_intrinsic, g_vp_pool, g_vs_pool);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,38 +22,79 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <bootstrap_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
constinit syscall::bf_syscall_t g_sys{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
constinit intrinsic_t g_intrinsic{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
constinit vp_pool_t g_vp_pool{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
constinit vs_pool_t g_vs_pool{};
|
||||
|
||||
/// @brief stores the bootstrap_t that this code will use
|
||||
constinit bootstrap_t g_bootstrap{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
constinit gs_t g_gs{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
thread_local tls_t g_tls{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
|
|
@ -63,25 +104,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
ret = g_bootstrap.dispatch( // --
|
||||
g_gs, // --
|
||||
g_tls, // --
|
||||
g_sys, // --
|
||||
g_intrinsic, // --
|
||||
g_vp_pool, // --
|
||||
g_vs_pool, // --
|
||||
bsl::to_u16(ppid));
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -89,18 +142,47 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -113,14 +195,13 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
|
||||
bsl::error() << "extension purposely exiting early using the wrong ABI\n";
|
||||
syscall::bf_control_op_wait();
|
||||
bf_control_op_wait();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -134,23 +215,59 @@ namespace integration
|
|||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
ret = g_sys.initialize(bsl::to_u32(version), &bootstrap_entry, &vmexit_entry, &fail_entry);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
ret = g_intrinsic.initialize(g_gs, g_tls);
|
||||
integration::require_success(ret);
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
ret = g_vp_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_vs_pool.initialize(g_gs, g_tls, g_sys, g_intrinsic);
|
||||
integration::require_success(ret);
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = g_bootstrap.initialize(g_gs, g_tls, g_sys, g_intrinsic, g_vp_pool, g_vs_pool);
|
||||
integration::require_success(ret);
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
285
kernel/integration/fast_fail_recover_from_assert.cpp
Normal file
285
kernel/integration/fast_fail_recover_from_assert.cpp
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::error() << "fail entry called with errc " // --
|
||||
<< bsl::hex(errc) // --
|
||||
<< " and addr " // --
|
||||
<< bsl::hex(addr) // --
|
||||
<< bsl::endl; // --
|
||||
|
||||
bsl::expects(g_mut_sys.bf_vs_op_advance_ip_and_run_current());
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
bsl::assert("force the fail entry to be called", bsl::here());
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
342
kernel/integration/fast_fail_recover_from_page_fault.cpp
Normal file
342
kernel/integration/fast_fail_recover_from_page_fault.cpp
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <page_4k_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
constexpr auto min_addr{HYPERVISOR_EXT_DIRECT_MAP_ADDR};
|
||||
constexpr auto max_addr{(min_addr + HYPERVISOR_EXT_DIRECT_MAP_SIZE).checked()};
|
||||
|
||||
constexpr auto vector_mask{0x00000000FFFFFFFF_u64};
|
||||
constexpr auto vector_shft{0_u64};
|
||||
constexpr auto error_code_mask{0xFFFFFFFF00000000_u64};
|
||||
constexpr auto error_code_shft{32_u64};
|
||||
|
||||
auto const vector{(errc & vector_mask) >> vector_shft};
|
||||
auto const error_code{(errc & error_code_mask) >> error_code_shft};
|
||||
|
||||
bsl::debug() << "fail entry called with vector " // --
|
||||
<< bsl::hex(vector) // --
|
||||
<< " and ec " // --
|
||||
<< bsl::hex(error_code) // --
|
||||
<< " and addr " // --
|
||||
<< bsl::hex(addr) // --
|
||||
<< bsl::endl; // --
|
||||
|
||||
constexpr auto pf_vector{14_u64};
|
||||
if (bsl::unlikely(pf_vector != vector)) {
|
||||
bsl::error() << "vector " // --
|
||||
<< bsl::hex(vector) // --
|
||||
<< " is not supported by this fail handler" << bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
if (bsl::unlikely(addr <= min_addr)) {
|
||||
bsl::error() << "faulting address " // --
|
||||
<< bsl::hex(addr) // --
|
||||
<< " is not in the direct map" << bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
if (bsl::unlikely(addr >= max_addr)) {
|
||||
bsl::error() << "faulting address " // --
|
||||
<< bsl::hex(addr) // --
|
||||
<< " is not in the direct map" << bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
auto const phys{(addr - min_addr).checked()};
|
||||
auto const *const ret{g_mut_sys.bf_vm_op_map_direct<page_4k_t>({}, phys)};
|
||||
if (bsl::unlikely(nullptr == ret)) {
|
||||
bsl::error() << "failed to map in faulting address " // --
|
||||
<< bsl::hex(addr) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
return bf_control_op_again();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
static constinit bsl::safe_idx s_count{};
|
||||
++s_count;
|
||||
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
if (s_count == bsl::safe_idx::magic_1()) {
|
||||
constexpr auto phys{(HYPERVISOR_EXT_DIRECT_MAP_ADDR + HYPERVISOR_PAGE_SIZE).checked()};
|
||||
auto const *const ptr{reinterpret_cast<bsl::uint64 *>(phys.get())}; // NOLINT
|
||||
|
||||
bsl::uint64 const data{*ptr};
|
||||
bsl::print() << "data: " << bsl::hex(data) << bsl::endl;
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,32 +22,168 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -60,13 +196,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -81,22 +242,22 @@ namespace integration
|
|||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::safe_umx hndl{};
|
||||
syscall::bf_status_t ret{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle_impl(syscall::BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,81 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
|
|
@ -39,10 +105,85 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -55,13 +196,38 @@ namespace integration
|
|||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_uint64_t::value_type const exit_reason) noexcept
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(exit_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -76,22 +242,22 @@ namespace integration
|
|||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::safe_umx hndl{};
|
||||
syscall::bf_status_t ret{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle_impl(syscall::BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_vmexit_impl(hndl.get(), &vmexit_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,81 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#include "integration_utils.hpp"
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <integration_utils.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
|
|
@ -39,10 +105,37 @@ namespace integration
|
|||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(syscall::bf_uint16_t::value_type const ppid) noexcept
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
bsl::discard(ppid);
|
||||
return syscall::bf_control_op_exit();
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -50,18 +143,91 @@ namespace integration
|
|||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the fail
|
||||
/// @param fail_reason the exit reason associated with the fail
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(
|
||||
syscall::bf_uint16_t::value_type const vsid,
|
||||
syscall::bf_status_t::value_type const fail_reason) noexcept
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
bsl::discard(vsid);
|
||||
bsl::discard(fail_reason);
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
return syscall::bf_control_op_exit();
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
|
|
@ -76,22 +242,22 @@ namespace integration
|
|||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::safe_umx hndl{};
|
||||
syscall::bf_status_t ret{};
|
||||
bf_status_t ret{};
|
||||
|
||||
if (bsl::unlikely(!syscall::bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
if (bsl::unlikely(!bf_is_spec1_supported(bsl::to_u32(version)))) {
|
||||
bsl::error() << "integration test not supported\n" << bsl::here();
|
||||
return syscall::bf_control_op_exit();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
ret = syscall::bf_handle_op_open_handle_impl(syscall::BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_handle_op_open_handle_impl(BF_SPEC_ID1_VAL.get(), hndl.data());
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_bootstrap_impl(hndl.get(), &bootstrap_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
ret = syscall::bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret == syscall::BF_STATUS_SUCCESS);
|
||||
ret = bf_callback_op_register_fail_impl(hndl.get(), &fail_entry);
|
||||
integration::require(ret == BF_STATUS_SUCCESS);
|
||||
|
||||
return syscall::bf_control_op_wait();
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,153 +0,0 @@
|
|||
/// @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 VMEXIT_T_HPP
|
||||
#define VMEXIT_T_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vmexit_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's VMExit handler
|
||||
///
|
||||
class vmexit_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vmexit_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vmexit_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the VMExit.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &exit_reason) noexcept -> bsl::errc_type
|
||||
{
|
||||
switch (exit_reason.get()) {
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bsl::error() << "unsupported vmexit " // --
|
||||
<< bsl::hex(exit_reason) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/// @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 VS_T_HPP
|
||||
#define VS_T_HPP
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VS
|
||||
///
|
||||
class vs_t final
|
||||
{
|
||||
/// @brief stores the ID associated with this vs_t
|
||||
bsl::safe_u16 m_id{bsl::safe_u16::failure()};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vs_t
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = i;
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a vs_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the vs_t to
|
||||
/// @param ppid the ID of the PP to assign the vs_t to
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vpid);
|
||||
bsl::discard(ppid);
|
||||
|
||||
ret = sys.bf_vs_op_init_as_root(m_id);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
/// @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 BOOTSTRAP_T_HPP
|
||||
#define BOOTSTRAP_T_HPP
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::bootstrap_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's bootstrap handler
|
||||
///
|
||||
class bootstrap_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this bootstrap_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the bootstrap_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the bootstrap process as needed. Note that
|
||||
/// the bootstrap callback is only called when starting the
|
||||
/// hypervisor on root VPs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param ppid the ID of the PP to bootstrap
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::errc_type
|
||||
{
|
||||
auto const vmid{syscall::BF_ROOT_VMID};
|
||||
|
||||
auto mut_vpid{vp_pool.allocate(gs, tls, sys, intrinsic, vmid, ppid)};
|
||||
if (bsl::unlikely(!mut_vpid)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
auto mut_vsid{vs_pool.allocate(gs, tls, sys, intrinsic, mut_vpid, ppid)};
|
||||
if (bsl::unlikely(!mut_vsid)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
return sys.bf_vs_op_run(vmid, mut_vpid, mut_vsid);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -22,20 +22,15 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef GS_T_HPP
|
||||
#define GS_T_HPP
|
||||
#ifndef ALLOCATED_STATUS_T_HPP
|
||||
#define ALLOCATED_STATUS_T_HPP
|
||||
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <basic_allocated_status_t.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::gs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Global Storage (GS).
|
||||
///
|
||||
struct gs_t final
|
||||
{};
|
||||
/// @brief defines the allocated_status_t used by the example
|
||||
using allocated_status_t = lib::basic_allocated_status_t;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -22,15 +22,15 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef TLB_FLUSH_TYPE_T_HPP
|
||||
#define TLB_FLUSH_TYPE_T_HPP
|
||||
#ifndef PAGE_4K_T_HPP
|
||||
#define PAGE_4K_T_HPP
|
||||
|
||||
#include <basic_tlb_flush_type_t.hpp>
|
||||
#include <basic_page_4k_t.hpp>
|
||||
|
||||
namespace mk
|
||||
namespace syscall
|
||||
{
|
||||
/// @brief defines the tlb_flush_type_t used by the microkernel
|
||||
using tlb_flush_type_t = lib::basic_tlb_flush_type_t;
|
||||
/// @brief defines the page_4k_t used by the microkernel
|
||||
using page_4k_t = lib::basic_page_4k_t;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -34,27 +34,6 @@
|
|||
|
||||
namespace integration
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Reports passed/failed so that a script can detect if the
|
||||
/// failed text shows up in the log.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param test the results to query
|
||||
/// @param sloc used to identify the location in the integration test
|
||||
/// where a check failed.
|
||||
///
|
||||
constexpr void
|
||||
verify(bool const test, bsl::source_location const &sloc = bsl::here()) noexcept
|
||||
{
|
||||
if (bsl::unlikely(!test)) {
|
||||
bsl::print() << bsl::red << "integration test failed";
|
||||
bsl::print() << bsl::rst << sloc;
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Reports passed/failed so that a script can detect if the
|
||||
/// failed text shows up in the log. If the test fails, this
|
||||
|
|
@ -89,8 +68,7 @@ namespace integration
|
|||
/// where a check failed.
|
||||
///
|
||||
constexpr void
|
||||
require_success(
|
||||
bsl::errc_type const ec, bsl::source_location const &sloc = bsl::here()) noexcept
|
||||
require(bsl::errc_type const ec, bsl::source_location const &sloc = bsl::here()) noexcept
|
||||
{
|
||||
if (bsl::unlikely(!ec.success())) {
|
||||
bsl::print() << bsl::red << "integration test failed";
|
||||
|
|
|
|||
123
kernel/integration/support/src/dispatch_bootstrap.hpp
Normal file
123
kernel/integration/support/src/dispatch_bootstrap.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/// @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 DISPATCH_BOOTSTRAP
|
||||
#define DISPATCH_BOOTSTRAP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_initialize.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the bootstrap process as needed. Note that
|
||||
/// the bootstrap callback is only called when starting the
|
||||
/// hypervisor on root VPs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param mut_tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param mut_vp_pool the vp_pool_t to use
|
||||
/// @param mut_vs_pool the vs_pool_t to use
|
||||
/// @param ppid the ID of the PP to bootstrap
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_bootstrap(
|
||||
gs_t const &gs,
|
||||
tls_t &mut_tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t &mut_vp_pool,
|
||||
vs_pool_t &mut_vs_pool,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::expects(ppid.is_valid_and_checked());
|
||||
bsl::expects(ppid != BF_INVALID_ID);
|
||||
|
||||
auto const ret{tls_initialize(mut_tls, mut_sys, intrinsic)};
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - In order to execcute bf_vs_op_run, which is what executes
|
||||
/// the hypervisor, we must have a VM, VP and VS.
|
||||
/// - The root VM is already created for us, so we don't need to
|
||||
/// create this ourselves. You only need to create VM's if you
|
||||
/// plan to add guest support with your extensions.
|
||||
///
|
||||
|
||||
constexpr auto vmid{BF_ROOT_VMID};
|
||||
|
||||
/// NOTE:
|
||||
/// - The VP in this simple example does nothing, but we still need
|
||||
/// to create one. The VP is used when you have more than one VS
|
||||
/// per VP (e.g., if you are implementing HyperV's VSM, or nested
|
||||
/// virtualization support). Otherwise, you will always have one
|
||||
/// VS for each VP, and they will appear as the same thing.
|
||||
/// - The VS is what stores the state associated with the VS. It
|
||||
/// is the thing that does most of the work, including storing
|
||||
/// the VMCS/VMCB and other CPU register state that is needed.
|
||||
///
|
||||
|
||||
auto const vpid{mut_vp_pool.allocate(gs, mut_tls, mut_sys, intrinsic, vmid)};
|
||||
if (bsl::unlikely(vpid.is_invalid())) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
auto const vsid{mut_vs_pool.allocate(gs, mut_tls, mut_sys, intrinsic, vpid, ppid)};
|
||||
if (bsl::unlikely(vsid.is_invalid())) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Run the newly created VP on behalf of the root VM using the
|
||||
/// newly created and initialized VS. Note that this version of
|
||||
/// the run function should only be used when starting the
|
||||
/// hypervisor, or switching the VM, VP or VS as it is slow.
|
||||
///
|
||||
|
||||
return mut_sys.bf_vs_op_run(vmid, vpid, vsid);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
103
kernel/integration/support/src/dispatch_fail.hpp
Normal file
103
kernel/integration/support/src/dispatch_fail.hpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/// @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 DISPATCH_FAIL
|
||||
#define DISPATCH_FAIL
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the fail as needed, or returns an error so
|
||||
/// that the microkernel can halt the PP.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_fail(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
syscall::bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t const &vp_pool,
|
||||
vs_pool_t const &vs_pool,
|
||||
bsl::safe_u64 const &errc,
|
||||
bsl::safe_u64 const &addr) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::expects(errc.is_valid_and_checked());
|
||||
bsl::expects(addr.is_valid_and_checked());
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
/// NOTE:
|
||||
/// - Tells the microkernel that we didn't handle the fast fail.
|
||||
/// When this occurs, the microkernel will halt this PP. In most
|
||||
/// cases, there are only two options for how to handle a fail:
|
||||
/// - Do the following, and report an error and halt.
|
||||
/// - Return to a parent VS and continue execution from there,
|
||||
/// which is typically only possible if you are implementing
|
||||
/// more than one VP/VS per PP (e.g., when implementing guest
|
||||
/// support, VSM support or nested virtualization support).
|
||||
///
|
||||
/// - Another use case is integration testing. We can also use this
|
||||
/// to generate faults that we can recover from to ensure the
|
||||
/// fault system works properly during testing.
|
||||
///
|
||||
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
298
kernel/integration/support/src/main.cpp
Normal file
298
kernel/integration/support/src/main.cpp
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/// @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 <bf_control_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_bootstrap.hpp>
|
||||
#include <dispatch_fail.hpp>
|
||||
#include <dispatch_vmexit.hpp>
|
||||
#include <gs_initialize.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// NOTE:
|
||||
/// - This is where we store all of our global and thread local variables.
|
||||
/// All of the variables are marked as static to ensure they are not
|
||||
/// visable to the rest of the code.
|
||||
/// - All global and thread local variables must be passed around from
|
||||
/// function to function as needed. This ensures that constexpr unit
|
||||
/// tests work properly as the rest of the code never relies on global
|
||||
/// variables. In addition, it dramatically simplifies unit testing, so
|
||||
/// enforcing this coding style, although annoying for the function
|
||||
/// signatures, makes working with the rest of the code a lot easier.
|
||||
/// - We use constinit here, which works around a specific AUTOSAR rule
|
||||
/// that does not allow global constructors/destructors. By using
|
||||
/// constinit, we are sure that runtime global constructors are not used.
|
||||
/// Bareflank does not attempt to run any init/fini sections of the
|
||||
/// ELF binary, so if you use accidentally forget constinit, the code
|
||||
/// will likely not execute and fail as a reminder. Instead, use the
|
||||
/// initialization/release pattern that this example provides.
|
||||
/// - From a unit testing point of view, each of these will have dummy
|
||||
/// versions that are used for testing. When the code is compiled, each
|
||||
/// source file and head file is compiled in isolation, meaning they are
|
||||
/// not given include folder access to all of the code. This means that
|
||||
/// each of these must be mocked, and the unit tests are given include
|
||||
/// access to the MOCK. This prevents the need for templates, and
|
||||
/// instead, all mock injection is done using the build system, greatly
|
||||
/// simplifying both the code and branch analysis during unit tests as
|
||||
/// the removal of templates also removes issues with branches being
|
||||
/// counted for each instantiaion of a template type.
|
||||
/// - Finally, some of these are not really needed for this simple example,
|
||||
/// but we added them for completness so that it is easier to get
|
||||
/// started with your own extension as more complicated code will likely
|
||||
/// need most of these if not all.
|
||||
///
|
||||
|
||||
/// @brief stores the bf_syscall_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit bf_syscall_t g_mut_sys{};
|
||||
/// @brief stores the intrinsic_t that this code will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit intrinsic_t g_mut_intrinsic{};
|
||||
|
||||
/// @brief stores the pool of VPs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vp_pool_t g_mut_vp_pool{};
|
||||
/// @brief stores the pool of VSs that we will use
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit vs_pool_t g_mut_vs_pool{};
|
||||
|
||||
/// @brief stores the Global Storage for this extension
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit gs_t g_mut_gs{};
|
||||
/// @brief stores the Thread Local Storage for this extension on this PP
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
constinit thread_local tls_t g_mut_tls{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the bootstrap entry function. This function is
|
||||
/// called on each PP while the hypervisor is being bootstrapped.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param ppid the physical process to bootstrap
|
||||
///
|
||||
extern "C" void
|
||||
bootstrap_entry(bsl::safe_u16::value_type const ppid) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the bootstrap handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_bootstrap( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(ppid))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The bootstrap handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a bootstrap is finished. If this is called, it
|
||||
/// is because the bootstrap handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the fast fail entry function. This is registered
|
||||
/// by the main function to execute whenever a fast fail occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param errc the reason for the failure, which is CPU
|
||||
/// specific. On x86, this is a combination of the exception
|
||||
/// vector and error code.
|
||||
/// @param addr contains a faulting address if the fail reason
|
||||
/// is associated with an error that involves a faulting address (
|
||||
/// for example like a page fault). Otherwise, the value of this
|
||||
/// input is undefined.
|
||||
///
|
||||
extern "C" void
|
||||
fail_entry(bsl::safe_u64::value_type const errc, bsl::safe_u64::value_type const addr) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the fast fail handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_fail( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u64(errc), // --
|
||||
bsl::to_u64(addr))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The fast fail handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a fast fail is finished. If this is called, it
|
||||
/// is because the fast fail handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the VMExit entry function. This is registered
|
||||
/// by the main function to execute whenever a VMExit occurs.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
///
|
||||
extern "C" void
|
||||
vmexit_entry(
|
||||
bsl::safe_u16::value_type const vsid, bsl::safe_u64::value_type const exit_reason) noexcept
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Call into the vmexit handler. This entry point serves as a
|
||||
/// trampoline between C and C++. Specifically, the microkernel
|
||||
/// cannot call a member function directly, and can only call
|
||||
/// a C style function.
|
||||
///
|
||||
|
||||
auto const ret{dispatch_vmexit( // --
|
||||
g_mut_gs, // --
|
||||
g_mut_tls, // --
|
||||
g_mut_sys, // --
|
||||
g_mut_intrinsic, // --
|
||||
g_mut_vp_pool, // --
|
||||
g_mut_vs_pool, // --
|
||||
bsl::to_u16(vsid), // --
|
||||
bsl::to_u64(exit_reason))};
|
||||
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - This code should never be reached. The VMExit handler should
|
||||
/// always call one of the "run" ABIs to return back to the
|
||||
/// microkernel when a VMExit is finished. If this is called, it
|
||||
/// is because the VMExit handler returned with an error.
|
||||
///
|
||||
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Implements the main entry function for this example
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param version the version of the spec implemented by the
|
||||
/// microkernel. This can be used to ensure the extension and the
|
||||
/// microkernel speak the same ABI.
|
||||
///
|
||||
extern "C" void
|
||||
ext_main_entry(bsl::uint32 const version) noexcept
|
||||
{
|
||||
bsl::errc_type mut_ret{};
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the bf_syscall_t. This will validate the ABI version,
|
||||
/// open a handle to the microkernel and register the required
|
||||
/// callbacks. If this fails, we call bf_control_op_exit, which is
|
||||
/// similar to exit() from POSIX, except that the return value is
|
||||
/// always the same.
|
||||
///
|
||||
|
||||
mut_ret = g_mut_sys.initialize( // --
|
||||
bsl::to_u32(version), // --
|
||||
&bootstrap_entry, // --
|
||||
&vmexit_entry, // --
|
||||
&fail_entry); // --
|
||||
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
mut_ret = gs_initialize(g_mut_gs, g_mut_sys, g_mut_intrinsic);
|
||||
if (bsl::unlikely(!mut_ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bf_control_op_exit();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vp_pool_t. This will give all of our vp_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vp_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Initialize the vs_pool_t. This will give all of our vs_t's
|
||||
/// their IDs so that they can be allocated.
|
||||
///
|
||||
|
||||
g_mut_vs_pool.initialize(g_mut_gs, g_mut_tls, g_mut_sys, g_mut_intrinsic);
|
||||
|
||||
/// NOTE:
|
||||
/// - Wait for callbacks. Note that this function does not return.
|
||||
/// The next time the extension is executed, it will be the
|
||||
/// bootstrap callback that was just previously registered, which
|
||||
/// will be called on each PP that is online. Failure to call this
|
||||
/// function leads to undefined behaviour (likely a page fault).
|
||||
/// - This is similar to the wait() function from POSIX after having
|
||||
/// just started some processes, with the difference being that
|
||||
/// this will never return, so there is no need to pass in status
|
||||
/// as there is nothing to process after this call.
|
||||
///
|
||||
|
||||
return bf_control_op_wait();
|
||||
}
|
||||
}
|
||||
251
kernel/integration/support/src/vp_pool_t.hpp
Normal file
251
kernel/integration/support/src/vp_pool_t.hpp
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/// @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 VP_POOL_T_HPP
|
||||
#define VP_POOL_T_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_t.hpp>
|
||||
|
||||
#include <bsl/array.hpp>
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_idx.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's VP pool
|
||||
///
|
||||
class vp_pool_t final
|
||||
{
|
||||
/// @brief stores the pool of vp_t objects
|
||||
bsl::array<vp_t, HYPERVISOR_MAX_VPS.get()> m_pool{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the vp_t associated with the provided vpid.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vpid the ID of the vp_t to get
|
||||
/// @return Returns the vp_t associated with the provided vpid.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
get_vp(bsl::safe_u16 const &vpid) noexcept -> vp_t *
|
||||
{
|
||||
bsl::expects(vpid.is_valid_and_checked());
|
||||
bsl::expects(vpid < bsl::to_u16(m_pool.size()));
|
||||
return m_pool.at_if(bsl::to_idx(vpid));
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the vp_t associated with the provided vpid.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vpid the ID of the vp_t to get
|
||||
/// @return Returns the vp_t associated with the provided vpid.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
get_vp(bsl::safe_u16 const &vpid) const noexcept -> vp_t const *
|
||||
{
|
||||
bsl::expects(vpid.is_valid_and_checked());
|
||||
bsl::expects(vpid < bsl::to_u16(m_pool.size()));
|
||||
return m_pool.at_if(bsl::to_idx(vpid));
|
||||
}
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vp_pool_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
initialize(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
for (bsl::safe_idx mut_i{}; mut_i < m_pool.size(); ++mut_i) {
|
||||
m_pool.at_if(mut_i)->initialize(gs, tls, sys, intrinsic, bsl::to_u16(mut_i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vp_pool_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
for (auto &mut_vp : m_pool) {
|
||||
mut_vp.release(gs, tls, sys, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a VP and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vmid the ID of the VM to assign the newly created VP to
|
||||
/// @return Returns ID of the newly allocated vp_t. Returns
|
||||
/// bsl::safe_u16::failure() on failure.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vmid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Ask the microkernel to create a VP and return the ID of the
|
||||
/// newly created VP.
|
||||
///
|
||||
|
||||
auto const vpid{mut_sys.bf_vp_op_create_vp(vmid)};
|
||||
if (bsl::unlikely(vpid.is_invalid())) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Once a VP has been created, the microkernel returns the ID
|
||||
/// of the newly created VP. We can use this ID to determine
|
||||
/// which vp_t to allocate.
|
||||
///
|
||||
|
||||
return this->get_vp(vpid)->allocate(gs, tls, mut_sys, intrinsic, vmid);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Deallocates the requested vp_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the vp_t to deallocate
|
||||
///
|
||||
constexpr void
|
||||
deallocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vpid) noexcept
|
||||
{
|
||||
auto *const pmut_vp{this->get_vp(vpid)};
|
||||
|
||||
/// NOTE:
|
||||
/// - If the requested VP was allocated, we need to tell the
|
||||
/// microkernel to destroy it. Once that is done we can
|
||||
/// deallocate the vp_t so that it can be used again in the
|
||||
/// future.
|
||||
///
|
||||
|
||||
if (pmut_vp->is_allocated()) {
|
||||
bsl::expects(mut_sys.bf_vp_op_destroy_vp(vpid));
|
||||
pmut_vp->deallocate(gs, tls, mut_sys, intrinsic);
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if the requested vp_t is allocated,
|
||||
/// false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vpid the ID of the vp_t to query
|
||||
/// @return Returns true if the requested vp_t is allocated,
|
||||
/// false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_allocated(bsl::safe_u16 const &vpid) const noexcept -> bool
|
||||
{
|
||||
return this->get_vp(vpid)->is_allocated();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if the requested vp_t is deallocated,
|
||||
/// false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vpid the ID of the vp_t to query
|
||||
/// @return Returns true if the requested vp_t is deallocated,
|
||||
/// false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_deallocated(bsl::safe_u16 const &vpid) const noexcept -> bool
|
||||
{
|
||||
return this->get_vp(vpid)->is_deallocated();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the VM the requested vp_t is assigned
|
||||
/// to. If the vp_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vpid the ID of the vp_t to query
|
||||
/// @return Returns the ID of the VM the requested vp_t is assigned
|
||||
/// to. If the vp_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_vm(bsl::safe_u16 const &vpid) const noexcept -> bsl::safe_u16
|
||||
{
|
||||
return this->get_vp(vpid)->assigned_vm();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
221
kernel/integration/support/src/vp_t.hpp
Normal file
221
kernel/integration/support/src/vp_t.hpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/// @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 VP_T_HPP
|
||||
#define VP_T_HPP
|
||||
|
||||
#include <allocated_status_t.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/ensures.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VP
|
||||
///
|
||||
class vp_t final
|
||||
{
|
||||
/// @brief stores the ID associated with this vp_t
|
||||
bsl::safe_u16 m_id{};
|
||||
/// @brief stores whether or not this vp_t is allocated.
|
||||
allocated_status_t m_allocated{};
|
||||
/// @brief stores the ID of the VM this vp_t is assigned to
|
||||
bsl::safe_u16 m_assigned_vmid{};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vp_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vp_t
|
||||
///
|
||||
constexpr void
|
||||
initialize(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept
|
||||
{
|
||||
bsl::expects(this->id() == BF_INVALID_ID);
|
||||
bsl::expects(m_allocated == allocated_status_t::deallocated);
|
||||
|
||||
bsl::expects(i.is_valid_and_checked());
|
||||
bsl::expects(i != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = ~i;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vp_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
this->deallocate(gs, tls, sys, intrinsic);
|
||||
m_id = {};
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of this vp_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of this vp_t
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
id() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_id.is_valid_and_checked());
|
||||
return ~m_id;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates the vp_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vmid the ID of the VM to assign the vp_t to
|
||||
/// @return Returns ID of this vp_t
|
||||
///
|
||||
[[maybe_unused]] constexpr auto
|
||||
allocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vmid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::expects(this->id() != BF_INVALID_ID);
|
||||
bsl::expects(allocated_status_t::deallocated == m_allocated);
|
||||
|
||||
bsl::expects(vmid.is_valid_and_checked());
|
||||
bsl::expects(vmid != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_assigned_vmid = ~vmid;
|
||||
m_allocated = allocated_status_t::allocated;
|
||||
|
||||
return this->id();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Deallocates the vp_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
deallocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_assigned_vmid = {};
|
||||
m_allocated = allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vp_t is allocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vp_t is allocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_allocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::allocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vp_t is deallocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vp_t is deallocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_deallocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the VM this vp_t is assigned to. If
|
||||
/// this vp_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of the VM this vp_t is assigned to. If
|
||||
/// this vp_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_vm() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_assigned_vmid.is_valid_and_checked());
|
||||
return ~m_assigned_vmid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
270
kernel/integration/support/src/vs_pool_t.hpp
Normal file
270
kernel/integration/support/src/vs_pool_t.hpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/// @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 VS_POOL_T_HPP
|
||||
#define VS_POOL_T_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vs_t.hpp>
|
||||
|
||||
#include <bsl/array.hpp>
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_idx.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's VS pool
|
||||
///
|
||||
class vs_pool_t final
|
||||
{
|
||||
/// @brief stores the pool of vs_t objects
|
||||
bsl::array<vs_t, HYPERVISOR_MAX_VSS.get()> m_pool{};
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the vs_t associated with the provided vsid.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to get
|
||||
/// @return Returns the vs_t associated with the provided vsid.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
get_vs(bsl::safe_u16 const &vsid) noexcept -> vs_t *
|
||||
{
|
||||
bsl::expects(vsid.is_valid_and_checked());
|
||||
bsl::expects(vsid < bsl::to_u16(m_pool.size()));
|
||||
return m_pool.at_if(bsl::to_idx(vsid));
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the vs_t associated with the provided vsid.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to get
|
||||
/// @return Returns the vs_t associated with the provided vsid.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
get_vs(bsl::safe_u16 const &vsid) const noexcept -> vs_t const *
|
||||
{
|
||||
bsl::expects(vsid.is_valid_and_checked());
|
||||
bsl::expects(vsid < bsl::to_u16(m_pool.size()));
|
||||
return m_pool.at_if(bsl::to_idx(vsid));
|
||||
}
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_pool_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
initialize(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
for (bsl::safe_idx mut_i{}; mut_i < m_pool.size(); ++mut_i) {
|
||||
m_pool.at_if(mut_i)->initialize(gs, tls, sys, intrinsic, bsl::to_u16(mut_i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_pool_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
for (auto &mut_vs : m_pool) {
|
||||
mut_vs.release(gs, tls, sys, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a VS and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the newly created VS to
|
||||
/// @param ppid the ID of the PP to assign the newly created VS to
|
||||
/// @return Returns ID of the newly allocated vs_t. Returns
|
||||
/// bsl::safe_u16::failure() on failure.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
/// NOTE:
|
||||
/// - Ask the microkernel to create a VS and return the ID of the
|
||||
/// newly created VS.
|
||||
///
|
||||
|
||||
auto const vsid{mut_sys.bf_vs_op_create_vs(vpid, ppid)};
|
||||
if (bsl::unlikely(vsid.is_invalid())) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Once a VS has been created, the microkernel returns the ID
|
||||
/// of the newly created VS. We can use this ID to determine
|
||||
/// which vs_t to allocate.
|
||||
///
|
||||
|
||||
return this->get_vs(vsid)->allocate(gs, tls, mut_sys, intrinsic, vpid, ppid);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Deallocates the requested vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vsid the ID of the vs_t to deallocate
|
||||
///
|
||||
constexpr void
|
||||
deallocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vsid) noexcept
|
||||
{
|
||||
auto *const pmut_vs{this->get_vs(vsid)};
|
||||
|
||||
/// NOTE:
|
||||
/// - If the requested VS was allocated, we need to tell the
|
||||
/// microkernel to destroy it. Once that is done we can
|
||||
/// deallocate the vs_t so that it can be used again in the
|
||||
/// future.
|
||||
///
|
||||
|
||||
if (pmut_vs->is_allocated()) {
|
||||
bsl::expects(mut_sys.bf_vs_op_destroy_vs(vsid));
|
||||
pmut_vs->deallocate(gs, tls, mut_sys, intrinsic);
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if the requested vs_t is allocated,
|
||||
/// false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to query
|
||||
/// @return Returns true if the requested vs_t is allocated,
|
||||
/// false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_allocated(bsl::safe_u16 const &vsid) const noexcept -> bool
|
||||
{
|
||||
return this->get_vs(vsid)->is_allocated();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if the requested vs_t is deallocated,
|
||||
/// false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to query
|
||||
/// @return Returns true if the requested vs_t is deallocated,
|
||||
/// false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_deallocated(bsl::safe_u16 const &vsid) const noexcept -> bool
|
||||
{
|
||||
return this->get_vs(vsid)->is_deallocated();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the VP the requested vs_t is assigned
|
||||
/// to. If the vs_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to query
|
||||
/// @return Returns the ID of the VP the requested vs_t is assigned
|
||||
/// to. If the vs_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_vp(bsl::safe_u16 const &vsid) const noexcept -> bsl::safe_u16
|
||||
{
|
||||
return this->get_vs(vsid)->assigned_vp();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the PP the requested vs_t is assigned
|
||||
/// to. If the vs_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param vsid the ID of the vs_t to query
|
||||
/// @return Returns the ID of the PP the requested vs_t is assigned
|
||||
/// to. If the vs_t is not assigned, BF_INVALID_ID is
|
||||
/// returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_pp(bsl::safe_u16 const &vsid) const noexcept -> bsl::safe_u16
|
||||
{
|
||||
return this->get_vs(vsid)->assigned_pp();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
94
kernel/integration/support/src/x64/amd/dispatch_vmexit.hpp
Normal file
94
kernel/integration/support/src/x64/amd/dispatch_vmexit.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/// @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 DISPATCH_VMEXIT_HPP
|
||||
#define DISPATCH_VMEXIT_HPP
|
||||
|
||||
#include <bf_debug_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_vmexit_cpuid.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the VMExit.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_vmexit(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t const &vp_pool,
|
||||
vs_pool_t const &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &exit_reason) noexcept -> bsl::errc_type
|
||||
{
|
||||
constexpr auto exit_reason_cpuid{0x72_u64};
|
||||
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
switch (exit_reason.get()) {
|
||||
case exit_reason_cpuid.get(): {
|
||||
return dispatch_vmexit_cpuid(gs, tls, mut_sys, intrinsic, vsid);
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bsl::error() << "unsupported vmexit: " << bsl::hex(exit_reason) << bsl::endl;
|
||||
bf_debug_op_dump_vs(vsid);
|
||||
bsl::print() << bsl::here();
|
||||
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -22,27 +22,38 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef TLS_T_HPP
|
||||
#define TLS_T_HPP
|
||||
#ifndef GS_INITIALIZE_HPP
|
||||
#define GS_INITIALIZE_HPP
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
|
||||
namespace integration
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::tls_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Thread Local Storage (TLS).
|
||||
/// @brief Initializes the Global Storage (GS).
|
||||
///
|
||||
struct tls_t final
|
||||
{};
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
gs_initialize(gs_t const &gs, bf_syscall_t const &sys, intrinsic_t const &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
/// @brief defines the max size supported for the TLS block
|
||||
constexpr auto MAX_TLS_SIZE{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
/// @brief ensure that the tls_t does not exceed the max supported size
|
||||
static_assert(!(sizeof(tls_t) > MAX_TLS_SIZE));
|
||||
return bsl::errc_success;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -22,27 +22,27 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef TLS_T_HPP
|
||||
#define TLS_T_HPP
|
||||
#ifndef GS_T_HPP
|
||||
#define GS_T_HPP
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::tls_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Thread Local Storage (TLS).
|
||||
/// @brief Defines the extension's Global Storage (GS).
|
||||
/// Extensions can use this to store global variables as needed.
|
||||
/// The gs_t can also be used during unit testing to store testing
|
||||
/// specific logic and data to ensure tests can support constexpr
|
||||
/// style unit testing. Also note that this is stored in the arch
|
||||
/// specific folders as it usually needs to store arch specific
|
||||
/// resources.
|
||||
///
|
||||
struct tls_t final
|
||||
{};
|
||||
|
||||
/// @brief defines the max size supported for the TLS block
|
||||
constexpr auto MAX_TLS_SIZE{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
/// @brief ensure that the tls_t does not exceed the max supported size
|
||||
static_assert(!(sizeof(tls_t) > MAX_TLS_SIZE));
|
||||
struct gs_t final
|
||||
{
|
||||
/// @brief dummy data for example purposes only.
|
||||
bsl::safe_umx dummy;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
59
kernel/integration/support/src/x64/amd/tls_initialize.hpp
Normal file
59
kernel/integration/support/src/x64/amd/tls_initialize.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/// @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 TLS_INITIALIZE_HPP
|
||||
#define TLS_INITIALIZE_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes the Thread Local Storage (TLS).
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
tls_initialize(tls_t const &tls, bf_syscall_t const &sys, intrinsic_t const &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -28,15 +28,31 @@
|
|||
#include <bsl/convert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::tls_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Thread Local Storage (TLS).
|
||||
/// Extensions can use this to store data specific to a PP as needed.
|
||||
/// The tls_t can also be used during unit testing to store testing
|
||||
/// specific logic and data to ensure tests can support constexpr
|
||||
/// style unit testing. Also note that this is stored in the arch
|
||||
/// specific folders as it usually needs to store arch specific
|
||||
/// resources. In this simple example, we leave this empty.
|
||||
///
|
||||
/// <!-- notes -->
|
||||
/// @note IMPORTANT: Extensions are limited to a single 4k page for the
|
||||
/// TLS data. Technically, extensions are given 2 4k pages, but one of
|
||||
/// the pages is dedicated to TLS data defined by the specification
|
||||
/// and populated by the microkernel (e.g., the general purpose
|
||||
/// registers and ID information). For this reason, if more than a
|
||||
/// page is needed, the TLS block should store pointers to memory that
|
||||
/// is allocated.
|
||||
///
|
||||
struct tls_t final
|
||||
{};
|
||||
{
|
||||
/// @brief dummy data for example purposes only.
|
||||
bsl::safe_umx dummy;
|
||||
};
|
||||
|
||||
/// @brief defines the max size supported for the TLS block
|
||||
constexpr auto MAX_TLS_SIZE{HYPERVISOR_PAGE_SIZE};
|
||||
266
kernel/integration/support/src/x64/amd/vs_t.hpp
Normal file
266
kernel/integration/support/src/x64/amd/vs_t.hpp
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/// @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 VS_T_HPP
|
||||
#define VS_T_HPP
|
||||
|
||||
#include <allocated_status_t.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/ensures.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VS
|
||||
///
|
||||
class vs_t final
|
||||
{
|
||||
/// @brief stores the ID associated with this vs_t
|
||||
bsl::safe_u16 m_id{};
|
||||
/// @brief stores whether or not this vs_t is allocated.
|
||||
allocated_status_t m_allocated{};
|
||||
/// @brief stores the ID of the VP this vs_t is assigned to
|
||||
bsl::safe_u16 m_assigned_vpid{};
|
||||
/// @brief stores the ID of the PP this vs_t is assigned to
|
||||
bsl::safe_u16 m_assigned_ppid{};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vs_t
|
||||
///
|
||||
constexpr void
|
||||
initialize(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept
|
||||
{
|
||||
bsl::expects(this->id() == BF_INVALID_ID);
|
||||
bsl::expects(m_allocated == allocated_status_t::deallocated);
|
||||
|
||||
bsl::expects(i.is_valid_and_checked());
|
||||
bsl::expects(i != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = ~i;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
this->deallocate(gs, tls, sys, intrinsic);
|
||||
m_id = {};
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of this vs_t
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
id() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_id.is_valid_and_checked());
|
||||
return ~m_id;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates the vs_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the vs_t to
|
||||
/// @param ppid the ID of the PP to assign the vs_t to
|
||||
/// @return Returns ID of this vs_t
|
||||
///
|
||||
[[maybe_unused]] constexpr auto
|
||||
allocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
auto const vsid{this->id()};
|
||||
|
||||
bsl::expects(vsid != BF_INVALID_ID);
|
||||
bsl::expects(allocated_status_t::deallocated == m_allocated);
|
||||
|
||||
bsl::expects(vpid.is_valid_and_checked());
|
||||
bsl::expects(vpid != BF_INVALID_ID);
|
||||
bsl::expects(ppid.is_valid_and_checked());
|
||||
bsl::expects(ppid != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
constexpr auto guest_asid_val{0x1_u64};
|
||||
constexpr auto guest_asid_idx{bf_reg_t::bf_reg_t_guest_asid};
|
||||
bsl::expects(mut_sys.bf_vs_op_write(this->id(), guest_asid_idx, guest_asid_val));
|
||||
|
||||
constexpr auto intercept1_val{0x00040000_u64};
|
||||
constexpr auto intercept1_idx{bf_reg_t::bf_reg_t_intercept_instruction1};
|
||||
bsl::expects(mut_sys.bf_vs_op_write(this->id(), intercept1_idx, intercept1_val));
|
||||
|
||||
constexpr auto intercept2_val{0x00000001_u64};
|
||||
constexpr auto intercept2_idx{bf_reg_t::bf_reg_t_intercept_instruction2};
|
||||
bsl::expects(mut_sys.bf_vs_op_write(this->id(), intercept2_idx, intercept2_val));
|
||||
|
||||
if (mut_sys.is_vs_a_root_vs(this->id())) {
|
||||
bsl::expects(mut_sys.bf_vs_op_init_as_root(this->id()));
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
m_assigned_vpid = ~vpid;
|
||||
m_assigned_ppid = ~ppid;
|
||||
m_allocated = allocated_status_t::allocated;
|
||||
|
||||
return vsid;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Deallocates the vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
deallocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_assigned_ppid = {};
|
||||
m_assigned_vpid = {};
|
||||
m_allocated = allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vs_t is allocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vs_t is allocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_allocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::allocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vs_t is deallocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vs_t is deallocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_deallocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the VP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of the VP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_vp() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_assigned_vpid.is_valid_and_checked());
|
||||
return ~m_assigned_vpid;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the PP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of the PP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_pp() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_assigned_ppid.is_valid_and_checked());
|
||||
return ~m_assigned_ppid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
191
kernel/integration/support/src/x64/dispatch_vmexit_cpuid.hpp
Normal file
191
kernel/integration/support/src/x64/dispatch_vmexit_cpuid.hpp
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/// @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 DISPATCH_VMEXIT_CPUID_HPP
|
||||
#define DISPATCH_VMEXIT_CPUID_HPP
|
||||
|
||||
#include <bf_debug_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <cpuid_commands.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Handles the CPUID VMexit
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_vmexit_cpuid(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vsid) noexcept -> bsl::errc_type
|
||||
{
|
||||
auto mut_rax{mut_sys.bf_tls_rax()};
|
||||
auto mut_rcx{mut_sys.bf_tls_rcx()};
|
||||
|
||||
/// NOTE:
|
||||
/// - Before we execute CPUID, we need to check to see if we have
|
||||
/// received a CPUID command. If we have, we need to handle this
|
||||
/// CPUID differently. These are defined in the loader.
|
||||
///
|
||||
|
||||
if (loader::CPUID_COMMAND_EAX == bsl::to_u32_unsafe(mut_rax)) {
|
||||
switch (bsl::to_u32_unsafe(mut_rcx).get()) {
|
||||
case loader::CPUID_COMMAND_ECX_STOP.get(): {
|
||||
|
||||
/// NOTE:
|
||||
/// - If this is the first PP to stop (which is the
|
||||
/// last PP in the list as we stop in reverse order),
|
||||
/// print out how much memory was used by the
|
||||
/// hypervisor. This is optional of course.
|
||||
///
|
||||
|
||||
auto const last_online_ppid{(mut_sys.bf_tls_online_pps() - 1_u16).checked()};
|
||||
if (mut_sys.bf_tls_ppid() == last_online_ppid) {
|
||||
bsl::print() << bsl::endl;
|
||||
bf_debug_op_dump_page_pool();
|
||||
bsl::print() << bsl::endl;
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - The following is another optional debug feature that
|
||||
/// will show a log of the most recent VMExits that have
|
||||
/// occurred.
|
||||
///
|
||||
|
||||
if constexpr (bsl::debug_level_is_at_least_vv()) {
|
||||
bsl::print() << bsl::endl;
|
||||
bf_debug_op_dump_vmexit_log(mut_sys.bf_tls_ppid());
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
/// - Report that the root OS is no longer in a VM for
|
||||
/// this specific PP. Note that you can do whatever
|
||||
/// you want here, this is just the default behavior.
|
||||
/// To report success on promotion after promotion
|
||||
/// takes place would require that the loader reports
|
||||
/// success, which we do not do as we are not sure
|
||||
/// what the extension wants, so the message here
|
||||
/// should state that we are "about to", and not that
|
||||
/// it is "done", because it might fail.
|
||||
///
|
||||
|
||||
bsl::debug() << bsl::rst << "about to" // --
|
||||
<< bsl::red << " promote " // --
|
||||
<< bsl::rst << "root OS on pp " // --
|
||||
<< bsl::cyn << bsl::hex(mut_sys.bf_tls_ppid()) // --
|
||||
<< bsl::rst << bsl::endl; // --
|
||||
|
||||
/// NOTE:
|
||||
/// - The promote ABI will load the microkernel by
|
||||
/// replacing the CPU's state withthe VP state
|
||||
/// associated with the provided VSID. If all
|
||||
/// goes well, bf_vs_op_promote will not return,
|
||||
/// and the system will continue executing with the
|
||||
/// hypervisor turned off.
|
||||
///
|
||||
|
||||
mut_sys.bf_tls_set_rax(loader::CPUID_COMMAND_RAX_SUCCESS);
|
||||
return mut_sys.bf_vs_op_promote(vsid);
|
||||
}
|
||||
|
||||
case loader::CPUID_COMMAND_ECX_REPORT_ON.get(): {
|
||||
|
||||
/// NOTE:
|
||||
/// - Report that the root OS is now in a VM for this
|
||||
/// specific PP.
|
||||
///
|
||||
|
||||
bsl::debug() << bsl::rst << "root OS had been" // --
|
||||
<< bsl::grn << " demoted " // --
|
||||
<< bsl::rst << "to vm " // --
|
||||
<< bsl::cyn << bsl::hex(mut_sys.bf_tls_vmid()) // --
|
||||
<< bsl::rst << " on pp " // --
|
||||
<< bsl::cyn << bsl::hex(mut_sys.bf_tls_ppid()) // --
|
||||
<< bsl::rst << bsl::endl; // --
|
||||
|
||||
mut_sys.bf_tls_set_rax(loader::CPUID_COMMAND_RAX_SUCCESS);
|
||||
return mut_sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
|
||||
case loader::CPUID_COMMAND_ECX_REPORT_OFF.get(): {
|
||||
|
||||
/// NOTE:
|
||||
/// - There is nothing to do here as we report off
|
||||
/// right before promotion takes place.
|
||||
///
|
||||
|
||||
mut_sys.bf_tls_set_rax(loader::CPUID_COMMAND_RAX_SUCCESS);
|
||||
return mut_sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
|
||||
default: {
|
||||
bsl::error() << "unsupported cpuid command " // --
|
||||
<< bsl::hex(mut_rcx) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mut_sys.bf_tls_set_rax(loader::CPUID_COMMAND_RAX_FAILURE);
|
||||
return mut_sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
|
||||
auto mut_rbx{mut_sys.bf_tls_rbx()};
|
||||
auto mut_rdx{mut_sys.bf_tls_rdx()};
|
||||
intrinsic.cpuid(gs, tls, mut_rax, mut_rbx, mut_rcx, mut_rdx);
|
||||
|
||||
mut_sys.bf_tls_set_rax(mut_rax);
|
||||
mut_sys.bf_tls_set_rbx(mut_rbx);
|
||||
mut_sys.bf_tls_set_rcx(mut_rcx);
|
||||
mut_sys.bf_tls_set_rdx(mut_rdx);
|
||||
return mut_sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
106
kernel/integration/support/src/x64/intel/dispatch_vmexit.hpp
Normal file
106
kernel/integration/support/src/x64/intel/dispatch_vmexit.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/// @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 DISPATCH_VMEXIT_HPP
|
||||
#define DISPATCH_VMEXIT_HPP
|
||||
|
||||
#include <bf_debug_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <dispatch_vmexit_cpuid.hpp>
|
||||
#include <dispatch_vmexit_nmi.hpp>
|
||||
#include <dispatch_vmexit_nmi_window.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the VMExit.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_vmexit(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
vp_pool_t const &vp_pool,
|
||||
vs_pool_t const &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &exit_reason) noexcept -> bsl::errc_type
|
||||
{
|
||||
constexpr auto exit_reason_nmi{0x0_u64};
|
||||
constexpr auto exit_reason_nmi_window{0x8_u64};
|
||||
constexpr auto exit_reason_cpuid{0xA_u64};
|
||||
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
switch (exit_reason.get()) {
|
||||
case exit_reason_nmi.get(): {
|
||||
return dispatch_vmexit_nmi(gs, tls, mut_sys, vsid);
|
||||
}
|
||||
|
||||
case exit_reason_nmi_window.get(): {
|
||||
return dispatch_vmexit_nmi_window(gs, tls, mut_sys, vsid);
|
||||
}
|
||||
|
||||
case exit_reason_cpuid.get(): {
|
||||
return dispatch_vmexit_cpuid(gs, tls, mut_sys, intrinsic, vsid);
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bsl::error() << "unsupported vmexit: " << bsl::hex(exit_reason) << bsl::endl;
|
||||
bf_debug_op_dump_vs(vsid);
|
||||
bsl::print() << bsl::here();
|
||||
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/// @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 DISPATCH_VMEXIT_NMI_HPP
|
||||
#define DISPATCH_VMEXIT_NMI_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Handle NMIs. This is required by Intel.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_vmexit_nmi(
|
||||
gs_t const &gs, tls_t const &tls, bf_syscall_t &mut_sys, bsl::safe_u16 const &vsid) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
constexpr auto vmcs_set_nmi_window_exiting{0x400000_u64};
|
||||
constexpr auto ctls_idx{bf_reg_t::bf_reg_t_primary_proc_based_vm_execution_ctls};
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
||||
/// NOTE:
|
||||
/// - If we caught an NMI, we need to inject it into the VM. To do
|
||||
/// this, all we do is enable the NMI window, which will tell us
|
||||
/// when we can safely inject the NMI.
|
||||
/// - Note that the microkernel will do the same thing. If an NMI
|
||||
/// fires while the hypevisor is running, it will enable the NMI
|
||||
/// window, which the extension will see as a VMExit, and must
|
||||
/// from there, inject the NMI into the appropriate VS.
|
||||
///
|
||||
|
||||
auto mut_val{mut_sys.bf_vs_op_read(vsid, ctls_idx)};
|
||||
bsl::expects(mut_val.is_valid_and_checked());
|
||||
|
||||
mut_val |= vmcs_set_nmi_window_exiting;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, ctls_idx, mut_val));
|
||||
|
||||
return mut_sys.bf_vs_op_run_current();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/// @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 DISPATCH_VMEXIT_NMI_WINDOW_HPP
|
||||
#define DISPATCH_VMEXIT_NMI_WINDOW_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Handle NMIs Windows
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch_vmexit_nmi_window(
|
||||
gs_t const &gs, tls_t const &tls, bf_syscall_t &mut_sys, bsl::safe_u16 const &vsid) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
constexpr auto nmi_info{0x80000202_u64};
|
||||
constexpr auto vmcs_clear_nmi_window_exiting{0xFFBFFFFF_u64};
|
||||
constexpr auto ctls_idx{bf_reg_t::bf_reg_t_primary_proc_based_vm_execution_ctls};
|
||||
constexpr auto info_idx{bf_reg_t::bf_reg_t_vmentry_interrupt_information_field};
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
||||
/// NOTE:
|
||||
/// - If we see this exit, it is because an NMI fired. There are two
|
||||
/// situations where this could occur, either while the hypervisor
|
||||
/// is running, or the VS is running. In either case, we need to
|
||||
/// clear the NMI window and inject the NMI into the appropriate
|
||||
/// VS so that it can be handled. Note that Intel requires that
|
||||
/// we handle NMIs, and they actually happen a lot with Linux based
|
||||
/// on what hardware you are using (e.g., a laptop).
|
||||
///
|
||||
|
||||
auto mut_val{mut_sys.bf_vs_op_read(vsid, ctls_idx)};
|
||||
bsl::expects(mut_val.is_valid_and_checked());
|
||||
|
||||
mut_val &= vmcs_clear_nmi_window_exiting;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, ctls_idx, mut_val));
|
||||
|
||||
/// NOTE:
|
||||
/// - Inject an NMI. If the NMI window was enabled, it is because we
|
||||
/// need to inject a NMI. Note that the NMI window can be enabled
|
||||
/// both by this extension, as well as by the microkernel itself,
|
||||
/// so we are required to implement it on Intel.
|
||||
///
|
||||
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, info_idx, nmi_info));
|
||||
return mut_sys.bf_vs_op_run_current();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -22,46 +22,45 @@
|
|||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
/// SOFTWARE.
|
||||
|
||||
#ifndef FAST_FAIL_HPP
|
||||
#define FAST_FAIL_HPP
|
||||
#ifndef GS_INITIALIZE_HPP
|
||||
#define GS_INITIALIZE_HPP
|
||||
|
||||
#include <ext_t.hpp>
|
||||
#include <return_to_vmexit_loop.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <basic_page_4k_t.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace mk
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Provides the main entry point for fast fails that occur
|
||||
/// after a successful launch of the hypervisor. Once this occurs,
|
||||
/// the fast fail path either has to be handled by an extension,
|
||||
/// or a halt() will occur.
|
||||
/// @brief Initializes the Global Storage (GS).
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param mut_tls the current TLS block
|
||||
/// @param mut_intrinsic the intrinsic_t to use
|
||||
/// @param pmut_ext the ext_t to handle the fail
|
||||
/// @return Returns bsl::errc_success if the fail was handled,
|
||||
/// bsl::errc_failure otherwise.
|
||||
/// @param mut_gs the gs_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
fast_fail(tls_t &mut_tls, intrinsic_t &mut_intrinsic, ext_t *const pmut_ext) noexcept
|
||||
gs_initialize(gs_t &mut_gs, bf_syscall_t &mut_sys, intrinsic_t const &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
if (nullptr != pmut_ext) {
|
||||
auto const ret{pmut_ext->fail(mut_tls, mut_intrinsic)};
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
return bsl::errc_success;
|
||||
mut_gs.msr_bitmap =
|
||||
mut_sys.bf_mem_op_alloc_page<lib::basic_page_4k_t>(mut_gs.msr_bitmap_phys);
|
||||
if (bsl::unlikely(nullptr == mut_gs.msr_bitmap)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
|
||||
return bsl::errc_failure;
|
||||
return bsl::errc_success;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -25,19 +25,27 @@
|
|||
#ifndef GS_T_HPP
|
||||
#define GS_T_HPP
|
||||
|
||||
#include <basic_page_4k_t.hpp>
|
||||
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::gs_t
|
||||
/// @class example::gs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Global Storage (GS).
|
||||
/// Extensions can use this to store global variables as needed.
|
||||
/// The gs_t can also be used during unit testing to store testing
|
||||
/// specific logic and data to ensure tests can support constexpr
|
||||
/// style unit testing. Also note that this is stored in the arch
|
||||
/// specific folders as it usually needs to store arch specific
|
||||
/// resources.
|
||||
///
|
||||
struct gs_t final
|
||||
{
|
||||
/// @brief stores the MSR bitmap used by this vs_t
|
||||
void *msr_bitmap{};
|
||||
lib::basic_page_4k_t *msr_bitmap{};
|
||||
/// @brief stores the physical address of the MSR bitmap above
|
||||
bsl::safe_umx msr_bitmap_phys{};
|
||||
};
|
||||
59
kernel/integration/support/src/x64/intel/tls_initialize.hpp
Normal file
59
kernel/integration/support/src/x64/intel/tls_initialize.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/// @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 TLS_INITIALIZE_HPP
|
||||
#define TLS_INITIALIZE_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes the Thread Local Storage (TLS).
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
tls_initialize(tls_t const &tls, bf_syscall_t const &sys, intrinsic_t const &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
66
kernel/integration/support/src/x64/intel/tls_t.hpp
Normal file
66
kernel/integration/support/src/x64/intel/tls_t.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/// @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 TLS_T_HPP
|
||||
#define TLS_T_HPP
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// @class example::tls_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's Thread Local Storage (TLS).
|
||||
/// Extensions can use this to store data specific to a PP as needed.
|
||||
/// The tls_t can also be used during unit testing to store testing
|
||||
/// specific logic and data to ensure tests can support constexpr
|
||||
/// style unit testing. Also note that this is stored in the arch
|
||||
/// specific folders as it usually needs to store arch specific
|
||||
/// resources. In this simple example, we leave this empty.
|
||||
///
|
||||
/// <!-- notes -->
|
||||
/// @note IMPORTANT: Extensions are limited to a single 4k page for the
|
||||
/// TLS data. Technically, extensions are given 2 4k pages, but one of
|
||||
/// the pages is dedicated to TLS data defined by the specification
|
||||
/// and populated by the microkernel (e.g., the general purpose
|
||||
/// registers and ID information). For this reason, if more than a
|
||||
/// page is needed, the TLS block should store pointers to memory that
|
||||
/// is allocated.
|
||||
///
|
||||
struct tls_t final
|
||||
{
|
||||
/// @brief dummy data for example purposes only.
|
||||
bsl::safe_umx dummy;
|
||||
};
|
||||
|
||||
/// @brief defines the max size supported for the TLS block
|
||||
constexpr auto MAX_TLS_SIZE{HYPERVISOR_PAGE_SIZE};
|
||||
|
||||
/// @brief ensure that the tls_t does not exceed the max supported size
|
||||
static_assert(!(sizeof(tls_t) > MAX_TLS_SIZE));
|
||||
}
|
||||
|
||||
#endif
|
||||
323
kernel/integration/support/src/x64/intel/vs_t.hpp
Normal file
323
kernel/integration/support/src/x64/intel/vs_t.hpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
/// @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 VS_T_HPP
|
||||
#define VS_T_HPP
|
||||
|
||||
#include <allocated_status_t.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/convert.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/ensures.hpp>
|
||||
#include <bsl/expects.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the masked version of the VMCS control fields
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param val the value of the control fields read from the MSRs
|
||||
/// @return The masked version of the control fields.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
ctls_mask(bsl::safe_u64 const &val) noexcept -> bsl::safe_u64
|
||||
{
|
||||
constexpr auto mask{0x00000000FFFFFFFF_u64};
|
||||
constexpr auto shift{32_u64};
|
||||
return ((val & mask) & (val >> shift)).checked();
|
||||
};
|
||||
|
||||
/// @class example::vs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VS
|
||||
///
|
||||
class vs_t final
|
||||
{
|
||||
/// @brief stores the ID associated with this vs_t
|
||||
bsl::safe_u16 m_id{};
|
||||
/// @brief stores whether or not this vs_t is allocated.
|
||||
allocated_status_t m_allocated{};
|
||||
/// @brief stores the ID of the VP this vs_t is assigned to
|
||||
bsl::safe_u16 m_assigned_vpid{};
|
||||
/// @brief stores the ID of the PP this vs_t is assigned to
|
||||
bsl::safe_u16 m_assigned_ppid{};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vs_t
|
||||
///
|
||||
constexpr void
|
||||
initialize(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept
|
||||
{
|
||||
bsl::expects(this->id() == BF_INVALID_ID);
|
||||
bsl::expects(m_allocated == allocated_status_t::deallocated);
|
||||
|
||||
bsl::expects(i.is_valid_and_checked());
|
||||
bsl::expects(i != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = ~i;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
this->deallocate(gs, tls, sys, intrinsic);
|
||||
m_id = {};
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of this vs_t
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
id() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_id.is_valid_and_checked());
|
||||
return ~m_id;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates the vs_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the vs_t to
|
||||
/// @param ppid the ID of the PP to assign the vs_t to
|
||||
/// @return Returns ID of this vs_t
|
||||
///
|
||||
[[maybe_unused]] constexpr auto
|
||||
allocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t &mut_sys,
|
||||
intrinsic_t const &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
bf_reg_t mut_idx{};
|
||||
auto const vsid{this->id()};
|
||||
|
||||
bsl::expects(vsid != BF_INVALID_ID);
|
||||
bsl::expects(allocated_status_t::deallocated == m_allocated);
|
||||
|
||||
bsl::expects(vpid.is_valid_and_checked());
|
||||
bsl::expects(vpid != BF_INVALID_ID);
|
||||
bsl::expects(ppid.is_valid_and_checked());
|
||||
bsl::expects(ppid != BF_INVALID_ID);
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
auto const vmcs_vpid_val{bsl::safe_u64::magic_1()};
|
||||
constexpr auto vmcs_vpid_idx{bf_reg_t::bf_reg_t_virtual_processor_identifier};
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, vmcs_vpid_idx, vmcs_vpid_val));
|
||||
|
||||
constexpr auto vmcs_link_ptr_val{0xFFFFFFFFFFFFFFFF_u64};
|
||||
constexpr auto vmcs_link_ptr_idx{bf_reg_t::bf_reg_t_vmcs_link_pointer};
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, vmcs_link_ptr_idx, vmcs_link_ptr_val));
|
||||
|
||||
bsl::safe_u64 mut_pin_ctls{};
|
||||
bsl::safe_u64 mut_proc_ctls{};
|
||||
bsl::safe_u64 mut_exit_ctls{};
|
||||
bsl::safe_u64 mut_entry_ctls{};
|
||||
bsl::safe_u64 mut_proc2_ctls{};
|
||||
|
||||
constexpr auto enable_msr_bitmaps{0x10000000_u64};
|
||||
constexpr auto enable_proc2_ctls{0x80000000_u64};
|
||||
|
||||
mut_proc_ctls |= enable_msr_bitmaps;
|
||||
mut_proc_ctls |= enable_proc2_ctls;
|
||||
|
||||
constexpr auto enable_ia32e_mode{0x00000200_u64};
|
||||
|
||||
mut_entry_ctls |= enable_ia32e_mode;
|
||||
|
||||
constexpr auto enable_vpid{0x00000020_u64};
|
||||
constexpr auto enable_rdtscp{0x00000008_u64};
|
||||
constexpr auto enable_invpcid{0x00001000_u64};
|
||||
constexpr auto enable_xsave{0x00100000_u64};
|
||||
constexpr auto enable_uwait{0x04000000_u64};
|
||||
|
||||
mut_proc2_ctls |= enable_vpid;
|
||||
mut_proc2_ctls |= enable_rdtscp;
|
||||
mut_proc2_ctls |= enable_invpcid;
|
||||
mut_proc2_ctls |= enable_xsave;
|
||||
mut_proc2_ctls |= enable_uwait;
|
||||
|
||||
mut_idx = bf_reg_t::bf_reg_t_pin_based_vm_execution_ctls;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, mut_idx, mut_pin_ctls));
|
||||
|
||||
mut_idx = bf_reg_t::bf_reg_t_primary_proc_based_vm_execution_ctls;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, mut_idx, mut_proc_ctls));
|
||||
|
||||
mut_idx = bf_reg_t::bf_reg_t_vmexit_ctls;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, mut_idx, mut_exit_ctls));
|
||||
|
||||
mut_idx = bf_reg_t::bf_reg_t_vmentry_ctls;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, mut_idx, mut_entry_ctls));
|
||||
|
||||
mut_idx = bf_reg_t::bf_reg_t_secondary_proc_based_vm_execution_ctls;
|
||||
bsl::expects(mut_sys.bf_vs_op_write(vsid, mut_idx, mut_proc2_ctls));
|
||||
|
||||
if (mut_sys.is_vs_a_root_vs(vsid)) {
|
||||
bsl::expects(mut_sys.bf_vs_op_init_as_root(vsid));
|
||||
}
|
||||
else {
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
m_assigned_vpid = ~vpid;
|
||||
m_assigned_ppid = ~ppid;
|
||||
m_allocated = allocated_status_t::allocated;
|
||||
|
||||
return vsid;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Deallocates the vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
deallocate(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bf_syscall_t const &sys,
|
||||
intrinsic_t const &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_assigned_ppid = {};
|
||||
m_assigned_vpid = {};
|
||||
m_allocated = allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vs_t is allocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vs_t is allocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_allocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::allocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns true if this vs_t is deallocated, false otherwise
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns true if this vs_t is deallocated, false otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
is_deallocated() const noexcept -> bool
|
||||
{
|
||||
return m_allocated == allocated_status_t::deallocated;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the VP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of the VP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_vp() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_assigned_vpid.is_valid_and_checked());
|
||||
return ~m_assigned_vpid;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Returns the ID of the PP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @return Returns the ID of the PP this vs_t is assigned to. If
|
||||
/// this vs_t is not assigned, BF_INVALID_ID is returned.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
assigned_pp() const noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::ensures(m_assigned_ppid.is_valid_and_checked());
|
||||
return ~m_assigned_ppid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -35,15 +35,15 @@ intrinsic_cpuid_impl:
|
|||
mov r10, rdx
|
||||
mov r11, rcx
|
||||
|
||||
mov eax, [rdi]
|
||||
mov ebx, [rsi]
|
||||
mov ecx, [r10]
|
||||
mov edx, [r11]
|
||||
mov eax, [rsi]
|
||||
mov ebx, [r10]
|
||||
mov ecx, [r11]
|
||||
mov edx, [r8]
|
||||
cpuid
|
||||
mov [rdi], eax
|
||||
mov [rsi], ebx
|
||||
mov [r10], ecx
|
||||
mov [r11], edx
|
||||
mov [rsi], eax
|
||||
mov [r10], ebx
|
||||
mov [r11], ecx
|
||||
mov [r8], edx
|
||||
|
||||
pop rbx
|
||||
ret
|
||||
|
|
@ -25,25 +25,29 @@
|
|||
#ifndef INTRINSIC_IMPL_PROTOTYPES_HPP
|
||||
#define INTRINSIC_IMPL_PROTOTYPES_HPP
|
||||
|
||||
#include <gs_t.hpp>
|
||||
|
||||
#include <bsl/cstdint.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// <!-- description -->
|
||||
/// @brief Executes the CPUID instruction given the provided EAX and ECX
|
||||
/// and returns the results
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param rax the index used by CPUID, returns resulting rax
|
||||
/// @param rbx returns resulting rbx
|
||||
/// @param rcx the subindex used by CPUID, returns the resulting rcx
|
||||
/// @param rdx returns resulting rdx
|
||||
/// @param gs ignored
|
||||
/// @param pmut_rax the index used by CPUID, returns resulting rax
|
||||
/// @param pmut_rbx returns resulting rbx
|
||||
/// @param pmut_rcx the subindex used by CPUID, returns the resulting rcx
|
||||
/// @param pmut_rdx returns resulting rdx
|
||||
///
|
||||
extern "C" void intrinsic_cpuid_impl(
|
||||
bsl::uint64 *const rax,
|
||||
bsl::uint64 *const rbx,
|
||||
bsl::uint64 *const rcx,
|
||||
bsl::uint64 *const rdx) noexcept;
|
||||
gs_t const *const gs,
|
||||
bsl::uint64 *const pmut_rax,
|
||||
bsl::uint64 *const pmut_rbx,
|
||||
bsl::uint64 *const pmut_rcx,
|
||||
bsl::uint64 *const pmut_rdx) noexcept;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -26,52 +26,46 @@
|
|||
#define INTRINSIC_HPP
|
||||
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_cpuid_impl.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
namespace syscall
|
||||
{
|
||||
/// @class integration::intrinsic_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Provides raw access to intrinsics.
|
||||
/// @brief Provides raw access to intrinsics. Instead of using global
|
||||
/// functions, the intrinsics class provides a means for the rest of
|
||||
/// the extension to mock the intrinsics when needed during testing.
|
||||
///
|
||||
class intrinsic_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this intrinsic_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(gs_t &gs, tls_t &tls) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the intrinsic_t.
|
||||
/// @brief Executes the CPUID instruction given the provided
|
||||
/// EAX and ECX and returns the results.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param mut_rax the index used by CPUID, returns resulting rax
|
||||
/// @param mut_rbx returns resulting rbx
|
||||
/// @param mut_rcx the subindex used by CPUID, returns the resulting rcx
|
||||
/// @param mut_rdx returns resulting rdx
|
||||
///
|
||||
static constexpr void
|
||||
release(gs_t &gs, tls_t &tls) noexcept
|
||||
cpuid(
|
||||
gs_t const &gs,
|
||||
tls_t const &tls,
|
||||
bsl::safe_u64 &mut_rax,
|
||||
bsl::safe_u64 &mut_rbx,
|
||||
bsl::safe_u64 &mut_rcx,
|
||||
bsl::safe_u64 &mut_rdx) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
intrinsic_cpuid_impl(
|
||||
&gs, mut_rax.data(), mut_rbx.data(), mut_rcx.data(), mut_rdx.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
/// @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 VP_POOL_T_HPP
|
||||
#define VP_POOL_T_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_t.hpp>
|
||||
|
||||
#include <bsl/array.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/finally.hpp>
|
||||
#include <bsl/finally_assert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vp_pool_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's vp_pool_t
|
||||
///
|
||||
class vp_pool_t final
|
||||
{
|
||||
/// @brief stores the pool of VPs
|
||||
bsl::array<vp_t, HYPERVISOR_MAX_VS.get()> m_pool{};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vp_pool_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
initialize(
|
||||
gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
bsl::finally_assert release_on_error{
|
||||
[this, &gs, &tls, &sys, &intrinsic]() noexcept -> void {
|
||||
this->release(gs, tls, sys, intrinsic);
|
||||
}};
|
||||
|
||||
bsl::errc_type ret{};
|
||||
for (bsl::safe_umx i{}; i < m_pool.size(); ++i) {
|
||||
ret = m_pool.at_if(i)->initialize(gs, tls, sys, intrinsic, bsl::to_u16(i));
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
release_on_error.ignore();
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vp_pool_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
{
|
||||
for (bsl::safe_umx i{}; i < m_pool.size(); ++i) {
|
||||
m_pool.at_if(i)->release(gs, tls, sys, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a VP and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vmid the ID of the VM to assign the newly created VP to
|
||||
/// @param ppid the ID of the PP to assign the newly created VP to
|
||||
/// @return Returns the ID of the newly created VP on
|
||||
/// success, or bsl::safe_u16::failure() on failure.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &vmid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
auto mut_vpid{sys.bf_vp_op_create_vp(vmid, ppid)};
|
||||
if (bsl::unlikely(!mut_vpid)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
bsl::finally destroy_vp_on_error{[&sys, &mut_vpid]() noexcept -> void {
|
||||
bsl::discard(sys.bf_vp_op_destroy_vp(mut_vpid));
|
||||
}};
|
||||
|
||||
auto *const vp{m_pool.at_if(bsl::to_idx(mut_vpid))};
|
||||
if (bsl::unlikely(nullptr == vp)) {
|
||||
bsl::error() << "mut_vpid " // --
|
||||
<< bsl::hex(mut_vpid) // --
|
||||
<< " provided by the microkernel is invalid" // --
|
||||
<< " or greater than or equal to the HYPERVISOR_MAX_VS " // --
|
||||
<< bsl::hex(HYPERVISOR_MAX_VS) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
ret = vp->allocate(gs, tls, sys, intrinsic, vmid, ppid);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
destroy_vp_on_error.ignore();
|
||||
return mut_vpid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/// @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 VP_T_HPP
|
||||
#define VP_T_HPP
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vp_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VP
|
||||
///
|
||||
class vp_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vp_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vp_t
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(i);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vp_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a vp_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vmid the ID of the VM to assign the vp_t to
|
||||
/// @param ppid the ID of the PP to assign the vp_t to
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
allocate(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &vmid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vmid);
|
||||
bsl::discard(ppid);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
/// @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 VS_POOL_T_HPP
|
||||
#define VS_POOL_T_HPP
|
||||
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vs_t.hpp>
|
||||
|
||||
#include <bsl/array.hpp>
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/finally.hpp>
|
||||
#include <bsl/finally_assert.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vs_pool_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's vs_pool_t
|
||||
///
|
||||
class vs_pool_t final
|
||||
{
|
||||
/// @brief stores the pool of VSs
|
||||
bsl::array<vs_t, HYPERVISOR_MAX_VSS.get()> m_pool{};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_pool_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
initialize(
|
||||
gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
-> bsl::errc_type
|
||||
{
|
||||
bsl::finally_assert release_on_error{
|
||||
[this, &gs, &tls, &sys, &intrinsic]() noexcept -> void {
|
||||
this->release(gs, tls, sys, intrinsic);
|
||||
}};
|
||||
|
||||
bsl::errc_type ret{};
|
||||
for (bsl::safe_idx i{}; i < m_pool.size(); ++i) {
|
||||
ret = m_pool.at_if(i)->initialize(gs, tls, sys, intrinsic, bsl::to_u16(i));
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bsl::touch();
|
||||
}
|
||||
|
||||
release_on_error.ignore();
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_pool_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
{
|
||||
for (bsl::safe_idx i{}; i < m_pool.size(); ++i) {
|
||||
m_pool.at_if(i)->release(gs, tls, sys, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a VS and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the newly created VS to
|
||||
/// @param ppid the ID of the PP to assign the newly created VS to
|
||||
/// @return Returns the ID of the newly created VS on
|
||||
/// success, or bsl::safe_u16::failure() on failure.
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::safe_u16
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
auto mut_vsid{sys.bf_vs_op_create_vs(vpid, ppid)};
|
||||
if (bsl::unlikely(!mut_vsid)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
bsl::finally destroy_vs_on_error{[&sys, &mut_vsid]() noexcept -> void {
|
||||
bsl::discard(sys.bf_vs_op_destroy_vs(mut_vsid));
|
||||
}};
|
||||
|
||||
auto *const vs{m_pool.at_if(bsl::to_idx(mut_vsid))};
|
||||
if (bsl::unlikely(nullptr == vs)) {
|
||||
bsl::error() << "mut_vsid " // --
|
||||
<< bsl::hex(mut_vsid) // --
|
||||
<< " provided by the microkernel is invalid" // --
|
||||
<< " or greater than or equal to the HYPERVISOR_MAX_VSS " // --
|
||||
<< bsl::hex(HYPERVISOR_MAX_VSS) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
ret = vs->allocate(gs, tls, sys, intrinsic, vpid, ppid);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
destroy_vs_on_error.ignore();
|
||||
return mut_vsid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/// @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 INTRINSIC_HPP
|
||||
#define INTRINSIC_HPP
|
||||
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_cpuid_impl.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::intrinsic_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Provides raw access to intrinsics.
|
||||
///
|
||||
class intrinsic_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this intrinsic_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(gs_t &gs, tls_t &tls) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the intrinsic_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(gs_t &gs, tls_t &tls) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Executes the CPUID instruction given the provided
|
||||
/// EAX and ECX and returns the results.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param rax the index used by CPUID, returns resulting rax
|
||||
/// @param rbx returns resulting rbx
|
||||
/// @param rcx the subindex used by CPUID, returns the resulting rcx
|
||||
/// @param rdx returns resulting rdx
|
||||
///
|
||||
static constexpr void
|
||||
cpuid(
|
||||
bsl::safe_u64 &rax, bsl::safe_u64 &rbx, bsl::safe_u64 &rcx, bsl::safe_u64 &rdx) noexcept
|
||||
{
|
||||
intrinsic_cpuid_impl(rax.data(), rbx.data(), rcx.data(), rdx.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
/// @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 VMEXIT_T_HPP
|
||||
#define VMEXIT_T_HPP
|
||||
|
||||
#include <bf_debug_ops.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <cpuid_commands.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
#include <vp_pool_t.hpp>
|
||||
#include <vs_pool_t.hpp>
|
||||
|
||||
#include <bsl/debug.hpp>
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace example
|
||||
{
|
||||
/// @class example::vmexit_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's VMExit handler
|
||||
///
|
||||
class vmexit_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vmexit_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vmexit_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Handles the CPUID VMexit
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
handle_cpuid(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool,
|
||||
bsl::safe_u16 const &vsid) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(vp_pool);
|
||||
bsl::discard(vs_pool);
|
||||
|
||||
bsl::errc_type ret{};
|
||||
|
||||
auto rax{sys.bf_tls_rax()};
|
||||
auto rbx{sys.bf_tls_rbx()};
|
||||
auto rcx{sys.bf_tls_rcx()};
|
||||
auto rdx{sys.bf_tls_rdx()};
|
||||
|
||||
if (loader::CPUID_COMMAND_EAX == bsl::to_u32_unsafe(rax)) {
|
||||
switch (bsl::to_u32_unsafe(rcx).get()) {
|
||||
case loader::CPUID_COMMAND_ECX_STOP.get(): {
|
||||
sys.bf_tls_set_rax(loader::CPUID_COMMAND_RAX_SUCCESS);
|
||||
|
||||
ret = sys.bf_vs_op_advance_ip(vsid);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sys.bf_vs_op_promote(vsid);
|
||||
}
|
||||
|
||||
case loader::CPUID_COMMAND_ECX_REPORT_ON.get(): {
|
||||
break;
|
||||
}
|
||||
|
||||
case loader::CPUID_COMMAND_ECX_REPORT_OFF.get(): {
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
bsl::error() << "unsupported cpuid command " // --
|
||||
<< bsl::hex(rcx) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
|
||||
intrinsic.cpuid(rax, rbx, rcx, rdx);
|
||||
|
||||
sys.bf_tls_set_rax(rax);
|
||||
sys.bf_tls_set_rbx(rbx);
|
||||
sys.bf_tls_set_rcx(rcx);
|
||||
sys.bf_tls_set_rdx(rdx);
|
||||
|
||||
return sys.bf_vs_op_advance_ip_and_run_current();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Dispatches the VMExit.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vp_pool the vp_pool_t to use
|
||||
/// @param vs_pool the vs_pool_t to use
|
||||
/// @param vsid the ID of the VS that generated the VMExit
|
||||
/// @param exit_reason the exit reason associated with the VMExit
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
dispatch(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
vp_pool_t &vp_pool,
|
||||
vs_pool_t &vs_pool,
|
||||
bsl::safe_u16 const &vsid,
|
||||
bsl::safe_u64 const &exit_reason) noexcept -> bsl::errc_type
|
||||
{
|
||||
constexpr auto exit_reason_cpuid{0x72_u64};
|
||||
|
||||
switch (exit_reason.get()) {
|
||||
case exit_reason_cpuid.get(): {
|
||||
return handle_cpuid(gs, tls, sys, intrinsic, vp_pool, vs_pool, vsid);
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bsl::error() << "unsupported vmexit " // --
|
||||
<< bsl::hex(exit_reason) // --
|
||||
<< bsl::endl // --
|
||||
<< bsl::here(); // --
|
||||
|
||||
return bsl::errc_failure;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
/// @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 VS_T_HPP
|
||||
#define VS_T_HPP
|
||||
|
||||
#include <bf_constants.hpp>
|
||||
#include <bf_syscall_t.hpp>
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_t.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
#include <bsl/touch.hpp>
|
||||
#include <bsl/unlikely.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::vs_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Defines the extension's notion of a VS
|
||||
///
|
||||
class vs_t final
|
||||
{
|
||||
/// @brief stores the ID associated with this vs_t
|
||||
bsl::safe_u16 m_id{bsl::safe_u16::failure()};
|
||||
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this vs_t
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param i the ID for this vs_t
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
initialize(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &i) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = i;
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the vs_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
///
|
||||
constexpr void
|
||||
release(gs_t &gs, tls_t &tls, syscall::bf_syscall_t &sys, intrinsic_t &intrinsic) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(sys);
|
||||
bsl::discard(intrinsic);
|
||||
|
||||
m_id = bsl::safe_u16::failure();
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Allocates a vs_t and returns it's ID
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @param sys the bf_syscall_t to use
|
||||
/// @param intrinsic the intrinsic_t to use
|
||||
/// @param vpid the ID of the VP to assign the vs_t to
|
||||
/// @param ppid the ID of the PP to assign the vs_t to
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] constexpr auto
|
||||
allocate(
|
||||
gs_t &gs,
|
||||
tls_t &tls,
|
||||
syscall::bf_syscall_t &sys,
|
||||
intrinsic_t &intrinsic,
|
||||
bsl::safe_u16 const &vpid,
|
||||
bsl::safe_u16 const &ppid) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::errc_type ret{};
|
||||
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
bsl::discard(intrinsic);
|
||||
bsl::discard(vpid);
|
||||
bsl::discard(ppid);
|
||||
|
||||
ret = sys.bf_vs_op_init_as_root(m_id);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr auto guest_asid_val{0x1_u64};
|
||||
ret = sys.bf_vs_op_write(m_id, syscall::bf_reg_t::bf_reg_t_guest_asid, guest_asid_val);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr auto intercept_instr1_val{0x00040000_u64};
|
||||
ret = sys.bf_vs_op_write(
|
||||
m_id, syscall::bf_reg_t::bf_reg_t_intercept_instruction1, intercept_instr1_val);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr auto intercept_instr2_val{0x00000001_u64};
|
||||
ret = sys.bf_vs_op_write(
|
||||
m_id, syscall::bf_reg_t::bf_reg_t_intercept_instruction2, intercept_instr2_val);
|
||||
if (bsl::unlikely(!ret)) {
|
||||
bsl::print<bsl::V>() << bsl::here();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/// @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 INTRINSIC_HPP
|
||||
#define INTRINSIC_HPP
|
||||
|
||||
#include <gs_t.hpp>
|
||||
#include <intrinsic_cpuid_impl.hpp>
|
||||
#include <tls_t.hpp>
|
||||
|
||||
#include <bsl/discard.hpp>
|
||||
#include <bsl/errc_type.hpp>
|
||||
#include <bsl/safe_integral.hpp>
|
||||
|
||||
namespace integration
|
||||
{
|
||||
/// @class integration::intrinsic_t
|
||||
///
|
||||
/// <!-- description -->
|
||||
/// @brief Provides raw access to intrinsics.
|
||||
///
|
||||
class intrinsic_t final
|
||||
{
|
||||
public:
|
||||
/// <!-- description -->
|
||||
/// @brief Initializes this intrinsic_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
/// @return Returns bsl::errc_success on success, bsl::errc_failure
|
||||
/// and friends otherwise
|
||||
///
|
||||
[[nodiscard]] static constexpr auto
|
||||
initialize(gs_t &gs, tls_t &tls) noexcept -> bsl::errc_type
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
|
||||
return bsl::errc_success;
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Release the intrinsic_t.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param gs the gs_t to use
|
||||
/// @param tls the tls_t to use
|
||||
///
|
||||
static constexpr void
|
||||
release(gs_t &gs, tls_t &tls) noexcept
|
||||
{
|
||||
bsl::discard(gs);
|
||||
bsl::discard(tls);
|
||||
}
|
||||
|
||||
/// <!-- description -->
|
||||
/// @brief Executes the CPUID instruction given the provided
|
||||
/// EAX and ECX and returns the results.
|
||||
///
|
||||
/// <!-- inputs/outputs -->
|
||||
/// @param rax the index used by CPUID, returns resulting rax
|
||||
/// @param rbx returns resulting rbx
|
||||
/// @param rcx the subindex used by CPUID, returns the resulting rcx
|
||||
/// @param rdx returns resulting rdx
|
||||
///
|
||||
static constexpr void
|
||||
cpuid(
|
||||
bsl::safe_u64 &rax, bsl::safe_u64 &rbx, bsl::safe_u64 &rcx, bsl::safe_u64 &rdx) noexcept
|
||||
{
|
||||
intrinsic_cpuid_impl(rax.data(), rbx.data(), rcx.data(), rdx.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue