diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 058cb8190..fd01c228f 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v4 + - name: Install Ninja + run: choco install ninja - name: Generate cache keys run: | echo "cargo=${{ runner.os }}-cargo" >> $env:GITHUB_OUTPUT diff --git a/CMakePresets.json b/CMakePresets.json index d2f45fd17..f4813cfa3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -11,8 +11,8 @@ { "name": "windows", "inherits": "default", - "generator": "Visual Studio 17 2022", - "architecture": "x64", + "generator": "Ninja", + "toolchainFile": "cmake/Windows.MSVC.toolchain.cmake", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -21,11 +21,17 @@ }, { "name": "windows-release", - "inherits": "windows" + "inherits": "windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } }, { "name": "windows-debug", - "inherits": "windows" + "inherits": "windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } }, { "name": "linux", @@ -79,13 +85,11 @@ "buildPresets": [ { "name": "windows-release", - "configurePreset": "windows-release", - "configuration": "Release" + "configurePreset": "windows-release" }, { "name": "windows-debug", - "configurePreset": "windows-debug", - "configuration": "Debug" + "configurePreset": "windows-debug" }, { "name": "linux-release", diff --git a/README.md b/README.md index fb2f7034e..036eef826 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ Obliteration supports only 4KB/8KB/16KB pages. Most people should not have any p - Rust on the latest stable channel - CMake 3.21+ - Make sure you have `Add CMake to the system PATH` selected when installing +- [Ninja](https://ninja-build.org) + - Make sure Ninja is added to `PATH` ### Linux prerequisites diff --git a/cmake/VSWhere.cmake b/cmake/VSWhere.cmake new file mode 100644 index 000000000..c2ea310a9 --- /dev/null +++ b/cmake/VSWhere.cmake @@ -0,0 +1,132 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +include_guard() + +#[[==================================================================================================================== + toolchain_validate_vs_files + --------------------------- + + Note: Not supported for consumption outside of the toolchain files. + + Validates the the specified folder exists and contains the specified files. + + toolchain_validate_vs_files( + > + > + ...> + ) + + If the folder or files are missing, then a FATAL_ERROR is reported. +====================================================================================================================]]# +function(toolchain_validate_vs_files) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS FOLDER DESCRIPTION) + set(MULTI_VALUE_KEYWORDS FILES) + + cmake_parse_arguments(PARSE_ARGV 0 VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(NOT EXISTS ${VS_FOLDER}) + message(FATAL_ERROR "Folder not present - ${VS_FOLDER} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + + foreach(FILE ${VS_FILES}) + if(NOT EXISTS "${VS_FOLDER}/${FILE}") + message(FATAL_ERROR "File not present - ${VS_FOLDER}/${FILE} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + endforeach() +endfunction() + +#[[==================================================================================================================== + findVisualStudio + ---------------- + + Finds a Visual Studio instance, and sets CMake variables based on properties of the found instance. + + findVisualStudio( + [VERSION ] + [PRERELEASE ] + [PRODUCTS ] + [REQUIRES ...] + PROPERTIES + < > + ) +====================================================================================================================]]# +function(findVisualStudio) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS VERSION PRERELEASE PRODUCTS) + set(MULTI_VALUE_KEYWORDS REQUIRES PROPERTIES) + + cmake_parse_arguments(PARSE_ARGV 0 FIND_VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + find_program(VSWHERE_PATH + NAMES vswhere vswhere.exe + HINTS "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/Installer" + ) + + if(VSWHERE_PATH STREQUAL "VSWHERE_PATH-NOTFOUND") + message(FATAL_ERROR "'vswhere' isn't found.") + endif() + + set(VSWHERE_COMMAND ${VSWHERE_PATH} -latest) + + if(FIND_VS_PRERELEASE) + list(APPEND VSWHERE_COMMAND -prerelease) + endif() + + if(FIND_VS_PRODUCTS) + list(APPEND VSWHERE_COMMAND -products ${FIND_VS_PRODUCTS}) + endif() + + if(FIND_VS_REQUIRES) + list(APPEND VSWHERE_COMMAND -requires ${FIND_VS_REQUIRES}) + endif() + + if(FIND_VS_VERSION) + list(APPEND VSWHERE_COMMAND -version "${FIND_VS_VERSION}") + endif() + + message(VERBOSE "findVisualStudio: VSWHERE_COMMAND = ${VSWHERE_COMMAND}") + + execute_process( + COMMAND ${VSWHERE_COMMAND} + OUTPUT_VARIABLE VSWHERE_OUTPUT + ) + + message(VERBOSE "findVisualStudio: VSWHERE_OUTPUT = ${VSWHERE_OUTPUT}") + + # Matches `VSWHERE_PROPERTY` in the `VSWHERE_OUTPUT` text in the format written by vswhere. + # The matched value is assigned to the variable `VARIABLE_NAME` in the parent scope. + function(getVSWhereProperty VSWHERE_OUTPUT VSWHERE_PROPERTY VARIABLE_NAME) + string(REGEX MATCH "${VSWHERE_PROPERTY}: [^\r\n]*" VSWHERE_VALUE "${VSWHERE_OUTPUT}") + string(REPLACE "${VSWHERE_PROPERTY}: " "" VSWHERE_VALUE "${VSWHERE_VALUE}") + set(${VARIABLE_NAME} "${VSWHERE_VALUE}" PARENT_SCOPE) + endfunction() + + while(FIND_VS_PROPERTIES) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_PROPERTY) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_CMAKE_VARIABLE) + getVSWhereProperty("${VSWHERE_OUTPUT}" ${VSWHERE_PROPERTY} VSWHERE_VALUE) + set(${VSWHERE_CMAKE_VARIABLE} ${VSWHERE_VALUE} PARENT_SCOPE) + endwhile() +endfunction() diff --git a/cmake/Windows.Kits.cmake b/cmake/Windows.Kits.cmake new file mode 100644 index 000000000..c9e1d6829 --- /dev/null +++ b/cmake/Windows.Kits.cmake @@ -0,0 +1,141 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# | CMake Variable | Description | +# |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the tooling to use. Defaults to x64. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to use. Defaults to the highest installed, that is no higher than CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | The maximum version of the Windows SDK to use, for example '10.0.19041.0'. Defaults to nothing | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# +# The following variables will be set: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_MT | The path to the 'mt' tool. | +# | CMAKE_RC_COMPILER | The path to the 'rc' tool. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to be used. | +# | MDMERGE_TOOL | The path to the 'mdmerge' tool. | +# | MIDL_COMPILER | The path to the 'midl' compiler. | +# | WINDOWS_KITS_BIN_PATH | The path to the folder containing the Windows Kits binaries. | +# | WINDOWS_KITS_INCLUDE_PATH | The path to the folder containing the Windows Kits include files. | +# | WINDOWS_KITS_LIB_PATH | The path to the folder containing the Windows Kits library files. | +# | WINDOWS_KITS_REFERENCES_PATH | The path to the folder containing the Windows Kits references. | +# +include_guard() + +if(NOT CMAKE_SYSTEM_VERSION) + set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) +endif() + +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + get_filename_component(CMAKE_WINDOWS_KITS_10_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + if ("${CMAKE_WINDOWS_KITS_10_DIR}" STREQUAL "/registry") + unset(CMAKE_WINDOWS_KITS_10_DIR) + endif() +endif() + +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + message(FATAL_ERROR "Unable to find an installed Windows SDK, and one wasn't specified.") +endif() + +# If a CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION wasn't specified, find the highest installed version that is no higher +# than the host version +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + file(GLOB WINDOWS_KITS_VERSIONS RELATIVE "${CMAKE_WINDOWS_KITS_10_DIR}/lib" "${CMAKE_WINDOWS_KITS_10_DIR}/lib/*") + list(FILTER WINDOWS_KITS_VERSIONS INCLUDE REGEX "10\\.0\\.") + list(SORT WINDOWS_KITS_VERSIONS COMPARE NATURAL ORDER DESCENDING) + while(WINDOWS_KITS_VERSIONS) + list(POP_FRONT WINDOWS_KITS_VERSIONS CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Defaulting version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS_EQUAL CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Choosing version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + message(VERBOSE "Windows.Kits: Not suitable: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + endwhile() +endif() + +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + message(FATAL_ERROR "A Windows SDK could not be found.") +endif() + +set(WINDOWS_KITS_BIN_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_INCLUDE_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_LIB_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_REFERENCES_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/References" CACHE PATH "" FORCE) +set(WINDOWS_KITS_PLATFORM_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/Platforms/UAP/${CMAKE_SYSTEM_VERSION}/Platform.xml" CACHE PATH "" FORCE) + +if(NOT EXISTS ${WINDOWS_KITS_BIN_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_BIN_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_INCLUDE_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_INCLUDE_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_LIB_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_LIB_PATH}' does not exist.") +endif() + +set(CMAKE_MT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mt.exe") +set(CMAKE_RC_COMPILER_INIT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/rc.exe") +set(CMAKE_RC_FLAGS_INIT "/nologo") + +set(MIDL_COMPILER "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/midl.exe") +set(MDMERGE_TOOL "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mdmerge.exe") + +# Windows SDK +if((CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x64)) + set(WINDOWS_KITS_TARGET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x86)) + set(WINDOWS_KITS_TARGET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify Windows Kits architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +foreach(LANG C CXX RC) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/ucrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/shared") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/um") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/winrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/cppwinrt") +endforeach() + +link_directories("${WINDOWS_KITS_LIB_PATH}/ucrt/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_LIB_PATH}/um/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_REFERENCES_PATH}/${WINDOWS_KITS_TARGET_ARCHITECTURE}") diff --git a/cmake/Windows.MSVC.toolchain.cmake b/cmake/Windows.MSVC.toolchain.cmake new file mode 100644 index 000000000..caadcd083 --- /dev/null +++ b/cmake/Windows.MSVC.toolchain.cmake @@ -0,0 +1,217 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# This CMake toolchain file configures a CMake, non-'Visual Studio Generator' build to use +# the MSVC compilers and tools. +# +# The following variables can be used to configure the behavior of this toolchain file: +# +# | CMake Variable | Description | +# |---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_PROCESSOR | The processor to compiler for. One of 'x86', 'x64'/'AMD64', 'arm', 'arm64'. Defaults to ${CMAKE_HOST_SYSTEM_PROCESSOR}. | +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the toolset to use. Defaults to 'x64'. | +# | CMAKE_VS_PRODUCTS | One or more Visual Studio Product IDs to consider. Defaults to '*' | +# | CMAKE_VS_VERSION_PRERELEASE | Whether 'prerelease' versions of Visual Studio should be considered. Defaults to 'OFF' | +# | CMAKE_VS_VERSION_RANGE | A verson range for VS instances to find. For example, '[16.0,17.0)' will find versions '16.*'. Defaults to '[16.0,17.0)' | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# | VS_EXPERIMENTAL_MODULE | Whether experimental module support should be enabled. | +# | VS_INSTALLATION_PATH | The location of the root of the Visual Studio installation. If not specified VSWhere will be used to search for one. | +# | VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset to use. For example, 14.29.30133. Defaults to the highest available. | +# | VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME | Whether the compiler should link with the ATLMFC runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# | VS_USE_SPECTRE_MITIGATION_RUNTIME | Whether the compiler should link with a runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# +# The toolchain file will set the following variables: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_C_COMPILER | The path to the C compiler to use. | +# | CMAKE_CXX_COMPILER | The path to the C++ compiler to use. | +# | CMAKE_MT | The path to the 'mt.exe' tool to use. | +# | CMAKE_RC_COMPILER | The path tp the 'rc.exe' tool to use. | +# | CMAKE_SYSTEM_NAME | Windows | +# | CMAKE_VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset being used - e.g. 14.29.30133. | +# | WIN32 | 1 | +# | MSVC | 1 | +# | MSVC_VERSION | The '' version of the C++ compiler being used. For example, '1929' | +# +# Resources: +# +# +cmake_minimum_required(VERSION 3.20) + +include_guard() + +if(NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)) + return() +endif() + +set(UNUSED ${CMAKE_TOOLCHAIN_FILE}) # Note: only to prevent cmake unused variable warninig +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + CMAKE_CROSSCOMPILING + CMAKE_SYSTEM_PROCESSOR + CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE + CMAKE_VS_PRODUCTS + CMAKE_VS_VERSION_PRERELEASE + CMAKE_VS_VERSION_RANGE + VS_INSTALLATION_PATH + VS_INSTALLATION_VERSION + VS_PLATFORM_TOOLSET_VERSION +) +set(CMAKE_CROSSCOMPILING TRUE) +set(WIN32 1) +set(MSVC 1) + +include("${CMAKE_CURRENT_LIST_DIR}/VSWhere.cmake") + +if(NOT CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() + +if(NOT CMAKE_VS_VERSION_RANGE) + set(CMAKE_VS_VERSION_RANGE "[16.0,)") +endif() + +if(NOT CMAKE_VS_VERSION_PRERELEASE) + set(CMAKE_VS_VERSION_PRERELEASE OFF) +endif() + +if(NOT CMAKE_VS_PRODUCTS) + set(CMAKE_VS_PRODUCTS "*") +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) +endif() + +if(NOT VS_USE_SPECTRE_MITIGATION_RUNTIME) + set(VS_USE_SPECTRE_MITIGATION_RUNTIME OFF) +endif() + +# Find Visual Studio +# +if(NOT VS_INSTALLATION_PATH) + findVisualStudio( + VERSION ${CMAKE_VS_VERSION_RANGE} + PRERELEASE ${CMAKE_VS_VERSION_PRERELEASE} + PRODUCTS ${CMAKE_VS_PRODUCTS} + PROPERTIES + installationVersion VS_INSTALLATION_VERSION + installationPath VS_INSTALLATION_PATH + ) +endif() + +message(VERBOSE "VS_INSTALLATION_VERSION = ${VS_INSTALLATION_VERSION}") +message(VERBOSE "VS_INSTALLATION_PATH = ${VS_INSTALLATION_PATH}") + +if(NOT VS_INSTALLATION_PATH) + message(FATAL_ERROR "Unable to find Visual Studio") +endif() + +cmake_path(NORMAL_PATH VS_INSTALLATION_PATH) + +set(VS_MSVC_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC") + +# Use 'VS_PLATFORM_TOOLSET_VERSION' to resolve 'CMAKE_VS_PLATFORM_TOOLSET_VERSION' +# +if(NOT VS_PLATFORM_TOOLSET_VERSION) + if(VS_TOOLSET_VERSION) + message(WARNING "Old versions of WindowsToolchain incorrectly used 'VS_TOOLSET_VERSION' to specify the VS toolset version. This functionality is being deprecated - please use 'VS_PLATFORM_TOOLSET_VERSION' instead.") + set(VS_PLATFORM_TOOLSET_VERSION ${VS_TOOLSET_VERSION}) + else() + file(GLOB VS_PLATFORM_TOOLSET_VERSIONS RELATIVE ${VS_MSVC_PATH} ${VS_MSVC_PATH}/*) + list(SORT VS_PLATFORM_TOOLSET_VERSIONS COMPARE NATURAL ORDER DESCENDING) + list(POP_FRONT VS_PLATFORM_TOOLSET_VERSIONS VS_PLATFORM_TOOLSET_VERSION) + unset(VS_PLATFORM_TOOLSET_VERSIONS) + endif() +endif() + +set(CMAKE_VS_PLATFORM_TOOLSET_VERSION ${VS_PLATFORM_TOOLSET_VERSION}) +set(VS_TOOLSET_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC/${CMAKE_VS_PLATFORM_TOOLSET_VERSION}") + +# Set the tooling variables, include_directories and link_directories +# + +# Map CMAKE_SYSTEM_PROCESSOR values to CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE that identifies the tools that should +# be used to produce code for the CMAKE_SYSTEM_PROCESSOR. +if((CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x64)) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x86)) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify compiler architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(CMAKE_CXX_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") +set(CMAKE_C_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +endif() + +# Compiler +foreach(LANG C CXX RC) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/ATLMFC/include") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/include") +endforeach() + +if(VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "ATLMFC Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + atls.lib + ) + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +if(VS_USE_SPECTRE_MITIGATION_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + msvcrt.lib vcruntime.lib vcruntimed.lib + ) + link_directories("${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +link_directories("${VS_TOOLSET_PATH}/lib/x86/store/references") + +# Module support +if(VS_EXPERIMENTAL_MODULE) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /experimental:module") + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /stdIfcDir \"${VS_TOOLSET_PATH}/ifc/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}\"") +endif() + +# Windows Kits +include("${CMAKE_CURRENT_LIST_DIR}/Windows.Kits.cmake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1765f9853..032622b6d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,8 @@ endif() add_custom_target(core COMMAND cargo build $,--profile=dev,--release> - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + BYPRODUCTS ${KERNEL} ${LIBCORE}) # Setup application target. add_executable(obliteration WIN32 MACOSX_BUNDLE diff --git a/src/kernel/src/ee/native/mod.rs b/src/kernel/src/ee/native/mod.rs index 4b545c95c..199eda213 100644 --- a/src/kernel/src/ee/native/mod.rs +++ b/src/kernel/src/ee/native/mod.rs @@ -1,7 +1,8 @@ use crate::fs::{VPath, VPathBuf}; -use crate::process::{Protections, VThread}; +use crate::process::VThread; use crate::rtld::{CodeWorkspaceError, Memory, Module, UnprotectSegmentError}; -use crate::syscalls::{SysIn, SysOut, Syscalls}; +use crate::syscalls::{SysIn, SysOut}; +use crate::vm::Protections; use byteorder::{ByteOrder, LE}; use iced_x86::code_asm::{ al, dword_ptr, ecx, get_gpr64, qword_ptr, r10, r11, r11d, r12, r13, r14, r15, r8, r9, rax, rbp, @@ -10,13 +11,12 @@ use iced_x86::code_asm::{ use iced_x86::{Code, Decoder, DecoderOptions, Instruction, OpKind, Register}; use std::any::Any; use std::mem::{size_of, transmute}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use thiserror::Error; /// An implementation of [`ExecutionEngine`] for running the PS4 binary natively. #[derive(Debug)] pub struct NativeEngine { - syscalls: OnceLock, xsave_area: i32, } @@ -30,14 +30,7 @@ impl NativeEngine { assert!(xsave_area > 0); - Arc::new(Self { - syscalls: OnceLock::new(), - xsave_area, - }) - } - - pub fn set_syscalls(&self, v: Syscalls) { - self.syscalls.set(v).unwrap(); + Arc::new(Self { xsave_area }) } pub fn setup_module(self: &Arc, md: &mut Module) -> Result<(), SetupModuleError> { @@ -447,7 +440,12 @@ impl NativeEngine { /// # Safety /// This method cannot be called from Rust. unsafe extern "sysv64" fn syscall(&self, i: &SysIn, o: &mut SysOut) -> i64 { - self.syscalls.get().unwrap().exec(i, o) + VThread::current() + .unwrap() + .proc() + .abi() + .syscalls() + .exec(i, o) } /// # Safety diff --git a/src/kernel/src/fs/dev/vnode.rs b/src/kernel/src/fs/dev/vnode.rs index 8680229b5..97de8aed1 100644 --- a/src/kernel/src/fs/dev/vnode.rs +++ b/src/kernel/src/fs/dev/vnode.rs @@ -2,8 +2,7 @@ use super::dirent::Dirent; use super::{AllocVnodeError, DevFs}; use crate::errno::{Errno, EIO, ENOENT, ENOTDIR, ENXIO}; use crate::fs::{ - check_access, Access, IoCmd, OpenFlags, RevokeFlags, VFileType, Vnode, VnodeAttrs, VnodeItem, - VnodeType, + check_access, Access, IoCmd, RevokeFlags, Uio, UioMut, Vnode, VnodeAttrs, VnodeType, }; use crate::process::VThread; use macros::Errno; @@ -167,6 +166,24 @@ impl crate::fs::VnodeBackend for VnodeBackend { // TODO: Implement this. todo!() } + + fn read( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut UioMut, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result> { + todo!() + } + + fn write( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut Uio, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result> { + todo!() + } } /// Represents an error when [`lookup()`] is failed. diff --git a/src/kernel/src/fs/file.rs b/src/kernel/src/fs/file.rs index 2fa3b97d8..b5db7ca8e 100644 --- a/src/kernel/src/fs/file.rs +++ b/src/kernel/src/fs/file.rs @@ -2,7 +2,7 @@ use super::{CharacterDevice, IoCmd, Offset, Stat, TruncateLength, Uio, UioMut, V use crate::dmem::BlockPool; use crate::errno::Errno; use crate::errno::{EINVAL, ENOTTY, ENXIO, EOPNOTSUPP}; -use crate::fs::PollEvents; +use crate::fs::{IoVec, PollEvents}; use crate::kqueue::KernelQueue; use crate::net::Socket; use crate::process::VThread; @@ -19,6 +19,7 @@ use thiserror::Error; pub struct VFile { ty: VFileType, // f_type flags: VFileFlags, // f_flag + offset: i64, // f_offset } impl VFile { @@ -26,6 +27,7 @@ impl VFile { Self { ty, flags: VFileFlags::empty(), + offset: 0, } } @@ -90,7 +92,7 @@ impl VFile { fn read(&self, buf: &mut UioMut, td: Option<&VThread>) -> Result> { match &self.ty { - VFileType::Vnode(vn) => vn.read(self, buf, td), + VFileType::Vnode(vn) => FileBackend::read(vn, self, buf, td), VFileType::Socket(so) | VFileType::IpcSocket(so) => so.read(self, buf, td), VFileType::KernelQueue(kq) => kq.read(self, buf, td), VFileType::SharedMemory(shm) => shm.read(self, buf, td), @@ -101,7 +103,7 @@ impl VFile { fn write(&self, buf: &mut Uio, td: Option<&VThread>) -> Result> { match &self.ty { - VFileType::Vnode(vn) => vn.write(self, buf, td), + VFileType::Vnode(vn) => FileBackend::write(vn, self, buf, td), VFileType::Socket(so) | VFileType::IpcSocket(so) => so.write(self, buf, td), VFileType::KernelQueue(kq) => kq.write(self, buf, td), VFileType::SharedMemory(shm) => shm.write(self, buf, td), @@ -154,12 +156,39 @@ impl Seek for VFile { fn seek(&mut self, _pos: SeekFrom) -> std::io::Result { todo!() } + + fn rewind(&mut self) -> std::io::Result<()> { + self.offset = 0; + + Ok(()) + } + + fn stream_position(&mut self) -> std::io::Result { + if let Ok(offset) = self.offset.try_into() { + Ok(offset) + } else { + todo!() + } + } } impl Read for VFile { - #[allow(unused_variables)] // TODO: Remove when implementing. fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - todo!() + let ref mut iovec = IoVec::from_slice(buf); + + let mut uio = UioMut::from_single_vec(iovec); + + let Ok(read) = VFile::read(self, &mut uio, None) else { + todo!() + }; + + if let Ok(read) = TryInto::::try_into(read) { + self.offset += read; + } else { + todo!() + } + + Ok(read) } } diff --git a/src/kernel/src/fs/host/file.rs b/src/kernel/src/fs/host/file.rs index a4036db39..51250bf4e 100644 --- a/src/kernel/src/fs/host/file.rs +++ b/src/kernel/src/fs/host/file.rs @@ -1,3 +1,4 @@ +use crate::fs::UioMut; use std::collections::HashMap; use std::io::Error; use std::mem::zeroed; @@ -272,6 +273,16 @@ impl HostFile { } } + #[cfg(unix)] + pub(super) fn read(&self, buf: &mut UioMut) -> Result { + todo!() + } + + #[cfg(windows)] + pub(super) fn read(&self, buf: &mut UioMut) -> Result { + todo!() + } + #[cfg(unix)] fn stat(&self) -> Result { use libc::fstat; diff --git a/src/kernel/src/fs/host/vnode.rs b/src/kernel/src/fs/host/vnode.rs index 087d7b296..fcb956cc6 100644 --- a/src/kernel/src/fs/host/vnode.rs +++ b/src/kernel/src/fs/host/vnode.rs @@ -1,7 +1,7 @@ use super::file::HostFile; use super::{GetVnodeError, HostFs}; use crate::errno::{Errno, EEXIST, EIO, ENOENT, ENOTDIR}; -use crate::fs::{Access, IoCmd, Mode, OpenFlags, VFileType, Vnode, VnodeAttrs, VnodeType}; +use crate::fs::{Access, IoCmd, Mode, Uio, UioMut, Vnode, VnodeAttrs, VnodeType}; use crate::process::VThread; use crate::ucred::{Gid, Uid}; use macros::Errno; @@ -123,6 +123,26 @@ impl crate::fs::VnodeBackend for VnodeBackend { Ok(vn) } + + fn read( + &self, + _: &Arc, + buf: &mut UioMut, + _: Option<&VThread>, + ) -> Result> { + let read = self.file.read(buf).map_err(ReadError::ReadFailed)?; + + Ok(read) + } + + fn write( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut Uio, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result> { + todo!() + } } /// Represents an error when [`getattr()`] fails. @@ -182,3 +202,10 @@ impl From for MkDirError { } } } + +#[derive(Debug, Error, Errno)] +enum ReadError { + #[error("read failed")] + #[errno(EIO)] + ReadFailed(#[source] std::io::Error), +} diff --git a/src/kernel/src/fs/mount.rs b/src/kernel/src/fs/mount.rs index aeb84f615..43167a435 100644 --- a/src/kernel/src/fs/mount.rs +++ b/src/kernel/src/fs/mount.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use std::convert::Infallible; use std::fmt::Formatter; use std::fmt::{Debug, Display, Error}; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::hint::unreachable_unchecked; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock, RwLockWriteGuard}; diff --git a/src/kernel/src/fs/null/vnode.rs b/src/kernel/src/fs/null/vnode.rs index 5b1af1b80..68002203c 100644 --- a/src/kernel/src/fs/null/vnode.rs +++ b/src/kernel/src/fs/null/vnode.rs @@ -1,7 +1,8 @@ use crate::{ errno::{Errno, EISDIR, EROFS}, fs::{ - null::hash::NULL_HASHTABLE, perm::Access, Mount, MountFlags, Vnode, VnodeAttrs, VnodeType, + null::hash::NULL_HASHTABLE, perm::Access, Mount, MountFlags, Uio, UioMut, Vnode, + VnodeAttrs, VnodeType, }, process::VThread, }; @@ -89,6 +90,34 @@ impl crate::fs::VnodeBackend for VnodeBackend { Ok(vnode) } + + fn read( + &self, + _: &Arc, + buf: &mut UioMut, + td: Option<&VThread>, + ) -> Result> { + let read = self + .lower + .read(buf, td) + .map_err(ReadError::ReadFromLowerFailed)?; + + Ok(read) + } + + fn write( + &self, + _: &Arc, + buf: &mut Uio, + td: Option<&VThread>, + ) -> Result> { + let written = self + .lower + .write(buf, td) + .map_err(WriteError::WriteFromLowerFailed)?; + + Ok(written) + } } /// See `null_nodeget` on the PS4 for a reference. @@ -149,3 +178,15 @@ pub(super) enum OpenError { #[derive(Debug, Error, Errno)] pub(super) enum NodeGetError {} + +#[derive(Debug, Error, Errno)] +enum ReadError { + #[error("read from lower vnode failed")] + ReadFromLowerFailed(#[source] Box), +} + +#[derive(Debug, Error, Errno)] +enum WriteError { + #[error("write from lower vnode failed")] + WriteFromLowerFailed(#[source] Box), +} diff --git a/src/kernel/src/fs/tmp/node.rs b/src/kernel/src/fs/tmp/node.rs index 7f373a306..11fd4c96d 100644 --- a/src/kernel/src/fs/tmp/node.rs +++ b/src/kernel/src/fs/tmp/node.rs @@ -1,6 +1,6 @@ use super::{AllocVnodeError, TempFs}; use crate::errno::{Errno, ENOENT, ENOSPC}; -use crate::fs::{Access, OpenFlags, VFileType, Vnode, VnodeAttrs, VnodeType}; +use crate::fs::{Access, Uio, UioMut, Vnode, VnodeAttrs, VnodeType}; use crate::process::VThread; use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; use macros::Errno; @@ -201,6 +201,24 @@ impl crate::fs::VnodeBackend for VnodeBackend { Ok(vnode) } + + fn read( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut UioMut, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result> { + todo!() + } + + fn write( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut Uio, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result> { + todo!() + } } /// Represents an error when [`Nodes::alloc()`] fails. diff --git a/src/kernel/src/fs/uio.rs b/src/kernel/src/fs/uio.rs index 634f5a4d9..e82512b28 100644 --- a/src/kernel/src/fs/uio.rs +++ b/src/kernel/src/fs/uio.rs @@ -34,6 +34,18 @@ impl<'a> IoVec<'a> { _phantom: std::marker::PhantomData, } } + + pub fn from_slice(slice: &'a mut [u8]) -> Self { + Self { + base: slice.as_ptr(), + len: slice.len(), + _phantom: std::marker::PhantomData, + } + } + + pub fn len(&self) -> usize { + self.len + } } pub struct Uio<'a> { @@ -84,6 +96,15 @@ impl<'a> UioMut<'a> { Ok(Self { vecs, bytes_left }) } + + pub fn from_single_vec(vec: &'a mut IoVec<'a>) -> Self { + let bytes_left = vec.len; + + Self { + vecs: std::slice::from_mut(vec), + bytes_left, + } + } } #[derive(Debug, Error, Errno)] diff --git a/src/kernel/src/fs/vnode.rs b/src/kernel/src/fs/vnode.rs index 211d5aaae..3db9ebbb4 100644 --- a/src/kernel/src/fs/vnode.rs +++ b/src/kernel/src/fs/vnode.rs @@ -1,6 +1,6 @@ use super::{ - unixify_access, Access, CharacterDevice, FileBackend, IoCmd, Mode, Mount, OpenFlags, - RevokeFlags, Stat, TruncateLength, Uio, UioMut, VFile, VFileType, + unixify_access, Access, CharacterDevice, FileBackend, IoCmd, Mode, Mount, RevokeFlags, Stat, + TruncateLength, Uio, UioMut, VFile, }; use crate::arnd; use crate::errno::{Errno, ENOTDIR, ENOTTY, EOPNOTSUPP, EPERM}; @@ -138,27 +138,41 @@ impl Vnode { pub fn revoke(self: &Arc, flags: RevokeFlags) -> Result<(), Box> { self.backend.revoke(self, flags) } + + pub fn read( + self: &Arc, + buf: &mut UioMut, + td: Option<&VThread>, + ) -> Result> { + self.backend.read(self, buf, td) + } + + pub fn write( + self: &Arc, + buf: &mut Uio, + td: Option<&VThread>, + ) -> Result> { + self.backend.write(self, buf, td) + } } impl FileBackend for Vnode { - #[allow(unused_variables)] // TODO: remove when implementing fn read( self: &Arc, - file: &VFile, + _: &VFile, buf: &mut UioMut, td: Option<&VThread>, ) -> Result> { - todo!() + self.backend.read(self, buf, td) } - #[allow(unused_variables)] // TODO: remove when implementing fn write( self: &Arc, - file: &VFile, + _: &VFile, buf: &mut Uio, td: Option<&VThread>, ) -> Result> { - todo!() + self.backend.write(self, buf, td) } #[allow(unused_variables)] // TODO: remove when implementing @@ -299,6 +313,22 @@ pub(super) trait VnodeBackend: Debug + Send + Sync + 'static { ) -> Result<(), Box> { panic!("vop_revoke called"); } + + /// An implementation of `vop_read`. + fn read( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut UioMut, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result>; + + /// An implementation of `vop_write`. + fn write( + &self, + #[allow(unused_variables)] vn: &Arc, + #[allow(unused_variables)] buf: &mut Uio, + #[allow(unused_variables)] td: Option<&VThread>, + ) -> Result>; } /// An implementation of `vattr` struct. diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 64a66e14e..2cb935818 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -59,9 +59,11 @@ mod shm; mod signal; mod syscalls; mod sysctl; +mod sysent; mod time; mod ucred; mod umtx; +mod vm; fn main() -> Exit { run().into() @@ -355,6 +357,15 @@ fn run() -> Result<(), KernelError> { TimeManager::new(&mut syscalls); KernelQueueManager::new(&mut syscalls); NetManager::new(&mut syscalls); + NamedObjManager::new(&mut syscalls); + OsemManager::new(&mut syscalls); + UmtxManager::new(&mut syscalls); + + // Initialize runtime linker. + info!("Initializing runtime linker."); + + let ee = NativeEngine::new(); + let ld = RuntimeLinker::new(&fs, &ee, &mut syscalls); // TODO: Get correct budget name from the PS4. let budget_id = budget.create(Budget::new("big app", ProcType::BigApp)); @@ -365,8 +376,8 @@ fn run() -> Result<(), KernelError> { ProcType::BigApp, 1, // See sys_budget_set on the PS4. root, - "QXuNNl0Zhn", - &mut syscalls, + system_component, + syscalls, )?; info!( @@ -375,18 +386,6 @@ fn run() -> Result<(), KernelError> { proc.vm().stack().end() ); - NamedObjManager::new(&mut syscalls, &proc); - OsemManager::new(&mut syscalls, &proc); - UmtxManager::new(&mut syscalls); - - // Initialize runtime linker. - info!("Initializing runtime linker."); - - let ee = NativeEngine::new(); - let ld = RuntimeLinker::new(&fs, &ee, &mut syscalls); - - ee.set_syscalls(syscalls); - // TODO: Check if this credential is actually correct for game main thread. let cred = Arc::new(Ucred::new( Uid::ROOT, diff --git a/src/kernel/src/namedobj/mod.rs b/src/kernel/src/namedobj/mod.rs index 1cad50b20..f7f8c151d 100644 --- a/src/kernel/src/namedobj/mod.rs +++ b/src/kernel/src/namedobj/mod.rs @@ -1,33 +1,29 @@ -use crate::{ - errno::EINVAL, - idt::Entry, - info, - process::{VProc, VThread}, - syscalls::{SysErr, SysIn, SysOut, Syscalls}, -}; +use crate::errno::EINVAL; +use crate::idt::Entry; +use crate::info; +use crate::process::VThread; +use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use std::sync::Arc; -pub struct NamedObjManager { - proc: Arc, -} +pub struct NamedObjManager {} impl NamedObjManager { - pub fn new(sys: &mut Syscalls, proc: &Arc) -> Arc { - let namedobj = Arc::new(Self { proc: proc.clone() }); + pub fn new(sys: &mut Syscalls) -> Arc { + let namedobj = Arc::new(Self {}); sys.register(557, &namedobj, Self::sys_namedobj_create); namedobj } - fn sys_namedobj_create(self: &Arc, _: &VThread, i: &SysIn) -> Result { + fn sys_namedobj_create(self: &Arc, td: &VThread, i: &SysIn) -> Result { // Get arguments. let name = unsafe { i.args[0].to_str(32) }?.ok_or(SysErr::Raw(EINVAL))?; let data: usize = i.args[1].into(); let flags: u32 = i.args[2].try_into().unwrap(); // Allocate the entry. - let mut table = self.proc.objects_mut(); + let mut table = td.proc().objects_mut(); let obj = NamedObj::new(name, data); diff --git a/src/kernel/src/osem/mod.rs b/src/kernel/src/osem/mod.rs index e5110fd1e..9ec370f39 100644 --- a/src/kernel/src/osem/mod.rs +++ b/src/kernel/src/osem/mod.rs @@ -2,24 +2,21 @@ use crate::errno::EINVAL; use crate::idt::Entry; use crate::process::VThread; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; -use crate::VProc; use bitflags::bitflags; use std::sync::Arc; -pub struct OsemManager { - proc: Arc, -} +pub struct OsemManager {} impl OsemManager { - pub fn new(sys: &mut Syscalls, proc: &Arc) -> Arc { - let osem = Arc::new(Self { proc: proc.clone() }); + pub fn new(sys: &mut Syscalls) -> Arc { + let osem = Arc::new(Self {}); sys.register(549, &osem, Self::sys_osem_create); osem } - fn sys_osem_create(self: &Arc, _: &VThread, i: &SysIn) -> Result { + fn sys_osem_create(self: &Arc, td: &VThread, i: &SysIn) -> Result { let name = unsafe { i.args[0].to_str(32) }?.unwrap(); let flags = { let flags = i.args[1].try_into().unwrap(); @@ -36,7 +33,7 @@ impl OsemManager { flags }; - let mut objects = self.proc.objects_mut(); + let mut objects = td.proc().objects_mut(); let id = objects .alloc_infallible(|_| Entry::new(Some(name.to_owned()), Osem::new(flags), 0x120)); diff --git a/src/kernel/src/process/filedesc.rs b/src/kernel/src/process/filedesc.rs index 27ecdf2bb..d57675529 100644 --- a/src/kernel/src/process/filedesc.rs +++ b/src/kernel/src/process/filedesc.rs @@ -2,7 +2,6 @@ use crate::budget::BudgetType; use crate::errno::{Errno, EBADF}; use crate::fs::{VFile, VFileFlags, VFileType, Vnode}; use crate::kqueue::KernelQueue; -use bitflags::bitflags; use gmtx::{Gutex, GutexGroup}; use macros::Errno; use std::collections::VecDeque; diff --git a/src/kernel/src/process/mod.rs b/src/kernel/src/process/mod.rs index 6b1330252..59861d5a6 100644 --- a/src/kernel/src/process/mod.rs +++ b/src/kernel/src/process/mod.rs @@ -10,7 +10,9 @@ use crate::signal::{ }; use crate::signal::{SigChldFlags, Signal}; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use crate::sysent::ProcAbi; use crate::ucred::{AuthInfo, Gid, Privilege, Ucred, Uid}; +use crate::vm::{MemoryManagerError, Vm}; use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; use macros::Errno; use std::any::Any; @@ -22,7 +24,7 @@ use std::num::NonZeroI32; use std::ptr::null; use std::ptr::null_mut; use std::sync::atomic::{AtomicI32, AtomicPtr, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use thiserror::Error; pub use self::appinfo::*; @@ -34,7 +36,6 @@ pub use self::rlimit::*; pub use self::session::*; pub use self::signal::*; pub use self::thread::*; -pub use self::vm::*; mod appinfo; mod binary; @@ -45,7 +46,6 @@ mod rlimit; mod session; mod signal; mod thread; -mod vm; /// An implementation of `proc` structure. /// @@ -57,7 +57,8 @@ pub struct VProc { threads: Gutex>>, // p_threads cred: Ucred, // p_ucred group: Gutex>, // p_pgrp - vm: Arc, // p_vmspace + abi: OnceLock, // p_sysent + vm: Arc, // p_vmspace sigacts: Gutex, // p_sigacts files: Arc, // p_fd system_path: String, // p_randomized_path @@ -81,7 +82,7 @@ impl VProc { dmem_container: usize, root: Arc, system_path: impl Into, - sys: &mut Syscalls, + mut sys: Syscalls, ) -> Result, VProcInitError> { let cred = if auth.caps.is_system() { // TODO: The groups will be copied from the parent process, which is SceSysCore. @@ -99,7 +100,8 @@ impl VProc { threads: gg.spawn(Vec::new()), cred, group: gg.spawn(None), - vm: MemoryManager::new(sys)?, + abi: OnceLock::new(), + vm: Vm::new(&mut sys)?, sigacts: gg.spawn(SignalActs::new()), files: FileDesc::new(root), system_path: system_path.into(), @@ -115,6 +117,7 @@ impl VProc { uptc: AtomicPtr::new(null_mut()), }); + // TODO: Move all syscalls here to somewhere else. sys.register(20, &vp, Self::sys_getpid); sys.register(50, &vp, Self::sys_setlogin); sys.register(147, &vp, Self::sys_setsid); @@ -130,6 +133,8 @@ impl VProc { sys.register(587, &vp, Self::sys_get_authinfo); sys.register(602, &vp, Self::sys_randomized_path); + vp.abi.set(ProcAbi::new(sys)).unwrap(); + Ok(vp) } @@ -141,7 +146,11 @@ impl VProc { &self.cred } - pub fn vm(&self) -> &MemoryManager { + pub fn abi(&self) -> &ProcAbi { + self.abi.get().unwrap() + } + + pub fn vm(&self) -> &Arc { &self.vm } diff --git a/src/kernel/src/rtld/mem.rs b/src/kernel/src/rtld/mem.rs index b6de89a74..2f7ec0319 100644 --- a/src/kernel/src/rtld/mem.rs +++ b/src/kernel/src/rtld/mem.rs @@ -1,6 +1,7 @@ use super::MapError; use crate::fs::VFile; -use crate::process::{MappingFlags, MemoryUpdateError, Protections, VProc}; +use crate::process::VProc; +use crate::vm::{MappingFlags, MemoryUpdateError, Protections}; use elf::{Elf, ProgramFlags, ProgramType}; use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; use std::alloc::Layout; diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 383802b42..8a3119b1a 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -8,8 +8,9 @@ use crate::fs::{Fs, OpenError, VPath, VPathBuf}; use crate::idt::Entry; use crate::info; use crate::log::print; -use crate::process::{Binaries, MemoryUpdateError, MmapError, Protections, VThread}; +use crate::process::{Binaries, VThread}; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use crate::vm::{MemoryUpdateError, MmapError, Protections}; use bitflags::bitflags; use elf::{DynamicFlags, Elf, FileType, ReadProgramError, Relocation, Symbol}; use gmtx::{Gutex, GutexGroup}; @@ -118,6 +119,7 @@ impl RuntimeLinker { 0 }; + // TODO: Check exec_new_vmspace on the PS4 to see what we have missed here. // TODO: Apply remaining checks from exec_self_imgact. // Map eboot.bin. let mut app = Module::map( diff --git a/src/kernel/src/shm/mod.rs b/src/kernel/src/shm/mod.rs index 3c5b3e756..21237f664 100644 --- a/src/kernel/src/shm/mod.rs +++ b/src/kernel/src/shm/mod.rs @@ -111,22 +111,20 @@ impl SharedMemory { } impl FileBackend for SharedMemory { - #[allow(unused_variables)] fn read( self: &Arc, - file: &VFile, - buf: &mut UioMut, - td: Option<&VThread>, + _: &VFile, + _: &mut UioMut, + _: Option<&VThread>, ) -> Result> { Err(Box::new(DefaultFileBackendError::OperationNotSupported)) } - #[allow(unused_variables)] fn write( self: &Arc, - file: &VFile, - buf: &mut Uio, - td: Option<&VThread>, + _: &VFile, + _: &mut Uio, + _: Option<&VThread>, ) -> Result> { Err(Box::new(DefaultFileBackendError::OperationNotSupported)) } diff --git a/src/kernel/src/signal/set.rs b/src/kernel/src/signal/set.rs index 0d52a2266..50c5d4034 100644 --- a/src/kernel/src/signal/set.rs +++ b/src/kernel/src/signal/set.rs @@ -33,7 +33,7 @@ impl SignalSet { /// An implementation of `_SIG_WORD`. fn word(s: Signal) -> usize { // This is safe because `Signal` is guaranteed to be non-negative. - unsafe { (Self::idx(s) >> 5).try_into().unwrap() } + (Self::idx(s) >> 5).try_into().unwrap() } /// An implementation of `_SIG_BIT`. diff --git a/src/kernel/src/sysctl/mod.rs b/src/kernel/src/sysctl/mod.rs index 79ed2c9ea..05186077a 100644 --- a/src/kernel/src/sysctl/mod.rs +++ b/src/kernel/src/sysctl/mod.rs @@ -3,8 +3,9 @@ use crate::arnd::rand_bytes; use crate::errno::{ EFAULT, EINVAL, EISDIR, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOPNOTSUPP, EPERM, ESRCH, }; -use crate::process::{MemoryManager, VThread}; +use crate::process::VThread; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use crate::vm::Vm; use std::any::Any; use std::cmp::min; use std::ptr::null_mut; @@ -1079,7 +1080,7 @@ static HW_PAGESIZE: Oid = Oid { number: Sysctl::HW_PAGESIZE, kind: Sysctl::CTLFLAG_RD | Sysctl::CTLFLAG_MPSAFE | Sysctl::CTLFLAG_CAPRD | Sysctl::CTLTYPE_INT, arg1: None, - arg2: MemoryManager::VIRTUAL_PAGE_SIZE, + arg2: Vm::VIRTUAL_PAGE_SIZE, name: "pagesize", handler: Some(Sysctl::handle_int), fmt: "I", diff --git a/src/kernel/src/sysent/mod.rs b/src/kernel/src/sysent/mod.rs new file mode 100644 index 000000000..d12a41330 --- /dev/null +++ b/src/kernel/src/sysent/mod.rs @@ -0,0 +1,17 @@ +use crate::syscalls::Syscalls; + +/// Implementation of `sysentvec` structure. +#[derive(Debug)] +pub struct ProcAbi { + syscalls: Syscalls, // sv_size + sv_table +} + +impl ProcAbi { + pub fn new(syscalls: Syscalls) -> Self { + Self { syscalls } + } + + pub fn syscalls(&self) -> &Syscalls { + &self.syscalls + } +} diff --git a/src/kernel/src/process/vm/iter.rs b/src/kernel/src/vm/iter.rs similarity index 100% rename from src/kernel/src/process/vm/iter.rs rename to src/kernel/src/vm/iter.rs diff --git a/src/kernel/src/process/vm/mod.rs b/src/kernel/src/vm/mod.rs similarity index 97% rename from src/kernel/src/process/vm/mod.rs rename to src/kernel/src/vm/mod.rs index 27c6fd095..3e0508d1a 100644 --- a/src/kernel/src/process/vm/mod.rs +++ b/src/kernel/src/vm/mod.rs @@ -22,19 +22,19 @@ mod page; mod stack; mod storage; -/// Manage all paged memory that can be seen by a PS4 app. +/// Implementation of `vmspace` structure. #[derive(Debug)] -pub struct MemoryManager { - page_size: usize, +pub struct Vm { allocation_granularity: usize, allocations: RwLock>, // Key is Alloc::addr. stack: AppStack, } -impl MemoryManager { +impl Vm { /// Size of a memory page on PS4. pub const VIRTUAL_PAGE_SIZE: usize = 0x4000; + /// See `vmspace_alloc` on the PS4 for a reference. pub fn new(sys: &mut Syscalls) -> Result, MemoryManagerError> { // Check if page size on the host is supported. We don't need to check allocation // granularity because it is always multiply by page size, which is a correct value. @@ -49,9 +49,7 @@ impl MemoryManager { return Err(MemoryManagerError::UnsupportedPageSize); } - // TODO: Check exec_new_vmspace on the PS4 to see what we have missed here. let mut mm = Self { - page_size, allocation_granularity, allocations: RwLock::default(), stack: AppStack::new(), @@ -91,16 +89,6 @@ impl MemoryManager { Ok(mm) } - /// Gets size of page on the host system. - pub fn page_size(&self) -> usize { - self.page_size - } - - /// Gets allocation granularity on the host system. - pub fn allocation_granularity(&self) -> usize { - self.allocation_granularity - } - pub fn stack(&self) -> &AppStack { &self.stack } @@ -555,7 +543,7 @@ impl MemoryManager { // Check if the request is a guard for main stack. if addr == self.stack.guard() { - assert_eq!(len, MemoryManager::VIRTUAL_PAGE_SIZE); + assert_eq!(len, Self::VIRTUAL_PAGE_SIZE); assert!(prot.is_empty()); assert!(flags.intersects(MappingFlags::MAP_ANON)); assert_eq!(fd, -1); @@ -650,7 +638,7 @@ impl MemoryManager { } } -unsafe impl Sync for MemoryManager {} +unsafe impl Sync for Vm {} /// Contains information for an allocation of virtual pages. #[derive(Debug)] diff --git a/src/kernel/src/process/vm/page.rs b/src/kernel/src/vm/page.rs similarity index 82% rename from src/kernel/src/process/vm/page.rs rename to src/kernel/src/vm/page.rs index fa83fffd4..dd900cb5a 100644 --- a/src/kernel/src/process/vm/page.rs +++ b/src/kernel/src/vm/page.rs @@ -1,14 +1,14 @@ -use super::MemoryManager; +use super::Vm; /// Encapsulated one or more virtual pages. pub struct VPages<'a> { - mm: &'a MemoryManager, + mm: &'a Vm, ptr: *mut u8, len: usize, } impl<'a> VPages<'a> { - pub(super) fn new(mm: &'a MemoryManager, ptr: *mut u8, len: usize) -> Self { + pub(super) fn new(mm: &'a Vm, ptr: *mut u8, len: usize) -> Self { Self { mm, ptr, len } } diff --git a/src/kernel/src/process/vm/stack.rs b/src/kernel/src/vm/stack.rs similarity index 100% rename from src/kernel/src/process/vm/stack.rs rename to src/kernel/src/vm/stack.rs diff --git a/src/kernel/src/process/vm/storage.rs b/src/kernel/src/vm/storage.rs similarity index 100% rename from src/kernel/src/process/vm/storage.rs rename to src/kernel/src/vm/storage.rs