core: Fixup linux support (#195)

* tls: Implement for linux

* core: Implement memory manager for linux
This commit is contained in:
TheTurtle 2024-06-14 00:58:57 +03:00 committed by GitHub
parent e62690759d
commit 31bd502764
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 184 additions and 88 deletions

View File

@ -10,10 +10,6 @@
namespace Audio {
int SDLAudio::AudioInit() {
return SDL_InitSubSystem(SDL_INIT_AUDIO);
}
int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
Libraries::AudioOut::OrbisAudioOutParam format) {
using Libraries::AudioOut::OrbisAudioOutParam;

View File

@ -14,7 +14,6 @@ public:
SDLAudio() = default;
virtual ~SDLAudio() = default;
int AudioInit();
int AudioOutOpen(int type, u32 samples_num, u32 freq,
Libraries::AudioOut::OrbisAudioOutParam format);
s32 AudioOutOutput(s32 handle, const void* ptr);

View File

@ -183,9 +183,8 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
#endif
if (!IsOpen()) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}",
PathToUTF8String(file_path));
}
}

View File

@ -1,10 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <sstream>
#include <unordered_map>
#include "common/logging/log.h"
#include "common/path_util.h"

View File

@ -10,6 +10,7 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <sys/mman.h>
#endif
@ -215,25 +216,96 @@ enum PosixPageProtection {
struct AddressSpace::Impl {
Impl() {
UNREACHABLE();
// Allocate virtual address placeholder for our address space.
void* hint_address = reinterpret_cast<void*>(SYSTEM_MANAGED_MIN);
virtual_size = SystemSize + UserSize;
virtual_base = reinterpret_cast<u8*>(
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(Kernel_Vmm, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{};
}
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
backing_fd = memfd_create("BackingDmem", 0);
if (backing_fd < 0) {
LOG_CRITICAL(Kernel_Vmm, "memfd_create failed: {}", strerror(errno));
throw std::bad_alloc{};
}
// Defined to extend the file with zeros
int ret = ftruncate(backing_fd, BackingSize);
if (ret != 0) {
LOG_CRITICAL(Kernel_Vmm, "ftruncate failed with {}, are you out-of-memory?",
strerror(errno));
throw std::bad_alloc{};
}
// Map backing dmem handle.
backing_base = static_cast<u8*>(
mmap(nullptr, BackingSize, PROT_READ | PROT_WRITE, MAP_SHARED, backing_fd, 0));
if (backing_base == MAP_FAILED) {
LOG_CRITICAL(Kernel_Vmm, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{};
}
const VAddr start_addr = reinterpret_cast<VAddr>(virtual_base);
m_free_regions.insert({start_addr, start_addr + virtual_size});
}
void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, PosixPageProtection prot) {
UNREACHABLE();
return nullptr;
m_free_regions.subtract({virtual_addr, virtual_addr + size});
const int fd = phys_addr != -1 ? backing_fd : -1;
const int host_offset = phys_addr != -1 ? phys_addr : 0;
const int flag = phys_addr != -1 ? MAP_SHARED : (MAP_ANONYMOUS | MAP_PRIVATE);
void* ret = mmap(reinterpret_cast<void*>(virtual_addr), size, prot, MAP_FIXED | flag, fd,
host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
return ret;
}
void Unmap(VAddr virtual_addr, PAddr phys_addr, size_t size) {
UNREACHABLE();
// Check to see if we are adjacent to any regions.
auto start_address = virtual_addr;
auto end_address = start_address + size;
auto it = m_free_regions.find({start_address - 1, end_address + 1});
// If we are, join with them, ensuring we stay in bounds.
if (it != m_free_regions.end()) {
start_address = std::min(start_address, it->lower());
end_address = std::max(end_address, it->upper());
}
// Free the relevant region.
m_free_regions.insert({start_address, end_address});
// Return the adjusted pointers.
void* ret = mmap(reinterpret_cast<void*>(start_address), end_address - start_address,
PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
}
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
UNREACHABLE();
int flags = PROT_NONE;
if (read) {
flags |= PROT_READ;
}
if (write) {
flags |= PROT_WRITE;
}
if (execute) {
flags |= PROT_EXEC;
}
int ret = mprotect(reinterpret_cast<void*>(virtual_addr), size, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
}
int backing_fd;
u8* backing_base{};
u8* virtual_base{};
size_t virtual_size{};
boost::icl::interval_set<VAddr> m_free_regions;
};
#endif

View File

@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <zlib-ng.h>
#include "common/io_file.h"
#include "pkg.h"
#include "pkg_type.h"
#include "core/file_format/pkg.h"
#include "core/file_format/pkg_type.h"
static void DecompressPFSC(std::span<const char> compressed_data,
std::span<char> decompressed_data) {

View File

@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <cstdio>
#include <filesystem>
#include <string>
#include <unordered_map>

View File

@ -2,11 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <fstream>
#include <iostream>
#include "common/io_file.h"
#include "psf.h"
#include "core/file_format/psf.h"
PSF::PSF() = default;

View File

@ -192,9 +192,8 @@ int PS4_SYSV_ABI sceAudioOutGetSystemState() {
int PS4_SYSV_ABI sceAudioOutInit() {
audio = std::make_unique<Audio::SDLAudio>();
u32 result = audio->AudioInit() == 0 ? ORBIS_OK : ORBIS_AUDIO_OUT_ERROR_NOT_INIT;
LOG_INFO(Lib_AudioOut, "AudioInit returned {}", result);
return result;
LOG_INFO(Lib_AudioOut, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudioOutInitIpmiGetSession() {

View File

@ -393,6 +393,8 @@ void* createMutex(void* addr) {
if (addr == nullptr || *static_cast<ScePthreadMutex*>(addr) != nullptr) {
return addr;
}
static std::mutex mutex;
std::scoped_lock lk{mutex};
auto vaddr = reinterpret_cast<u64>(addr);
std::string name = fmt::format("mutex{:#x}", vaddr);
@ -464,7 +466,7 @@ int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) {
int result = pthread_mutexattr_init(&(*attr)->pth_mutex_attr);
result = (result == 0 ? scePthreadMutexattrSettype(attr, 1) : result);
result = (result == 0 ? scePthreadMutexattrSettype(attr, 2) : result);
result = (result == 0 ? scePthreadMutexattrSetprotocol(attr, 0) : result);
switch (result) {
@ -1165,6 +1167,10 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthre
return result;
}
int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) {
return pthread_once(reinterpret_cast<pthread_once_t*>(once_control), init_routine);
}
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy);
LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate);
@ -1191,6 +1197,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate);
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield);
LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack);
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, scePthreadOnce);
// mutex calls
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit);

View File

@ -27,7 +27,7 @@ struct PthreadMutexattrInternal;
struct PthreadCondInternal;
struct PthreadCondAttrInternal;
struct PthreadRwInternal;
struct PthreadRwLockAttrInernal;
struct PthreadRwLockAttrInternal;
using SceKernelSchedParam = ::sched_param;
using ScePthread = PthreadInternal*;
@ -37,7 +37,7 @@ using ScePthreadMutexattr = PthreadMutexattrInternal*;
using ScePthreadCond = PthreadCondInternal*;
using ScePthreadCondattr = PthreadCondAttrInternal*;
using OrbisPthreadRwlock = PthreadRwInternal*;
using OrbisPthreadRwlockattr = PthreadRwLockAttrInernal*;
using OrbisPthreadRwlockattr = PthreadRwLockAttrInternal*;
using pthreadEntryFunc = PS4_SYSV_ABI void* (*)(void*);
@ -86,7 +86,7 @@ struct PthreadCondAttrInternal {
pthread_condattr_t cond_attr;
};
struct PthreadRwLockAttrInernal {
struct PthreadRwLockAttrInternal {
u8 reserved[64];
pthread_rwlockattr_t attr_rwlock;
int type;

View File

@ -121,7 +121,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_gettype_np() {
}
int PS4_SYSV_ABI posix_pthread_rwlockattr_init(OrbisPthreadRwlockattr* attr) {
*attr = new PthreadRwLockAttrInernal{};
*attr = new PthreadRwLockAttrInternal{};
int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock);
if (result != 0) {
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_init: error = {}", result);
@ -161,7 +161,7 @@ int PS4_SYSV_ABI scePthreadRwlockattrGettype() {
}
int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr) {
*attr = new PthreadRwLockAttrInernal{};
*attr = new PthreadRwLockAttrInternal{};
int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock);
if (result != 0) {
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrInit: error = {}", result);

View File

@ -8,6 +8,8 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
namespace Core {
@ -53,9 +55,83 @@ Tcb* GetTcbBase() {
return reinterpret_cast<Tcb*>(TlsGetValue(slot));
}
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
using namespace Xbyak::util;
static void AllocTcbKey() {
slot = TlsAlloc();
}
static void PatchFsAccess(u8* code, const TLSPattern& tls_pattern, Xbyak::CodeGenerator& c) {
using namespace Xbyak::util;
const auto total_size = tls_pattern.pattern_size + tls_pattern.imm_size;
// Replace mov instruction with near jump to the trampoline.
static constexpr u32 NearJmpSize = 5;
auto patch = Xbyak::CodeGenerator(total_size, code);
patch.jmp(c.getCurr(), Xbyak::CodeGenerator::LabelType::T_NEAR);
patch.nop(total_size - NearJmpSize);
// Write the trampoline.
// The following logic is based on the wine implementation of TlsGetValue
// https://github.com/wine-mirror/wine/blob/a27b9551/dlls/kernelbase/thread.c#L719
static constexpr u32 TlsSlotsOffset = 0x1480;
static constexpr u32 TlsExpansionSlotsOffset = 0x1780;
static constexpr u32 TlsMinimumAvailable = 64;
const u32 teb_offset = slot < TlsMinimumAvailable ? TlsSlotsOffset : TlsExpansionSlotsOffset;
const u32 tls_index = slot < TlsMinimumAvailable ? slot : slot - TlsMinimumAvailable;
const auto target_reg = Xbyak::Reg64(tls_pattern.target_reg);
c.mov(target_reg, teb_offset);
c.putSeg(gs);
c.mov(target_reg, ptr[target_reg]); // Load the pointer to the table of tls slots.
c.mov(target_reg,
qword[target_reg + tls_index * sizeof(LPVOID)]); // Load the pointer to our buffer.
c.jmp(code + total_size); // Return to the instruction right after the mov.
}
#else
static pthread_key_t slot = 0;
void SetTcbBase(void* image_address) {
ASSERT(pthread_setspecific(slot, image_address) == 0);
}
Tcb* GetTcbBase() {
return reinterpret_cast<Tcb*>(pthread_getspecific(slot));
}
static void AllocTcbKey() {
slot = pthread_key_create(&slot, nullptr);
}
static void PatchFsAccess(u8* code, const TLSPattern& tls_pattern, Xbyak::CodeGenerator& c) {
using namespace Xbyak::util;
const auto total_size = tls_pattern.pattern_size + tls_pattern.imm_size;
// Replace mov instruction with near jump to the trampoline.
static constexpr u32 NearJmpSize = 5;
auto patch = Xbyak::CodeGenerator(total_size, code);
patch.jmp(c.getCurr(), Xbyak::CodeGenerator::LabelType::T_NEAR);
patch.nop(total_size - NearJmpSize);
// Write the trampoline.
// The following logic is based on the glibc implementation of pthread_getspecific
// https://github.com/bminor/glibc/blob/29807a27/nptl/pthread_getspecific.c#L23
static constexpr u32 PthreadKeySecondLevelSize = 32;
static constexpr u32 SpecificFirstBlockOffset = 0x308;
static constexpr u32 SelfInTcbheadOffset = 16;
static constexpr u32 PthreadKeyDataSize = 16;
ASSERT(slot < PthreadKeySecondLevelSize);
const auto target_reg = Xbyak::Reg64(tls_pattern.target_reg);
c.putSeg(fs);
c.mov(target_reg, qword[SelfInTcbheadOffset]); // Load self member pointer of tcbhead_t.
c.add(target_reg, SpecificFirstBlockOffset + sizeof(uintptr_t) + slot * PthreadKeyDataSize);
c.jmp(code + total_size); // Return to the instruction right after the mov.
}
#endif
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
u8* code = reinterpret_cast<u8*>(segment_addr);
auto remaining_size = segment_size;
@ -89,7 +165,7 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
// Allocate slot in the process if not done already.
if (slot == 0) {
slot = TlsAlloc();
AllocTcbKey();
}
// Replace bogus instruction prefix with nops if it exists.
@ -98,30 +174,8 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
patch.nop(BadPrefix.size());
}
// Replace mov instruction with near jump to the trampoline.
static constexpr u32 NearJmpSize = 5;
auto patch = Xbyak::CodeGenerator(total_size, code);
patch.jmp(c.getCurr(), Xbyak::CodeGenerator::LabelType::T_NEAR);
patch.nop(total_size - NearJmpSize);
// Write the trampoline.
// The following logic is based on the wine implementation of TlsGetValue
// https://github.com/wine-mirror/wine/blob/a27b9551/dlls/kernelbase/thread.c#L719
static constexpr u32 TlsSlotsOffset = 0x1480;
static constexpr u32 TlsExpansionSlotsOffset = 0x1780;
static constexpr u32 TlsMinimumAvailable = 64;
const u32 teb_offset =
slot < TlsMinimumAvailable ? TlsSlotsOffset : TlsExpansionSlotsOffset;
const u32 tls_index = slot < TlsMinimumAvailable ? slot : slot - TlsMinimumAvailable;
const auto target_reg = Xbyak::Reg64(tls_pattern.target_reg);
c.mov(target_reg, teb_offset);
c.putSeg(gs);
c.mov(target_reg, ptr[target_reg]); // Load the pointer to the table of tls slots.
c.mov(
target_reg,
qword[target_reg + tls_index * sizeof(LPVOID)]); // Load the pointer to our buffer.
c.jmp(code + total_size); // Return to the instruction right after the mov.
// Patch access to FS register to a trampoline.
PatchFsAccess(code, tls_pattern, c);
// Move ahead in module.
code += total_size - 1;
@ -133,20 +187,4 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
}
}
#else
void SetTcbBase(void* image_address) {
UNREACHABLE_MSG("Thread local storage is unimplemented on posix platforms!");
}
Tcb* GetTcbBase() {
UNREACHABLE_MSG("Thread local storage is unimplemented on posix platforms!");
}
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
UNREACHABLE_MSG("Thread local storage is unimplemented on posix platforms!");
}
#endif
} // namespace Core

View File

@ -40,10 +40,6 @@ Emulator::Emulator() : window{WindowWidth, WindowHeight, controller} {
Common::Log::Initialize();
Common::Log::Start();
// Start discord integration
discord_rpc.init();
discord_rpc.update(Discord::RPCStatus::Idling, "");
// Initialize kernel and library facilities.
Libraries::Kernel::init_pthreads();
Libraries::InitHLELibs(&linker->GetHLESymbols());
@ -52,7 +48,6 @@ Emulator::Emulator() : window{WindowWidth, WindowHeight, controller} {
Emulator::~Emulator() {
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::save(config_dir / "config.toml");
discord_rpc.stop();
}
void Emulator::Run(const std::filesystem::path& file) {
@ -116,7 +111,7 @@ void Emulator::Run(const std::filesystem::path& file) {
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
// Begin main window loop until the application exits
static constexpr std::chrono::microseconds FlipPeriod{100000};
static constexpr std::chrono::microseconds FlipPeriod{10};
while (window.isOpen()) {
window.waitEvent();

View File

@ -7,7 +7,6 @@
#include <thread>
#include <common/singleton.h>
#include "common/discord.h"
#include "core/linker.h"
#include "input/controller.h"
#include "sdl_window.h"
@ -23,7 +22,6 @@ public:
private:
void LoadSystemModules(const std::filesystem::path& file);
Discord::RPC discord_rpc;
Input::GameController* controller = Common::Singleton<Input::GameController>::Instance();
Core::Linker* linker = Common::Singleton<Core::Linker>::Instance();
Frontend::WindowSDL window;

View File

@ -19,6 +19,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
UNREACHABLE_MSG("Failed to initialize SDL video subsystem: {}", SDL_GetError());
}
SDL_InitSubSystem(SDL_INIT_AUDIO);
const std::string title = "shadPS4 v" + std::string(Common::VERSION);
SDL_PropertiesID props = SDL_CreateProperties();

View File

@ -695,7 +695,7 @@ struct Liverpool {
NumberFormat NumFormat() const {
// There is a small difference between T# and CB number types, account for it.
return info.number_type == AmdGpu::NumberFormat::SnormNz ? AmdGpu::NumberFormat::Srgb
: info.number_type;
: info.number_type.Value();
}
};

View File

@ -32,7 +32,7 @@ struct Buffer {
};
u32 GetStride() const noexcept {
return stride == 0 ? 1U : stride;
return stride == 0 ? 1U : stride.Value();
}
u32 GetStrideElements(u32 element_size) const noexcept {