Files
FEX-Emu--FEX/Source/Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp
T
Ryan Houdek 2079f6b3c7 Improves compile ability for older libraries
Adds a header only include utility folder that can be included from
everywhere.

Contains syscall helpers for older glibc and defines for older Linux
uapi headers missing some defines.
2021-12-24 15:43:25 -08:00

774 lines
24 KiB
C++

/*
$info$
tags: LinuxSyscalls|common
desc: Emulated /proc/cpuinfo, version, osrelease, etc
$end_info$
*/
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CodeLoader.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Utils/LogManager.h>
#include <git_version.h>
#include <cstring>
#include <fcntl.h>
#include <filesystem>
#include <ostream>
#include <sstream>
#include <stdio.h>
#include <system_error>
#include <unistd.h>
#include <utility>
#include <vector>
using string = std::string;
namespace FEX::EmulatedFile {
/**
* @brief Generates a temporary file using raw FDs
*
* Since we are hooking syscalls that are expecting to use raw FDs, we need to make sure to also use raw FDs.
* The guest application can leave these FDs dangling.
*
* Using glibc tmpfile creates a FILE which glibc tracks and will try cleaning up on application exit.
* If we are running a 32-bit application then this dangling FILE will be allocated using the FEX allcator
* Which will have already been cleaned up on shutdown.
*
* Dangling raw FD is safe since if the guest doesn't close them, then the kernel cleans them up on application close.
*
* @return A temporary file that we can use
*/
static int GenTmpFD() {
int fd = open("/tmp", O_RDWR | O_TMPFILE | O_EXCL | S_IRUSR | S_IWUSR);
return fd;
}
std::string GenerateCPUInfo(FEXCore::Context::Context *ctx, uint32_t CPUCores) {
std::ostringstream cpu_stream{};
auto res_0 = FEXCore::Context::RunCPUIDFunction(ctx, 0, 0);
auto res_1 = FEXCore::Context::RunCPUIDFunction(ctx, 1, 0);
auto res_6 = FEXCore::Context::RunCPUIDFunction(ctx, 6, 0);
auto res_7 = FEXCore::Context::RunCPUIDFunction(ctx, 7, 0);
auto res_10 = FEXCore::Context::RunCPUIDFunction(ctx, 0x10, 0);
auto res_8000_0001 = FEXCore::Context::RunCPUIDFunction(ctx, 0x8000'0001, 0);
auto res_8000_0007 = FEXCore::Context::RunCPUIDFunction(ctx, 0x8000'0007, 0);
auto res_8000_0008 = FEXCore::Context::RunCPUIDFunction(ctx, 0x8000'0008, 0);
auto res_8000_000a = FEXCore::Context::RunCPUIDFunction(ctx, 0x8000'000a, 0);
auto res_8000_001f = FEXCore::Context::RunCPUIDFunction(ctx, 0x8000'001f, 0);
union VendorID {
struct {
uint32_t id;
char Str[13];
};
struct {
FEXCore::CPUID::FunctionResults cpuid;
uint8_t null;
};
};
union ModelName {
struct {
char Str[49];
};
struct {
FEXCore::CPUID::FunctionResults cpuid_2;
FEXCore::CPUID::FunctionResults cpuid_3;
FEXCore::CPUID::FunctionResults cpuid_4;
uint8_t null;
};
};
union Info {
FEXCore::CPUID::FunctionResults cpuid;
struct {
unsigned Stepping : 4;
unsigned Model : 4;
unsigned FamilyID : 4;
unsigned Type : 4;
unsigned ExModelID : 4;
unsigned ExFamilyID : 8;
unsigned : 4;
};
};
VendorID vendorid {};
vendorid.cpuid = {res_0.eax, res_0.ebx, res_0.edx, res_0.ecx};
vendorid.null = 0;
Info info {res_1};
uint32_t Family = info.FamilyID + (info.FamilyID == 0xF ? info.ExFamilyID : 0);
std::ostringstream flags_data{};
// Generate the flags data up front
// This is the same per core
{
#define FLAG(flag, name) if (flag) { flags_data << name << " "; }
FLAG(res_1.edx & (1 << 0), "fpu")
FLAG(res_1.edx & (1 << 1), "vme")
FLAG(res_1.edx & (1 << 2), "de")
FLAG(res_1.edx & (1 << 3), "pse")
FLAG(res_1.edx & (1 << 4), "tsc")
FLAG(res_1.edx & (1 << 5), "msr")
FLAG(res_1.edx & (1 << 6), "pae")
FLAG(res_1.edx & (1 << 7), "mce")
FLAG(res_1.edx & (1 << 8), "cx8")
FLAG(res_1.edx & (1 << 9), "apic")
FLAG(res_1.edx & (1 << 11), "sep")
FLAG(res_1.edx & (1 << 12), "mtrr")
FLAG(res_1.edx & (1 << 13), "pge")
FLAG(res_1.edx & (1 << 14), "mca")
FLAG(res_1.edx & (1 << 15), "cmov")
FLAG(res_1.edx & (1 << 16), "pat")
FLAG(res_1.edx & (1 << 17), "pse36")
FLAG(res_1.edx & (1 << 18), "pn")
FLAG(res_1.edx & (1 << 19), "clflush")
FLAG(res_1.edx & (1 << 21), "ds") // XXX
FLAG(res_1.edx & (1 << 22), "acpi") // XXX
FLAG(res_1.edx & (1 << 23), "mmx")
FLAG(res_1.edx & (1 << 24), "fxsr")
FLAG(res_1.edx & (1 << 25), "sse")
FLAG(res_1.edx & (1 << 26), "sse2")
FLAG(res_1.edx & (1 << 27), "ss")
FLAG(res_1.edx & (1 << 28), "ht")
FLAG(res_1.edx & (1 << 29), "tm")
FLAG(res_1.edx & (1 << 30), "ia64")
FLAG(res_1.edx & (1 << 31), "pbe")
FLAG(res_8000_0001.edx & (1 << 11),
"syscall")
FLAG(res_8000_0001.edx & (1 << 19),
"mp")
FLAG(res_8000_0001.edx & (1 << 20),
"nx")
FLAG(res_8000_0001.edx & (1 << 22),
"mmxext")
FLAG(res_8000_0001.edx & (1 << 25),
"fxsr_opt")
FLAG(res_8000_0001.edx & (1 << 26),
"pdpe1gb")
FLAG(res_8000_0001.edx & (1 << 27),
"rdtscp")
FLAG(res_8000_0001.edx & (1 << 29),
"lm")
FLAG(res_8000_0001.edx & (1 << 31),
"3dnow")
FLAG(res_8000_0001.edx & (1 << 30),
"3dnowext")
FLAG(res_8000_0007.edx & (1 << 8),
"constant_tsc")
// We are not a uniprocessor running in SMP mode
FLAG(false, "up")
// Timer is always running
FLAG(true, "art")
// No Intel perfmon
FLAG(false, "arch_perfmon")
// No precise event based sampling
FLAG(false, "pebs")
// No branch trace store
FLAG(false, "bts")
FLAG(true, "rep_good")
FLAG(res_8000_0007.edx & (1 << 12),
"tm")
// Always support long nop
FLAG(true, "nopl")
// Always expose topology information
FLAG(true, "xtoplogy")
// Atom/geode only?
FLAG(false, "tsc_reliable")
FLAG(res_8000_0007.edx & (1 << 8),
"nonstop_tsc")
// We always support CPUID
FLAG(true, "cpuid")
FLAG(Family > 0x16,
"extd_apicid")
FLAG(false, "amd_dcm") // Never claim to be a multi node processor
FLAG(res_8000_0007.edx & (1 << 11),
"aperfmperf")
// Need to check ARM documentation if we can support this?
FLAG(false, "nonstop_tsc_s3")
// We can calculate this flag on AArch64
FLAG(true, "tsc_known_freq")
FLAG(res_1.ecx & (1 << 0),
"pni")
FLAG(res_1.ecx & (1 << 1),
"pclmulqdq")
FLAG(res_1.ecx & (1 << 2),
"dtes64")
FLAG(res_1.ecx & (1 << 3),
"monitor")
FLAG(res_1.ecx & (1 << 4),
"ds_cpl")
FLAG(res_1.ecx & (1 << 5),
"vmx")
FLAG(res_1.ecx & (1 << 6),
"smx")
FLAG(res_1.ecx & (1 << 7),
"est")
FLAG(res_1.ecx & (1 << 8),
"tm2")
FLAG(res_1.ecx & (1 << 9),
"ssse3")
FLAG(res_1.ecx & (1 << 11),
"sdbg")
FLAG(res_1.ecx & (1 << 12),
"fma")
FLAG(res_1.ecx & (1 << 13),
"cx16")
FLAG(res_1.ecx & (1 << 14),
"xptr")
FLAG(res_1.ecx & (1 << 15),
"pdcm")
FLAG(res_1.ecx & (1 << 17),
"pcid")
FLAG(res_1.ecx & (1 << 18),
"dca")
FLAG(res_1.ecx & (1 << 19),
"sse4_1")
FLAG(res_1.ecx & (1 << 20),
"sse4_2")
FLAG(res_1.ecx & (1 << 21),
"x2apic")
FLAG(res_1.ecx & (1 << 22),
"movbe")
FLAG(res_1.ecx & (1 << 23),
"popcnt")
FLAG(res_1.ecx & (1 << 24),
"tsc_deadline_timer")
FLAG(res_1.ecx & (1 << 25),
"aes")
FLAG(res_1.ecx & (1 << 26),
"xsave")
FLAG(res_1.ecx & (1 << 28),
"avx")
FLAG(res_1.ecx & (1 << 29),
"f16c")
FLAG(res_1.ecx & (1 << 30),
"rdrand")
FLAG(res_1.ecx & (1 << 31),
"hypervisor")
FLAG(res_8000_0001.ecx & (1 << 0),
"lahf_lm")
FLAG(res_8000_0001.ecx & (1 << 1),
"cmp_legacy")
FLAG(res_8000_0001.ecx & (1 << 2),
"svm")
FLAG(res_8000_0001.ecx & (1 << 3),
"extapic")
FLAG(res_8000_0001.ecx & (1 << 4),
"cr8_legacy")
FLAG(res_8000_0001.ecx & (1 << 5),
"abm")
FLAG(res_8000_0001.ecx & (1 << 6),
"sse4a")
FLAG(res_8000_0001.ecx & (1 << 7),
"misalignsse")
FLAG(res_8000_0001.ecx & (1 << 8),
"3dnowprefetch")
FLAG(res_8000_0001.ecx & (1 << 9),
"osvw")
FLAG(res_8000_0001.ecx & (1 << 10),
"ibs")
FLAG(res_8000_0001.ecx & (1 << 11),
"xop")
FLAG(res_8000_0001.ecx & (1 << 12),
"skinit")
FLAG(res_8000_0001.ecx & (1 << 13),
"wdt")
FLAG(res_8000_0001.ecx & (1 << 15),
"lwp")
FLAG(res_8000_0001.ecx & (1 << 16),
"fma4")
FLAG(res_8000_0001.ecx & (1 << 17),
"tce")
FLAG(res_8000_0001.ecx & (1 << 19),
"nodeid_msr")
FLAG(res_8000_0001.ecx & (1 << 21),
"tbm")
FLAG(res_8000_0001.ecx & (1 << 22),
"topoext")
FLAG(res_8000_0001.ecx & (1 << 23),
"perfctr_core")
FLAG(res_8000_0001.ecx & (1 << 24),
"perfctr_nb")
FLAG(res_8000_0001.ecx & (1 << 26),
"bpext")
FLAG(res_8000_0001.ecx & (1 << 27),
"ptsc")
FLAG(res_8000_0001.ecx & (1 << 28),
"perfctr_llc")
FLAG(res_8000_0001.ecx & (1 << 29),
"mwaitx")
// We don't support ring 3 supporting mwait
FLAG(false, "ring3mwait")
// We don't support Intel CPUID fault support
FLAG(false, "cpuid_fault")
FLAG(res_8000_0007.edx & (1 << 9),
"cpb")
FLAG(res_6.ecx & (1 << 3),
"epb")
FLAG(res_10.ebx & (1 << 1),
"cat_l3")
FLAG(res_10.ebx & (1 << 2),
"cat_l2")
FLAG(false, // Needs leaf support
"cdp_l3")
FLAG(false, "invpcid_single")
FLAG(res_8000_0007.edx & (1 << 7),
"hw_pstate")
FLAG(res_8000_001f.eax & (1 << 0),
"sme")
// Kernel page table isolation.
FLAG(false, "pti")
// We don't support Intel's Protected Processor Inventory Number
FLAG(false, "intel_ppin")
FLAG(false, // Needs leaf support
"cdp_l2")
FLAG(res_8000_0008.ebx & (1 << 6),
"mba")
FLAG(res_8000_001f.eax & (1 << 1),
"sev")
{
// Speculative bug workarounds
// We don't claim to have these bugs, so we don't need to claim these flags
FLAG(res_7.edx & (1 << 31),
"ssbd")
FLAG(false, "ibrs")
FLAG(false, "ibpb")
FLAG(res_7.edx & (1 << 27),
"stibp")
FLAG(false, "ibrs_enhanced")
}
// We don't support Intel's TPR Shadow feature
FLAG(false, "tpr_shadow")
// Intel virtual NMI
FLAG(false, "vnmi")
// Intel FlexPriority
FLAG(false, "flexpriority")
// Intel Extended page table
FLAG(false, "ept")
// Intel virtual processor ID
FLAG(false, "vpid")
// Prefer VMMCall to VMCall
FLAG(false, "vmmcall")
// Intel extended page table access dirty bit
FLAG(false, "ept_ad")
FLAG(res_7.ebx & (1 << 0),
"fsgsbase")
FLAG(res_7.ebx & (1 << 1),
"tsc_adjust")
FLAG(res_7.ebx & (1 << 3),
"bmi1")
FLAG(res_7.ebx & (1 << 4),
"hle")
FLAG(res_7.ebx & (1 << 5),
"avx2")
FLAG(res_7.ebx & (1 << 7),
"smep")
FLAG(res_7.ebx & (1 << 8),
"bmi2")
FLAG(res_7.ebx & (1 << 9),
"erms")
FLAG(res_7.ebx & (1 << 10),
"invpcid")
FLAG(res_7.ebx & (1 << 11),
"rtm")
FLAG(false, // Needs leaf support
"cqm")
FLAG(res_7.ebx & (1 << 14),
"mpx")
FLAG(false, // Needs leaf support
"rdt_a")
FLAG(res_7.ebx & (1 << 16),
"avx512f")
FLAG(res_7.ebx & (1 << 17),
"avx512dq")
FLAG(res_7.ebx & (1 << 18),
"rdseed")
FLAG(res_7.ebx & (1 << 19),
"adx")
FLAG(res_7.ebx & (1 << 20),
"smap")
FLAG(res_7.ebx & (1 << 21),
"avx512ifma")
FLAG(res_7.ebx & (1 << 23),
"clflushopt")
FLAG(res_7.ebx & (1 << 24),
"clwb")
FLAG(res_7.ebx & (1 << 25),
"intel_pt")
FLAG(res_7.ebx & (1 << 26),
"avx512pf")
FLAG(res_7.ebx & (1 << 27),
"avx512er")
FLAG(res_7.ebx & (1 << 28),
"avx512cd")
FLAG(res_7.ebx & (1 << 29),
"sha_ni")
FLAG(res_7.ebx & (1 << 30),
"avx512bw")
FLAG(res_7.ebx & (1 << 31),
"avx512vl")
FLAG(false, // Needs leaf support // res_d.eax & (1 << 0) // Leaf 1h
"xsaveopt")
FLAG(false, // Needs leaf support // res_d.eax & (1 << 1) // Leaf 1h
"xsavec")
FLAG(false, // Needs leaf support // res_d.eax & (1 << 2) // Leaf 1h
"xgetbv1")
FLAG(false, // Needs leaf support // res_d.eax & (1 << 3) // Leaf 1h
"xsaves")
FLAG(false, // Needs leaf support
"avx512_bf16")
FLAG(res_8000_0008.ebx & (1 << 0),
"clzero")
FLAG(res_8000_0008.ebx & (1 << 1),
"irperf")
FLAG(res_8000_0008.ebx & (1 << 2),
"xsaveerptr")
// Intel digital thermal sensor
FLAG(false, "dtherm")
// Intel turbo boost
FLAG(false, "ida")
FLAG(res_6.eax & (1 << 2),
"arat")
// Power limit notification controls
FLAG(false, "pln")
// Intel package thermal status
FLAG(false, "pts")
// Intel Hardware P-state features
FLAG(false, "hwp")
FLAG(false, "hwp_notify")
FLAG(false, "hwp_act_window")
FLAG(false, "hwp_epp")
FLAG(false, "hwp_pkg_req")
FLAG(res_8000_000a.ebx & (1 << 0),
"npt")
FLAG(res_8000_000a.ebx & (1 << 1),
"lbrv")
FLAG(res_8000_000a.ebx & (1 << 2),
"svm_lock")
FLAG(res_8000_000a.ebx & (1 << 3),
"nrip_save")
FLAG(res_8000_000a.ebx & (1 << 4),
"tsc_scale")
FLAG(res_8000_000a.ebx & (1 << 5),
"vmcb_clean")
FLAG(res_8000_000a.ebx & (1 << 6),
"flushbyasid")
FLAG(res_8000_000a.ebx & (1 << 7),
"decodeassists")
FLAG(res_8000_000a.ebx & (1 << 10),
"pausefilter")
FLAG(res_8000_000a.ebx & (1 << 12),
"pfthreshold")
FLAG(res_8000_000a.ebx & (1 << 13),
"avic")
FLAG(res_8000_000a.ebx & (1 << 15),
"v_vmsave_vmload")
FLAG(res_8000_000a.ebx & (1 << 16),
"vgif")
FLAG(res_7.ecx & (1 << 1),
"avx512vbmi")
FLAG(res_7.ecx & (1 << 2),
"umip")
FLAG(res_7.ecx & (1 << 3),
"pku")
FLAG(res_7.ecx & (1 << 4),
"ospke")
FLAG(res_7.ecx & (1 << 5),
"waitpkg")
FLAG(res_7.ecx & (1 << 6),
"avx512_vbmi2")
FLAG(res_7.ecx & (1 << 8),
"gfni")
FLAG(res_7.ecx & (1 << 9),
"vaes")
FLAG(res_7.ecx & (1 << 10),
"vpclmulqdq")
FLAG(res_7.ecx & (1 << 11),
"avx512_vnni")
FLAG(res_7.ecx & (1 << 12),
"avx512_bitalg")
FLAG(res_7.ecx & (1 << 13),
"tme")
FLAG(res_7.ecx & (1 << 14),
"avx512_vpopcntdq")
FLAG(res_7.ecx & (1 << 16),
"la57")
FLAG(res_7.ecx & (1 << 22),
"rdpid")
FLAG(res_7.ecx & (1 << 25),
"cldemote")
FLAG(res_7.ecx & (1 << 27),
"movdiri")
FLAG(res_7.ecx & (1 << 28),
"movdir64b")
FLAG(res_8000_0007.ebx & (1 << 0),
"overflow_recov")
FLAG(res_8000_0007.ebx & (1 << 1),
"succor")
FLAG(res_8000_0007.ebx & (1 << 3),
"smca")
FLAG(res_7.edx & (1 << 2),
"avx512_4vnniw")
FLAG(res_7.edx & (1 << 3),
"avx512_4fmaps")
FLAG(res_7.edx & (1 << 4),
"fsrm")
FLAG(res_7.edx & (1 << 8),
"avx512_vp2intersect")
FLAG(res_7.edx & (1 << 10),
"md_clear")
FLAG(res_7.edx & (1 << 14),
"serialize")
FLAG(res_7.edx & (1 << 18),
"pconfig")
FLAG(res_7.edx & (1 << 19),
"arch_lbr")
FLAG(res_7.edx & (1 << 28),
"flush_l1d")
FLAG(res_7.edx & (1 << 29),
"arch_capabilities")
}
for (int i = 0; i < CPUCores; ++i) {
cpu_stream << "processor\t: " << i << std::endl; // Logical id
cpu_stream << "vendor_id\t: " << vendorid.Str << std::endl;
cpu_stream << "cpu family\t: " << Family << std::endl;
cpu_stream << "model\t\t: " << (info.Model + (info.FamilyID >= 6 ? (info.ExModelID << 4) : 0)) << std::endl;
ModelName modelname {};
auto res_8000_0002 = FEXCore::Context::RunCPUIDFunctionName(ctx, 0x8000'0002, 0, i);
auto res_8000_0003 = FEXCore::Context::RunCPUIDFunctionName(ctx, 0x8000'0003, 0, i);
auto res_8000_0004 = FEXCore::Context::RunCPUIDFunctionName(ctx, 0x8000'0004, 0, i);
modelname.cpuid_2 = res_8000_0002;
modelname.cpuid_3 = res_8000_0003;
modelname.cpuid_4 = res_8000_0004;
modelname.null = 0;
cpu_stream << "model name\t: " << modelname.Str << std::endl;
cpu_stream << "stepping\t: " << info.Stepping << std::endl;
cpu_stream << "microcode\t: 0x0" << std::endl;
cpu_stream << "cpu MHz\t\t: 3000" << std::endl;
cpu_stream << "cache size\t: 512 KB" << std::endl;
cpu_stream << "physical id\t: 0" << std::endl; // Socket id (always 0 for a single socket system)
cpu_stream << "siblings\t: " << CPUCores << std::endl; // Number of logical cores
cpu_stream << "core id\t\t: " << i << std::endl; // Physical id
cpu_stream << "cpu cores\t: " << CPUCores << std::endl; // Number of physical cores
cpu_stream << "apicid\t\t: " << i << std::endl;
cpu_stream << "initial apicid\t: " << i << std::endl;
cpu_stream << "fpu\t\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "fpu_exception\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "cpuid level\t: " << vendorid.id << std::endl;
cpu_stream << "wp\t\t: yes" << std::endl;
cpu_stream << "flags\t\t: " << flags_data.str() << std::endl;
// We don't have any bugs, don't question it
cpu_stream << "bugs\t\t: " << std::endl;
cpu_stream << "bogomips\t: 8000.0" << std::endl;
// These next four aren't necessarily correct
cpu_stream << "TLB size\t: 2560 4K pages" << std::endl;
cpu_stream << "clflush size\t: 64" << std::endl;
cpu_stream << "cache_alignment\t : 64" << std::endl;
// Cortex-A is 40 or 44 bits physical, and 48/52 virtual
// Choose the lesser configuration
cpu_stream << "address sizes\t: 40 bits physical, 48 bits virtual" << std::endl;
// No power management but required to report
cpu_stream << "power management: " << std::endl;
cpu_stream << std::endl;
}
return cpu_stream.str();
}
EmulatedFDManager::EmulatedFDManager(FEXCore::Context::Context *ctx)
: CTX {ctx} {
FDReadCreators["/proc/cpuinfo"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD();
write(FD, (void*)&cpu_info.at(0), cpu_info.size());
lseek(FD, 0, SEEK_SET);
return FD;
};
FDReadCreators["/proc/sys/kernel/osrelease"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD();
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
char Tmp[64]{};
snprintf(Tmp, sizeof(Tmp), "%d.%d.%d\n",
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
// + 1 to ensure null at the end
write(FD, Tmp, strlen(Tmp) + 1);
lseek(FD, 0, SEEK_SET);
return FD;
};
FDReadCreators["/proc/version"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD();
// UTS version NEEDS to be in a format that can pass to `date -d`
// Format of this is Linux version <Release> (<Compile By>@<Compile Host>) (<Linux Compiler>) #<version> {SMP, PREEMPT, PREEMPT_RT} <UTS version>\n"
const char kernel_version[] = "Linux version %d.%d.%d (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n";
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
char Tmp[sizeof(kernel_version) + 64]{};
snprintf(Tmp, sizeof(Tmp), kernel_version,
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
// + 1 to ensure null at the end
write(FD, Tmp, strlen(Tmp) + 1);
lseek(FD, 0, SEEK_SET);
return FD;
};
auto NumCPUCores = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD();
write(FD, (void*)&cpus_online.at(0), cpus_online.size());
lseek(FD, 0, SEEK_SET);
return FD;
};
FDReadCreators["/sys/devices/system/cpu/online"] = NumCPUCores;
FDReadCreators["/sys/devices/system/cpu/present"] = NumCPUCores;
string procAuxv = string("/proc/") + std::to_string(getpid()) + string("/auxv");
FDReadCreators[procAuxv] = &EmulatedFDManager::ProcAuxv;
FDReadCreators["/proc/self/auxv"] = &EmulatedFDManager::ProcAuxv;
auto cmdline_handler = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD();
auto CodeLoader = FEX::HLE::_SyscallHandler->GetCodeLoader();
auto Args = CodeLoader->GetApplicationArguments();
char NullChar{};
// cmdline is an array of null terminated arguments
for (size_t i = 0; i < Args->size(); ++i) {
auto &Arg = Args->at(i);
write(FD, Arg.c_str(), Arg.size());
// Finish off with a null terminator
write(FD, &NullChar, sizeof(uint8_t));
}
// One additional null terminator to finish the list
lseek(FD, 0, SEEK_SET);
return FD;
};
FDReadCreators["/proc/self/cmdline"] = cmdline_handler;
FDReadCreators["/proc/" + std::to_string(::getpid()) + "/cmdline"] = cmdline_handler;
cpus_online = "0";
uint64_t CPUCores = ThreadsConfig();
if (CPUCores > 1) {
cpus_online += "-" + std::to_string(CPUCores - 1);
}
cpu_info = GenerateCPUInfo(ctx, CPUCores);
}
EmulatedFDManager::~EmulatedFDManager() {
}
int32_t EmulatedFDManager::OpenAt(int dirfs, const char *pathname, int flags, uint32_t mode) {
std::string Path{};
if (((pathname && pathname[0] != '/') || // If pathname exists then it must not be absolute
!pathname) &&
dirfs != AT_FDCWD) {
auto get_fdpath = [](int fd) -> std::string {
std::error_code ec;
return std::filesystem::canonical(std::filesystem::path("/proc/self/fd") / std::to_string(fd), ec).string();
};
// Passed in a dirfd that isn't magic FDCWD
// We need to get the path from the fd now
Path = get_fdpath(dirfs);
if (pathname) {
if (!Path.empty()) {
// If the path returned empty then we don't need a separator
Path += "/";
}
Path += pathname;
}
}
else {
if (!pathname || strlen(pathname) == 0) {
return -1;
}
else if (pathname) {
Path = pathname;
}
}
std::error_code ec;
bool exists = std::filesystem::exists(Path, ec);
if (ec) {
return -1;
}
string cpath = exists ? std::filesystem::canonical(Path, ec)
: std::filesystem::path(Path).lexically_normal(); // *Note: this doesn't transform to absolute
if (ec) {
return -1;
}
auto Creator = FDReadCreators.find(cpath);
if (Creator == FDReadCreators.end()) {
return -1;
}
return Creator->second(CTX, dirfs, Path.c_str(), flags, mode);
}
int32_t EmulatedFDManager::ProcAuxv(FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode)
{
uint64_t auxvBase=0, auxvSize=0;
FEX::HLE::_SyscallHandler->GetCodeLoader()->GetAuxv(auxvBase, auxvSize);
if (!auxvBase) {
LogMan::Msg::DFmt("Failed to get Auxv stack address");
return -1;
}
int FD = GenTmpFD();
write(FD, (void*)auxvBase, auxvSize);
lseek(FD, 0, SEEK_SET);
return FD;
}
}