core: Reorganize

This commit is contained in:
GPUCode
2023-11-06 01:11:54 +02:00
parent 89cf4dbfcb
commit 369d92fa56
73 changed files with 724 additions and 572 deletions

View File

@@ -0,0 +1,89 @@
#include "common/debug.h"
#include "core/hle/kernel/objects/event_queue.h"
#include "Lib/Timer.h"
namespace Core::Kernel {
EqueueInternal::~EqueueInternal() = default;
int EqueueInternal::addEvent(const EqueueEvent& event) {
std::scoped_lock lock{m_mutex};
if (m_events.size() > 0) {
BREAKPOINT();
}
// TODO check if event is already exists and return it. Currently we just add in m_events array
m_events.push_back(event);
if (event.isTriggered) {
BREAKPOINT(); // we don't support that either yet
}
return 0;
}
int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
std::unique_lock lock{m_mutex};
u32 timeElapsed = 0;
Lib::Timer t;
t.Start();
for (;;) {
int ret = getTriggeredEvents(ev, num);
if (ret > 0 || (timeElapsed >= micros && micros != 0)) {
return ret;
}
if (micros == 0) {
m_cond.wait(lock);
} else {
m_cond.wait_for(lock, std::chrono::microseconds(micros - timeElapsed));
}
timeElapsed = static_cast<uint32_t>(t.GetTimeSec() * 1000000.0);
}
return 0;
}
bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
std::scoped_lock lock{m_mutex};
if (m_events.size() > 1) {
BREAKPOINT(); // we currently support one event
}
auto& event = m_events[0];
if (event.filter.trigger_event_func != nullptr) {
event.filter.trigger_event_func(&event, trigger_data);
} else {
event.isTriggered = true;
}
m_cond.notify_one();
return true;
}
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
int ret = 0;
if (m_events.size() > 1) {
BREAKPOINT(); // we currently support one event
}
auto& event = m_events[0];
if (event.isTriggered) {
ev[ret++] = event.event;
if (event.filter.reset_event_func != nullptr) {
event.filter.reset_event_func(&event);
}
}
return ret;
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,78 @@
#pragma once
#include <mutex>
#include <string>
#include <vector>
#include "common/types.h"
namespace Core::Kernel {
constexpr s16 EVFILT_READ = -1;
constexpr s16 EVFILT_WRITE = -2;
constexpr s16 EVFILT_AIO = -3; // attached to aio requests
constexpr s16 EVFILT_VNODE = -4; // attached to vnodes
constexpr s16 EVFILT_PROC = -5; // attached to struct proc
constexpr s16 EVFILT_SIGNAL = -6; // attached to struct proc
constexpr s16 EVFILT_TIMER = -7; // timers
constexpr s16 EVFILT_FS = -9; // filesystem events
constexpr s16 EVFILT_LIO = -10; // attached to lio requests
constexpr s16 EVFILT_USER = -11; // User events
constexpr s16 EVFILT_POLLING = -12;
constexpr s16 EVFILT_VIDEO_OUT = -13;
constexpr s16 EVFILT_GRAPHICS_CORE = -14;
constexpr s16 EVFILT_HRTIMER = -15;
constexpr s16 EVFILT_UVD_TRAP = -16;
constexpr s16 EVFILT_VCE_TRAP = -17;
constexpr s16 EVFILT_SDMA_TRAP = -18;
constexpr s16 EVFILT_REG_EV = -19;
constexpr s16 EVFILT_GPU_EXCEPTION = -20;
constexpr s16 EVFILT_GPU_SYSTEM_EXCEPTION = -21;
constexpr s16 EVFILT_GPU_DBGGC_EV = -22;
constexpr s16 EVFILT_SYSCOUNT = 22;
class EqueueInternal;
struct EqueueEvent;
using TriggerFunc = void (*)(EqueueEvent* event, void* trigger_data);
using ResetFunc = void (*)(EqueueEvent* event);
using DeleteFunc = void (*)(EqueueInternal* eq, EqueueEvent* event);
struct SceKernelEvent {
u64 ident = 0; /* identifier for this event */
s16 filter = 0; /* filter for event */
u16 flags = 0;
u32 fflags = 0;
s64 data = 0;
void* udata = nullptr; /* opaque user data identifier */
};
struct Filter {
void* data = nullptr;
TriggerFunc trigger_event_func = nullptr;
ResetFunc reset_event_func = nullptr;
DeleteFunc delete_event_func = nullptr;
};
struct EqueueEvent {
bool isTriggered = false;
SceKernelEvent event;
Filter filter;
};
class EqueueInternal {
public:
EqueueInternal() = default;
virtual ~EqueueInternal();
void setName(const std::string& m_name) { this->m_name = m_name; }
int addEvent(const EqueueEvent& event);
int waitForEvents(SceKernelEvent* ev, int num, u32 micros);
bool triggerEvent(u64 ident, s16 filter, void* trigger_data);
int getTriggeredEvents(SceKernelEvent* ev, int num);
private:
std::string m_name;
std::mutex m_mutex;
std::vector<EqueueEvent> m_events;
std::condition_variable m_cond;
};
} // namespace Core::Kernel

View File

@@ -0,0 +1,68 @@
#include "core/hle/kernel/objects/physical_memory.h"
namespace Core::Kernel {
static u64 AlignUp(u64 pos, u64 align) {
return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos);
}
bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
u64* physAddrOut, int memoryType) {
std::scoped_lock lock{m_mutex};
u64 find_free_pos = 0;
// iterate through allocated blocked and find the next free position
for (const auto& block : m_allocatedBlocks) {
u64 n = block.start_addr + block.size;
if (n > find_free_pos) {
find_free_pos = n;
}
}
// align free position
find_free_pos = 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) {
AllocatedBlock block{};
block.size = len;
block.start_addr = find_free_pos;
block.memoryType = memoryType;
block.gpu_mode = GPU::MemoryMode::NoAccess;
block.map_size = 0;
block.map_virtual_addr = 0;
block.prot = 0;
block.cpu_mode = VirtualMemory::MemoryMode::NoAccess;
m_allocatedBlocks.push_back(block);
*physAddrOut = find_free_pos;
return true;
}
return false;
}
bool PhysicalMemory::Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot,
VirtualMemory::MemoryMode cpu_mode, GPU::MemoryMode gpu_mode) {
std::scoped_lock lock{m_mutex};
for (auto& b : m_allocatedBlocks) {
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size) {
if (b.map_virtual_addr != 0 || b.map_size != 0) {
return false;
}
b.map_virtual_addr = virtual_addr;
b.map_size = len;
b.prot = prot;
b.cpu_mode = cpu_mode;
b.gpu_mode = gpu_mode;
return true;
}
}
return false;
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,36 @@
#pragma once
#include <mutex>
#include <vector>
#include "common/types.h"
#include "core/virtual_memory.h"
#include "core/PS4/GPU/gpu_memory.h"
namespace Core::Kernel {
class PhysicalMemory {
public:
struct AllocatedBlock {
u64 start_addr;
u64 size;
int memoryType;
u64 map_virtual_addr;
u64 map_size;
int prot;
VirtualMemory::MemoryMode cpu_mode;
GPU::MemoryMode gpu_mode;
};
PhysicalMemory() {}
virtual ~PhysicalMemory() {}
public:
bool Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, u64* physAddrOut, int memoryType);
bool Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot, VirtualMemory::MemoryMode cpu_mode, GPU::MemoryMode gpu_mode);
private:
std::vector<AllocatedBlock> m_allocatedBlocks;
std::mutex m_mutex;
};
} // namespace Core::Kernel

View File

@@ -0,0 +1,123 @@
#include "common/debug.h"
#include "core/hle/kernel/ThreadManagement.h"
#include "core/hle/error_codes.h"
namespace Core::Kernel {
thread_local PthreadInternal* g_pthread_self = nullptr;
PThreadCxt* g_pthread_cxt = nullptr;
void Pthread_Init_Self_MainThread() {
g_pthread_self = new PthreadInternal{};
scePthreadAttrInit(&g_pthread_self->attr);
g_pthread_self->pth = pthread_self();
g_pthread_self->name = "Main_Thread";
}
int scePthreadAttrInit(ScePthreadAttr* attr) {
*attr = new PthreadAttrInternal{};
int result = pthread_attr_init(&(*attr)->pth_attr);
(*attr)->affinity = 0x7f;
(*attr)->guard_size = 0x1000;
SceKernelSchedParam param{};
param.sched_priority = 700;
result = (result == 0 ? scePthreadAttrSetinheritsched(attr, 4) : result);
result = (result == 0 ? scePthreadAttrSetschedparam(attr, &param) : result);
result = (result == 0 ? scePthreadAttrSetschedpolicy(attr, SCHED_OTHER) : result);
result = (result == 0 ? scePthreadAttrSetdetachstate(attr, PTHREAD_CREATE_JOINABLE) : result);
switch (result) {
case 0: return SCE_OK;
case ENOMEM: return SCE_KERNEL_ERROR_ENOMEM;
default: return SCE_KERNEL_ERROR_EINVAL;
}
}
int scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate) {
if (attr == nullptr || *attr == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
}
int pstate = PTHREAD_CREATE_JOINABLE;
switch (detachstate) {
case 0: pstate = PTHREAD_CREATE_JOINABLE; break;
case 1: pstate = PTHREAD_CREATE_DETACHED; break;
default: BREAKPOINT(); // unknown state
}
int result = pthread_attr_setdetachstate(&(*attr)->pth_attr, pstate);
(*attr)->detached = (pstate == PTHREAD_CREATE_DETACHED);
if (result == 0) {
return SCE_OK;
}
return SCE_KERNEL_ERROR_EINVAL;
}
int scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched) {
if (attr == nullptr || *attr == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
}
int pinherit_sched = PTHREAD_INHERIT_SCHED;
switch (inheritSched) {
case 0: pinherit_sched = PTHREAD_EXPLICIT_SCHED; break;
case 4: pinherit_sched = PTHREAD_INHERIT_SCHED; break;
default: BREAKPOINT(); // unknown inheritSched
}
int result = pthread_attr_setinheritsched(&(*attr)->pth_attr, pinherit_sched);
if (result == 0) {
return SCE_OK;
}
return SCE_KERNEL_ERROR_EINVAL;
}
int scePthreadAttrSetschedparam(ScePthreadAttr* attr, const SceKernelSchedParam* param) {
if (param == nullptr || attr == nullptr || *attr == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
}
SceKernelSchedParam pparam{};
if (param->sched_priority <= 478) {
pparam.sched_priority = +2;
} else if (param->sched_priority >= 733) {
pparam.sched_priority = -2;
} else {
pparam.sched_priority = 0;
}
int result = pthread_attr_setschedparam(&(*attr)->pth_attr, &pparam);
if (result == 0) {
return SCE_OK;
}
return SCE_KERNEL_ERROR_EINVAL;
}
int scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy) {
if (attr == nullptr || *attr == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
}
if (policy != SCHED_OTHER) {
BREAKPOINT(); // invest if policy is other and if winpthreadlibrary support it
}
(*attr)->policy = policy;
int result = pthread_attr_setschedpolicy(&(*attr)->pth_attr, policy);
if (result == 0) {
return SCE_OK;
}
return SCE_KERNEL_ERROR_EINVAL;
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,42 @@
#pragma once
#define _TIMESPEC_DEFINED
#include <pthread.h>
#include <sched.h>
#include "common/types.h"
#include <string>
namespace Core::Kernel {
struct PthreadAttrInternal;
using SceKernelSchedParam = ::sched_param;
using ScePthreadAttr = PthreadAttrInternal*;
struct PthreadInternal {
u08 reserved[4096];
std::string name;
pthread_t pth;
ScePthreadAttr attr;
};
struct PthreadAttrInternal {
u08 reserved[64];
u64 affinity;
size_t guard_size;
int policy;
bool detached;
pthread_attr_t pth_attr;
};
class PThreadCxt {};
void Pthread_Init_Self_MainThread();
int scePthreadAttrInit(ScePthreadAttr* attr);
int scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate);
int scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched);
int scePthreadAttrSetschedparam(ScePthreadAttr* attr, const SceKernelSchedParam* param);
int scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy);
} // namespace Core::Kernel

View File

@@ -0,0 +1,13 @@
#include "common/log.h"
#include "core/hle/kernel/cpu_management.h"
#include "core/hle/libraries/libs.h"
#include "Util/config.h"
namespace Core::Kernel {
int PS4_SYSV_ABI sceKernelIsNeoMode() {
PRINT_FUNCTION_NAME();
return Config::isNeoMode();
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,9 @@
#pragma once
#include "common/types.h"
namespace Core::Kernel {
int PS4_SYSV_ABI sceKernelIsNeoMode();
} // namespace Core::Kernel

View File

@@ -0,0 +1,71 @@
#include "common/debug.h"
#include "common/log.h"
#include "core/hle/kernel/event_queues.h"
#include "core/hle/error_codes.h"
#include "core/hle/libraries/libs.h"
namespace Core::Kernel {
constexpr bool log_file_equeues = true; // disable it to disable logging
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
PRINT_FUNCTION_NAME();
if (eq == nullptr) {
LOG_TRACE_IF(log_file_equeues, "sceKernelCreateEqueue returned SCE_KERNEL_ERROR_EINVAL eq invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if (name == nullptr) {
LOG_TRACE_IF(log_file_equeues, "sceKernelCreateEqueue returned SCE_KERNEL_ERROR_EFAULT name invalid\n");
return SCE_KERNEL_ERROR_EFAULT;
}
if (name == NULL) {
LOG_TRACE_IF(log_file_equeues, "sceKernelCreateEqueue returned SCE_KERNEL_ERROR_EINVAL name is null\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if (strlen(name) > 31) { // max is 32 including null terminator
LOG_TRACE_IF(log_file_equeues, "sceKernelCreateEqueue returned SCE_KERNEL_ERROR_ENAMETOOLONG name size exceeds 32 bytes\n");
return SCE_KERNEL_ERROR_ENAMETOOLONG;
}
*eq = new EqueueInternal;
(*eq)->setName(std::string(name));
LOG_INFO_IF(log_file_equeues, "sceKernelCreateEqueue created with name \"{}\"\n", name);
return SCE_OK;
}
int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev,
int num, int* out, SceKernelUseconds* timo) {
PRINT_FUNCTION_NAME();
if (eq == nullptr) {
return SCE_KERNEL_ERROR_EBADF;
}
if (ev == nullptr) {
return SCE_KERNEL_ERROR_EFAULT;
}
if (num < 1) {
return SCE_KERNEL_ERROR_EINVAL;
}
if (timo == nullptr) { // wait until an event arrives without timing out
*out = eq->waitForEvents(ev, num, 0);
}
if (timo != nullptr) {
// Only events that have already arrived at the time of this function call can be received
if (*timo == 0) {
BREAKPOINT();
} else {
// Wait until an event arrives with timing out
BREAKPOINT();
}
}
return SCE_OK;
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,14 @@
#pragma once
#include "core/hle/kernel/objects/event_queue.h"
namespace Core::Kernel {
using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*;
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name);
int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev,
int num, int* out, SceKernelUseconds *timo);
} // namespace Core::Kernel

View File

@@ -0,0 +1,126 @@
#include <bit>
#include <magic_enum.hpp>
#include <core/PS4/GPU/gpu_memory.h>
#include <core/virtual_memory.h>
#include "common/log.h"
#include "common/debug.h"
#include "common/singleton.h"
#include "core/hle/kernel/memory_management.h"
#include "core/hle/libraries/libs.h"
#include "core/hle/kernel/Objects/physical_memory.h"
#include "core/hle/error_codes.h"
namespace Core::Kernel {
constexpr bool log_file_memory = true; // disable it to disable logging
bool is16KBAligned(u64 n) {
return ((n % (16ull * 1024) == 0));
}
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() {
PRINT_FUNCTION_NAME();
return SCE_KERNEL_MAIN_DMEM_SIZE;
}
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, u64 alignment, int memoryType, s64* physAddrOut) {
PRINT_FUNCTION_NAME();
if (searchStart < 0 || searchEnd <= searchStart) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EINVAL searchStart,searchEnd invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
bool isInRange = (searchStart < len && searchEnd > len);
if (len <= 0 || !is16KBAligned(len) || !isInRange) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EINVAL memory range invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if ((alignment != 0 || is16KBAligned(alignment)) && !std::has_single_bit(alignment)) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EINVAL alignment invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if (physAddrOut == nullptr) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EINVAL physAddrOut is null\n");
return SCE_KERNEL_ERROR_EINVAL;
}
auto memtype = magic_enum::enum_cast<MemoryTypes>(memoryType);
LOG_INFO_IF(log_file_memory, "search_start = {:#x}\n", searchStart);
LOG_INFO_IF(log_file_memory, "search_end = {:#x}\n", searchEnd);
LOG_INFO_IF(log_file_memory, "len = {:#x}\n", len);
LOG_INFO_IF(log_file_memory, "alignment = {:#x}\n", alignment);
LOG_INFO_IF(log_file_memory, "memory_type = {}\n", magic_enum::enum_name(memtype.value()));
u64 physical_addr = 0;
auto* physical_memory = Common::Singleton<PhysicalMemory>::Instance();
if (!physical_memory->Alloc(searchStart, searchEnd, len, alignment, &physical_addr, memoryType)) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EAGAIN can't allocate physical memory\n");
return SCE_KERNEL_ERROR_EAGAIN;
}
*physAddrOut = static_cast<s64>(physical_addr);
LOG_INFO_IF(true, "physAddrOut = {:#x}\n", physical_addr);
return SCE_OK;
}
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment) {
PRINT_FUNCTION_NAME();
if (len == 0 || !is16KBAligned(len)) {
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL len invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if (!is16KBAligned(directMemoryStart)) {
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL directMemoryStart invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
if (alignment != 0) {
if ((!std::has_single_bit(alignment) && !is16KBAligned(alignment))) {
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL alignment invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
}
LOG_INFO_IF(log_file_memory, "len = {:#x}\n", len);
LOG_INFO_IF(log_file_memory, "prot = {:#x}\n", prot);
LOG_INFO_IF(log_file_memory, "flags = {:#x}\n", flags);
LOG_INFO_IF(log_file_memory, "directMemoryStart = {:#x}\n", directMemoryStart);
LOG_INFO_IF(log_file_memory, "alignment = {:#x}\n", alignment);
VirtualMemory::MemoryMode cpu_mode = VirtualMemory::MemoryMode::NoAccess;
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
switch (prot) {
case 0x32:
case 0x33: // SCE_KERNEL_PROT_CPU_READ|SCE_KERNEL_PROT_CPU_WRITE|SCE_KERNEL_PROT_GPU_READ|SCE_KERNEL_PROT_GPU_ALL
cpu_mode = VirtualMemory::MemoryMode::ReadWrite;
gpu_mode = GPU::MemoryMode::ReadWrite;
break;
default: BREAKPOINT();
}
auto in_addr = reinterpret_cast<u64>(*addr);
u64 out_addr = 0;
if (flags == 0) {
out_addr = VirtualMemory::memory_alloc_aligned(in_addr, len, cpu_mode, alignment);
}
LOG_INFO_IF(log_file_memory, "in_addr = {:#x}\n", in_addr);
LOG_INFO_IF(log_file_memory, "out_addr = {:#x}\n", out_addr);
*addr = reinterpret_cast<void*>(out_addr); // return out_addr to first functions parameter
if (out_addr == 0) {
return SCE_KERNEL_ERROR_ENOMEM;
}
auto* physical_memory = Common::Singleton<PhysicalMemory>::Instance();
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode, gpu_mode)) {
BREAKPOINT();
}
if (gpu_mode != GPU::MemoryMode::NoAccess) {
GPU::memorySetAllocArea(out_addr, len);
}
return SCE_OK;
}
} // namespace Core::Kernel

View File

@@ -0,0 +1,34 @@
#pragma once
#include "common/types.h"
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5376_MB; // ~ 6GB
namespace Core::Kernel {
enum MemoryTypes : u32 {
SCE_KERNEL_WB_ONION = 0, // write - back mode (Onion bus)
SCE_KERNEL_WC_GARLIC = 3, // write - combining mode (Garlic bus)
SCE_KERNEL_WB_GARLIC = 10 // write - back mode (Garlic bus)
};
enum MemoryFlags : u32 {
SCE_KERNEL_MAP_FIXED = 0x0010, // Fixed
SCE_KERNEL_MAP_NO_OVERWRITE = 0x0080,
SCE_KERNEL_MAP_NO_COALESCE = 0x400000
};
enum MemoryProtection : u32 {
SCE_KERNEL_PROT_CPU_READ = 0x01, // Permit reads from the CPU
SCE_KERNEL_PROT_CPU_RW = 0x02, // Permit reads/writes from the CPU
SCE_KERNEL_PROT_CPU_WRITE = 0x02, // Permit reads/writes from the CPU (same)
SCE_KERNEL_PROT_GPU_READ = 0x10, // Permit reads from the GPU
SCE_KERNEL_PROT_GPU_WRITE = 0x20, // Permit writes from the GPU
SCE_KERNEL_PROT_GPU_RW = 0x30 // Permit reads/writes from the GPU
};
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, u64 alignment, int memoryType, s64* physAddrOut);
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment);
} // namespace Core::Kernel