Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
raziel1000 | c6af9d87b5 | |
raphaelthegreat | 56fd31f511 | |
raphaelthegreat | 48a158f18a | |
georgemoralis | 79e0081fce | |
raphaelthegreat | 611745170e | |
raphaelthegreat | 1dd815d07d | |
raphaelthegreat | 1356805978 | |
georgemoralis | c6a09e21f8 | |
georgemoralis | 0c705c10cb | |
georgemoralis | b6e65c29fc | |
raphaelthegreat | ea2e4f7b5c | |
raphaelthegreat | 511595aca7 | |
raphaelthegreat | 2bbe1349c2 | |
raphaelthegreat | 772891bfa7 |
|
@ -154,8 +154,14 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
|||
src/core/libraries/system/userservice.h
|
||||
src/core/libraries/app_content/app_content.cpp
|
||||
src/core/libraries/app_content/app_content.h
|
||||
src/core/libraries/rtc/rtc.cpp
|
||||
src/core/libraries/rtc/rtc.h
|
||||
src/core/libraries/disc_map/disc_map.cpp
|
||||
src/core/libraries/disc_map/disc_map.h
|
||||
src/core/libraries/disc_map/disc_map_codes.h
|
||||
)
|
||||
|
||||
|
||||
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
src/core/libraries/videoout/driver.cpp
|
||||
src/core/libraries/videoout/driver.h
|
||||
|
@ -287,6 +293,8 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
src/core/linker.h
|
||||
src/core/memory.cpp
|
||||
src/core/memory.h
|
||||
src/core/module.cpp
|
||||
src/core/module.h
|
||||
src/core/platform.h
|
||||
src/core/memory.h
|
||||
src/core/tls.cpp
|
||||
|
|
|
@ -66,7 +66,7 @@ int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
|||
port.sample_size = 4;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown format");
|
||||
UNREACHABLE_MSG("Unknown format {}", u32(format));
|
||||
}
|
||||
|
||||
for (int i = 0; i < port.channels_num; i++) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T alignUp(T value, std::size_t size) {
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
|
@ -17,14 +17,14 @@ template <typename T>
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T alignDown(T value, std::size_t size) {
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool is16KBAligned(T value) {
|
||||
[[nodiscard]] constexpr bool Is16KBAligned(T value) {
|
||||
return (value & 0x3FFF) == 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||
SUB(Lib, Screenshot) \
|
||||
SUB(Lib, LibCInternal) \
|
||||
SUB(Lib, AppContent) \
|
||||
SUB(Lib, Rtc) \
|
||||
SUB(Lib, DiscMap) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
|
|
|
@ -69,6 +69,8 @@ enum class Class : u8 {
|
|||
Lib_Screenshot, ///< The LibSceScreenshot implementation
|
||||
Lib_LibCInternal, ///< The LibCInternal implementation.
|
||||
Lib_AppContent, ///< The LibSceAppContent implementation.
|
||||
Lib_Rtc, ///< The LibSceRtc implementation.
|
||||
Lib_DiscMap, ///< The LibSceDiscMap implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
|
|
|
@ -102,14 +102,14 @@ struct AddressSpace::Impl {
|
|||
|
||||
// Perform the map.
|
||||
void* ptr = nullptr;
|
||||
if (phys_addr) {
|
||||
if (phys_addr != -1) {
|
||||
ptr = MapViewOfFile3(backing_handle, process, reinterpret_cast<PVOID>(virtual_addr),
|
||||
phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
|
||||
} else {
|
||||
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
|
||||
MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
|
||||
}
|
||||
ASSERT(ptr);
|
||||
ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
* If zero is provided the mapping is considered as private.
|
||||
* @return A pointer to the mapped memory.
|
||||
*/
|
||||
void* Map(VAddr virtual_addr, size_t size, u64 alignment = 0, PAddr phys_addr = 0);
|
||||
void* Map(VAddr virtual_addr, size_t size, u64 alignment = 0, PAddr phys_addr = -1);
|
||||
|
||||
/// Unmaps specified virtual memory area.
|
||||
void Unmap(VAddr virtual_addr, size_t size);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/core.h>
|
||||
#include "common/assert.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
|
||||
namespace Core::FileSys {
|
||||
|
@ -27,16 +29,15 @@ void MntPoints::UnmountAll() {
|
|||
|
||||
std::string MntPoints::GetHostDirectory(const std::string& guest_directory) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& pair : m_mnt_pairs) {
|
||||
if (pair.guest_path.starts_with(guest_directory)) {
|
||||
return pair.host_path + guest_directory;
|
||||
}
|
||||
}
|
||||
// hack for relative path , get app0 and assuming it goes from there
|
||||
for (auto& pair : m_mnt_pairs) {
|
||||
if (pair.guest_path.starts_with("/app0")) {
|
||||
// horrible code but it works :D
|
||||
int find = guest_directory.find(pair.guest_path);
|
||||
if (find == 0) {
|
||||
std::string npath =
|
||||
guest_directory.substr(pair.guest_path.size(), guest_directory.size() - 1);
|
||||
std::replace(pair.host_path.begin(), pair.host_path.end(), '\\', '/');
|
||||
return pair.host_path + guest_directory;
|
||||
return pair.host_path + npath;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
|
@ -54,6 +55,7 @@ std::string MntPoints::GetHostFile(const std::string& guest_file) {
|
|||
return pair.host_path + npath;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||
"AudioOutOpen id = {} port_type = {} index = {} lenght= {} sample_rate = {} "
|
||||
"param_type = {}",
|
||||
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
|
||||
GetAudioOutParam(param_type));
|
||||
GetAudioOutParam(param_type & 0xFF));
|
||||
if ((port_type < 0 || port_type > 4) && (port_type != 127)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Invalid port type");
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE;
|
||||
|
@ -243,10 +243,6 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||
LOG_ERROR(Lib_AudioOut, "Invalid sample rate");
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ;
|
||||
}
|
||||
if (param_type < 0 || param_type > 7) {
|
||||
LOG_ERROR(Lib_AudioOut, "Invalid format");
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
if (length != 256 && length != 512 && length != 768 && length != 1024 && length != 1280 &&
|
||||
length != 1536 && length != 1792 && length != 2048) {
|
||||
LOG_ERROR(Lib_AudioOut, "Invalid length");
|
||||
|
@ -255,7 +251,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||
if (index != 0) {
|
||||
LOG_ERROR(Lib_AudioOut, "index is not valid !=0 {}", index);
|
||||
}
|
||||
int result = audio->AudioOutOpen(port_type, length, sample_rate, param_type);
|
||||
int result = audio->AudioOutOpen(port_type, length, sample_rate, OrbisAudioOutParam(param_type & 0xFF));
|
||||
if (result == -1) {
|
||||
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
|
||||
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Generated By moduleGenerator
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "disc_map.h"
|
||||
#include "disc_map_codes.h"
|
||||
|
||||
namespace Libraries::DiscMap {
|
||||
|
||||
int PS4_SYSV_ABI sceDiscMapGetPackageSize() {
|
||||
LOG_WARNING(Lib_DiscMap, "(DUMMY) called");
|
||||
return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD() {
|
||||
LOG_WARNING(Lib_DiscMap, "(DUMMY) called");
|
||||
return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_7C980FFB0AA27E7A() {
|
||||
LOG_ERROR(Lib_DiscMap, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9() {
|
||||
LOG_ERROR(Lib_DiscMap, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_E7EBCE96E92F91F8() {
|
||||
LOG_ERROR(Lib_DiscMap, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("fl1eoDnwQ4s", "libSceDiscMap", 1, "libSceDiscMap", 1, 1,
|
||||
sceDiscMapGetPackageSize);
|
||||
LIB_FUNCTION("lbQKqsERhtE", "libSceDiscMap", 1, "libSceDiscMap", 1, 1,
|
||||
sceDiscMapIsRequestOnHDD);
|
||||
LIB_FUNCTION("fJgP+wqifno", "libSceDiscMap", 1, "libSceDiscMap", 1, 1, Func_7C980FFB0AA27E7A);
|
||||
LIB_FUNCTION("ioKMruft1ek", "libSceDiscMap", 1, "libSceDiscMap", 1, 1, Func_8A828CAEE7EDD5E9);
|
||||
LIB_FUNCTION("5+vOlukvkfg", "libSceDiscMap", 1, "libSceDiscMap", 1, 1, Func_E7EBCE96E92F91F8);
|
||||
};
|
||||
|
||||
} // namespace Libraries::DiscMap
|
|
@ -0,0 +1,20 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
namespace Libraries::DiscMap {
|
||||
|
||||
int PS4_SYSV_ABI sceDiscMapGetPackageSize();
|
||||
int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD();
|
||||
int PS4_SYSV_ABI Func_7C980FFB0AA27E7A();
|
||||
int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9();
|
||||
int PS4_SYSV_ABI Func_E7EBCE96E92F91F8();
|
||||
|
||||
void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::DiscMap
|
|
@ -0,0 +1,10 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
constexpr int ORBIS_DISC_MAP_ERROR_INVALID_ARGUMENT = 0x81100001;
|
||||
constexpr int ORBIS_DISC_MAP_ERROR_LOCATION_NOT_MAPPED = 0x81100002;
|
||||
constexpr int ORBIS_DISC_MAP_ERROR_FILE_NOT_FOUND = 0x81100003;
|
||||
constexpr int ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO = 0x81100004;
|
||||
constexpr int ORBIS_DISC_MAP_ERROR_FATAL = 0x811000FF;
|
|
@ -367,8 +367,17 @@ int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175() {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (size > 0xff) {
|
||||
if constexpr (g_fair_hw_init) {
|
||||
ASSERT_MSG(0, "Not implemented");
|
||||
} else {
|
||||
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xff);
|
||||
}
|
||||
return 0x100; // it is a size, not a retcode
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -379,11 +388,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size) {
|
|||
if constexpr (g_fair_hw_init) {
|
||||
ASSERT_MSG(0, "Not implemented");
|
||||
} else {
|
||||
cmdbuf = cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xff);
|
||||
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xff);
|
||||
}
|
||||
return 0x100; // it is a size, not a retcode
|
||||
}
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size) {
|
||||
|
@ -393,11 +402,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size) {
|
|||
if constexpr (g_fair_hw_init) {
|
||||
ASSERT_MSG(0, "Not implemented");
|
||||
} else {
|
||||
cmdbuf = cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xff);
|
||||
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xff);
|
||||
}
|
||||
return 0x100; // it is a size, not a retcode
|
||||
}
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState() {
|
||||
|
|
|
@ -54,7 +54,7 @@ int PS4_SYSV_ABI sceGnmDrawIndirect();
|
|||
int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti();
|
||||
int PS4_SYSV_ABI sceGnmDrawIndirectMulti();
|
||||
int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState();
|
||||
int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175();
|
||||
int PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size);
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size);
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size);
|
||||
int PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState();
|
||||
|
|
|
@ -31,17 +31,35 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
|||
bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0;
|
||||
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0;
|
||||
|
||||
if (directory) {
|
||||
UNREACHABLE(); // not supported yet
|
||||
} else {
|
||||
u32 handle = h->CreateHandle();
|
||||
auto* file = h->GetFile(handle);
|
||||
if (directory) {
|
||||
file->is_directory = true;
|
||||
file->m_guest_name = path;
|
||||
file->m_host_name = mnt->GetHostDirectory(file->m_guest_name);
|
||||
if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist
|
||||
UNREACHABLE(); // not supported yet
|
||||
} else {
|
||||
if (create) {
|
||||
return handle; // dir already exists
|
||||
} else {
|
||||
// get dirents TODO
|
||||
file->dirents_index = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file->m_guest_name = path;
|
||||
file->m_host_name = mnt->GetHostFile(file->m_guest_name);
|
||||
if (read) {
|
||||
file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
|
||||
} else if (write && create && truncate) {
|
||||
file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
} else if (rdwr) {
|
||||
if (create) { // Create an empty file first.
|
||||
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
}
|
||||
// RW, then scekernelWrite is called and savedata is written just fine now.
|
||||
file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -49,10 +67,9 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
|||
h->DeleteHandle(handle);
|
||||
return SCE_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
}
|
||||
file->is_opened = true;
|
||||
return handle;
|
||||
}
|
||||
return -1; // dummy
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode) {
|
||||
|
@ -197,12 +214,23 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) {
|
|||
|
||||
int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
|
||||
int result = sceKernelStat(path, sb);
|
||||
return result;
|
||||
if (result < 0) {
|
||||
UNREACHABLE(); // TODO
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) {
|
||||
LOG_INFO(Lib_Kernel, "path = {}", path);
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
std::string path_name = mnt->GetHostFile(path);
|
||||
if (!std::filesystem::exists(path_name)) {
|
||||
return SCE_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
|
||||
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
|
||||
|
@ -215,6 +243,8 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("Cg4srZ6TKbU", "libkernel", 1, "libkernel", 1, 1, sceKernelRead);
|
||||
LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir);
|
||||
LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat);
|
||||
LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability);
|
||||
|
||||
LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat);
|
||||
|
||||
// openOrbis (to check if it is valid out of OpenOrbis
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/cpu_management.h"
|
||||
#include "core/libraries/kernel/event_flag/event_flag.h"
|
||||
|
@ -52,10 +54,6 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
|
|||
return SCE_OK;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void* iov_base; /* Base address. */
|
||||
size_t iov_len; /* Length. */
|
||||
|
@ -165,7 +163,27 @@ s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) {
|
|||
|
||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||
struct OrbisTimesec* st, unsigned long* dst_sec) {
|
||||
return ORBIS_OK;
|
||||
LOG_TRACE(Kernel, "Called");
|
||||
timeval val;
|
||||
timezone tmz;
|
||||
gettimeofday(&val, &tmz);
|
||||
|
||||
if (local_time) {
|
||||
*local_time = (tmz.tz_minuteswest + tmz.tz_dsttime) * 60 + time;
|
||||
}
|
||||
|
||||
if (st) {
|
||||
st->t = time;
|
||||
st->dst_sec = tmz.tz_dsttime * 60;
|
||||
st->west_sec = tmz.tz_minuteswest * 60;
|
||||
}
|
||||
|
||||
if (dst_sec) {
|
||||
*dst_sec = tmz.tz_dsttime * 60;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
|
||||
const auto* time_zone = std::chrono::current_zone();
|
||||
auto info = time_zone->get_info(std::chrono::system_clock::now());
|
||||
|
||||
|
@ -199,19 +217,80 @@ s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
|
|||
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
|
||||
}
|
||||
|
||||
size_t PS4_SYSV_ABI sceKernelPread(int fd, void* buf, size_t count, uint64_t offset) {
|
||||
long unsigned int read_bytes = 0;
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
||||
|
||||
overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
|
||||
overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
|
||||
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto& file = h->GetFile(fd)->f;
|
||||
file.Seek(offset);
|
||||
return file.ReadRaw<u8>(buf, count);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp,
|
||||
u32 flags, const void* pOpt, int* pRes) {
|
||||
LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args);
|
||||
|
||||
if (flags != 0) {
|
||||
return 0x80020016;
|
||||
}
|
||||
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto path = mnt->GetHostFile(moduleFileName);
|
||||
|
||||
// Load PRX module.
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u32 handle = linker->LoadModule(path);
|
||||
auto* module = linker->GetModule(handle);
|
||||
linker->Relocate(module);
|
||||
|
||||
// Retrieve and verify proc param according to libkernel.
|
||||
const u64* param = module->GetProcParam<u64*>();
|
||||
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
|
||||
module->Start(args, argp, 0);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->GetModule(handle);
|
||||
*addrp = module->FindByName(symbol);
|
||||
if (*addrp == nullptr) {
|
||||
return 0x80020003;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
||||
// obj
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||
// memory
|
||||
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory);
|
||||
LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAllocateMainDirectMemory);
|
||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
|
||||
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
|
||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
|
||||
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
|
||||
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
|
||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||
LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelCheckedReleaseDirectMemory);
|
||||
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
|
||||
LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory);
|
||||
LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory);
|
||||
LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery);
|
||||
LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1,
|
||||
_sceKernelRtldSetApplicationHeapAPI);
|
||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule);
|
||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
|
||||
|
||||
// equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
||||
|
@ -223,12 +302,12 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
|
||||
LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap);
|
||||
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
|
||||
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
|
||||
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam);
|
||||
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
|
||||
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion);
|
||||
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
|
||||
LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread);
|
||||
|
||||
Libraries::Kernel::fileSystemSymbolsRegister(sym);
|
||||
Libraries::Kernel::timeSymbolsRegister(sym);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
@ -16,18 +17,23 @@ u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() {
|
|||
return SCE_KERNEL_MAIN_DMEM_SIZE;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) {
|
||||
auto* memory = Core::Memory::Instance();
|
||||
return memory->Free(start, len);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
|
||||
u64 alignment, int memoryType, s64* physAddrOut) {
|
||||
if (searchStart < 0 || searchEnd <= searchStart) {
|
||||
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
const bool is_in_range = (searchStart < len && searchEnd > len);
|
||||
if (len <= 0 || !Common::is16KBAligned(len) || !is_in_range) {
|
||||
const bool is_in_range = searchEnd - searchStart >= len;
|
||||
if (len <= 0 || !Common::Is16KBAligned(len) || !is_in_range) {
|
||||
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if ((alignment != 0 || Common::is16KBAligned(alignment)) && !std::has_single_bit(alignment)) {
|
||||
if (alignment != 0 && !Common::Is16KBAligned(alignment)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
@ -48,23 +54,30 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
|
|||
return SCE_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment) {
|
||||
LOG_INFO(
|
||||
Kernel_Vmm,
|
||||
"len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, alignment = {:#x}",
|
||||
len, prot, flags, directMemoryStart, alignment);
|
||||
s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType,
|
||||
s64* physAddrOut) {
|
||||
return sceKernelAllocateDirectMemory(0, SCE_KERNEL_MAIN_DMEM_SIZE, len, alignment, memoryType,
|
||||
physAddrOut);
|
||||
}
|
||||
|
||||
if (len == 0 || !Common::is16KBAligned(len)) {
|
||||
int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment,
|
||||
const char* name) {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, alignment = "
|
||||
"{:#x}, name = {}",
|
||||
len, prot, flags, directMemoryStart, alignment, name);
|
||||
|
||||
if (len == 0 || !Common::Is16KBAligned(len)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (!Common::is16KBAligned(directMemoryStart)) {
|
||||
if (!Common::Is16KBAligned(directMemoryStart)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (alignment != 0) {
|
||||
if ((!std::has_single_bit(alignment) && !Common::is16KBAligned(alignment))) {
|
||||
if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) {
|
||||
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
@ -78,10 +91,21 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
|||
directMemoryStart, alignment);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment) {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"redirected to sceKernelMapNamedDirectMemory: "
|
||||
"len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, alignment = "
|
||||
"{:#x}",
|
||||
len, prot, flags, directMemoryStart, alignment);
|
||||
return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment,
|
||||
"Turtle");
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||
int flags, const char* name) {
|
||||
|
||||
if (len == 0 || !Common::is16KBAligned(len)) {
|
||||
if (len == 0 || !Common::Is16KBAligned(len)) {
|
||||
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
@ -127,4 +151,15 @@ int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInf
|
|||
return memory->DirectMemoryQuery(offset, flags == 1, query_info);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
|
||||
size_t infoSize) {
|
||||
auto* memory = Core::Memory::Instance();
|
||||
return memory->VirtualQuery(std::bit_cast<VAddr>(addr), flags, info);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->SetHeapApiFunc(func);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/types.h"
|
||||
|
||||
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5376_MB; // ~ 6GB
|
||||
|
@ -36,9 +37,31 @@ struct OrbisQueryInfo {
|
|||
int memoryType;
|
||||
};
|
||||
|
||||
struct OrbisVirtualQueryInfo {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
size_t offset;
|
||||
s32 protection;
|
||||
s32 memory_type;
|
||||
union {
|
||||
BitField<0, 1, u32> is_flexible;
|
||||
BitField<1, 1, u32> is_direct;
|
||||
BitField<2, 1, u32> is_stack;
|
||||
BitField<3, 1, u32> is_pooled;
|
||||
BitField<4, 1, u32> is_commited;
|
||||
};
|
||||
std::array<char, 32> name;
|
||||
};
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
|
||||
s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len);
|
||||
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
|
||||
u64 alignment, int memoryType, s64* physAddrOut);
|
||||
s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType,
|
||||
s64* physAddrOut);
|
||||
int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment,
|
||||
const char* name);
|
||||
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment);
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot,
|
||||
|
@ -50,4 +73,9 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
|
|||
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
|
||||
size_t infoSize);
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
|
||||
size_t infoSize);
|
||||
|
||||
void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -20,7 +20,7 @@ bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignmen
|
|||
}
|
||||
|
||||
// Align free position
|
||||
find_free_pos = Common::alignUp(find_free_pos, alignment);
|
||||
find_free_pos = Common::AlignUp(find_free_pos, alignment);
|
||||
|
||||
// If the new position is between searchStart - searchEnd , allocate a new block
|
||||
if (find_free_pos >= searchStart && find_free_pos + len <= searchEnd) {
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
@ -32,6 +34,10 @@ void init_pthreads() {
|
|||
ScePthreadAttr default_attr = nullptr;
|
||||
scePthreadAttrInit(&default_attr);
|
||||
g_pthread_cxt->SetDefaultAttr(default_attr);
|
||||
// default rwlock init
|
||||
ScePthreadRwAttr default_rwattr = nullptr;
|
||||
scePthreadRwlockattrInit(&default_rwattr);
|
||||
g_pthread_cxt->setDefaultRwattr(default_rwattr);
|
||||
|
||||
g_pthread_cxt->SetPthreadPool(new PThreadPool);
|
||||
}
|
||||
|
@ -82,6 +88,29 @@ int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr) {
|
|||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadAttrGetstack(ScePthreadAttr* attr, void** addr, size_t* size) {
|
||||
|
||||
int result = pthread_attr_getstack(&(*attr)->pth_attr, addr, size);
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadAttrGetstack: result = {}", result);
|
||||
|
||||
if (result == 0) {
|
||||
return SCE_OK;
|
||||
}
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadAttrDestroy(attr);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadAttrSetguardsize(ScePthreadAttr* attr, size_t guard_size) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
|
@ -501,7 +530,7 @@ int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) {
|
|||
|
||||
int result = pthread_mutex_lock(&(*mutex)->pth_mutex);
|
||||
if (result != 0) {
|
||||
LOG_INFO(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
|
||||
// LOG_INFO(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
|
||||
}
|
||||
switch (result) {
|
||||
case 0:
|
||||
|
@ -524,7 +553,7 @@ int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) {
|
|||
|
||||
int result = pthread_mutex_unlock(&(*mutex)->pth_mutex);
|
||||
if (result != 0) {
|
||||
LOG_INFO(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
|
||||
// LOG_INFO(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
|
||||
}
|
||||
switch (result) {
|
||||
case 0:
|
||||
|
@ -664,6 +693,128 @@ int PS4_SYSV_ABI scePthreadCondTimedwait(ScePthreadCond* cond, ScePthreadMutex*
|
|||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadCondDestroy(ScePthreadCond* cond) {
|
||||
if (cond == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_cond_destroy(&(*cond)->cond);
|
||||
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadCondDestroy, result={}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case EBUSY:
|
||||
return SCE_KERNEL_ERROR_EBUSY;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_init(ScePthreadCondattr* attr) {
|
||||
int result = scePthreadCondattrInit(attr);
|
||||
LOG_INFO(Kernel_Pthread, "redirect to scePthreadCondattrInit: result = {}", result);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr) {
|
||||
int result = scePthreadCondInit(cond, attr, "");
|
||||
LOG_INFO(Kernel_Pthread, "redirect to scePthreadCondInit: result = {}", result);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_destroy(ScePthreadCondattr* attr) {
|
||||
int result = scePthreadCondattrDestroy(attr);
|
||||
LOG_INFO(Kernel_Pthread, "redirect to scePthreadCondattrDestroy: result = {}", result);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_init(ScePthreadAttr* attr) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadAttrInit(attr);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksize) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadAttrSetstacksize(attr, stacksize);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadAttrSetdetachstate(attr, detachstate);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(ScePthreadMutexattr* attr) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadMutexattrInit(attr);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit");
|
||||
int result = scePthreadMutexattrSettype(attr, type);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(ScePthreadMutexattr* attr) {
|
||||
int result = scePthreadMutexattrDestroy(attr);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr, int protocol) {
|
||||
int result = scePthreadMutexattrSetprotocol(attr, protocol);
|
||||
LOG_INFO(Kernel_Pthread, "redirect to scePthreadMutexattrSetprotocol: result = {}", result);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit");
|
||||
int result = scePthreadMutexInit(mutex, attr, nullptr);
|
||||
|
@ -700,6 +851,14 @@ int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_trylock(ScePthreadMutex* mutex) {
|
||||
int result = scePthreadMutexTrylock(mutex);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond) {
|
||||
LOG_INFO(Kernel_Pthread,
|
||||
"posix posix_pthread_cond_broadcast redirect to scePthreadCondBroadcast");
|
||||
|
@ -757,21 +916,26 @@ int PS4_SYSV_ABI sceKernelNanosleep(const SceKernelTimespec* rqtp, SceKernelTime
|
|||
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
u64 nanos = rqtp->tv_sec * 1000000000 + rqtp->tv_nsec;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
u64 nanos = rqtp->tv_sec * 1000000000L + rqtp->tv_nsec;
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
|
||||
if (rmtp != nullptr) {
|
||||
UNREACHABLE(); // not supported yet
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
||||
rmtp->tv_sec = (diff / 1000000000L);
|
||||
rmtp->tv_nsec = (diff % 1000000000L);
|
||||
}
|
||||
return SCE_OK;
|
||||
}
|
||||
int PS4_SYSV_ABI nanosleep(const SceKernelTimespec* rqtp, SceKernelTimespec* rmtp) {
|
||||
|
||||
int PS4_SYSV_ABI posix_nanosleep(const SceKernelTimespec* rqtp, SceKernelTimespec* rmtp) {
|
||||
int result = sceKernelNanosleep(rqtp, rmtp);
|
||||
if (result < 0) {
|
||||
UNREACHABLE(); // TODO return posix error code
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int pthread_copy_attributes(ScePthreadAttr* dst, const ScePthreadAttr* src) {
|
||||
if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
|
@ -829,6 +993,8 @@ static void cleanup_thread(void* arg) {
|
|||
static void* run_thread(void* arg) {
|
||||
auto* thread = static_cast<ScePthread>(arg);
|
||||
Common::SetCurrentThreadName(thread->name.c_str());
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->InitTlsForThread();
|
||||
void* ret = nullptr;
|
||||
g_pthread_self = thread;
|
||||
pthread_cleanup_push(cleanup_thread, thread);
|
||||
|
@ -898,6 +1064,19 @@ int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr
|
|||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr,
|
||||
pthreadEntryFunc start_routine, void* arg) {
|
||||
LOG_INFO(Kernel_Pthread, "posix pthread_create redirect to scePthreadCreate");
|
||||
int result = scePthreadCreate(thread, attr, start_routine, arg, "PS4_Thread");
|
||||
if (result != 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ScePthread PThreadPool::Create() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
|
@ -925,7 +1104,13 @@ void PS4_SYSV_ABI scePthreadYield() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) {
|
||||
LOG_INFO(Kernel_Pthread, "thread create name = {}", thread->name);
|
||||
LOG_INFO(Kernel_Pthread, "thread detach name = {}", thread->name);
|
||||
thread->is_detached = true;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) {
|
||||
LOG_INFO(Kernel_Pthread, "thread detach name = {}", thread->name);
|
||||
thread->is_detached = true;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
@ -1022,6 +1207,177 @@ int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) {
|
|||
return (thread1 == thread2 ? 1 : 0);
|
||||
}
|
||||
|
||||
struct TlsIndex {
|
||||
u64 ti_module;
|
||||
u64 ti_offset;
|
||||
};
|
||||
|
||||
void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->TlsGetAddr(index->ti_module, index->ti_offset);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) {
|
||||
return sem_init(sem, pshared, value);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) {
|
||||
return sem_wait(sem);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_post(sem_t* sem) {
|
||||
return sem_post(sem);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit");
|
||||
int result = scePthreadMutexDestroy(mutex);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** value_ptr) {
|
||||
return pthread_join(thread->pth, value_ptr);
|
||||
}
|
||||
|
||||
/****
|
||||
* rwlock
|
||||
*/
|
||||
int PS4_SYSV_ABI scePthreadRwlockInit(ScePthreadRw* thread, ScePthreadRwAttr* attr,
|
||||
const char* name) {
|
||||
*thread = new PthreadRwInternal{};
|
||||
if (thread == nullptr || *thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
attr = g_pthread_cxt->getDefaultRwattr();
|
||||
}
|
||||
|
||||
int result = pthread_rwlock_init(&(*thread)->pth_rwlock, &(*attr)->attr_rwlock);
|
||||
// LOG_INFO(Kernel_Pthread, "scePthreadRwlockInit: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case ENOMEM:
|
||||
return SCE_KERNEL_ERROR_ENOMEM;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockRdlock(ScePthreadRw* thread) {
|
||||
if (thread == nullptr || *thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_rdlock(&(*thread)->pth_rwlock);
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadRwlockRdlock: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case EBUSY:
|
||||
return SCE_KERNEL_ERROR_EBUSY;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockWrlock(ScePthreadRw* thread) {
|
||||
if (thread == nullptr || *thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_wrlock(&(*thread)->pth_rwlock);
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadRwlockWrlock: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case EBUSY:
|
||||
return SCE_KERNEL_ERROR_EBUSY;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockUnlock(ScePthreadRw* thread) {
|
||||
if (thread == nullptr || *thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_unlock(&(*thread)->pth_rwlock);
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadRwlockUnlock: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case EBUSY:
|
||||
return SCE_KERNEL_ERROR_EBUSY;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockDestroy(ScePthreadRw* thread) {
|
||||
if (thread == nullptr || *thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_destroy(&(*thread)->pth_rwlock);
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadRwlockDestroy: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case EBUSY:
|
||||
return SCE_KERNEL_ERROR_EBUSY;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrInit(ScePthreadRwAttr* attr) {
|
||||
*attr = new PthreadRwLockAttrInernal{};
|
||||
|
||||
int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock);
|
||||
// LOG_INFO(Kernel_Pthread, "scePthreadRwlockattrInit: result = {}", result);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
case ENOMEM:
|
||||
return SCE_KERNEL_ERROR_ENOMEM;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(ScePthreadRw* thread) {
|
||||
int result = scePthreadRwlockRdlock(thread);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_unlock(ScePthreadRw* thread) {
|
||||
int result = scePthreadRwlockUnlock(thread);
|
||||
if (result < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sched_get_priority_max() {
|
||||
return ORBIS_KERNEL_PRIO_FIFO_HIGHEST;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1029,15 +1385,19 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedparam);
|
||||
LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrInit);
|
||||
LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrDestroy);
|
||||
LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack);
|
||||
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach);
|
||||
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
|
||||
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual);
|
||||
|
||||
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf);
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, pthread_self);
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, pthread_self);
|
||||
LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity);
|
||||
LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity);
|
||||
LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet);
|
||||
LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize);
|
||||
LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr);
|
||||
|
||||
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity);
|
||||
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate);
|
||||
|
@ -1061,21 +1421,61 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrDestroy);
|
||||
LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, scePthreadCondSignal);
|
||||
LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, scePthreadCondTimedwait);
|
||||
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, scePthreadCondDestroy);
|
||||
// posix calls
|
||||
LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init);
|
||||
LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setstacksize);
|
||||
LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setdetachstate);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy);
|
||||
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
|
||||
LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init);
|
||||
LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_condattr_destroy);
|
||||
|
||||
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
|
||||
LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_settype);
|
||||
LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_setprotocol);
|
||||
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
|
||||
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
|
||||
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
|
||||
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
|
||||
LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock);
|
||||
LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
||||
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
|
||||
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
|
||||
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
|
||||
LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_destroy);
|
||||
|
||||
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
|
||||
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
|
||||
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, clock_gettime);
|
||||
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, clock_gettime);
|
||||
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, nanosleep);
|
||||
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
|
||||
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep); // CUSA18841
|
||||
|
||||
// Rwlock funstions
|
||||
LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockInit);
|
||||
LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrInit);
|
||||
LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockRdlock);
|
||||
LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockWrlock);
|
||||
LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockUnlock);
|
||||
LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockDestroy);
|
||||
LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock);
|
||||
LIB_FUNCTION("EgmLo6EWgso", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_unlock);
|
||||
|
||||
// openorbis weird functions
|
||||
LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
|
||||
LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
|
||||
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||
|
||||
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, sched_get_priority_max);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <vector>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <semaphore.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
|
@ -23,6 +24,8 @@ struct PthreadMutexInternal;
|
|||
struct PthreadMutexattrInternal;
|
||||
struct PthreadCondInternal;
|
||||
struct PthreadCondAttrInternal;
|
||||
struct PthreadRwInternal;
|
||||
struct PthreadRwLockAttrInernal;
|
||||
|
||||
using SceKernelSchedParam = ::sched_param;
|
||||
using ScePthread = PthreadInternal*;
|
||||
|
@ -31,6 +34,8 @@ using ScePthreadMutex = PthreadMutexInternal*;
|
|||
using ScePthreadMutexattr = PthreadMutexattrInternal*;
|
||||
using ScePthreadCond = PthreadCondInternal*;
|
||||
using ScePthreadCondattr = PthreadCondAttrInternal*;
|
||||
using ScePthreadRw = PthreadRwInternal*;
|
||||
using ScePthreadRwAttr = PthreadRwLockAttrInernal*;
|
||||
|
||||
using pthreadEntryFunc = PS4_SYSV_ABI void* (*)(void*);
|
||||
|
||||
|
@ -39,6 +44,10 @@ struct SceKernelTimespec {
|
|||
int64_t tv_nsec;
|
||||
};
|
||||
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767;
|
||||
|
||||
struct PthreadInternal {
|
||||
u8 reserved[4096];
|
||||
std::string name;
|
||||
|
@ -84,6 +93,19 @@ struct PthreadCondAttrInternal {
|
|||
pthread_condattr_t cond_attr;
|
||||
};
|
||||
|
||||
struct PthreadRwLockAttrInernal {
|
||||
u64 initialized = 1;
|
||||
u8 reserved[64];
|
||||
pthread_rwlockattr_t attr_rwlock;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct PthreadRwInternal {
|
||||
u64 initialized = 1;
|
||||
pthread_rwlock_t pth_rwlock;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class PThreadPool {
|
||||
public:
|
||||
ScePthread Create();
|
||||
|
@ -119,12 +141,19 @@ public:
|
|||
void SetPthreadPool(PThreadPool* pool) {
|
||||
m_pthread_pool = pool;
|
||||
}
|
||||
ScePthreadRwAttr* getDefaultRwattr() {
|
||||
return &m_default_Rwattr;
|
||||
}
|
||||
void setDefaultRwattr(ScePthreadRwAttr attr) {
|
||||
m_default_Rwattr = attr;
|
||||
}
|
||||
|
||||
private:
|
||||
ScePthreadMutexattr m_default_mutexattr = nullptr;
|
||||
ScePthreadCondattr m_default_condattr = nullptr;
|
||||
ScePthreadAttr m_default_attr = nullptr;
|
||||
PThreadPool* m_pthread_pool = nullptr;
|
||||
ScePthreadRwAttr m_default_Rwattr = nullptr;
|
||||
};
|
||||
|
||||
void init_pthreads();
|
||||
|
@ -153,6 +182,7 @@ int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type)
|
|||
int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
|
||||
int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex);
|
||||
/****
|
||||
* Cond calls
|
||||
*/
|
||||
|
@ -160,6 +190,7 @@ int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondat
|
|||
const char* name);
|
||||
int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr);
|
||||
int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond);
|
||||
int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr);
|
||||
/****
|
||||
* Posix calls
|
||||
*/
|
||||
|
@ -168,5 +199,18 @@ int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex);
|
|||
int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond);
|
||||
|
||||
/****
|
||||
* rwlock calls
|
||||
*/
|
||||
int PS4_SYSV_ABI scePthreadRwlockInit(ScePthreadRw* thread, ScePthreadRwAttr* attr,
|
||||
const char* name);
|
||||
int PS4_SYSV_ABI scePthreadRwlockRdlock(ScePthreadRw* thread);
|
||||
int PS4_SYSV_ABI scePthreadRwlockWrlock(ScePthreadRw* thread);
|
||||
int PS4_SYSV_ABI scePthreadRwlockUnlock(ScePthreadRw* thread);
|
||||
int PS4_SYSV_ABI scePthreadRwlockDestroy(ScePthreadRw* thread);
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrInit(ScePthreadRwAttr* attr);
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(ScePthreadRw* thread);
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_unlock(ScePthreadRw* thread);
|
||||
|
||||
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <time.h>
|
||||
#include "common/native_clock.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static u64 initial_ptc;
|
||||
|
@ -30,6 +37,83 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
|||
return clock->GetUptime();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval* tp) {}
|
||||
|
||||
#define FILETIME_1970 116444736000000000ull /* seconds between 1/1/1601 and 1/1/1970 */
|
||||
#define HECTONANOSEC_PER_SEC 10000000ull
|
||||
|
||||
int PS4_SYSV_ABI getntptimeofday(struct timespec* tp, struct timezone* z) {
|
||||
int res = 0;
|
||||
union {
|
||||
unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
|
||||
FILETIME ft;
|
||||
} _now;
|
||||
TIME_ZONE_INFORMATION TimeZoneInformation;
|
||||
DWORD tzi;
|
||||
|
||||
if (z != NULL) {
|
||||
if ((tzi = GetTimeZoneInformation(&TimeZoneInformation)) != TIME_ZONE_ID_INVALID) {
|
||||
z->tz_minuteswest = TimeZoneInformation.Bias;
|
||||
if (tzi == TIME_ZONE_ID_DAYLIGHT)
|
||||
z->tz_dsttime = 1;
|
||||
else
|
||||
z->tz_dsttime = 0;
|
||||
} else {
|
||||
z->tz_minuteswest = 0;
|
||||
z->tz_dsttime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tp != NULL) {
|
||||
typedef void(WINAPI * GetSystemTimeAsFileTime_t)(LPFILETIME);
|
||||
static GetSystemTimeAsFileTime_t GetSystemTimeAsFileTime_p /* = 0 */;
|
||||
|
||||
/* Set function pointer during first call */
|
||||
GetSystemTimeAsFileTime_t get_time =
|
||||
__atomic_load_n(&GetSystemTimeAsFileTime_p, __ATOMIC_RELAXED);
|
||||
if (get_time == NULL) {
|
||||
/* Use GetSystemTimePreciseAsFileTime() if available (Windows 8 or later) */
|
||||
get_time = (GetSystemTimeAsFileTime_t)(intptr_t)GetProcAddress(
|
||||
GetModuleHandle("kernel32.dll"),
|
||||
"GetSystemTimePreciseAsFileTime"); /* <1us precision on Windows 10 */
|
||||
if (get_time == NULL)
|
||||
get_time = GetSystemTimeAsFileTime; /* >15ms precision on Windows 10 */
|
||||
__atomic_store_n(&GetSystemTimeAsFileTime_p, get_time, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
get_time(&_now.ft); /* 100 nano-seconds since 1-1-1601 */
|
||||
_now.ns100 -= FILETIME_1970; /* 100 nano-seconds since 1-1-1970 */
|
||||
tp->tv_sec = _now.ns100 / HECTONANOSEC_PER_SEC; /* seconds since 1-1-1970 */
|
||||
tp->tv_nsec = (long)(_now.ns100 % HECTONANOSEC_PER_SEC) * 100; /* nanoseconds */
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI gettimeofday(struct timeval* p, struct timezone* z) {
|
||||
struct timespec tp;
|
||||
|
||||
if (getntptimeofday(&tp, z))
|
||||
return -1;
|
||||
p->tv_sec = tp.tv_sec;
|
||||
p->tv_usec = (tp.tv_nsec / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(seconds));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
clock = std::make_unique<Common::NativeClock>();
|
||||
initial_ptc = clock->GetUptime();
|
||||
|
@ -39,6 +123,12 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
sceKernelGetProcessTimeCounterFrequency);
|
||||
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
||||
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
|
||||
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, gettimeofday);
|
||||
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, gettimeofday);
|
||||
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
|
||||
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
|
||||
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
|
||||
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, sceKernelSleep);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -11,11 +11,28 @@ class SymbolsResolver;
|
|||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
struct SceKernelTimeval {
|
||||
time_t tv_sec;
|
||||
s64 tv_usec;
|
||||
};
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes W of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
struct timeval {
|
||||
long tv_sec;
|
||||
long tv_usec;
|
||||
};
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetTscFrequency();
|
||||
u64 PS4_SYSV_ABI sceKernelGetProcessTime();
|
||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
|
||||
u64 PS4_SYSV_ABI sceKernelReadTsc();
|
||||
int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval* tp);
|
||||
int PS4_SYSV_ABI gettimeofday(struct timeval* tp, struct timezone* tzp);
|
||||
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
|
|
|
@ -43,6 +43,10 @@ float PS4_SYSV_ABI internal_expf(float x) {
|
|||
return expf(x);
|
||||
}
|
||||
|
||||
double PS4_SYSV_ABI internal_pow(double base, double exponent) {
|
||||
return pow(base, exponent);
|
||||
}
|
||||
|
||||
void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("NFLs+dRJGNg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1,
|
||||
internal_memcpy_s);
|
||||
|
@ -55,6 +59,7 @@ void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("DfivPArhucg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1,
|
||||
internal_memcmp);
|
||||
LIB_FUNCTION("8zsu04XNsZ4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_expf);
|
||||
LIB_FUNCTION("9LCjpWyQ5Zc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_pow);
|
||||
};
|
||||
|
||||
} // namespace Libraries::LibcInternal
|
|
@ -16,6 +16,7 @@ int PS4_SYSV_ABI internal_memcpy_s(void* dest, size_t destsz, const void* src, s
|
|||
int PS4_SYSV_ABI internal_strcpy_s(char* dest, size_t dest_size, const char* src);
|
||||
int PS4_SYSV_ABI internal_memcmp(const void* s1, const void* s2, size_t n);
|
||||
float PS4_SYSV_ABI internal_expf(float x);
|
||||
double PS4_SYSV_ABI internal_pow(double base, double exponent);
|
||||
|
||||
void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::LibcInternal
|
|
@ -5,6 +5,7 @@
|
|||
#include "core/libraries/app_content/app_content.h"
|
||||
#include "core/libraries/audio/audioin.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/disc_map/disc_map.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/libc/libc.h"
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "core/libraries/np_score/np_score.h"
|
||||
#include "core/libraries/np_trophy/np_trophy.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
#include "core/libraries/save_data/savedata.h"
|
||||
#include "core/libraries/screenshot/screenshot.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
|
@ -28,7 +30,6 @@
|
|||
#include "core/libraries/system/systemservice.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
|
||||
namespace Libraries {
|
||||
|
||||
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
|
@ -61,6 +62,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
|||
Libraries::ScreenShot::RegisterlibSceScreenShot(sym);
|
||||
Libraries::LibcInternal::RegisterlibSceLibcInternal(sym);
|
||||
Libraries::AppContent::RegisterlibSceAppContent(sym);
|
||||
Libraries::Rtc::RegisterlibSceRtc(sym);
|
||||
Libraries::DiscMap::RegisterlibSceDiscMap(sym);
|
||||
}
|
||||
|
||||
} // namespace Libraries
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Generated By moduleGenerator
|
||||
#include <chrono>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "rtc.h"
|
||||
|
||||
namespace Libraries::Rtc {
|
||||
|
||||
int PS4_SYSV_ABI sceRtcCheckValid() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcCompareTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcConvertLocalTimeToUtc() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcEnd() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC2822() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339LocalTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339Precise() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentClock() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentClockLocalTime(OrbisRtcDateTime* pTime) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto now_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
|
||||
auto epoch = now_ns.time_since_epoch();
|
||||
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(epoch);
|
||||
|
||||
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm local_tm = *std::localtime(&now_time_t);
|
||||
|
||||
pTime->year = local_tm.tm_year + 1900;
|
||||
pTime->month = local_tm.tm_mon + 1;
|
||||
pTime->day = local_tm.tm_mday;
|
||||
pTime->hour = local_tm.tm_hour;
|
||||
pTime->minute = local_tm.tm_min;
|
||||
pTime->second = local_tm.tm_sec;
|
||||
pTime->microsecond = micros.count() % 1000000;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick) {
|
||||
pTick->tick = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day) {
|
||||
std::tm timeinfo = {0};
|
||||
timeinfo.tm_year = year - 1900;
|
||||
timeinfo.tm_mon = month - 1;
|
||||
timeinfo.tm_mday = day;
|
||||
std::mktime(&timeinfo);
|
||||
return timeinfo.tm_wday;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetDaysInMonth() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetDosTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetTickResolution() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetTime_t() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcGetWin32FileTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcInit() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcIsLeapYear() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcParseDateTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcParseRFC3339() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetConf() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentAdNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentDebugNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentNetworkTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetDosTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetTick() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetTime_t() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcSetWin32FileTime() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddDays() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddHours() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddMicroseconds() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddMinutes() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddMonths() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddSeconds() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddTicks() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddWeeks() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRtcTickAddYears() {
|
||||
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("lPEBYdVX0XQ", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcCheckValid);
|
||||
LIB_FUNCTION("fNaZ4DbzHAE", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcCompareTick);
|
||||
LIB_FUNCTION("8Yr143yEnRo", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcConvertLocalTimeToUtc);
|
||||
LIB_FUNCTION("M1TvFst-jrM", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcConvertUtcToLocalTime);
|
||||
LIB_FUNCTION("8SljQx6pDP8", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcEnd);
|
||||
LIB_FUNCTION("eiuobaF-hK4", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcFormatRFC2822);
|
||||
LIB_FUNCTION("AxHBk3eat04", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcFormatRFC2822LocalTime);
|
||||
LIB_FUNCTION("WJ3rqFwymew", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcFormatRFC3339);
|
||||
LIB_FUNCTION("DwuHIlLGW8I", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcFormatRFC3339LocalTime);
|
||||
LIB_FUNCTION("lja0nNPWojg", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcFormatRFC3339Precise);
|
||||
LIB_FUNCTION("tOZ6fwwHZOA", "libSceRtc", 1, "libSceRtc", 1, 1,
|
||||
sceRtcFormatRFC3339PreciseLocalTime);
|
||||
LIB_FUNCTION("LN3Zcb72Q0c", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentAdNetworkTick);
|
||||
LIB_FUNCTION("8lfvnRMqwEM", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentClock);
|
||||
LIB_FUNCTION("ZPD1YOKI+Kw", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentClockLocalTime);
|
||||
LIB_FUNCTION("Ot1DE3gif84", "libSceRtc", 1, "libSceRtc", 1, 1,
|
||||
sceRtcGetCurrentDebugNetworkTick);
|
||||
LIB_FUNCTION("zO9UL3qIINQ", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentNetworkTick);
|
||||
LIB_FUNCTION("HWxHOdbM-Pg", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentRawNetworkTick);
|
||||
LIB_FUNCTION("18B2NS1y9UU", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetCurrentTick);
|
||||
LIB_FUNCTION("CyIK-i4XdgQ", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetDayOfWeek);
|
||||
LIB_FUNCTION("3O7Ln8AqJ1o", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetDaysInMonth);
|
||||
LIB_FUNCTION("E7AR4o7Ny7E", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetDosTime);
|
||||
LIB_FUNCTION("8w-H19ip48I", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetTick);
|
||||
LIB_FUNCTION("jMNwqYr4R-k", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetTickResolution);
|
||||
LIB_FUNCTION("BtqmpTRXHgk", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetTime_t);
|
||||
LIB_FUNCTION("jfRO0uTjtzA", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcGetWin32FileTime);
|
||||
LIB_FUNCTION("LlodCMDbk3o", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcInit);
|
||||
LIB_FUNCTION("Ug8pCwQvh0c", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcIsLeapYear);
|
||||
LIB_FUNCTION("NxEI1KByvCI", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcParseDateTime);
|
||||
LIB_FUNCTION("99bMGglFW3I", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcParseRFC3339);
|
||||
LIB_FUNCTION("fFLgmNUpChg", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetConf);
|
||||
LIB_FUNCTION("sV2tK+yOhBU", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetCurrentAdNetworkTick);
|
||||
LIB_FUNCTION("VLDUPKmw5L8", "libSceRtc", 1, "libSceRtc", 1, 1,
|
||||
sceRtcSetCurrentDebugNetworkTick);
|
||||
LIB_FUNCTION("qhDBtIo+auw", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetCurrentNetworkTick);
|
||||
LIB_FUNCTION("d4fHLCGmY80", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetCurrentTick);
|
||||
LIB_FUNCTION("aYPCd1cChyg", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetDosTime);
|
||||
LIB_FUNCTION("ueega6v3GUw", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetTick);
|
||||
LIB_FUNCTION("bDEVVP4bTjQ", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetTime_t);
|
||||
LIB_FUNCTION("n5JiAJXsbcs", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcSetWin32FileTime);
|
||||
LIB_FUNCTION("NR1J0N7L2xY", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddDays);
|
||||
LIB_FUNCTION("MDc5cd8HfCA", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddHours);
|
||||
LIB_FUNCTION("XPIiw58C+GM", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddMicroseconds);
|
||||
LIB_FUNCTION("mn-tf4QiFzk", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddMinutes);
|
||||
LIB_FUNCTION("CL6y9q-XbuQ", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddMonths);
|
||||
LIB_FUNCTION("07O525HgICs", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddSeconds);
|
||||
LIB_FUNCTION("AqVMssr52Rc", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddTicks);
|
||||
LIB_FUNCTION("gI4t194c2W8", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddWeeks);
|
||||
LIB_FUNCTION("-5y2uJ62qS8", "libSceRtc", 1, "libSceRtc", 1, 1, sceRtcTickAddYears);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Rtc
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Rtc {
|
||||
|
||||
struct OrbisRtcTick {
|
||||
u64 tick;
|
||||
};
|
||||
|
||||
struct OrbisRtcDateTime {
|
||||
u16 year;
|
||||
u16 month;
|
||||
u16 day;
|
||||
u16 hour;
|
||||
u16 minute;
|
||||
u16 second;
|
||||
u16 microsecond;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceRtcCheckValid();
|
||||
int PS4_SYSV_ABI sceRtcCompareTick();
|
||||
int PS4_SYSV_ABI sceRtcConvertLocalTimeToUtc();
|
||||
int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime();
|
||||
int PS4_SYSV_ABI sceRtcEnd();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC2822();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339LocalTime();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339Precise();
|
||||
int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentClock();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentClockLocalTime(OrbisRtcDateTime* pTime);
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick);
|
||||
int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day);
|
||||
int PS4_SYSV_ABI sceRtcGetDaysInMonth();
|
||||
int PS4_SYSV_ABI sceRtcGetDosTime();
|
||||
int PS4_SYSV_ABI sceRtcGetTick();
|
||||
int PS4_SYSV_ABI sceRtcGetTickResolution();
|
||||
int PS4_SYSV_ABI sceRtcGetTime_t();
|
||||
int PS4_SYSV_ABI sceRtcGetWin32FileTime();
|
||||
int PS4_SYSV_ABI sceRtcInit();
|
||||
int PS4_SYSV_ABI sceRtcIsLeapYear();
|
||||
int PS4_SYSV_ABI sceRtcParseDateTime();
|
||||
int PS4_SYSV_ABI sceRtcParseRFC3339();
|
||||
int PS4_SYSV_ABI sceRtcSetConf();
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentAdNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentDebugNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentNetworkTick();
|
||||
int PS4_SYSV_ABI sceRtcSetCurrentTick();
|
||||
int PS4_SYSV_ABI sceRtcSetDosTime();
|
||||
int PS4_SYSV_ABI sceRtcSetTick();
|
||||
int PS4_SYSV_ABI sceRtcSetTime_t();
|
||||
int PS4_SYSV_ABI sceRtcSetWin32FileTime();
|
||||
int PS4_SYSV_ABI sceRtcTickAddDays();
|
||||
int PS4_SYSV_ABI sceRtcTickAddHours();
|
||||
int PS4_SYSV_ABI sceRtcTickAddMicroseconds();
|
||||
int PS4_SYSV_ABI sceRtcTickAddMinutes();
|
||||
int PS4_SYSV_ABI sceRtcTickAddMonths();
|
||||
int PS4_SYSV_ABI sceRtcTickAddSeconds();
|
||||
int PS4_SYSV_ABI sceRtcTickAddTicks();
|
||||
int PS4_SYSV_ABI sceRtcTickAddWeeks();
|
||||
int PS4_SYSV_ABI sceRtcTickAddYears();
|
||||
|
||||
void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Rtc
|
|
@ -340,7 +340,8 @@ s32 saveDataMount(u32 user_id, std::string dir_name, u32 mount_mode,
|
|||
std::to_string(user_id) / "savedata" / id / dir_name;
|
||||
switch (mount_mode) {
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDONLY:
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR: {
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR:
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDONLY | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF: {
|
||||
if (!std::filesystem::exists(mount_dir)) {
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
@ -364,6 +365,7 @@ s32 saveDataMount(u32 user_id, std::string dir_name, u32 mount_mode,
|
|||
mount_result->mount_status = 1;
|
||||
strncpy(mount_result->mount_point.data, g_mount_point.c_str(), 16);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR:
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR |
|
||||
ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: {
|
||||
if (!std::filesystem::exists(mount_dir)) {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <common/assert.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
|
@ -12,647 +11,23 @@
|
|||
#include "core/aerolib/aerolib.h"
|
||||
#include "core/aerolib/stubs.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/tls.h"
|
||||
#include "core/virtual_memory.h"
|
||||
#include <windows.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
static u64 LoadAddress = SYSTEM_RESERVED + CODE_BASE_OFFSET;
|
||||
static constexpr u64 CODE_BASE_INCR = 0x010000000u;
|
||||
|
||||
static u64 GetAlignedSize(const elf_program_header& phdr) {
|
||||
return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1)
|
||||
: phdr.p_memsz);
|
||||
}
|
||||
|
||||
static u64 CalculateBaseSize(const elf_header& ehdr, std::span<const elf_program_header> phdr) {
|
||||
u64 base_size = 0;
|
||||
for (u16 i = 0; i < ehdr.e_phnum; i++) {
|
||||
if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO)) {
|
||||
u64 last_addr = phdr[i].p_vaddr + GetAlignedSize(phdr[i]);
|
||||
if (last_addr > base_size) {
|
||||
base_size = last_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return base_size;
|
||||
}
|
||||
|
||||
static std::string EncodeId(u64 nVal) {
|
||||
std::string enc;
|
||||
const char pCodes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
||||
if (nVal < 0x40u) {
|
||||
enc += pCodes[nVal];
|
||||
} else {
|
||||
if (nVal < 0x1000u) {
|
||||
enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += pCodes[nVal & 0x3fu];
|
||||
} else {
|
||||
enc += pCodes[static_cast<u16>(nVal >> 12u) & 0x3fu];
|
||||
enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += pCodes[nVal & 0x3fu];
|
||||
}
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
Linker::Linker() = default;
|
||||
|
||||
Linker::~Linker() = default;
|
||||
|
||||
Module* Linker::LoadModule(const std::filesystem::path& elf_name) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (!std::filesystem::exists(elf_name)) {
|
||||
LOG_ERROR(Core_Linker, "Provided module {} does not exist", elf_name.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& m = m_modules.emplace_back();
|
||||
m = std::make_unique<Module>();
|
||||
m->elf.Open(elf_name);
|
||||
m->file_name = std::filesystem::path(elf_name).filename().string();
|
||||
|
||||
if (m->elf.IsElfFile()) {
|
||||
LoadModuleToMemory(m.get());
|
||||
LoadDynamicInfo(m.get());
|
||||
LoadSymbols(m.get());
|
||||
} else {
|
||||
m_modules.pop_back();
|
||||
return nullptr; // It is not a valid elf file //TODO check it why!
|
||||
}
|
||||
|
||||
return m.get();
|
||||
}
|
||||
|
||||
void Linker::LoadModuleToMemory(Module* m) {
|
||||
// get elf header, program header
|
||||
const auto elf_header = m->elf.GetElfHeader();
|
||||
const auto elf_pheader = m->elf.GetProgramHeader();
|
||||
|
||||
u64 base_size = CalculateBaseSize(elf_header, elf_pheader);
|
||||
m->aligned_base_size = (base_size & ~(static_cast<u64>(0x1000) - 1)) +
|
||||
0x1000; // align base size to 0x1000 block size (TODO is that the default
|
||||
// block size or it can be changed?
|
||||
|
||||
static constexpr u64 TrampolineSize = 8_MB;
|
||||
m->base_virtual_addr =
|
||||
VirtualMemory::memory_alloc(LoadAddress, m->aligned_base_size + TrampolineSize,
|
||||
VirtualMemory::MemoryMode::ExecuteReadWrite);
|
||||
|
||||
LoadAddress += CODE_BASE_INCR * (1 + m->aligned_base_size / CODE_BASE_INCR);
|
||||
|
||||
void* trampoline_addr = reinterpret_cast<void*>(m->base_virtual_addr + m->aligned_base_size);
|
||||
Xbyak::CodeGenerator c(TrampolineSize, trampoline_addr);
|
||||
|
||||
LOG_INFO(Core_Linker, "======== Load Module to Memory ========");
|
||||
LOG_INFO(Core_Linker, "base_virtual_addr ......: {:#018x}", m->base_virtual_addr);
|
||||
LOG_INFO(Core_Linker, "base_size ..............: {:#018x}", base_size);
|
||||
LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", m->aligned_base_size);
|
||||
|
||||
for (u16 i = 0; i < elf_header.e_phnum; i++) {
|
||||
switch (elf_pheader[i].p_type) {
|
||||
case PT_LOAD:
|
||||
case PT_SCE_RELRO:
|
||||
if (elf_pheader[i].p_memsz != 0) {
|
||||
u64 segment_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr;
|
||||
u64 segment_file_size = elf_pheader[i].p_filesz;
|
||||
u64 segment_memory_size = GetAlignedSize(elf_pheader[i]);
|
||||
auto segment_mode = m->elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags);
|
||||
LOG_INFO(Core_Linker, "program header = [{}] type = {}", i,
|
||||
m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
|
||||
LOG_INFO(Core_Linker, "segment_addr ..........: {:#018x}", segment_addr);
|
||||
LOG_INFO(Core_Linker, "segment_file_size .....: {}", segment_file_size);
|
||||
LOG_INFO(Core_Linker, "segment_memory_size ...: {}", segment_memory_size);
|
||||
LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode);
|
||||
|
||||
m->elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size);
|
||||
|
||||
if (elf_pheader[i].p_flags & PF_EXEC) {
|
||||
PatchTLS(segment_addr, segment_file_size, c);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "p_memsz==0 in type {}",
|
||||
m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
|
||||
}
|
||||
break;
|
||||
case PT_DYNAMIC:
|
||||
if (elf_pheader[i].p_filesz != 0) {
|
||||
m->m_dynamic.resize(elf_pheader[i].p_filesz);
|
||||
m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic.data()),
|
||||
elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "p_filesz==0 in type {}",
|
||||
m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
|
||||
}
|
||||
break;
|
||||
case PT_SCE_DYNLIBDATA:
|
||||
if (elf_pheader[i].p_filesz != 0) {
|
||||
m->m_dynamic_data.resize(elf_pheader[i].p_filesz);
|
||||
m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic_data.data()),
|
||||
elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "p_filesz==0 in type {}",
|
||||
m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
|
||||
}
|
||||
break;
|
||||
case PT_TLS:
|
||||
m->tls.image_virtual_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr;
|
||||
m->tls.image_size = GetAlignedSize(elf_pheader[i]);
|
||||
LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", m->tls.image_virtual_addr);
|
||||
LOG_INFO(Core_Linker, "TLS image size = {}", m->tls.image_size);
|
||||
break;
|
||||
case PT_SCE_PROCPARAM:
|
||||
m->proc_param_virtual_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Core_Linker, "Unimplemented type {}",
|
||||
m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
|
||||
}
|
||||
}
|
||||
LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}",
|
||||
m->elf.GetElfEntry() + m->base_virtual_addr);
|
||||
}
|
||||
|
||||
void Linker::LoadDynamicInfo(Module* m) {
|
||||
for (const auto* dyn = reinterpret_cast<elf_dynamic*>(m->m_dynamic.data());
|
||||
dyn->d_tag != DT_NULL; dyn++) {
|
||||
switch (dyn->d_tag) {
|
||||
case DT_SCE_HASH: // Offset of the hash table.
|
||||
m->dynamic_info.hash_table =
|
||||
reinterpret_cast<void*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_HASHSZ: // Size of the hash table
|
||||
m->dynamic_info.hash_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_STRTAB: // Offset of the string table.
|
||||
m->dynamic_info.str_table =
|
||||
reinterpret_cast<char*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_STRSZ: // Size of the string table.
|
||||
m->dynamic_info.str_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_SYMTAB: // Offset of the symbol table.
|
||||
m->dynamic_info.symbol_table =
|
||||
reinterpret_cast<elf_symbol*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_SYMTABSZ: // Size of the symbol table.
|
||||
m->dynamic_info.symbol_table_total_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_INIT:
|
||||
m->dynamic_info.init_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_FINI:
|
||||
m->dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_SCE_PLTGOT: // Offset of the global offset table.
|
||||
m->dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_SCE_JMPREL: // Offset of the table containing jump slots.
|
||||
m->dynamic_info.jmp_relocation_table =
|
||||
reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_PLTRELSZ: // Size of the global offset table.
|
||||
m->dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_PLTREL: // The type of relocations in the relocation table. Should be DT_RELA
|
||||
m->dynamic_info.jmp_relocation_type = dyn->d_un.d_val;
|
||||
if (m->dynamic_info.jmp_relocation_type != DT_RELA) {
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_PLTREL is NOT DT_RELA should check!");
|
||||
}
|
||||
break;
|
||||
case DT_SCE_RELA: // Offset of the relocation table.
|
||||
m->dynamic_info.relocation_table =
|
||||
reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_RELASZ: // Size of the relocation table.
|
||||
m->dynamic_info.relocation_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_RELAENT: // The size of relocation table entries.
|
||||
m->dynamic_info.relocation_table_entries_size = dyn->d_un.d_val;
|
||||
if (m->dynamic_info.relocation_table_entries_size !=
|
||||
0x18) // this value should always be 0x18
|
||||
{
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_RELAENT is NOT 0x18 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_INIT_ARRAY: // Address of the array of pointers to initialization functions
|
||||
m->dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_FINI_ARRAY: // Address of the array of pointers to termination functions
|
||||
m->dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_INIT_ARRAYSZ: // Size in bytes of the array of initialization functions
|
||||
m->dynamic_info.init_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_FINI_ARRAYSZ: // Size in bytes of the array of terminationfunctions
|
||||
m->dynamic_info.fini_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_PREINIT_ARRAY: // Address of the array of pointers to pre - initialization functions
|
||||
m->dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_PREINIT_ARRAYSZ: // Size in bytes of the array of pre - initialization functions
|
||||
m->dynamic_info.preinit_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_SYMENT: // The size of symbol table entries
|
||||
m->dynamic_info.symbol_table_entries_size = dyn->d_un.d_val;
|
||||
if (m->dynamic_info.symbol_table_entries_size !=
|
||||
0x18) // this value should always be 0x18
|
||||
{
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_SYMENT is NOT 0x18 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_DEBUG:
|
||||
m->dynamic_info.debug = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_TEXTREL:
|
||||
m->dynamic_info.textrel = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_FLAGS:
|
||||
m->dynamic_info.flags = dyn->d_un.d_val;
|
||||
if (m->dynamic_info.flags != 0x04) // this value should always be DF_TEXTREL (0x04)
|
||||
{
|
||||
LOG_WARNING(Core_Linker, "DT_FLAGS is NOT 0x04 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_NEEDED: // Offset of the library string in the string table to be linked in.
|
||||
if (m->dynamic_info.str_table !=
|
||||
nullptr) // in theory this should already be filled from about just make a test case
|
||||
{
|
||||
m->dynamic_info.needed.push_back(m->dynamic_info.str_table + dyn->d_un.d_val);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "DT_NEEDED str table is not loaded should check!");
|
||||
}
|
||||
break;
|
||||
case DT_SCE_NEEDED_MODULE: {
|
||||
ModuleInfo info{};
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = m->dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
m->dynamic_info.import_modules.push_back(info);
|
||||
} break;
|
||||
case DT_SCE_IMPORT_LIB: {
|
||||
LibraryInfo info{};
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = m->dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
m->dynamic_info.import_libs.push_back(info);
|
||||
} break;
|
||||
case DT_SCE_FINGERPRINT:
|
||||
// The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for
|
||||
// the given app. How exactly this is generated isn't known, however it is not necessary
|
||||
// to have a valid fingerprint. While an invalid fingerprint will cause a warning to be
|
||||
// printed to the kernel log, the ELF will still load and run.
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_IMPORT_LIB_ATTR:
|
||||
// The upper 32-bits should contain the module index multiplied by 0x10000. The lower
|
||||
// 32-bits should be a constant 0x9.
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ......: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_ORIGINAL_FILENAME:
|
||||
m->dynamic_info.filename = m->dynamic_info.str_table + dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_MODULE_INFO: // probably only useable in shared modules
|
||||
{
|
||||
ModuleInfo info{};
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = m->dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
m->dynamic_info.export_modules.push_back(info);
|
||||
} break;
|
||||
case DT_SCE_MODULE_ATTR:
|
||||
// TODO?
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_EXPORT_LIB: {
|
||||
LibraryInfo info{};
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = m->dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
m->dynamic_info.export_libs.push_back(info);
|
||||
} break;
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "unsupported dynamic tag ..........: {:#018x}", dyn->d_tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ModuleInfo* Linker::FindModule(const Module& m, const std::string& id) {
|
||||
const auto& import_modules = m.dynamic_info.import_modules;
|
||||
int index = 0;
|
||||
for (const auto& mod : import_modules) {
|
||||
if (mod.enc_id.compare(id) == 0) {
|
||||
return &import_modules.at(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
const auto& export_modules = m.dynamic_info.export_modules;
|
||||
index = 0;
|
||||
for (const auto& mod : export_modules) {
|
||||
if (mod.enc_id.compare(id) == 0) {
|
||||
return &export_modules.at(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const LibraryInfo* Linker::FindLibrary(const Module& m, const std::string& id) {
|
||||
const auto& import_libs = m.dynamic_info.import_libs;
|
||||
int index = 0;
|
||||
for (const auto& lib : import_libs) {
|
||||
if (lib.enc_id.compare(id) == 0) {
|
||||
return &import_libs.at(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
const auto& export_libs = m.dynamic_info.export_libs;
|
||||
index = 0;
|
||||
for (const auto& lib : export_libs) {
|
||||
if (lib.enc_id.compare(id) == 0) {
|
||||
return &export_libs.at(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Linker::LoadSymbols(Module* m) {
|
||||
|
||||
const auto symbol_database = [this](Module* m, Loader::SymbolsResolver* symbol,
|
||||
bool export_func) {
|
||||
if (m->dynamic_info.symbol_table == nullptr || m->dynamic_info.str_table == nullptr ||
|
||||
m->dynamic_info.symbol_table_total_size == 0) {
|
||||
LOG_INFO(Core_Linker, "Symbol table not found!");
|
||||
return;
|
||||
}
|
||||
for (auto* sym = m->dynamic_info.symbol_table;
|
||||
reinterpret_cast<u8*>(sym) < reinterpret_cast<u8*>(m->dynamic_info.symbol_table) +
|
||||
m->dynamic_info.symbol_table_total_size;
|
||||
sym++) {
|
||||
std::string id = std::string(m->dynamic_info.str_table + sym->st_name);
|
||||
auto bind = sym->GetBind();
|
||||
auto type = sym->GetType();
|
||||
auto visibility = sym->GetVisibility();
|
||||
const auto ids = Common::SplitString(id, '#');
|
||||
if (ids.size() == 3) {
|
||||
const auto* library = FindLibrary(*m, ids.at(1));
|
||||
const auto* module = FindModule(*m, ids.at(2));
|
||||
ASSERT_MSG(library && module, "Unable to find library and module");
|
||||
if ((bind == STB_GLOBAL || bind == STB_WEAK) &&
|
||||
(type == STT_FUN || type == STT_OBJECT) &&
|
||||
export_func == (sym->st_value != 0)) {
|
||||
std::string nidName = "";
|
||||
auto aeronid = AeroLib::FindByNid(ids.at(0).c_str());
|
||||
if (aeronid != nullptr) {
|
||||
nidName = aeronid->name;
|
||||
} else {
|
||||
nidName = "UNK";
|
||||
}
|
||||
Loader::SymbolResolver sym_r{};
|
||||
sym_r.name = ids.at(0);
|
||||
sym_r.nidName = nidName;
|
||||
sym_r.library = library->name;
|
||||
sym_r.library_version = library->version;
|
||||
sym_r.module = module->name;
|
||||
sym_r.module_version_major = module->version_major;
|
||||
sym_r.module_version_minor = module->version_minor;
|
||||
switch (type) {
|
||||
case STT_NOTYPE:
|
||||
sym_r.type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
case STT_FUN:
|
||||
sym_r.type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
sym_r.type = Loader::SymbolType::Object;
|
||||
break;
|
||||
default:
|
||||
sym_r.type = Loader::SymbolType::Unknown;
|
||||
break;
|
||||
}
|
||||
symbol->AddSymbol(sym_r,
|
||||
(export_func ? sym->st_value + m->base_virtual_addr : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
symbol_database(m, &m->export_sym, true);
|
||||
symbol_database(m, &m->import_sym, false);
|
||||
}
|
||||
|
||||
void Linker::Relocate(Module* m) {
|
||||
const auto relocate = [this](u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) {
|
||||
auto type = rel->GetType();
|
||||
auto symbol = rel->GetSymbol();
|
||||
auto addend = rel->rel_addend;
|
||||
auto* symbolsTlb = m->dynamic_info.symbol_table;
|
||||
auto* namesTlb = m->dynamic_info.str_table;
|
||||
|
||||
u64 rel_value = 0;
|
||||
u64 rel_base_virtual_addr = m->base_virtual_addr;
|
||||
u64 rel_virtual_addr = m->base_virtual_addr + rel->rel_offset;
|
||||
bool rel_isResolved = false;
|
||||
Loader::SymbolType rel_sym_type = Loader::SymbolType::Unknown;
|
||||
std::string rel_name;
|
||||
|
||||
switch (type) {
|
||||
case R_X86_64_RELATIVE:
|
||||
rel_value = rel_base_virtual_addr + addend;
|
||||
rel_isResolved = true;
|
||||
break;
|
||||
case R_X86_64_DTPMOD64:
|
||||
rel_value = reinterpret_cast<uint64_t>(m);
|
||||
rel_isResolved = true;
|
||||
rel_sym_type = Loader::SymbolType::Tls;
|
||||
break;
|
||||
case R_X86_64_GLOB_DAT:
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
addend = 0;
|
||||
case R_X86_64_64: {
|
||||
auto sym = symbolsTlb[symbol];
|
||||
auto sym_bind = sym.GetBind();
|
||||
auto sym_type = sym.GetType();
|
||||
auto sym_visibility = sym.GetVisibility();
|
||||
u64 symbol_vitrual_addr = 0;
|
||||
Loader::SymbolRecord symrec{};
|
||||
switch (sym_type) {
|
||||
case STT_FUN:
|
||||
rel_sym_type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
rel_sym_type = Loader::SymbolType::Object;
|
||||
break;
|
||||
case STT_NOTYPE:
|
||||
rel_sym_type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown symbol type {}", sym_type);
|
||||
}
|
||||
if (sym_visibility != 0) // should be zero log if else
|
||||
{
|
||||
LOG_INFO(Core_Linker, "symbol visilibity !=0");
|
||||
}
|
||||
switch (sym_bind) {
|
||||
case STB_LOCAL:
|
||||
symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value;
|
||||
break;
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK: {
|
||||
rel_name = namesTlb + sym.st_name;
|
||||
Resolve(rel_name, rel_sym_type, m, &symrec);
|
||||
symbol_vitrual_addr = symrec.virtual_address;
|
||||
} break;
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
|
||||
}
|
||||
rel_isResolved = (symbol_vitrual_addr != 0);
|
||||
rel_value = (rel_isResolved ? symbol_vitrual_addr + addend : 0);
|
||||
rel_name = symrec.name;
|
||||
} break;
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "UNK type {:#010x} rel symbol : {:#010x}", type, symbol);
|
||||
}
|
||||
|
||||
if (rel_isResolved) {
|
||||
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
|
||||
} else {
|
||||
LOG_INFO(Core_Linker, "function not patched! {}", rel_name);
|
||||
}
|
||||
};
|
||||
|
||||
u32 idx = 0;
|
||||
for (auto* rel = m->dynamic_info.relocation_table;
|
||||
reinterpret_cast<u8*>(rel) < reinterpret_cast<u8*>(m->dynamic_info.relocation_table) +
|
||||
m->dynamic_info.relocation_table_size;
|
||||
rel++, idx++) {
|
||||
relocate(idx, rel, m, false);
|
||||
}
|
||||
idx = 0;
|
||||
for (auto* rel = m->dynamic_info.jmp_relocation_table;
|
||||
reinterpret_cast<u8*>(rel) < reinterpret_cast<u8*>(m->dynamic_info.jmp_relocation_table) +
|
||||
m->dynamic_info.jmp_relocation_table_size;
|
||||
rel++, idx++) {
|
||||
relocate(idx, rel, m, true);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool contains(const std::vector<T>& vecObj, const T& element) {
|
||||
auto it = std::find(vecObj.begin(), vecObj.end(), element);
|
||||
return it != vecObj.end();
|
||||
}
|
||||
|
||||
Module* Linker::FindExportedModule(const ModuleInfo& module, const LibraryInfo& library) {
|
||||
// std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
const auto& export_libs = m->dynamic_info.export_libs;
|
||||
const auto& export_modules = m->dynamic_info.export_modules;
|
||||
|
||||
if (contains(export_libs, library) && contains(export_modules, module)) {
|
||||
return m.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Module* m,
|
||||
Loader::SymbolRecord* return_info) {
|
||||
// std::scoped_lock lock{m_mutex};
|
||||
const auto ids = Common::SplitString(name, '#');
|
||||
if (ids.size() == 3) {
|
||||
const auto* library = FindLibrary(*m, ids.at(1));
|
||||
const auto* module = FindModule(*m, ids.at(2));
|
||||
ASSERT_MSG(library && module, "Unable to find library and module");
|
||||
|
||||
Loader::SymbolResolver sr{};
|
||||
sr.name = ids.at(0);
|
||||
sr.library = library->name;
|
||||
sr.library_version = library->version;
|
||||
sr.module = module->name;
|
||||
sr.module_version_major = module->version_major;
|
||||
sr.module_version_minor = module->version_minor;
|
||||
sr.type = sym_type;
|
||||
|
||||
const Loader::SymbolRecord* rec = nullptr;
|
||||
|
||||
rec = m_hle_symbols.FindSymbol(sr);
|
||||
if (rec == nullptr) {
|
||||
// check if it an export function
|
||||
if (auto* p = FindExportedModule(*module, *library);
|
||||
p != nullptr && p->export_sym.GetSize() > 0) {
|
||||
rec = p->export_sym.FindSymbol(sr);
|
||||
}
|
||||
}
|
||||
if (rec != nullptr) {
|
||||
*return_info = *rec;
|
||||
} else {
|
||||
auto aeronid = AeroLib::FindByNid(sr.name.c_str());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
return_info->virtual_address = AeroLib::GetStub(aeronid->nid);
|
||||
} else {
|
||||
return_info->virtual_address = AeroLib::GetStub(sr.name.c_str());
|
||||
return_info->name = "Unknown !!!";
|
||||
}
|
||||
LOG_ERROR(Core_Linker, "Linker: Stub resolved {} as {} (lib: {}, mod: {})", sr.name,
|
||||
return_info->name, library->name, module->name);
|
||||
}
|
||||
} else {
|
||||
return_info->virtual_address = 0;
|
||||
return_info->name = name;
|
||||
LOG_ERROR(Core_Linker, "Not Resolved {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
u64 Linker::GetProcParam() {
|
||||
// std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (!m->elf.IsSharedLib()) {
|
||||
return m->proc_param_virtual_addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
using exit_func_t = PS4_SYSV_ABI void (*)();
|
||||
using entry_func_t = PS4_SYSV_ABI void (*)(EntryParams* params, exit_func_t atexit_func);
|
||||
using module_ini_func_t = PS4_SYSV_ABI int (*)(size_t args, const void* argp, module_func_t func);
|
||||
|
||||
static PS4_SYSV_ABI int run_module(uint64_t addr, size_t args, const void* argp,
|
||||
module_func_t func) {
|
||||
return reinterpret_cast<module_ini_func_t>(addr)(args, argp, func);
|
||||
}
|
||||
|
||||
int Linker::StartModule(Module* m, size_t args, const void* argp, module_func_t func) {
|
||||
LOG_INFO(Core_Linker, "Module started : {}", m->file_name);
|
||||
return run_module(m->dynamic_info.init_virtual_addr + m->base_virtual_addr, args, argp, func);
|
||||
}
|
||||
|
||||
void Linker::StartAllModules() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (m->elf.IsSharedLib()) {
|
||||
StartModule(m.get(), 0, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
using ExitFunc = PS4_SYSV_ABI void (*)();
|
||||
|
||||
static PS4_SYSV_ABI void ProgramExitFunc() {
|
||||
fmt::print("exit function called\n");
|
||||
}
|
||||
|
||||
static void RunMainEntry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
||||
static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
|
||||
// reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have
|
||||
// a specific layout
|
||||
|
||||
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
|
||||
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
|
||||
|
||||
|
@ -672,42 +47,281 @@ static void RunMainEntry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
|||
: "rax", "rsi", "rdi");
|
||||
}
|
||||
|
||||
Linker::Linker() = default;
|
||||
|
||||
Linker::~Linker() = default;
|
||||
|
||||
void Linker::Execute() {
|
||||
if (Config::debugDump()) {
|
||||
DebugDump();
|
||||
}
|
||||
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
// Calculate static TLS size.
|
||||
for (const auto& module : m_modules) {
|
||||
if (module->tls.image_size != 0) {
|
||||
module->tls.modid = ++max_tls_index;
|
||||
}
|
||||
static_tls_size += module->tls.image_size;
|
||||
module->tls.offset = static_tls_size;
|
||||
}
|
||||
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
// Relocate all modules
|
||||
for (const auto& m : m_modules) {
|
||||
Relocate(m.get());
|
||||
}
|
||||
StartAllModules();
|
||||
|
||||
// Init primary thread.
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
InitTlsForThread(true);
|
||||
|
||||
// Start shared library modules
|
||||
for (auto& m : m_modules) {
|
||||
if (m->IsSharedLib()) {
|
||||
m->Start(0, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Start main module.
|
||||
EntryParams p{};
|
||||
p.argc = 1;
|
||||
p.argv[0] = "eboot.bin"; // hmm should be ok?
|
||||
p.argv[0] = "eboot.bin";
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (m->elf.IsSharedLib()) {
|
||||
continue;
|
||||
if (!m->IsSharedLib()) {
|
||||
RunMainEntry(m->GetEntryAddress(), &p, ProgramExitFunc);
|
||||
}
|
||||
if (m->tls.image_virtual_addr != 0) {
|
||||
SetTLSStorage(m->tls.image_virtual_addr);
|
||||
}
|
||||
RunMainEntry(m->elf.GetElfEntry() + m->base_virtual_addr, &p, ProgramExitFunc);
|
||||
}
|
||||
}
|
||||
|
||||
s32 Linker::LoadModule(const std::filesystem::path& elf_name) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
if (!std::filesystem::exists(elf_name)) {
|
||||
LOG_ERROR(Core_Linker, "Provided file {} does not exist", elf_name.string());
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto module = std::make_unique<Module>(elf_name);
|
||||
if (!module->IsValid()) {
|
||||
LOG_ERROR(Core_Linker, "Provided file {} is not valid ELF file", elf_name.string());
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_modules.emplace_back(std::move(module));
|
||||
return m_modules.size() - 1;
|
||||
}
|
||||
|
||||
void Linker::Relocate(Module* module) {
|
||||
module->ForEachRelocation([&](elf_relocation* rel, bool isJmpRel) {
|
||||
auto type = rel->GetType();
|
||||
auto symbol = rel->GetSymbol();
|
||||
auto addend = rel->rel_addend;
|
||||
auto* symbol_table = module->dynamic_info.symbol_table;
|
||||
auto* namesTlb = module->dynamic_info.str_table;
|
||||
|
||||
const VAddr rel_base_virtual_addr = module->GetBaseAddress();
|
||||
const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset;
|
||||
bool rel_is_resolved = false;
|
||||
u64 rel_value = 0;
|
||||
Loader::SymbolType rel_sym_type = Loader::SymbolType::Unknown;
|
||||
std::string rel_name;
|
||||
|
||||
switch (type) {
|
||||
case R_X86_64_RELATIVE:
|
||||
rel_value = rel_base_virtual_addr + addend;
|
||||
rel_is_resolved = true;
|
||||
break;
|
||||
case R_X86_64_DTPMOD64:
|
||||
rel_value = static_cast<u64>(module->tls.modid);
|
||||
rel_is_resolved = true;
|
||||
rel_sym_type = Loader::SymbolType::Tls;
|
||||
break;
|
||||
case R_X86_64_GLOB_DAT:
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
addend = 0;
|
||||
case R_X86_64_64: {
|
||||
auto sym = symbol_table[symbol];
|
||||
auto sym_bind = sym.GetBind();
|
||||
auto sym_type = sym.GetType();
|
||||
auto sym_visibility = sym.GetVisibility();
|
||||
u64 symbol_vitrual_addr = 0;
|
||||
Loader::SymbolRecord symrec{};
|
||||
switch (sym_type) {
|
||||
case STT_FUN:
|
||||
rel_sym_type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
rel_sym_type = Loader::SymbolType::Object;
|
||||
break;
|
||||
case STT_NOTYPE:
|
||||
rel_sym_type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown symbol type {}", sym_type);
|
||||
}
|
||||
|
||||
if (sym_visibility != 0) {
|
||||
LOG_INFO(Core_Linker, "symbol visilibity !=0");
|
||||
}
|
||||
|
||||
switch (sym_bind) {
|
||||
case STB_LOCAL:
|
||||
symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value;
|
||||
break;
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK: {
|
||||
rel_name = namesTlb + sym.st_name;
|
||||
Resolve(rel_name, rel_sym_type, module, &symrec);
|
||||
symbol_vitrual_addr = symrec.virtual_address;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
|
||||
}
|
||||
rel_is_resolved = (symbol_vitrual_addr != 0);
|
||||
rel_value = (rel_is_resolved ? symbol_vitrual_addr + addend : 0);
|
||||
rel_name = symrec.name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "UNK type {:#010x} rel symbol : {:#010x}", type, symbol);
|
||||
}
|
||||
|
||||
if (rel_is_resolved) {
|
||||
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
|
||||
} else {
|
||||
LOG_INFO(Core_Linker, "function not patched! {}", rel_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const Module* Linker::FindExportedModule(const ModuleInfo& module, const LibraryInfo& library) {
|
||||
const auto it = std::ranges::find_if(m_modules, [&](const auto& m) {
|
||||
return std::ranges::contains(m->GetExportLibs(), library) &&
|
||||
std::ranges::contains(m->GetExportModules(), module);
|
||||
});
|
||||
return it == m_modules.end() ? nullptr : it->get();
|
||||
}
|
||||
|
||||
void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Module* m,
|
||||
Loader::SymbolRecord* return_info) {
|
||||
const auto ids = Common::SplitString(name, '#');
|
||||
if (ids.size() != 3) {
|
||||
return_info->virtual_address = 0;
|
||||
return_info->name = name;
|
||||
LOG_ERROR(Core_Linker, "Not Resolved {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
const LibraryInfo* library = m->FindLibrary(ids[1]);
|
||||
const ModuleInfo* module = m->FindModule(ids[2]);
|
||||
ASSERT_MSG(library && module, "Unable to find library and module");
|
||||
|
||||
Loader::SymbolResolver sr{};
|
||||
sr.name = ids.at(0);
|
||||
sr.library = library->name;
|
||||
sr.library_version = library->version;
|
||||
sr.module = module->name;
|
||||
sr.module_version_major = module->version_major;
|
||||
sr.module_version_minor = module->version_minor;
|
||||
sr.type = sym_type;
|
||||
|
||||
const auto* record = m_hle_symbols.FindSymbol(sr);
|
||||
if (!record) {
|
||||
// Check if it an export function
|
||||
const auto* p = FindExportedModule(*module, *library);
|
||||
if (p && p->export_sym.GetSize() > 0) {
|
||||
record = p->export_sym.FindSymbol(sr);
|
||||
}
|
||||
}
|
||||
if (record) {
|
||||
*return_info = *record;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
return_info->virtual_address = AeroLib::GetStub(aeronid->nid);
|
||||
} else {
|
||||
return_info->virtual_address = AeroLib::GetStub(sr.name.c_str());
|
||||
return_info->name = "Unknown !!!";
|
||||
}
|
||||
LOG_ERROR(Core_Linker, "Linker: Stub resolved {} as {} (lib: {}, mod: {})", sr.name,
|
||||
return_info->name, library->name, module->name);
|
||||
}
|
||||
|
||||
void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
|
||||
DtvEntry* dtv_table = GetTcbBase()->tcb_dtv;
|
||||
ASSERT_MSG(dtv_table[0].counter == dtv_generation_counter,
|
||||
"Reallocation of DTV table is not supported");
|
||||
|
||||
void* module = (u8*)dtv_table[module_index + 1].pointer + offset;
|
||||
ASSERT_MSG(module, "DTV allocation is not supported");
|
||||
return module;
|
||||
}
|
||||
|
||||
void Linker::InitTlsForThread(bool is_primary) {
|
||||
static constexpr size_t TcbSize = 0x40;
|
||||
static constexpr size_t TlsAllocAlign = 0x20;
|
||||
const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize;
|
||||
|
||||
// The kernel module has a few different paths for TLS allocation.
|
||||
// For SDK < 1.7 it allocates both main and secondary thread blocks using libc mspace/malloc.
|
||||
// In games compiled with newer SDK, the main thread gets mapped from flexible memory,
|
||||
// with addr = 0, so system managed area. Here we will only implement the latter.
|
||||
void* addr_out{};
|
||||
if (is_primary) {
|
||||
const size_t tls_aligned = Common::AlignUp(total_tls_size, 16_KB);
|
||||
const int ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(&addr_out, tls_aligned,
|
||||
3, 0, "SceKernelPrimaryTcbTls");
|
||||
ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread");
|
||||
} else {
|
||||
if (heap_api_func) {
|
||||
addr_out = heap_api_func(total_tls_size);
|
||||
} else {
|
||||
addr_out = std::malloc(total_tls_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize allocated memory and allocate DTV table.
|
||||
const u32 num_dtvs = max_tls_index;
|
||||
std::memset(addr_out, 0, total_tls_size);
|
||||
DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2];
|
||||
|
||||
// Initialize thread control block
|
||||
u8* addr = reinterpret_cast<u8*>(addr_out);
|
||||
Tcb* tcb = reinterpret_cast<Tcb*>(addr + static_tls_size);
|
||||
tcb->tcb_self = tcb;
|
||||
tcb->tcb_dtv = dtv_table;
|
||||
|
||||
// Dtv[0] is the generation counter. libkernel puts their number into dtv[1] (why?)
|
||||
dtv_table[0].counter = dtv_generation_counter;
|
||||
dtv_table[1].counter = num_dtvs;
|
||||
|
||||
// Copy init images to TLS thread blocks and map them to DTV slots.
|
||||
for (const auto& module : m_modules) {
|
||||
if (module->tls.image_size == 0) {
|
||||
continue;
|
||||
}
|
||||
u8* dest = reinterpret_cast<u8*>(addr + static_tls_size - module->tls.offset);
|
||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||
std::memcpy(dest, src, module->tls.init_image_size);
|
||||
tcb->tcb_dtv[module->tls.modid + 1].pointer = dest;
|
||||
}
|
||||
|
||||
// Set pointer to FS base
|
||||
SetTcbBase(tcb);
|
||||
}
|
||||
|
||||
void Linker::DebugDump() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto& log_dir = Common::FS::GetUserPath(Common::FS::PathType::LogDir);
|
||||
const std::filesystem::path debug(log_dir / "debugdump");
|
||||
std::filesystem::create_directory(debug);
|
||||
for (const auto& m : m_modules) {
|
||||
// TODO make a folder with game id for being more unique?
|
||||
const std::filesystem::path filepath(debug / m.get()->file_name);
|
||||
const std::filesystem::path filepath(debug / m.get()->file.stem());
|
||||
std::filesystem::create_directory(filepath);
|
||||
m.get()->import_sym.DebugDump(filepath / "imports.txt");
|
||||
m.get()->export_sym.DebugDump(filepath / "exports.txt");
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
#include "core/module.h"
|
||||
|
||||
namespace Core {
|
||||
using module_func_t = int (*)(size_t args, const void* argp);
|
||||
|
||||
struct DynamicModuleInfo;
|
||||
class Linker;
|
||||
|
||||
|
@ -19,136 +18,50 @@ struct EntryParams {
|
|||
const char* argv[3];
|
||||
};
|
||||
|
||||
struct ModuleInfo {
|
||||
bool operator==(const ModuleInfo& other) const {
|
||||
return version_major == other.version_major && version_minor == other.version_minor &&
|
||||
name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u8 version_minor;
|
||||
u8 version_major;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct LibraryInfo {
|
||||
bool operator==(const LibraryInfo& other) const {
|
||||
return version == other.version && name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u16 version;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct PS4ThreadLocal {
|
||||
u64 image_virtual_addr = 0;
|
||||
u64 image_size = 0;
|
||||
u64 handler_virtual_addr = 0;
|
||||
};
|
||||
|
||||
struct DynamicModuleInfo {
|
||||
void* hash_table = nullptr;
|
||||
u64 hash_table_size = 0;
|
||||
|
||||
char* str_table = nullptr;
|
||||
u64 str_table_size = 0;
|
||||
|
||||
elf_symbol* symbol_table = nullptr;
|
||||
u64 symbol_table_total_size = 0;
|
||||
u64 symbol_table_entries_size = 0;
|
||||
|
||||
u64 init_virtual_addr = 0;
|
||||
u64 fini_virtual_addr = 0;
|
||||
u64 pltgot_virtual_addr = 0;
|
||||
u64 init_array_virtual_addr = 0;
|
||||
u64 fini_array_virtual_addr = 0;
|
||||
u64 preinit_array_virtual_addr = 0;
|
||||
u64 init_array_size = 0;
|
||||
u64 fini_array_size = 0;
|
||||
u64 preinit_array_size = 0;
|
||||
|
||||
elf_relocation* jmp_relocation_table = nullptr;
|
||||
u64 jmp_relocation_table_size = 0;
|
||||
s64 jmp_relocation_type = 0;
|
||||
|
||||
elf_relocation* relocation_table = nullptr;
|
||||
u64 relocation_table_size = 0;
|
||||
u64 relocation_table_entries_size = 0;
|
||||
|
||||
u64 debug = 0;
|
||||
u64 textrel = 0;
|
||||
u64 flags = 0;
|
||||
|
||||
std::vector<const char*> needed;
|
||||
std::vector<ModuleInfo> import_modules;
|
||||
std::vector<ModuleInfo> export_modules;
|
||||
std::vector<LibraryInfo> import_libs;
|
||||
std::vector<LibraryInfo> export_libs;
|
||||
|
||||
std::string filename; // Filename with absolute path
|
||||
};
|
||||
|
||||
// This struct keeps neccesary info about loaded modules. Main executeable is included too as well
|
||||
struct Module {
|
||||
Loader::Elf elf;
|
||||
u64 aligned_base_size = 0;
|
||||
u64 base_virtual_addr = 0;
|
||||
u64 proc_param_virtual_addr = 0;
|
||||
|
||||
std::string file_name;
|
||||
|
||||
std::vector<u8> m_dynamic;
|
||||
std::vector<u8> m_dynamic_data;
|
||||
DynamicModuleInfo dynamic_info{};
|
||||
|
||||
Loader::SymbolsResolver export_sym;
|
||||
Loader::SymbolsResolver import_sym;
|
||||
|
||||
PS4ThreadLocal tls;
|
||||
};
|
||||
using HeapApiFunc = PS4_SYSV_ABI void*(*)(size_t);
|
||||
|
||||
class Linker {
|
||||
public:
|
||||
Linker();
|
||||
virtual ~Linker();
|
||||
explicit Linker();
|
||||
~Linker();
|
||||
|
||||
Module* LoadModule(const std::filesystem::path& elf_name);
|
||||
void LoadModuleToMemory(Module* m);
|
||||
void LoadDynamicInfo(Module* m);
|
||||
void LoadSymbols(Module* m);
|
||||
Loader::SymbolsResolver& getHLESymbols() {
|
||||
Loader::SymbolsResolver& GetHLESymbols() {
|
||||
return m_hle_symbols;
|
||||
}
|
||||
void Relocate(Module* m);
|
||||
void Resolve(const std::string& name, Loader::SymbolType Symtype, Module* m,
|
||||
Loader::SymbolRecord* return_info);
|
||||
|
||||
VAddr GetProcParam() const {
|
||||
return m_modules[0]->GetProcParam();
|
||||
}
|
||||
|
||||
Module* GetModule(s32 index) const {
|
||||
return m_modules.at(index).get();
|
||||
}
|
||||
|
||||
void SetHeapApiFunc(void* func) {
|
||||
heap_api_func = *reinterpret_cast<HeapApiFunc*>(func);
|
||||
}
|
||||
|
||||
void* TlsGetAddr(u64 module_index, u64 offset);
|
||||
void InitTlsForThread(bool is_primary = false);
|
||||
|
||||
s32 LoadModule(const std::filesystem::path& elf_name);
|
||||
|
||||
void Relocate(Module* module);
|
||||
void Resolve(const std::string& name, Loader::SymbolType type,
|
||||
Module* module, Loader::SymbolRecord* return_info);
|
||||
void Execute();
|
||||
void DebugDump();
|
||||
u64 GetProcParam();
|
||||
|
||||
private:
|
||||
const ModuleInfo* FindModule(const Module& m, const std::string& id);
|
||||
const LibraryInfo* FindLibrary(const Module& program, const std::string& id);
|
||||
Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
||||
int StartModule(Module* m, size_t args, const void* argp, module_func_t func);
|
||||
void StartAllModules();
|
||||
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
||||
|
||||
std::mutex mutex;
|
||||
u32 dtv_generation_counter{1};
|
||||
size_t static_tls_size{};
|
||||
size_t max_tls_index{};
|
||||
HeapApiFunc heap_api_func{};
|
||||
std::vector<std::unique_ptr<Module>> m_modules;
|
||||
Loader::SymbolsResolver m_hle_symbols{};
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -445,7 +445,7 @@ std::string Elf::ElfHeaderStr() {
|
|||
return header;
|
||||
}
|
||||
|
||||
std::string Elf::ElfPheaderTypeStr(u32 type) {
|
||||
std::string_view Elf::ElfPheaderTypeStr(u32 type) {
|
||||
switch (type) {
|
||||
case PT_NULL:
|
||||
return "Null";
|
||||
|
@ -538,8 +538,4 @@ void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size) {
|
|||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool Elf::IsSharedLib() {
|
||||
return m_elf_header.e_type == ET_SCE_DYNAMIC;
|
||||
}
|
||||
|
||||
} // namespace Core::Loader
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -482,15 +481,18 @@ public:
|
|||
return m_elf_header.e_entry;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSharedLib() const {
|
||||
return m_elf_header.e_type == ET_SCE_DYNAMIC;
|
||||
}
|
||||
|
||||
std::string SElfHeaderStr();
|
||||
std::string SELFSegHeader(u16 no);
|
||||
std::string ElfHeaderStr();
|
||||
std::string ElfPHeaderStr(u16 no);
|
||||
std::string ElfPheaderTypeStr(u32 type);
|
||||
std::string_view ElfPheaderTypeStr(u32 type);
|
||||
std::string ElfPheaderFlagsStr(u32 flags);
|
||||
|
||||
void LoadSegment(u64 virtual_addr, u64 file_offset, u64 size);
|
||||
bool IsSharedLib();
|
||||
|
||||
private:
|
||||
Common::FS::IOFile m_f{};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/types.h"
|
||||
#include "core/aerolib/aerolib.h"
|
||||
|
@ -11,9 +11,7 @@
|
|||
namespace Core::Loader {
|
||||
|
||||
void SymbolsResolver::AddSymbol(const SymbolResolver& s, u64 virtual_addr) {
|
||||
SymbolRecord& r = m_symbols.emplace_back();
|
||||
r.name = GenerateName(s);
|
||||
r.virtual_address = virtual_addr;
|
||||
m_symbols.emplace_back(GenerateName(s), s.nidName, virtual_addr);
|
||||
}
|
||||
|
||||
std::string SymbolsResolver::GenerateName(const SymbolResolver& s) {
|
||||
|
@ -38,22 +36,13 @@ void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) {
|
|||
Common::FS::FileType::TextFile};
|
||||
for (const auto& symbol : m_symbols) {
|
||||
const auto ids = Common::SplitString(symbol.name, '#');
|
||||
std::string nidName = "";
|
||||
auto aeronid = AeroLib::FindByNid(ids.at(0).c_str());
|
||||
if (aeronid != nullptr) {
|
||||
nidName = aeronid->name;
|
||||
} else {
|
||||
nidName = "UNK";
|
||||
}
|
||||
const auto aeronid = AeroLib::FindByNid(ids.at(0).c_str());
|
||||
const auto nid_name = aeronid ? aeronid->name : "UNK";
|
||||
f.WriteString(
|
||||
fmt::format("0x{:<20x} {:<16} {:<60} {:<30} {:<2} {:<30} {:<2} {:<2} {:<10}\n",
|
||||
symbol.virtual_address, ids.at(0), nidName, ids.at(1), ids.at(2), ids.at(3),
|
||||
ids.at(4), ids.at(5), ids.at(6)));
|
||||
symbol.virtual_address, ids.at(0), nid_name, ids.at(1), ids.at(2),
|
||||
ids.at(3), ids.at(4), ids.at(5), ids.at(6)));
|
||||
}
|
||||
}
|
||||
|
||||
int SymbolsResolver::GetSize() {
|
||||
return m_symbols.size();
|
||||
}
|
||||
|
||||
} // namespace Core::Loader
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
|
@ -20,6 +21,7 @@ enum class SymbolType {
|
|||
|
||||
struct SymbolRecord {
|
||||
std::string name;
|
||||
std::string nid_name;
|
||||
u64 virtual_address;
|
||||
};
|
||||
|
||||
|
@ -42,6 +44,14 @@ public:
|
|||
void AddSymbol(const SymbolResolver& s, u64 virtual_addr);
|
||||
const SymbolRecord* FindSymbol(const SymbolResolver& s) const;
|
||||
|
||||
std::span<const SymbolRecord> GetSymbols() const {
|
||||
return m_symbols;
|
||||
}
|
||||
|
||||
size_t GetSize() const noexcept {
|
||||
return m_symbols.size();
|
||||
}
|
||||
|
||||
static std::string GenerateName(const SymbolResolver& s);
|
||||
|
||||
static std::string_view SymbolTypeToS(SymbolType sym_type) {
|
||||
|
@ -60,7 +70,6 @@ public:
|
|||
}
|
||||
|
||||
void DebugDump(const std::filesystem::path& file_name);
|
||||
int GetSize();
|
||||
|
||||
private:
|
||||
std::vector<SymbolRecord> m_symbols;
|
||||
|
|
|
@ -26,7 +26,7 @@ MemoryManager::~MemoryManager() = default;
|
|||
|
||||
PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
|
||||
int memory_type) {
|
||||
PAddr free_addr = 0;
|
||||
PAddr free_addr = search_start;
|
||||
|
||||
// Iterate through allocated blocked and find the next free position
|
||||
for (const auto& block : allocations) {
|
||||
|
@ -35,7 +35,9 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
|
|||
}
|
||||
|
||||
// Align free position
|
||||
free_addr = Common::alignUp(free_addr, alignment);
|
||||
if (alignment > 0) {
|
||||
free_addr = Common::AlignUp(free_addr, alignment);
|
||||
}
|
||||
ASSERT(free_addr >= search_start && free_addr + size <= search_end);
|
||||
|
||||
// Add the allocated region to the list and commit its pages.
|
||||
|
@ -43,7 +45,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
|
|||
return free_addr;
|
||||
}
|
||||
|
||||
void MemoryManager::Free(PAddr phys_addr, size_t size) {
|
||||
int MemoryManager::Free(PAddr phys_addr, size_t size) {
|
||||
const auto it = std::ranges::find_if(allocations, [&](const auto& alloc) {
|
||||
return alloc.base == phys_addr && alloc.size == size;
|
||||
});
|
||||
|
@ -51,12 +53,13 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
|
|||
|
||||
// Free the ranges.
|
||||
allocations.erase(it);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||
MemoryMapFlags flags, VMAType type, std::string_view name,
|
||||
PAddr phys_addr, u64 alignment) {
|
||||
VAddr mapped_addr = alignment > 0 ? Common::alignUp(virtual_addr, alignment) : virtual_addr;
|
||||
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
||||
SCOPE_EXIT {
|
||||
auto& new_vma = AddMapping(mapped_addr, size);
|
||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||
|
@ -99,7 +102,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
|||
}
|
||||
|
||||
// Perform the mapping.
|
||||
*out_addr = impl.Map(mapped_addr, size);
|
||||
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -135,6 +138,27 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int MemoryManager::VirtualQuery(VAddr addr, int flags, Libraries::Kernel::OrbisVirtualQueryInfo* info) {
|
||||
auto it = FindVMA(addr);
|
||||
while (it->second.type == VMAType::Free && flags == 1) {
|
||||
it++;
|
||||
}
|
||||
|
||||
const auto& vma = it->second;
|
||||
info->start = vma.base;
|
||||
info->end = vma.base + vma.size;
|
||||
info->is_flexible.Assign(vma.type == VMAType::Flexible);
|
||||
info->is_direct.Assign(vma.type == VMAType::Direct);
|
||||
info->is_commited.Assign(vma.type != VMAType::Free);
|
||||
if (vma.type == VMAType::Direct) {
|
||||
const auto it = std::ranges::find(allocations, vma.base, &DirectMemoryArea::base);
|
||||
ASSERT(it != allocations.end());
|
||||
info->memory_type = it->memory_type;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int MemoryManager::DirectMemoryQuery(PAddr addr, bool find_next,
|
||||
Libraries::Kernel::OrbisQueryInfo* out_info) {
|
||||
const auto it = std::ranges::find_if(allocations, [&](const DirectMemoryArea& alloc) {
|
||||
|
@ -216,7 +240,7 @@ MemoryManager::VMAHandle MemoryManager::MergeAdjacent(VMAHandle iter) {
|
|||
}
|
||||
|
||||
void MemoryManager::MapVulkanMemory(VAddr addr, size_t size) {
|
||||
return;
|
||||
//return;
|
||||
const vk::Device device = instance->GetDevice();
|
||||
const auto memory_props = instance->GetPhysicalDevice().getMemoryProperties();
|
||||
void* host_pointer = reinterpret_cast<void*>(addr);
|
||||
|
@ -288,7 +312,7 @@ void MemoryManager::MapVulkanMemory(VAddr addr, size_t size) {
|
|||
}
|
||||
|
||||
void MemoryManager::UnmapVulkanMemory(VAddr addr, size_t size) {
|
||||
return;
|
||||
//return;
|
||||
const auto it = mapped_memories.find(addr);
|
||||
ASSERT(it != mapped_memories.end() && it->second.buffer_size == size);
|
||||
mapped_memories.erase(it);
|
||||
|
|
|
@ -19,6 +19,7 @@ class Instance;
|
|||
|
||||
namespace Libraries::Kernel {
|
||||
struct OrbisQueryInfo;
|
||||
struct OrbisVirtualQueryInfo;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
@ -103,16 +104,18 @@ public:
|
|||
PAddr Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
|
||||
int memory_type);
|
||||
|
||||
void Free(PAddr phys_addr, size_t size);
|
||||
int Free(PAddr phys_addr, size_t size);
|
||||
|
||||
int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||
MemoryMapFlags flags, VMAType type, std::string_view name = "",
|
||||
PAddr phys_addr = 0, u64 alignment = 0);
|
||||
PAddr phys_addr = -1, u64 alignment = 0);
|
||||
|
||||
void UnmapMemory(VAddr virtual_addr, size_t size);
|
||||
|
||||
int QueryProtection(VAddr addr, void** start, void** end, u32* prot);
|
||||
|
||||
int VirtualQuery(VAddr addr, int flags, Libraries::Kernel::OrbisVirtualQueryInfo* info);
|
||||
|
||||
int DirectMemoryQuery(PAddr addr, bool find_next, Libraries::Kernel::OrbisQueryInfo* out_info);
|
||||
|
||||
std::pair<vk::Buffer, size_t> GetVulkanBuffer(VAddr addr);
|
||||
|
@ -121,7 +124,7 @@ private:
|
|||
VMAHandle FindVMA(VAddr target) {
|
||||
// Return first the VMA with base >= target.
|
||||
const auto it = vma_map.lower_bound(target);
|
||||
if (it->first == target) {
|
||||
if (it != vma_map.end() && it->first == target) {
|
||||
return it;
|
||||
}
|
||||
return std::prev(it);
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/aerolib/aerolib.h"
|
||||
#include "core/module.h"
|
||||
#include "core/tls.h"
|
||||
#include "core/virtual_memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, ModuleFunc func);
|
||||
|
||||
static u64 LoadAddress = SYSTEM_RESERVED + CODE_BASE_OFFSET;
|
||||
static constexpr u64 CODE_BASE_INCR = 0x010000000u;
|
||||
|
||||
static u64 GetAlignedSize(const elf_program_header& phdr) {
|
||||
return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1)
|
||||
: phdr.p_memsz);
|
||||
}
|
||||
|
||||
static u64 CalculateBaseSize(const elf_header& ehdr, std::span<const elf_program_header> phdr) {
|
||||
u64 base_size = 0;
|
||||
for (u16 i = 0; i < ehdr.e_phnum; i++) {
|
||||
if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO)) {
|
||||
const u64 last_addr = phdr[i].p_vaddr + GetAlignedSize(phdr[i]);
|
||||
base_size = std::max(last_addr, base_size);
|
||||
}
|
||||
}
|
||||
return base_size;
|
||||
}
|
||||
|
||||
static std::string EncodeId(u64 nVal) {
|
||||
std::string enc;
|
||||
static constexpr std::string_view codes =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
||||
if (nVal < 0x40u) {
|
||||
enc += codes[nVal];
|
||||
} else {
|
||||
if (nVal < 0x1000u) {
|
||||
enc += codes[static_cast<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += codes[nVal & 0x3fu];
|
||||
} else {
|
||||
enc += codes[static_cast<u16>(nVal >> 12u) & 0x3fu];
|
||||
enc += codes[static_cast<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += codes[nVal & 0x3fu];
|
||||
}
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
Module::Module(const std::filesystem::path& file_) : file{file_} {
|
||||
elf.Open(file);
|
||||
if (elf.IsElfFile()) {
|
||||
LoadModuleToMemory();
|
||||
LoadDynamicInfo();
|
||||
LoadSymbols();
|
||||
}
|
||||
}
|
||||
|
||||
Module::~Module() = default;
|
||||
|
||||
void Module::Start(size_t args, const void* argp, ModuleFunc func) {
|
||||
LOG_INFO(Core_Linker, "Module started : {}", file.filename().string());
|
||||
const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress();
|
||||
reinterpret_cast<EntryFunc>(addr)(args, argp, func);
|
||||
}
|
||||
|
||||
void Module::LoadModuleToMemory() {
|
||||
static constexpr size_t BlockAlign = 0x1000;
|
||||
static constexpr u64 TrampolineSize = 8_MB;
|
||||
|
||||
// Retrieve elf header and program header
|
||||
const auto elf_header = elf.GetElfHeader();
|
||||
const auto elf_pheader = elf.GetProgramHeader();
|
||||
const u64 base_size = CalculateBaseSize(elf_header, elf_pheader);
|
||||
aligned_base_size = Common::AlignUp(base_size, BlockAlign);
|
||||
|
||||
// Map module segments (and possible TLS trampolines)
|
||||
base_virtual_addr = VirtualMemory::memory_alloc(LoadAddress, aligned_base_size + TrampolineSize,
|
||||
VirtualMemory::MemoryMode::ExecuteReadWrite);
|
||||
LoadAddress += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR);
|
||||
|
||||
// Initialize trampoline generator.
|
||||
void* trampoline_addr = std::bit_cast<void*>(base_virtual_addr + aligned_base_size);
|
||||
Xbyak::CodeGenerator c(TrampolineSize, trampoline_addr);
|
||||
|
||||
LOG_INFO(Core_Linker, "======== Load Module to Memory ========");
|
||||
LOG_INFO(Core_Linker, "base_virtual_addr ......: {:#018x}", base_virtual_addr);
|
||||
LOG_INFO(Core_Linker, "base_size ..............: {:#018x}", base_size);
|
||||
LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", aligned_base_size);
|
||||
|
||||
for (u16 i = 0; i < elf_header.e_phnum; i++) {
|
||||
const auto header_type = elf.ElfPheaderTypeStr(elf_pheader[i].p_type);
|
||||
switch (elf_pheader[i].p_type) {
|
||||
case PT_LOAD:
|
||||
case PT_SCE_RELRO: {
|
||||
if (elf_pheader[i].p_memsz == 0) {
|
||||
LOG_ERROR(Core_Linker, "p_memsz==0 in type {}", header_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
const u64 segment_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
|
||||
const u64 segment_file_size = elf_pheader[i].p_filesz;
|
||||
const u64 segment_memory_size = GetAlignedSize(elf_pheader[i]);
|
||||
const auto segment_mode = elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags);
|
||||
LOG_INFO(Core_Linker, "program header = [{}] type = {}", i, header_type);
|
||||
LOG_INFO(Core_Linker, "segment_addr ..........: {:#018x}", segment_addr);
|
||||
LOG_INFO(Core_Linker, "segment_file_size .....: {}", segment_file_size);
|
||||
LOG_INFO(Core_Linker, "segment_memory_size ...: {}", segment_memory_size);
|
||||
LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode);
|
||||
|
||||
elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size);
|
||||
if (elf_pheader[i].p_flags & PF_EXEC) {
|
||||
PatchTLS(segment_addr, segment_memory_size, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PT_DYNAMIC:
|
||||
if (elf_pheader[i].p_filesz != 0) {
|
||||
m_dynamic.resize(elf_pheader[i].p_filesz);
|
||||
const VAddr segment_addr = std::bit_cast<VAddr>(m_dynamic.data());
|
||||
elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", header_type);
|
||||
}
|
||||
break;
|
||||
case PT_SCE_DYNLIBDATA:
|
||||
if (elf_pheader[i].p_filesz != 0) {
|
||||
m_dynamic_data.resize(elf_pheader[i].p_filesz);
|
||||
const VAddr segment_addr = std::bit_cast<VAddr>(m_dynamic_data.data());
|
||||
elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", header_type);
|
||||
}
|
||||
break;
|
||||
case PT_TLS:
|
||||
tls.init_image_size = elf_pheader[i].p_filesz;
|
||||
tls.align = elf_pheader[i].p_align;
|
||||
tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
|
||||
tls.image_size = GetAlignedSize(elf_pheader[i]);
|
||||
LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr);
|
||||
LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size);
|
||||
break;
|
||||
case PT_SCE_PROCPARAM:
|
||||
proc_param_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Core_Linker, "Unimplemented type {}", header_type);
|
||||
}
|
||||
}
|
||||
|
||||
const VAddr entry_addr = base_virtual_addr + elf.GetElfEntry();
|
||||
LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr);
|
||||
}
|
||||
|
||||
void Module::LoadDynamicInfo() {
|
||||
for (const auto* dyn = reinterpret_cast<elf_dynamic*>(m_dynamic.data()); dyn->d_tag != DT_NULL;
|
||||
dyn++) {
|
||||
switch (dyn->d_tag) {
|
||||
case DT_SCE_HASH: // Offset of the hash table.
|
||||
dynamic_info.hash_table =
|
||||
reinterpret_cast<void*>(m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_HASHSZ: // Size of the hash table
|
||||
dynamic_info.hash_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_STRTAB: // Offset of the string table.
|
||||
dynamic_info.str_table =
|
||||
reinterpret_cast<char*>(m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_STRSZ: // Size of the string table.
|
||||
dynamic_info.str_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_SYMTAB: // Offset of the symbol table.
|
||||
dynamic_info.symbol_table =
|
||||
reinterpret_cast<elf_symbol*>(m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_SYMTABSZ: // Size of the symbol table.
|
||||
dynamic_info.symbol_table_total_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_INIT:
|
||||
dynamic_info.init_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_FINI:
|
||||
dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_SCE_PLTGOT: // Offset of the global offset table.
|
||||
dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_SCE_JMPREL: // Offset of the table containing jump slots.
|
||||
dynamic_info.jmp_relocation_table =
|
||||
reinterpret_cast<elf_relocation*>(m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_PLTRELSZ: // Size of the global offset table.
|
||||
dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_PLTREL: // The type of relocations in the relocation table. Should be DT_RELA
|
||||
dynamic_info.jmp_relocation_type = dyn->d_un.d_val;
|
||||
if (dynamic_info.jmp_relocation_type != DT_RELA) {
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_PLTREL is NOT DT_RELA should check!");
|
||||
}
|
||||
break;
|
||||
case DT_SCE_RELA: // Offset of the relocation table.
|
||||
dynamic_info.relocation_table =
|
||||
reinterpret_cast<elf_relocation*>(m_dynamic_data.data() + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_SCE_RELASZ: // Size of the relocation table.
|
||||
dynamic_info.relocation_table_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_RELAENT: // The size of relocation table entries.
|
||||
dynamic_info.relocation_table_entries_size = dyn->d_un.d_val;
|
||||
if (dynamic_info.relocation_table_entries_size != 0x18) {
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_RELAENT is NOT 0x18 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_INIT_ARRAY: // Address of the array of pointers to initialization functions
|
||||
dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_FINI_ARRAY: // Address of the array of pointers to termination functions
|
||||
dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_INIT_ARRAYSZ: // Size in bytes of the array of initialization functions
|
||||
dynamic_info.init_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_FINI_ARRAYSZ: // Size in bytes of the array of terminationfunctions
|
||||
dynamic_info.fini_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_PREINIT_ARRAY: // Address of the array of pointers to pre - initialization functions
|
||||
dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr;
|
||||
break;
|
||||
case DT_PREINIT_ARRAYSZ: // Size in bytes of the array of pre - initialization functions
|
||||
dynamic_info.preinit_array_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_SYMENT: // The size of symbol table entries
|
||||
dynamic_info.symbol_table_entries_size = dyn->d_un.d_val;
|
||||
if (dynamic_info.symbol_table_entries_size != 0x18) {
|
||||
LOG_WARNING(Core_Linker, "DT_SCE_SYMENT is NOT 0x18 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_DEBUG:
|
||||
dynamic_info.debug = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_TEXTREL:
|
||||
dynamic_info.textrel = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_FLAGS:
|
||||
dynamic_info.flags = dyn->d_un.d_val;
|
||||
// This value should always be DF_TEXTREL (0x04)
|
||||
if (dynamic_info.flags != 0x04) {
|
||||
LOG_WARNING(Core_Linker, "DT_FLAGS is NOT 0x04 should check!");
|
||||
}
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
// Offset of the library string in the string table to be linked in.
|
||||
// In theory this should already be filled from about just make a test case
|
||||
if (dynamic_info.str_table) {
|
||||
dynamic_info.needed.push_back(dynamic_info.str_table + dyn->d_un.d_val);
|
||||
} else {
|
||||
LOG_ERROR(Core_Linker, "DT_NEEDED str table is not loaded should check!");
|
||||
}
|
||||
break;
|
||||
case DT_SCE_NEEDED_MODULE: {
|
||||
ModuleInfo& info = dynamic_info.import_modules.emplace_back();
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
break;
|
||||
}
|
||||
case DT_SCE_IMPORT_LIB: {
|
||||
LibraryInfo& info = dynamic_info.import_libs.emplace_back();
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
break;
|
||||
}
|
||||
case DT_SCE_FINGERPRINT:
|
||||
// The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for
|
||||
// the given app. How exactly this is generated isn't known, however it is not necessary
|
||||
// to have a valid fingerprint. While an invalid fingerprint will cause a warning to be
|
||||
// printed to the kernel log, the ELF will still load and run.
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_IMPORT_LIB_ATTR:
|
||||
// The upper 32-bits should contain the module index multiplied by 0x10000. The lower
|
||||
// 32-bits should be a constant 0x9.
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ......: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_ORIGINAL_FILENAME:
|
||||
dynamic_info.filename = dynamic_info.str_table + dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_SCE_MODULE_INFO: {
|
||||
ModuleInfo& info = dynamic_info.export_modules.emplace_back();
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
break;
|
||||
};
|
||||
case DT_SCE_MODULE_ATTR:
|
||||
LOG_INFO(Core_Linker, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}",
|
||||
dyn->d_un.d_val);
|
||||
break;
|
||||
case DT_SCE_EXPORT_LIB: {
|
||||
LibraryInfo& info = dynamic_info.export_libs.emplace_back();
|
||||
info.value = dyn->d_un.d_val;
|
||||
info.name = dynamic_info.str_table + info.name_offset;
|
||||
info.enc_id = EncodeId(info.id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "unsupported dynamic tag ..........: {:#018x}", dyn->d_tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Module::LoadSymbols() {
|
||||
const auto symbol_database = [this](Loader::SymbolsResolver& symbol, bool export_func) {
|
||||
if (!dynamic_info.symbol_table || !dynamic_info.str_table ||
|
||||
dynamic_info.symbol_table_total_size == 0) {
|
||||
LOG_INFO(Core_Linker, "Symbol table not found!");
|
||||
return;
|
||||
}
|
||||
for (auto* sym = dynamic_info.symbol_table;
|
||||
reinterpret_cast<u8*>(sym) < reinterpret_cast<u8*>(dynamic_info.symbol_table) +
|
||||
dynamic_info.symbol_table_total_size;
|
||||
sym++) {
|
||||
const u8 bind = sym->GetBind();
|
||||
const u8 type = sym->GetType();
|
||||
const u8 visibility = sym->GetVisibility();
|
||||
const auto id = std::string(dynamic_info.str_table + sym->st_name);
|
||||
const auto ids = Common::SplitString(id, '#');
|
||||
if (ids.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* library = FindLibrary(ids[1]);
|
||||
const auto* module = FindModule(ids[2]);
|
||||
ASSERT_MSG(library && module, "Unable to find library and module");
|
||||
if ((bind != STB_GLOBAL && bind != STB_WEAK) ||
|
||||
(type != STT_FUN && type != STT_OBJECT) || export_func != (sym->st_value != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto aeronid = AeroLib::FindByNid(ids.at(0).c_str());
|
||||
const auto nid_name = aeronid ? aeronid->name : "UNK";
|
||||
LOG_INFO(Core_Linker, "NidName {}", nid_name);
|
||||
|
||||
Loader::SymbolResolver sym_r{};
|
||||
sym_r.name = ids.at(0);
|
||||
sym_r.nidName = nid_name;
|
||||
sym_r.library = library->name;
|
||||
sym_r.library_version = library->version;
|
||||
sym_r.module = module->name;
|
||||
sym_r.module_version_major = module->version_major;
|
||||
sym_r.module_version_minor = module->version_minor;
|
||||
switch (type) {
|
||||
case STT_NOTYPE:
|
||||
sym_r.type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
case STT_FUN:
|
||||
sym_r.type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
sym_r.type = Loader::SymbolType::Object;
|
||||
break;
|
||||
default:
|
||||
sym_r.type = Loader::SymbolType::Unknown;
|
||||
break;
|
||||
}
|
||||
const VAddr sym_addr = export_func ? sym->st_value + base_virtual_addr : 0;
|
||||
symbol.AddSymbol(sym_r, sym_addr);
|
||||
}
|
||||
};
|
||||
symbol_database(export_sym, true);
|
||||
symbol_database(import_sym, false);
|
||||
}
|
||||
|
||||
const ModuleInfo* Module::FindModule(std::string_view id) {
|
||||
const auto& import_modules = dynamic_info.import_modules;
|
||||
for (u32 i = 0; const auto& mod : import_modules) {
|
||||
if (mod.enc_id == id) {
|
||||
return &import_modules[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
const auto& export_modules = dynamic_info.export_modules;
|
||||
for (u32 i = 0; const auto& mod : export_modules) {
|
||||
if (mod.enc_id == id) {
|
||||
return &export_modules[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const LibraryInfo* Module::FindLibrary(std::string_view id) {
|
||||
const auto& import_libs = dynamic_info.import_libs;
|
||||
for (u32 i = 0; const auto& lib : import_libs) {
|
||||
if (lib.enc_id == id) {
|
||||
return &import_libs[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
const auto& export_libs = dynamic_info.export_libs;
|
||||
for (u32 i = 0; const auto& lib : export_libs) {
|
||||
if (lib.enc_id == id) {
|
||||
return &export_libs[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Core
|
|
@ -0,0 +1,183 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
struct ModuleInfo {
|
||||
bool operator==(const ModuleInfo& other) const {
|
||||
return version_major == other.version_major && version_minor == other.version_minor &&
|
||||
name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u8 version_minor;
|
||||
u8 version_major;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct LibraryInfo {
|
||||
bool operator==(const LibraryInfo& other) const {
|
||||
return version == other.version && name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u16 version;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct ThreadLocalImage {
|
||||
u64 align;
|
||||
u64 image_size;
|
||||
u64 offset;
|
||||
u32 modid;
|
||||
VAddr image_virtual_addr;
|
||||
u64 init_image_size;
|
||||
};
|
||||
|
||||
struct DynamicModuleInfo {
|
||||
void* hash_table = nullptr;
|
||||
u64 hash_table_size = 0;
|
||||
|
||||
char* str_table = nullptr;
|
||||
u64 str_table_size = 0;
|
||||
|
||||
elf_symbol* symbol_table = nullptr;
|
||||
u64 symbol_table_total_size = 0;
|
||||
u64 symbol_table_entries_size = 0;
|
||||
|
||||
u64 init_virtual_addr = 0;
|
||||
u64 fini_virtual_addr = 0;
|
||||
u64 pltgot_virtual_addr = 0;
|
||||
u64 init_array_virtual_addr = 0;
|
||||
u64 fini_array_virtual_addr = 0;
|
||||
u64 preinit_array_virtual_addr = 0;
|
||||
u64 init_array_size = 0;
|
||||
u64 fini_array_size = 0;
|
||||
u64 preinit_array_size = 0;
|
||||
|
||||
elf_relocation* jmp_relocation_table = nullptr;
|
||||
u64 jmp_relocation_table_size = 0;
|
||||
s64 jmp_relocation_type = 0;
|
||||
|
||||
elf_relocation* relocation_table = nullptr;
|
||||
u64 relocation_table_size = 0;
|
||||
u64 relocation_table_entries_size = 0;
|
||||
|
||||
u64 debug = 0;
|
||||
u64 textrel = 0;
|
||||
u64 flags = 0;
|
||||
|
||||
std::vector<const char*> needed;
|
||||
std::vector<ModuleInfo> import_modules;
|
||||
std::vector<ModuleInfo> export_modules;
|
||||
std::vector<LibraryInfo> import_libs;
|
||||
std::vector<LibraryInfo> export_libs;
|
||||
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
using ModuleFunc = int (*)(size_t, const void*);
|
||||
|
||||
class Module {
|
||||
public:
|
||||
explicit Module(const std::filesystem::path& file);
|
||||
~Module();
|
||||
|
||||
VAddr GetBaseAddress() const noexcept {
|
||||
return base_virtual_addr;
|
||||
}
|
||||
|
||||
VAddr GetEntryAddress() const noexcept {
|
||||
return base_virtual_addr + elf.GetElfEntry();
|
||||
}
|
||||
|
||||
bool IsValid() const noexcept {
|
||||
return base_virtual_addr != 0;
|
||||
}
|
||||
|
||||
bool IsSharedLib() const noexcept {
|
||||
return elf.IsSharedLib();
|
||||
}
|
||||
|
||||
void* FindByName(std::string_view name) {
|
||||
const auto symbols = export_sym.GetSymbols();
|
||||
const auto it = std::ranges::find(symbols, name, &Loader::SymbolRecord::nid_name);
|
||||
if (it != symbols.end()) {
|
||||
return reinterpret_cast<void*>(it->virtual_address);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T = VAddr>
|
||||
const T GetProcParam() const noexcept {
|
||||
return reinterpret_cast<T>(proc_param_virtual_addr);
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetImportModules() const {
|
||||
return dynamic_info.import_modules;
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetExportModules() const {
|
||||
return dynamic_info.export_modules;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> GetImportLibs() const {
|
||||
return dynamic_info.import_libs;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> GetExportLibs() const {
|
||||
return dynamic_info.export_libs;
|
||||
}
|
||||
|
||||
void ForEachRelocation(auto&& func) {
|
||||
for (u32 i = 0; i < dynamic_info.relocation_table_size / sizeof(elf_relocation); i++) {
|
||||
func(&dynamic_info.relocation_table[i], false);
|
||||
}
|
||||
for (u32 i = 0; i < dynamic_info.jmp_relocation_table_size / sizeof(elf_relocation); i++) {
|
||||
func(&dynamic_info.jmp_relocation_table[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
void Start(size_t args, const void* argp, ModuleFunc func);
|
||||
void LoadModuleToMemory();
|
||||
void LoadDynamicInfo();
|
||||
void LoadSymbols();
|
||||
|
||||
const ModuleInfo* FindModule(std::string_view id);
|
||||
const LibraryInfo* FindLibrary(std::string_view id);
|
||||
|
||||
public:
|
||||
std::filesystem::path file;
|
||||
Loader::Elf elf;
|
||||
u64 aligned_base_size{};
|
||||
VAddr base_virtual_addr{};
|
||||
VAddr proc_param_virtual_addr{};
|
||||
DynamicModuleInfo dynamic_info{};
|
||||
std::vector<u8> m_dynamic;
|
||||
std::vector<u8> m_dynamic_data;
|
||||
Loader::SymbolsResolver export_sym;
|
||||
Loader::SymbolsResolver import_sym;
|
||||
ThreadLocalImage tls{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
|
@ -44,13 +44,15 @@ constexpr static TLSPattern TlsPatterns[] = {
|
|||
#ifdef _WIN32
|
||||
static DWORD slot = 0;
|
||||
|
||||
void SetTLSStorage(u64 image_address) {
|
||||
// Guest apps will use both positive and negative offsets to the TLS pointer.
|
||||
// User data at probably in negative offsets, while pthread data at positive offset.
|
||||
const BOOL result = TlsSetValue(slot, reinterpret_cast<LPVOID>(image_address));
|
||||
void SetTcbBase(void* image_address) {
|
||||
const BOOL result = TlsSetValue(slot, image_address);
|
||||
ASSERT(result != 0);
|
||||
}
|
||||
|
||||
Tcb* GetTcbBase() {
|
||||
return reinterpret_cast<Tcb*>(TlsGetValue(slot));
|
||||
}
|
||||
|
||||
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
|
@ -81,6 +83,7 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
|||
std::memcpy(&offset, code + tls_pattern.pattern_size, sizeof(u64));
|
||||
LOG_INFO(Core_Linker, "PATTERN64 FOUND at {}, reg: {} offset: {:#x}",
|
||||
fmt::ptr(code), tls_pattern.target_reg, offset);
|
||||
continue;
|
||||
}
|
||||
ASSERT(offset == 0);
|
||||
|
||||
|
|
|
@ -11,10 +11,28 @@ class CodeGenerator;
|
|||
|
||||
namespace Core {
|
||||
|
||||
/// Sets the data pointer that contains the TLS image.
|
||||
void SetTLSStorage(u64 image_address);
|
||||
union DtvEntry {
|
||||
size_t counter;
|
||||
void* pointer;
|
||||
};
|
||||
|
||||
struct Tcb {
|
||||
Tcb* tcb_self;
|
||||
DtvEntry* tcb_dtv;
|
||||
void* tcb_thread;
|
||||
};
|
||||
|
||||
/// Sets the data pointer to the TCB block.
|
||||
void SetTcbBase(void* image_address);
|
||||
|
||||
/// Retrieves Tcb structure for the calling thread.
|
||||
Tcb* GetTcbBase();
|
||||
|
||||
/// Patches any instructions that access guest TLS to use provided storage.
|
||||
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c);
|
||||
|
||||
class ThreadLocalStorage {
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
|
11
src/main.cpp
11
src/main.cpp
|
@ -76,10 +76,10 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
auto linker = Common::Singleton<Core::Linker>::Instance();
|
||||
Libraries::InitHLELibs(&linker->getHLESymbols());
|
||||
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||
linker->LoadModule(path);
|
||||
|
||||
// check if we have system modules to load
|
||||
// Check if we have system modules to load
|
||||
const auto& sys_module_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sys_module_path)) {
|
||||
if (entry.path().filename() == "libSceNgs2.sprx") {
|
||||
|
@ -87,6 +87,7 @@ int main(int argc, char* argv[]) {
|
|||
linker->LoadModule(entry.path().string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is a libc.prx in sce_module folder
|
||||
bool found = false;
|
||||
if (Config::isLleLibc()) {
|
||||
|
@ -94,7 +95,8 @@ int main(int argc, char* argv[]) {
|
|||
if (std::filesystem::is_directory(sce_module_folder)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) {
|
||||
if (entry.path().filename() == "libc.prx" ||
|
||||
entry.path().filename() == "libSceFios2.prx") {
|
||||
entry.path().filename() == "libSceFios2.prx" ||
|
||||
entry.path().filename() == "libSceNpToolkit.prx") {
|
||||
found = true;
|
||||
LOG_INFO(Loader, "Loading {}", entry.path().string().c_str());
|
||||
linker->LoadModule(entry.path().string().c_str());
|
||||
|
@ -103,8 +105,9 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
}
|
||||
if (!found) {
|
||||
Libraries::LibC::libcSymbolsRegister(&linker->getHLESymbols());
|
||||
Libraries::LibC::libcSymbolsRegister(&linker->GetHLESymbols());
|
||||
}
|
||||
|
||||
std::thread mainthread([linker]() { linker->Execute(); });
|
||||
Discord::RPC discordRPC;
|
||||
discordRPC.init();
|
||||
|
|
|
@ -95,9 +95,14 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp) {
|
|||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::FragCoord:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
case IR::Attribute::FragCoord: {
|
||||
const Id coord = ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.frag_coord, ctx.ConstU32(comp)));
|
||||
if (comp == 3) {
|
||||
return ctx.OpFDiv(ctx.F32[1], ctx.ConstF32(1.f), coord);
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Read attribute {}", attr);
|
||||
}
|
||||
|
|
|
@ -55,26 +55,48 @@ void Translator::S_ANDN2_B64(const GcnInst& inst) {
|
|||
const IR::U1 src0{get_src(inst.src[0])};
|
||||
const IR::U1 src1{get_src(inst.src[1])};
|
||||
const IR::U1 result{ir.LogicalAnd(src0, ir.LogicalNot(src1))};
|
||||
SetDst(inst.dst[0], result);
|
||||
ir.SetScc(result);
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ExecLo:
|
||||
ir.SetExec(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) {
|
||||
// This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs)
|
||||
// However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination
|
||||
// SGPR we have a special IR opcode for SPGRs that act as thread masks.
|
||||
ASSERT(inst.src[0].field == OperandField::VccLo);
|
||||
const IR::U1 exec{ir.GetExec()};
|
||||
const IR::U1 vcc{ir.GetVcc()};
|
||||
|
||||
// Mark destination SPGR as an EXEC context. This means we will use 1-bit
|
||||
// IR instruction whenever it's loaded.
|
||||
ASSERT(inst.dst[0].field == OperandField::ScalarGPR);
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::ScalarGPR: {
|
||||
const u32 reg = inst.dst[0].code;
|
||||
exec_contexts[reg] = true;
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(reg), exec);
|
||||
break;
|
||||
}
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(exec);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Update EXEC.
|
||||
ASSERT(inst.src[0].field == OperandField::VccLo);
|
||||
ir.SetExec(ir.LogicalAnd(exec, ir.GetVcc()));
|
||||
ir.SetExec(ir.LogicalAnd(exec, vcc));
|
||||
}
|
||||
|
||||
void Translator::S_MOV_B64(const GcnInst& inst) {
|
||||
|
@ -114,9 +136,17 @@ void Translator::S_OR_B64(bool negate, const GcnInst& inst) {
|
|||
if (negate) {
|
||||
result = ir.LogicalNot(result);
|
||||
}
|
||||
ASSERT(inst.dst[0].field == OperandField::VccLo);
|
||||
ir.SetVcc(result);
|
||||
ir.SetScc(result);
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_AND_B64(const GcnInst& inst) {
|
||||
|
@ -135,9 +165,17 @@ void Translator::S_AND_B64(const GcnInst& inst) {
|
|||
const IR::U1 src0{get_src(inst.src[0])};
|
||||
const IR::U1 src1{get_src(inst.src[1])};
|
||||
const IR::U1 result = ir.LogicalAnd(src0, src1);
|
||||
ASSERT(inst.dst[0].field == OperandField::VccLo);
|
||||
ir.SetVcc(result);
|
||||
ir.SetScc(result);
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_ADD_I32(const GcnInst& inst) {
|
||||
|
@ -169,6 +207,36 @@ void Translator::S_CSELECT_B32(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], IR::U32{ir.Select(ir.GetScc(), src0, src1)});
|
||||
}
|
||||
|
||||
void Translator::S_CSELECT_B64(const GcnInst& inst) {
|
||||
const auto get_src = [&](const InstOperand& operand) {
|
||||
switch (operand.field) {
|
||||
case OperandField::VccLo:
|
||||
return ir.GetVcc();
|
||||
case OperandField::ExecLo:
|
||||
return ir.GetExec();
|
||||
case OperandField::ScalarGPR:
|
||||
return ir.GetThreadBitScalarReg(IR::ScalarReg(operand.code));
|
||||
case OperandField::ConstZero:
|
||||
return ir.Imm1(false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
const IR::U1 src0{get_src(inst.src[0])};
|
||||
const IR::U1 src1{get_src(inst.src[1])};
|
||||
const IR::U1 result{ir.Select(ir.GetScc(), src0, src1)};
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_BFE_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
|
@ -179,4 +247,12 @@ void Translator::S_BFE_U32(const GcnInst& inst) {
|
|||
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
|
||||
}
|
||||
|
||||
void Translator::S_LSHL_B32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)));
|
||||
SetDst(inst.dst[0], result);
|
||||
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -5,30 +5,15 @@
|
|||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Load(IR::IREmitter& ir, int num_dwords, const IR::Value& handle, IR::ScalarReg dst_reg,
|
||||
const IR::U32U64& address) {
|
||||
for (u32 i = 0; i < num_dwords; i++) {
|
||||
if (handle.IsEmpty()) {
|
||||
ir.SetScalarReg(dst_reg++, ir.ReadConst(address, ir.Imm32(i)));
|
||||
} else {
|
||||
const IR::U32 index = ir.IAdd(address, ir.Imm32(i));
|
||||
ir.SetScalarReg(dst_reg++, ir.ReadConstBuffer(handle, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
||||
const auto& smrd = inst.control.smrd;
|
||||
ASSERT_MSG(smrd.imm, "Bindless texture loads unsupported");
|
||||
const IR::ScalarReg sbase{inst.src[0].code * 2};
|
||||
const IR::U32 offset =
|
||||
smrd.imm ? ir.Imm32(smrd.offset * 4)
|
||||
: IR::U32{ir.ShiftLeftLogical(ir.GetScalarReg(IR::ScalarReg(smrd.offset)),
|
||||
ir.Imm32(2))};
|
||||
const IR::U64 base =
|
||||
ir.PackUint2x32(ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1)));
|
||||
const IR::U64 address = ir.IAdd(base, offset);
|
||||
const IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||
Load(ir, num_dwords, {}, dst_reg, address);
|
||||
const IR::Value base = ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1));
|
||||
IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||
for (u32 i = 0; i < num_dwords; i++) {
|
||||
ir.SetScalarReg(dst_reg++, ir.ReadConst(base, ir.Imm32(smrd.offset + i)));
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
||||
|
@ -37,8 +22,11 @@ void Translator::S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
|||
const IR::U32 dword_offset =
|
||||
smrd.imm ? ir.Imm32(smrd.offset) : ir.GetScalarReg(IR::ScalarReg(smrd.offset));
|
||||
const IR::Value vsharp = ir.GetScalarReg(sbase);
|
||||
const IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||
Load(ir, num_dwords, vsharp, dst_reg, dword_offset);
|
||||
IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||
for (u32 i = 0; i < num_dwords; i++) {
|
||||
const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
|
||||
ir.SetScalarReg(dst_reg++, ir.ReadConstBuffer(vsharp, index));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -128,7 +128,11 @@ IR::U1U32F32 Translator::GetSrc(const InstOperand& operand, bool force_flt) {
|
|||
value = ir.GetExec();
|
||||
break;
|
||||
case OperandField::VccLo:
|
||||
if (force_flt) {
|
||||
value = ir.BitCast<IR::F32>(ir.GetVccLo());
|
||||
} else {
|
||||
value = ir.GetVccLo();
|
||||
}
|
||||
break;
|
||||
case OperandField::VccHi:
|
||||
value = ir.GetVccHi();
|
||||
|
@ -252,6 +256,12 @@ void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info)
|
|||
break;
|
||||
case Opcode::S_WAITCNT:
|
||||
break;
|
||||
case Opcode::S_LOAD_DWORDX4:
|
||||
translator.S_LOAD_DWORD(4, inst);
|
||||
break;
|
||||
case Opcode::S_LOAD_DWORDX8:
|
||||
translator.S_LOAD_DWORD(8, inst);
|
||||
break;
|
||||
case Opcode::S_BUFFER_LOAD_DWORD:
|
||||
translator.S_BUFFER_LOAD_DWORD(1, inst);
|
||||
break;
|
||||
|
@ -352,9 +362,18 @@ void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info)
|
|||
case Opcode::S_CMP_LG_U32:
|
||||
translator.S_CMP(ConditionOp::LG, false, inst);
|
||||
break;
|
||||
case Opcode::S_CMP_LG_I32:
|
||||
translator.S_CMP(ConditionOp::LG, true, inst);
|
||||
break;
|
||||
case Opcode::S_CMP_EQ_I32:
|
||||
translator.S_CMP(ConditionOp::EQ, true, inst);
|
||||
break;
|
||||
case Opcode::S_CMP_EQ_U32:
|
||||
translator.S_CMP(ConditionOp::EQ, false, inst);
|
||||
break;
|
||||
case Opcode::S_LSHL_B32:
|
||||
translator.S_LSHL_B32(inst);
|
||||
break;
|
||||
case Opcode::V_CNDMASK_B32:
|
||||
translator.V_CNDMASK_B32(inst);
|
||||
break;
|
||||
|
@ -505,13 +524,21 @@ void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info)
|
|||
case Opcode::S_CSELECT_B32:
|
||||
translator.S_CSELECT_B32(inst);
|
||||
break;
|
||||
case Opcode::S_CSELECT_B64:
|
||||
translator.S_CSELECT_B64(inst);
|
||||
break;
|
||||
case Opcode::S_BFE_U32:
|
||||
translator.S_BFE_U32(inst);
|
||||
break;
|
||||
case Opcode::V_RNDNE_F32:
|
||||
translator.V_RNDNE_F32(inst);
|
||||
break;
|
||||
case Opcode::S_NOP:
|
||||
case Opcode::S_CBRANCH_EXECZ:
|
||||
case Opcode::S_CBRANCH_SCC0:
|
||||
case Opcode::S_CBRANCH_SCC1:
|
||||
case Opcode::S_CBRANCH_VCCNZ:
|
||||
case Opcode::S_CBRANCH_VCCZ:
|
||||
case Opcode::S_BRANCH:
|
||||
case Opcode::S_WQM_B64:
|
||||
case Opcode::V_INTERP_P1_F32:
|
||||
|
@ -519,7 +546,7 @@ void Translate(IR::Block* block, std::span<const GcnInst> inst_list, Info& info)
|
|||
break;
|
||||
default:
|
||||
const u32 opcode = u32(inst.opcode);
|
||||
UNREACHABLE_MSG("Unknown opcode {}", opcode);
|
||||
throw NotImplementedException("Opcode {}", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ public:
|
|||
void S_AND_B32(const GcnInst& inst);
|
||||
void S_LSHR_B32(const GcnInst& inst);
|
||||
void S_CSELECT_B32(const GcnInst& inst);
|
||||
void S_CSELECT_B64(const GcnInst& inst);
|
||||
void S_BFE_U32(const GcnInst& inst);
|
||||
void S_LSHL_B32(const GcnInst& inst);
|
||||
|
||||
// Scalar Memory
|
||||
void S_LOAD_DWORD(int num_dwords, const GcnInst& inst);
|
||||
|
@ -101,6 +103,7 @@ public:
|
|||
void V_LSHR_B32(const GcnInst& inst);
|
||||
void V_ASHRREV_I32(const GcnInst& inst);
|
||||
void V_MAD_U32_U24(const GcnInst& inst);
|
||||
void V_RNDNE_F32(const GcnInst& inst);
|
||||
|
||||
// Vector Memory
|
||||
void BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, const GcnInst& inst);
|
||||
|
|
|
@ -33,7 +33,7 @@ void Translator::V_CNDMASK_B32(const GcnInst& inst) {
|
|||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
const IR::ScalarReg flag_reg{inst.src[2].code};
|
||||
const IR::U1 flag = inst.src[2].field == OperandField::ScalarGPR
|
||||
? ir.INotEqual(ir.GetScalarReg(flag_reg), ir.Imm32(0U))
|
||||
? ir.GetThreadBitScalarReg(flag_reg)
|
||||
: ir.GetVcc();
|
||||
|
||||
// We can treat the instruction as integer most of the time, but when a source is
|
||||
|
@ -85,21 +85,21 @@ void Translator::V_CVT_F32_U32(const GcnInst& inst) {
|
|||
}
|
||||
|
||||
void Translator::V_MAD_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0])};
|
||||
const IR::F32 src1{GetSrc(inst.src[1])};
|
||||
const IR::F32 src2{GetSrc(inst.src[2])};
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::F32 src1{GetSrc(inst.src[1], true)};
|
||||
const IR::F32 src2{GetSrc(inst.src[2], true)};
|
||||
SetDst(inst.dst[0], ir.FPFma(src0, src1, src2));
|
||||
}
|
||||
|
||||
void Translator::V_FRACT_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0])};
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
ir.SetVectorReg(dst_reg, ir.Fract(src0));
|
||||
}
|
||||
|
||||
void Translator::V_ADD_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0])};
|
||||
const IR::F32 src1{GetSrc(inst.src[1])};
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::F32 src1{GetSrc(inst.src[1], true)};
|
||||
SetDst(inst.dst[0], ir.FPAdd(src0, src1));
|
||||
}
|
||||
|
||||
|
@ -114,14 +114,14 @@ void Translator::V_CVT_OFF_F32_I4(const GcnInst& inst) {
|
|||
|
||||
void Translator::V_MED3_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::F32 src1{GetSrc(inst.src[1])};
|
||||
const IR::F32 src2{GetSrc(inst.src[2])};
|
||||
const IR::F32 src1{GetSrc(inst.src[1], true)};
|
||||
const IR::F32 src2{GetSrc(inst.src[2], true)};
|
||||
const IR::F32 mmx = ir.FPMin(ir.FPMax(src0, src1), src2);
|
||||
SetDst(inst.dst[0], ir.FPMax(ir.FPMin(src0, src1), mmx));
|
||||
}
|
||||
|
||||
void Translator::V_FLOOR_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0])};
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
ir.SetVectorReg(dst_reg, ir.FPFloor(src0));
|
||||
}
|
||||
|
@ -167,7 +167,17 @@ void Translator::V_CMP_F32(ConditionOp op, const GcnInst& inst) {
|
|||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
|
||||
switch (inst.dst[1].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::V_MAX_F32(const GcnInst& inst) {
|
||||
|
@ -357,4 +367,9 @@ void Translator::V_MAD_U32_U24(const GcnInst& inst) {
|
|||
V_MAD_I32_I24(inst);
|
||||
}
|
||||
|
||||
void Translator::V_RNDNE_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
SetDst(inst.dst[0], ir.FPRoundEven(src0));
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -31,7 +31,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
|
|||
|
||||
void Translator::IMAGE_SAMPLE(const GcnInst& inst) {
|
||||
const auto& mimg = inst.control.mimg;
|
||||
ASSERT(!mimg.da);
|
||||
//ASSERT(!mimg.da);
|
||||
|
||||
IR::VectorReg addr_reg{inst.src[0].code};
|
||||
IR::VectorReg dest_reg{inst.dst[0].code};
|
||||
|
|
|
@ -273,8 +273,8 @@ void IREmitter::WriteShared(int bit_size, const Value& value, const U32& offset)
|
|||
}*/
|
||||
}
|
||||
|
||||
U32 IREmitter::ReadConst(const U64& address, const U32& offset) {
|
||||
return Inst<U32>(Opcode::ReadConst, address, offset);
|
||||
U32 IREmitter::ReadConst(const Value& base, const U32& offset) {
|
||||
return Inst<U32>(Opcode::ReadConst, base, offset);
|
||||
}
|
||||
|
||||
F32 IREmitter::ReadConstBuffer(const Value& handle, const U32& index) {
|
||||
|
@ -774,6 +774,7 @@ U1 IREmitter::FPLessThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered
|
|||
}
|
||||
|
||||
U1 IREmitter::FPGreaterThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered) {
|
||||
ASSERT(lhs.Type() == rhs.Type());
|
||||
if (lhs.Type() != rhs.Type()) {
|
||||
throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type());
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
[[nodiscard]] U32U64 ReadShared(int bit_size, bool is_signed, const U32& offset);
|
||||
void WriteShared(int bit_size, const Value& value, const U32& offset);
|
||||
|
||||
[[nodiscard]] U32 ReadConst(const U64& address, const U32& offset);
|
||||
[[nodiscard]] U32 ReadConst(const Value& base, const U32& offset);
|
||||
[[nodiscard]] F32 ReadConstBuffer(const Value& handle, const U32& index);
|
||||
|
||||
[[nodiscard]] Value LoadBuffer(int num_dwords, const Value& handle, const Value& address,
|
||||
|
|
|
@ -15,7 +15,7 @@ OPCODE(Epilogue, Void,
|
|||
OPCODE(Discard, Void, )
|
||||
|
||||
// Constant memory operations
|
||||
OPCODE(ReadConst, U32, U64, U32, )
|
||||
OPCODE(ReadConst, U32, U32x2, U32, )
|
||||
OPCODE(ReadConstBuffer, F32, Opaque, U32, )
|
||||
OPCODE(ReadConstBufferU32, U32, Opaque, U32, )
|
||||
|
||||
|
|
|
@ -157,16 +157,15 @@ SharpLocation TrackSharp(const IR::Inst* inst) {
|
|||
ASSERT_MSG(inst->GetOpcode() == IR::Opcode::ReadConst, "Sharp load not from constant memory");
|
||||
|
||||
// Retrieve offset from base.
|
||||
IR::Inst* addr = inst->Arg(0).InstRecursive();
|
||||
u32 dword_offset = addr->Arg(1).U32();
|
||||
addr = addr->Arg(0).InstRecursive();
|
||||
ASSERT_MSG(addr->Arg(1).IsImmediate(), "Bindless not supported");
|
||||
dword_offset += addr->Arg(1).U32() >> 2;
|
||||
const u32 dword_offset = inst->Arg(1).U32();
|
||||
const IR::Inst* spgpr_base = inst->Arg(0).InstRecursive();
|
||||
|
||||
// Retrieve SGPR that holds sbase
|
||||
inst = addr->Arg(0).InstRecursive()->Arg(0).InstRecursive();
|
||||
ASSERT_MSG(inst->GetOpcode() == IR::Opcode::GetUserData, "Nested resource loads not supported");
|
||||
const IR::ScalarReg base = inst->Arg(0).ScalarReg();
|
||||
// Retrieve SGPR pair that holds sbase
|
||||
const IR::Inst* sbase0 = spgpr_base->Arg(0).InstRecursive();
|
||||
const IR::Inst* sbase1 = spgpr_base->Arg(1).InstRecursive();
|
||||
ASSERT_MSG(sbase0->GetOpcode() == IR::Opcode::GetUserData &&
|
||||
sbase1->GetOpcode() == IR::Opcode::GetUserData, "Nested resource loads not supported");
|
||||
const IR::ScalarReg base = sbase0->Arg(0).ScalarReg();
|
||||
|
||||
// Return retrieved location.
|
||||
return SharpLocation{
|
||||
|
@ -186,7 +185,7 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info,
|
|||
.stride = buffer.GetStride(),
|
||||
.num_records = u32(buffer.num_records),
|
||||
.used_types = BufferDataType(inst),
|
||||
.is_storage = true || IsBufferStore(inst),
|
||||
.is_storage = IsBufferStore(inst),
|
||||
});
|
||||
const auto inst_info = inst.Flags<IR::BufferInstInfo>();
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
|
@ -206,8 +205,8 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info,
|
|||
const u32 dword_offset = inst_info.inst_offset.Value() / sizeof(u32);
|
||||
IR::U32 address = ir.Imm32(dword_offset);
|
||||
if (inst_info.index_enable && inst_info.offset_enable) {
|
||||
const IR::U32 offset{ir.CompositeExtract(inst.Arg(1), 0)};
|
||||
const IR::U32 index{ir.CompositeExtract(inst.Arg(1), 1)};
|
||||
const IR::U32 offset{ir.CompositeExtract(inst.Arg(1), 1)};
|
||||
const IR::U32 index{ir.CompositeExtract(inst.Arg(1), 0)};
|
||||
address = ir.IAdd(ir.IMul(index, ir.Imm32(dword_stride)), address);
|
||||
address = ir.IAdd(address, ir.ShiftRightLogical(offset, ir.Imm32(2)));
|
||||
} else if (inst_info.index_enable) {
|
||||
|
|
|
@ -252,6 +252,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::DrawIndexOffset2: {
|
||||
const auto* draw_index_off = reinterpret_cast<const PM4CmdDrawIndexOffset2*>(header);
|
||||
regs.max_index_size = draw_index_off->max_size;
|
||||
regs.num_indices = draw_index_off->index_count;
|
||||
regs.draw_initiator = draw_index_off->draw_initiator;
|
||||
if (rasterizer) {
|
||||
rasterizer->Draw(true, draw_index_off->index_offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::DrawIndexAuto: {
|
||||
const auto* draw_index = reinterpret_cast<const PM4CmdDrawIndexAuto*>(header);
|
||||
regs.num_indices = draw_index->index_count;
|
||||
|
@ -272,6 +282,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::NumInstances: {
|
||||
const auto* num_instances = reinterpret_cast<const PM4CmdDrawNumInstances*>(header);
|
||||
regs.num_instances.num_instances = num_instances->num_instances;
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::IndexBase: {
|
||||
const auto* index_base = reinterpret_cast<const PM4CmdDrawIndexBase*>(header);
|
||||
regs.index_base_address.base_addr_lo = index_base->addr_lo;
|
||||
regs.index_base_address.base_addr_hi.Assign(index_base->addr_hi);
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::EventWrite: {
|
||||
// const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
|
||||
break;
|
||||
|
|
|
@ -548,4 +548,15 @@ struct PM4CmdDispatchDirect {
|
|||
u32 dispatch_initiator; ///< Dispatch Initiator Register
|
||||
};
|
||||
|
||||
struct PM4CmdDrawNumInstances {
|
||||
PM4Type3Header header;
|
||||
u32 num_instances;
|
||||
};
|
||||
|
||||
struct PM4CmdDrawIndexBase {
|
||||
PM4Type3Header header;
|
||||
u32 addr_lo;
|
||||
u32 addr_hi;
|
||||
};
|
||||
|
||||
} // namespace AmdGpu
|
||||
|
|
|
@ -14,6 +14,8 @@ vk::StencilOp StencilOp(Liverpool::StencilFunc op) {
|
|||
return vk::StencilOp::eKeep;
|
||||
case Liverpool::StencilFunc::Zero:
|
||||
return vk::StencilOp::eZero;
|
||||
case Liverpool::StencilFunc::ReplaceTest:
|
||||
return vk::StencilOp::eReplace;
|
||||
case Liverpool::StencilFunc::AddClamp:
|
||||
return vk::StencilOp::eIncrementAndClamp;
|
||||
case Liverpool::StencilFunc::SubClamp:
|
||||
|
@ -307,6 +309,12 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu
|
|||
if (data_format == AmdGpu::DataFormat::FormatBc3 && num_format == AmdGpu::NumberFormat::Srgb) {
|
||||
return vk::Format::eBc3SrgbBlock;
|
||||
}
|
||||
if (data_format == AmdGpu::DataFormat::Format16_16_16_16 && num_format == AmdGpu::NumberFormat::Sint) {
|
||||
return vk::Format::eR16G16B16A16Sint;
|
||||
}
|
||||
if (data_format == AmdGpu::DataFormat::FormatBc7 && num_format == AmdGpu::NumberFormat::Srgb) {
|
||||
return vk::Format::eBc7SrgbBlock;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
|
|
@ -81,8 +81,17 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
|
|||
|
||||
ComputePipeline::~ComputePipeline() = default;
|
||||
|
||||
void ComputePipeline::BindResources(Core::MemoryManager* memory,
|
||||
void ComputePipeline::BindResources(Core::MemoryManager* memory, StreamBuffer& staging,
|
||||
VideoCore::TextureCache& texture_cache) const {
|
||||
static constexpr u64 MinUniformAlignment = 64;
|
||||
|
||||
const auto map_staging = [&](auto src, size_t size) {
|
||||
const auto [data, offset, _] = staging.Map(size, MinUniformAlignment);
|
||||
std::memcpy(data, reinterpret_cast<const void*>(src), size);
|
||||
staging.Commit(size);
|
||||
return offset;
|
||||
};
|
||||
|
||||
// Bind resource buffers and textures.
|
||||
boost::container::static_vector<vk::DescriptorBufferInfo, 4> buffer_infos;
|
||||
boost::container::static_vector<vk::DescriptorImageInfo, 8> image_infos;
|
||||
|
@ -94,8 +103,9 @@ void ComputePipeline::BindResources(Core::MemoryManager* memory,
|
|||
const u32 size = vsharp.GetSize();
|
||||
const VAddr addr = vsharp.base_address.Value();
|
||||
texture_cache.OnCpuWrite(addr);
|
||||
const auto [vk_buffer, offset] = memory->GetVulkanBuffer(addr);
|
||||
buffer_infos.emplace_back(vk_buffer, offset, size);
|
||||
const u32 offset = map_staging(addr, size);
|
||||
//const auto [vk_buffer, offset] = memory->GetVulkanBuffer(addr);
|
||||
buffer_infos.emplace_back(staging.Handle(), offset, size);
|
||||
set_writes.push_back({
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
.dstBinding = binding++,
|
||||
|
|
|
@ -31,7 +31,8 @@ public:
|
|||
return *pipeline;
|
||||
}
|
||||
|
||||
void BindResources(Core::MemoryManager* memory, VideoCore::TextureCache& texture_cache) const;
|
||||
void BindResources(Core::MemoryManager* memory, StreamBuffer& staging,
|
||||
VideoCore::TextureCache& texture_cache) const;
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
|
|
|
@ -63,8 +63,8 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pVertexAttributeDescriptions = attributes.data(),
|
||||
};
|
||||
|
||||
ASSERT_MSG(key.prim_type != Liverpool::PrimitiveType::RectList || IsEmbeddedVs(),
|
||||
"Rectangle List primitive type is only supported for embedded VS");
|
||||
// ASSERT_MSG(key.prim_type != Liverpool::PrimitiveType::RectList || IsEmbeddedVs(),
|
||||
// "Rectangle List primitive type is only supported for embedded VS");
|
||||
|
||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
||||
.topology = LiverpoolToVK::PrimitiveType(key.prim_type),
|
||||
|
|
|
@ -160,8 +160,13 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
|||
inst_pool.ReleaseContents();
|
||||
|
||||
// Recompile shader to IR.
|
||||
try {
|
||||
const Shader::Info info = MakeShaderInfo(stage, pgm->user_data, regs);
|
||||
programs[i] = Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info));
|
||||
} catch (const Shader::Exception& e) {
|
||||
LOG_ERROR(Render_Vulkan, "{}", e.what());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// Compile IR to SPIR-V
|
||||
auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding);
|
||||
|
|
|
@ -32,10 +32,10 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_,
|
|||
|
||||
Rasterizer::~Rasterizer() = default;
|
||||
|
||||
void Rasterizer::Draw(bool is_indexed) {
|
||||
void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
const auto& regs = liverpool->regs;
|
||||
const u32 num_indices = SetupIndexBuffer(is_indexed);
|
||||
const u32 num_indices = SetupIndexBuffer(is_indexed, index_offset);
|
||||
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
||||
pipeline->BindResources(memory, vertex_index_buffer, texture_cache);
|
||||
|
||||
|
@ -85,18 +85,16 @@ void Rasterizer::Draw(bool is_indexed) {
|
|||
}
|
||||
|
||||
void Rasterizer::DispatchDirect() {
|
||||
compute_done = true;
|
||||
return;
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
const auto& cs_program = liverpool->regs.cs_program;
|
||||
const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline();
|
||||
pipeline->BindResources(memory, texture_cache);
|
||||
pipeline->BindResources(memory, vertex_index_buffer, texture_cache);
|
||||
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle());
|
||||
cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z);
|
||||
}
|
||||
|
||||
u32 Rasterizer::SetupIndexBuffer(bool& is_indexed) {
|
||||
u32 Rasterizer::SetupIndexBuffer(bool& is_indexed, u32 index_offset) {
|
||||
// Emulate QuadList primitive type with CPU made index buffer.
|
||||
const auto& regs = liverpool->regs;
|
||||
if (liverpool->regs.primitive_type == Liverpool::PrimitiveType::QuadList) {
|
||||
|
@ -132,7 +130,7 @@ u32 Rasterizer::SetupIndexBuffer(bool& is_indexed) {
|
|||
|
||||
// Bind index buffer.
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.bindIndexBuffer(vertex_index_buffer.Handle(), offset, index_type);
|
||||
cmdbuf.bindIndexBuffer(vertex_index_buffer.Handle(), offset + index_offset * index_size, index_type);
|
||||
return regs.num_indices;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,12 @@ public:
|
|||
VideoCore::TextureCache& texture_cache, AmdGpu::Liverpool* liverpool);
|
||||
~Rasterizer();
|
||||
|
||||
void Draw(bool is_indexed);
|
||||
void Draw(bool is_indexed, u32 index_offset = 0);
|
||||
|
||||
void DispatchDirect();
|
||||
|
||||
private:
|
||||
u32 SetupIndexBuffer(bool& is_indexed);
|
||||
u32 SetupIndexBuffer(bool& is_indexed, u32 index_offset);
|
||||
void MapMemory(VAddr addr, size_t size);
|
||||
|
||||
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
||||
|
@ -49,7 +49,6 @@ private:
|
|||
Core::MemoryManager* memory;
|
||||
PipelineCache pipeline_cache;
|
||||
StreamBuffer vertex_index_buffer;
|
||||
bool compute_done{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -92,14 +92,14 @@ StreamBuffer::~StreamBuffer() {
|
|||
|
||||
std::tuple<u8*, u64, bool> StreamBuffer::Map(u64 size, u64 alignment) {
|
||||
if (!is_coherent && type == BufferType::Stream) {
|
||||
size = Common::alignUp(size, instance.NonCoherentAtomSize());
|
||||
size = Common::AlignUp(size, instance.NonCoherentAtomSize());
|
||||
}
|
||||
|
||||
ASSERT(size <= stream_buffer_size);
|
||||
mapped_size = size;
|
||||
|
||||
if (alignment > 0) {
|
||||
offset = Common::alignUp(offset, alignment);
|
||||
offset = Common::AlignUp(offset, alignment);
|
||||
}
|
||||
|
||||
bool invalidate{false};
|
||||
|
@ -124,7 +124,7 @@ std::tuple<u8*, u64, bool> StreamBuffer::Map(u64 size, u64 alignment) {
|
|||
|
||||
void StreamBuffer::Commit(u64 size) {
|
||||
if (!is_coherent && type == BufferType::Stream) {
|
||||
size = Common::alignUp(size, instance.NonCoherentAtomSize());
|
||||
size = Common::AlignUp(size, instance.NonCoherentAtomSize());
|
||||
}
|
||||
|
||||
ASSERT_MSG(size <= mapped_size, "Reserved size {} is too small compared to {}", mapped_size,
|
||||
|
|
|
@ -58,7 +58,7 @@ LONG WINAPI GuestFaultSignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
|
|||
}
|
||||
#endif
|
||||
|
||||
static constexpr u64 StreamBufferSize = 128_MB;
|
||||
static constexpr u64 StreamBufferSize = 512_MB;
|
||||
static constexpr u64 PageShift = 12;
|
||||
|
||||
TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_)
|
||||
|
@ -114,7 +114,7 @@ Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
|
|||
std::unique_lock lock{m_page_table};
|
||||
boost::container::small_vector<ImageId, 2> image_ids;
|
||||
ForEachImageInRegion(cpu_address, info.guest_size_bytes, [&](ImageId image_id, Image& image) {
|
||||
if (image.cpu_addr == cpu_address) {
|
||||
if (image.cpu_addr == cpu_address && image.info.size.width == info.size.width) {
|
||||
image_ids.push_back(image_id);
|
||||
}
|
||||
});
|
||||
|
@ -321,6 +321,10 @@ void TextureCache::UntrackImage(Image& image, ImageId image_id) {
|
|||
void TextureCache::UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta) {
|
||||
std::scoped_lock lk{mutex};
|
||||
const u64 num_pages = ((addr + size - 1) >> PageShift) - (addr >> PageShift) + 1;
|
||||
if (num_pages == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 page_start = addr >> PageShift;
|
||||
const u64 page_end = page_start + num_pages;
|
||||
|
||||
|
|
Loading…
Reference in New Issue