mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 19:00:17 +02:00
- Do compiler/architecture checks EARLY, don't waste time doing random configuration stuff if the user can't even compile in the first place - MSVC is unsupported, I assume? So add a check to disallow. There's literally no MSVC or MSC_VER checks anywhere, so... - Rather than using the MSVC architecture definitions, use our own `ARCHITECTURE_arm64` et al. Hijacking existing "standard" definitions is a very bad idea. Also makes it more readable in CMake - Change the x86 host check to `x86|amd64`. Some systems still refer to themselves as x86 despite being 64-bit for... reasons, and I saw one a very long time ago that referred to it as amd64. This should basically never come up, nor is it really relevant given that FEX is for arm64... but it kinda annoyed me so whatever. TODOs: - Should we check `CMAKE_SIZEOF_VOID_P (equal) 64`? I don't think anyone is even trying to compile this thing on armv7 or older, but might as well? maybe? - What's the status of *BSD, Solaris, macOS? Technically macOS does support Wine, not sure about the others. Signed-off-by: crueter <crueter@eden-emu.dev>
260 lines
10 KiB
C++
260 lines
10 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "Common/cpp-optparse/OptionParser.h"
|
|
#include "Common/Config.h"
|
|
#include "Common/FEXServerClient.h"
|
|
#include "Common/HostFeatures.h"
|
|
#include "git_version.h"
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Utils/PrctlUtils.h>
|
|
#include <FEXCore/fextl/fmt.h>
|
|
#include <FEXCore/fextl/string.h>
|
|
#include <FEXHeaderUtils/Filesystem.h>
|
|
|
|
#include <cstdio>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <sys/prctl.h>
|
|
|
|
namespace {
|
|
struct TSOEmulationFacts {
|
|
bool LSE {}, LSE2 {};
|
|
bool HardwareTSO {};
|
|
bool LRCPC1 {}, LRCPC2 {}, LRCPC3 {};
|
|
};
|
|
|
|
#ifdef ARCHITECTURE_arm64
|
|
bool CheckForHardwareTSO() {
|
|
// Check to see if this is supported.
|
|
auto Result = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0);
|
|
if (Result == -1) {
|
|
// Unsupported, early exit.
|
|
return false;
|
|
}
|
|
|
|
if (Result == PR_SET_MEM_MODEL_DEFAULT) {
|
|
// Try to set the TSO mode if we are currently default.
|
|
Result = prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_TSO, 0, 0, 0);
|
|
if (Result == 0) {
|
|
Result = prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_DEFAULT, 0, 0, 0);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
enum ISAR0_FIELDS {
|
|
LSE = 20,
|
|
};
|
|
|
|
enum ISAR1_FIELDS {
|
|
LRCPC = 20,
|
|
};
|
|
|
|
enum MMFR2_FIELDS {
|
|
AT = 32,
|
|
};
|
|
|
|
constexpr static uint32_t IDFIELDMASK = 0b1111;
|
|
uint64_t GetISAR0() {
|
|
uint64_t Result {};
|
|
asm("mrs %0, ID_AA64ISAR0_EL1;" : "=r"(Result));
|
|
return Result;
|
|
}
|
|
|
|
uint64_t GetISAR1() {
|
|
uint64_t Result {};
|
|
asm("mrs %0, ID_AA64ISAR1_EL1;" : "=r"(Result));
|
|
return Result;
|
|
}
|
|
|
|
uint64_t GetMMFR2() {
|
|
uint64_t Result {};
|
|
asm("mrs %0, ID_AA64MMFR2_EL1;" : "=r"(Result));
|
|
return Result;
|
|
}
|
|
|
|
TSOEmulationFacts GetTSOEmulationFacts() {
|
|
const auto ISAR0 = GetISAR0();
|
|
const auto ISAR1 = GetISAR1();
|
|
const auto MMFR2 = GetMMFR2();
|
|
|
|
return {
|
|
.LSE = ((ISAR0 >> ISAR0_FIELDS::LSE) & IDFIELDMASK) >= 0b0010,
|
|
.LSE2 = ((MMFR2 >> MMFR2_FIELDS::AT) & IDFIELDMASK) >= 0b0001,
|
|
.HardwareTSO = CheckForHardwareTSO(),
|
|
.LRCPC1 = ((ISAR1 >> ISAR1_FIELDS::LRCPC) & IDFIELDMASK) >= 0b0001,
|
|
.LRCPC2 = ((ISAR1 >> ISAR1_FIELDS::LRCPC) & IDFIELDMASK) >= 0b0010,
|
|
.LRCPC3 = ((ISAR1 >> ISAR1_FIELDS::LRCPC) & IDFIELDMASK) >= 0b0011,
|
|
};
|
|
}
|
|
#else
|
|
TSOEmulationFacts GetTSOEmulationFacts() {
|
|
return {};
|
|
}
|
|
#endif
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv, char** envp) {
|
|
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
|
FEXCore::Config::Initialize();
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateGlobalMainLayer());
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateMainLayer());
|
|
// No FEX arguments passed through command line
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateEnvironmentLayer(envp));
|
|
|
|
// Load the arguments
|
|
optparse::OptionParser Parser = optparse::OptionParser().description("Simple application to get a couple of FEX options");
|
|
|
|
Parser.add_option("--install-prefix").action("store_true").help("Print the FEX install prefix");
|
|
|
|
Parser.add_option("--app").help("Load an application profile for this application if it exists");
|
|
|
|
Parser.add_option("--current-rootfs").action("store_true").help("Print the directory that contains the FEX rootfs. Mounted in the case of squashfs");
|
|
|
|
Parser.add_option("--tso-emulation-info").action("store_true").help("Print how FEX is emulating the x86-TSO memory model.");
|
|
|
|
#ifdef ARCHITECTURE_arm64
|
|
Parser.add_option("--identification-reg-info").action("store_true").help("Print identification registers");
|
|
#endif
|
|
|
|
Parser.add_option("--version").action("store_true").help("Print the installed FEX-Emu version");
|
|
|
|
optparse::Values Options = Parser.parse_args(argc, argv);
|
|
|
|
if (Options.is_set_by_user("app")) {
|
|
// Load the application config if one was provided
|
|
const auto ProgramName = FHU::Filesystem::GetFilename(Options["app"]);
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_GLOBAL_APP));
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_LOCAL_APP));
|
|
|
|
auto SteamID = getenv("SteamAppId");
|
|
if (SteamID) {
|
|
// If a SteamID exists then let's search for Steam application configs as well.
|
|
// We want to key off both the SteamAppId number /and/ the executable since we may not want to thunk all binaries.
|
|
const auto SteamAppName = fextl::fmt::format("Steam_{}_{}", SteamID, ProgramName);
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP));
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_LOCAL_STEAM_APP));
|
|
}
|
|
}
|
|
|
|
FEXCore::Config::Load();
|
|
|
|
// Reload the meta layer
|
|
FEXCore::Config::ReloadMetaLayer();
|
|
|
|
if (Options.is_set_by_user("version")) {
|
|
fprintf(stdout, GIT_DESCRIBE_STRING "\n");
|
|
}
|
|
|
|
if (Options.is_set_by_user("install_prefix")) {
|
|
char SelfPath[PATH_MAX];
|
|
auto Result = readlink("/proc/self/exe", SelfPath, PATH_MAX);
|
|
if (Result == -1) {
|
|
Result = 0;
|
|
}
|
|
auto InstallPrefix = std::filesystem::path(&SelfPath[0], &SelfPath[Result]).parent_path().parent_path().string();
|
|
fprintf(stdout, "%s\n", InstallPrefix.c_str());
|
|
}
|
|
|
|
if (Options.is_set_by_user("current_rootfs")) {
|
|
int ServerFD = FEXServerClient::ConnectToServer();
|
|
if (ServerFD != -1) {
|
|
auto RootFS = FEXServerClient::RequestRootFSPath(ServerFD);
|
|
if (!RootFS.empty()) {
|
|
fprintf(stdout, "%s\n", RootFS.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Options.is_set_by_user("tso_emulation_info")) {
|
|
auto TSOFacts = GetTSOEmulationFacts();
|
|
const char* GPRMemoryTSOEmulation {};
|
|
const char* MemcpyMemoryTSOEmulation {};
|
|
const char* VectorMemoryTSOEmulation {};
|
|
const char* UnalignedMemoryLoadStoreTSOEmulation {};
|
|
|
|
if (TSOFacts.HardwareTSO) {
|
|
GPRMemoryTSOEmulation = "\e[32mHardware TSO\e[0m";
|
|
} else if (TSOFacts.LRCPC3) {
|
|
GPRMemoryTSOEmulation = "\e[32mLRCPC3\e[0m";
|
|
} else if (TSOFacts.LRCPC2) {
|
|
GPRMemoryTSOEmulation = "\e[32mLRCPC2\e[0m";
|
|
} else if (TSOFacts.LRCPC1) {
|
|
GPRMemoryTSOEmulation = "\e[32mLRCPC\e[0m";
|
|
} else {
|
|
GPRMemoryTSOEmulation = "\e[31mAtomics\e[0m";
|
|
}
|
|
|
|
// Memcpy only uses Hardware TSO, LRCPC, and Atomics.
|
|
if (TSOFacts.HardwareTSO) {
|
|
MemcpyMemoryTSOEmulation = "\e[32mHardware TSO\e[0m";
|
|
} else if (TSOFacts.LRCPC1) {
|
|
MemcpyMemoryTSOEmulation = "\e[32mLRCPC\e[0m";
|
|
} else {
|
|
MemcpyMemoryTSOEmulation = "\e[31mAtomics\e[0m";
|
|
}
|
|
|
|
if (TSOFacts.HardwareTSO) {
|
|
VectorMemoryTSOEmulation = "\e[32mHardware TSO\e[0m";
|
|
} else if (TSOFacts.LRCPC3) {
|
|
VectorMemoryTSOEmulation = "\e[32mLRCPC3\e[0m";
|
|
} else {
|
|
VectorMemoryTSOEmulation = "\e[31mHalf-Barriers\e[0m";
|
|
}
|
|
|
|
if (TSOFacts.HardwareTSO) {
|
|
UnalignedMemoryLoadStoreTSOEmulation = "\e[32mHardware TSO\e[0m";
|
|
} else {
|
|
UnalignedMemoryLoadStoreTSOEmulation = "\e[31mHalf-Barriers\e[0m";
|
|
}
|
|
|
|
fprintf(stdout, "Hardware Features:\n");
|
|
fprintf(stdout, "\tMemory atomics emulation method: %s\n", TSOFacts.LSE ? "\e[32mLSE\e[0m" : "\e[31mLL/SC\e[0m");
|
|
fprintf(stdout, "\tUnaligned atomic memory granularity: %s\n", TSOFacts.LSE2 ? "\e[32m16-byte\e[0m" : "\e[31mNatural alignment\e[0m");
|
|
///< TODO: Once TME is supported by hardware this can change.
|
|
fprintf(stdout, "\tUnaligned memory loadstore emulation: %s\n", UnalignedMemoryLoadStoreTSOEmulation);
|
|
fprintf(stdout, "\t16-Byte split-lock atomic emulation: %s\n", TSOFacts.LSE ? "\e[31mTearing CAS loops\e[0m" : "\e[31mTearing LL/SC loops\e[0m");
|
|
fprintf(stdout, "\t64-Byte split-lock atomic emulation: %s\n", TSOFacts.LSE ? "\e[31mTearing CAS loops\e[0m" : "\e[31mTearing LL/SC loops\e[0m");
|
|
fprintf(stdout, "\tGPR memory model emulation: %s\n", GPRMemoryTSOEmulation);
|
|
fprintf(stdout, "\tMemcpy memory model emulation: %s\n", MemcpyMemoryTSOEmulation);
|
|
fprintf(stdout, "\tVector memory model emulation: %s\n", VectorMemoryTSOEmulation);
|
|
|
|
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
|
|
FEX_CONFIG_OPT(MemcpySetTSOEnabled, MEMCPYSETTSOENABLED);
|
|
FEX_CONFIG_OPT(VectorTSOEnabled, VECTORTSOENABLED);
|
|
FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED);
|
|
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
|
|
fprintf(stderr, "Strict: %d\n", StrictInProcessSplitLocks());
|
|
|
|
fprintf(stdout, "\nConfiguration:\n");
|
|
fprintf(stdout, "\tTSO Emulation: %s\n", TSOEnabled() ? "Enabled" : "Disabled");
|
|
fprintf(stdout, "\tMemcpy TSO Emulation: %s\n", TSOEnabled() && MemcpySetTSOEnabled() ? "Enabled" : "Disabled");
|
|
fprintf(stdout, "\tVector TSO Emulation: %s\n", TSOEnabled() && VectorTSOEnabled() ? "Enabled" : "Disabled");
|
|
fprintf(stdout, "\tHalf-barrier unaligned TSO emulation: %s\n", TSOEnabled() && HalfBarrierTSOEnabled() ? "Enabled" : "Disabled");
|
|
fprintf(stdout, "\t16-Byte strict split-lock emulation: %s\n", StrictInProcessSplitLocks() ? "In-process mutex" : "Tearing");
|
|
fprintf(stdout, "\t64-Byte strict split-lock emulation: %s\n", StrictInProcessSplitLocks() ? "In-process mutex" : "Tearing");
|
|
}
|
|
|
|
#ifdef ARCHITECTURE_arm64
|
|
if (Options.is_set_by_user("identification_reg_info")) {
|
|
auto Features = FEX::GetCPUFeaturesFromIDRegisters();
|
|
fextl::string features {};
|
|
features += fmt::format("isar0=0x{:x},", Features.ISAR0.Get());
|
|
features += fmt::format("isar1=0x{:x},", Features.ISAR1.Get());
|
|
features += fmt::format("isar2=0x{:x},", Features.ISAR2.Get());
|
|
features += fmt::format("pfr0=0x{:x},", Features.PFR0.Get());
|
|
features += fmt::format("pfr1=0x{:x},", Features.PFR1.Get());
|
|
features += fmt::format("midr=0x{:x},", Features.MIDR.Get());
|
|
features += fmt::format("mmfr0=0x{:x},", Features.MMFR0.Get());
|
|
features += fmt::format("mmfr1=0x{:x},", Features.MMFR1.Get());
|
|
features += fmt::format("mmfr2=0x{:x},", Features.MMFR2.Get());
|
|
features += fmt::format("zfr0=0x{:x},", Features.ZFR0.Get());
|
|
features += fmt::format("dczid=0x{:x},", Features.DCZID.Get());
|
|
features += fmt::format("svevl=0x{:x}", Features.SVEVL.Get());
|
|
fprintf(stderr, "Features: '%s'\n", features.c_str());
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|