Add 3GX plugin loader (#6172)
* Initial plugin loader support * More plugin loader progress * Organize code and more plugin features * Fix clang-format * Fix compilation and add android gui * Fix clang-format * Fix macos build * Fix copy-paste bug and clang-format * More merge fixes * Make suggestions * Move global variable to static member * Fix typo * Apply suggestions * Proper initialization order * Allocate plugin memory from SYSTEM instead of APPLICATION * Do not mark free pages as RWX * Fix plugins in old 3DS mode. * Implement KernelSetState and notif 0x203 * Apply changes * Remove unused variable * Fix dynarmic commit * Sublicense files with MIT License * Remove non-ascii characters from license
This commit is contained in:
parent
48ee112ceb
commit
016ce6c286
38 changed files with 1911 additions and 42 deletions
|
@ -82,6 +82,20 @@ enum class MemoryRegion : u16 {
|
|||
BASE = 3,
|
||||
};
|
||||
|
||||
union CoreVersion {
|
||||
CoreVersion(u32 version) : raw(version) {}
|
||||
CoreVersion(u32 major_ver, u32 minor_ver, u32 revision_ver) {
|
||||
revision.Assign(revision_ver);
|
||||
minor.Assign(minor_ver);
|
||||
major.Assign(major_ver);
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
BitField<8, 8, u32> revision;
|
||||
BitField<16, 8, u32> minor;
|
||||
BitField<24, 8, u32> major;
|
||||
};
|
||||
|
||||
class KernelSystem {
|
||||
public:
|
||||
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
|
|
|
@ -245,6 +245,25 @@ std::optional<u32> MemoryRegionInfo::LinearAllocate(u32 size) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<u32> MemoryRegionInfo::RLinearAllocate(u32 size) {
|
||||
ASSERT(!is_locked);
|
||||
|
||||
// Find the first sufficient continuous block from the upper address
|
||||
for (auto iter = free_blocks.rbegin(); iter != free_blocks.rend(); ++iter) {
|
||||
auto interval = *iter;
|
||||
ASSERT(interval.bounds() == boost::icl::interval_bounds::right_open());
|
||||
if (interval.upper() - interval.lower() >= size) {
|
||||
Interval allocated(interval.upper() - size, interval.upper());
|
||||
free_blocks -= allocated;
|
||||
used += size;
|
||||
return allocated.lower();
|
||||
}
|
||||
}
|
||||
|
||||
// No sufficient block found
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MemoryRegionInfo::Free(u32 offset, u32 size) {
|
||||
if (is_locked) {
|
||||
return;
|
||||
|
|
|
@ -60,6 +60,14 @@ struct MemoryRegionInfo {
|
|||
*/
|
||||
std::optional<u32> LinearAllocate(u32 size);
|
||||
|
||||
/**
|
||||
* Allocates memory from the linear heap with only size specified.
|
||||
* @param size size of the memory to allocate.
|
||||
* @returns the address offset to the found block, searching from the end of FCRAM; null if
|
||||
* there is no enough space
|
||||
*/
|
||||
std::optional<u32> RLinearAllocate(u32 size);
|
||||
|
||||
/**
|
||||
* Frees one segment of memory. The memory must have been allocated as heap or linear heap.
|
||||
* @param offset the region address offset to the beginning of FCRAM.
|
||||
|
|
|
@ -12,12 +12,15 @@
|
|||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/serialization/boost_vector.hpp"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::Process)
|
||||
|
@ -36,6 +39,7 @@ void Process::serialize(Archive& ar, const unsigned int file_version) {
|
|||
ar&(boost::container::vector<AddressMapping, boost::container::dtl::static_storage_allocator<
|
||||
AddressMapping, 8, 0, true>>&)address_mappings;
|
||||
ar& flags.raw;
|
||||
ar& no_thread_restrictions;
|
||||
ar& kernel_version;
|
||||
ar& ideal_processor;
|
||||
ar& status;
|
||||
|
@ -186,12 +190,24 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
|||
kernel.HandleSpecialMapping(vm_manager, mapping);
|
||||
}
|
||||
|
||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||
if (plgldr) {
|
||||
plgldr->OnProcessRun(*this, kernel);
|
||||
}
|
||||
|
||||
status = ProcessStatus::Running;
|
||||
|
||||
vm_manager.LogLayout(Log::Level::Debug);
|
||||
Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, SharedFrom(this));
|
||||
}
|
||||
|
||||
void Process::Exit() {
|
||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||
if (plgldr) {
|
||||
plgldr->OnProcessExit(*this, kernel);
|
||||
}
|
||||
}
|
||||
|
||||
VAddr Process::GetLinearHeapAreaAddress() const {
|
||||
// Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
|
||||
// the extra RAM in the n3DS.
|
||||
|
@ -449,7 +465,7 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe
|
|||
}
|
||||
|
||||
Kernel::Process::Process(KernelSystem& kernel)
|
||||
: Object(kernel), handle_table(kernel), vm_manager(kernel.memory), kernel(kernel) {
|
||||
: Object(kernel), handle_table(kernel), vm_manager(kernel.memory, *this), kernel(kernel) {
|
||||
kernel.memory.RegisterPageTable(vm_manager.page_table);
|
||||
}
|
||||
Kernel::Process::~Process() {
|
||||
|
|
|
@ -174,6 +174,7 @@ public:
|
|||
/// processes access to specific I/O regions and device memory.
|
||||
boost::container::static_vector<AddressMapping, 8> address_mappings;
|
||||
ProcessFlags flags;
|
||||
bool no_thread_restrictions = false;
|
||||
/// Kernel compatibility version for this process
|
||||
u16 kernel_version = 0;
|
||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||
|
@ -200,6 +201,11 @@ public:
|
|||
*/
|
||||
void Run(s32 main_thread_priority, u32 stack_size);
|
||||
|
||||
/**
|
||||
* Called when the process exits by svc
|
||||
*/
|
||||
void Exit();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Memory Management
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
@ -65,6 +67,15 @@ struct MemoryInfo {
|
|||
u32 state;
|
||||
};
|
||||
|
||||
/// Values accepted by svcKernelSetState, only the known values are listed
|
||||
/// (the behaviour of other values are known, but their purpose is unclear and irrelevant).
|
||||
enum class KernelState {
|
||||
/**
|
||||
* Reboots the console
|
||||
*/
|
||||
KERNEL_STATE_REBOOT = 7,
|
||||
};
|
||||
|
||||
struct PageInfo {
|
||||
u32 flags;
|
||||
};
|
||||
|
@ -85,6 +96,11 @@ enum class SystemInfoType {
|
|||
* For the ARM11 NATIVE_FIRM kernel, this is 5, for processes sm, fs, pm, loader, and pxi."
|
||||
*/
|
||||
KERNEL_SPAWNED_PIDS = 26,
|
||||
/**
|
||||
* Check if the current system is a new 3DS. This parameter is not available on real systems,
|
||||
* but can be used by homebrew applications.
|
||||
*/
|
||||
NEW_3DS_INFO = 0x10001,
|
||||
/**
|
||||
* Gets citra related information. This parameter is not available on real systems,
|
||||
* but can be used by homebrew applications to get some emulator info.
|
||||
|
@ -92,6 +108,134 @@ enum class SystemInfoType {
|
|||
CITRA_INFORMATION = 0x20000,
|
||||
};
|
||||
|
||||
enum class ProcessInfoType {
|
||||
/**
|
||||
* Returns the amount of private (code, data, regular heap) and shared memory used by the
|
||||
* process + total supervisor-mode stack size + page-rounded size of the external handle table.
|
||||
* This is the amount of physical memory the process is using, minus TLS, main thread stack and
|
||||
* linear memory.
|
||||
*/
|
||||
PRIVATE_AND_SHARED_USED_MEMORY = 0,
|
||||
|
||||
/**
|
||||
* Returns the amount of <related unused field> + total supervisor-mode stack size +
|
||||
* page-rounded size of the external handle table.
|
||||
*/
|
||||
SUPERVISOR_AND_HANDLE_USED_MEMORY = 1,
|
||||
|
||||
/**
|
||||
* Returns the amount of private (code, data, heap) memory used by the process + total
|
||||
* supervisor-mode stack size + page-rounded size of the external handle table.
|
||||
*/
|
||||
PRIVATE_SHARED_SUPERVISOR_HANDLE_USED_MEMORY = 2,
|
||||
|
||||
/**
|
||||
* Returns the amount of <related unused field> + total supervisor-mode stack size +
|
||||
* page-rounded size of the external handle table.
|
||||
*/
|
||||
SUPERVISOR_AND_HANDLE_USED_MEMORY2 = 3,
|
||||
|
||||
/**
|
||||
* Returns the amount of handles in use by the process.
|
||||
*/
|
||||
USED_HANDLE_COUNT = 4,
|
||||
|
||||
/**
|
||||
* Returns the highest count of handles that have been open at once by the process.
|
||||
*/
|
||||
HIGHEST_HANDLE_COUNT = 5,
|
||||
|
||||
/**
|
||||
* Returns *(u32*)(KProcess+0x234) which is always 0.
|
||||
*/
|
||||
KPROCESS_0X234 = 6,
|
||||
|
||||
/**
|
||||
* Returns the number of threads of the process.
|
||||
*/
|
||||
THREAD_COUNT = 7,
|
||||
|
||||
/**
|
||||
* Returns the maximum number of threads which can be opened by this process (always 0).
|
||||
*/
|
||||
MAX_THREAD_AMOUNT = 8,
|
||||
|
||||
/**
|
||||
* Originally this only returned 0xD8E007ED. Now with v11.3 this returns the memregion for the
|
||||
* process: out low u32 = KProcess "Kernel flags from the exheader kernel descriptors" & 0xF00
|
||||
* (memory region flag). High out u32 = 0.
|
||||
*/
|
||||
MEMORY_REGION_FLAGS = 19,
|
||||
|
||||
/**
|
||||
* Low u32 = (0x20000000 - <LINEAR virtual-memory base for this process>). That is, the output
|
||||
* value is the value which can be added to LINEAR memory vaddrs for converting to
|
||||
* physical-memory addrs.
|
||||
*/
|
||||
LINEAR_BASE_ADDR_OFFSET = 20,
|
||||
|
||||
/**
|
||||
* Returns the VA -> PA conversion offset for the QTM static mem block reserved in the exheader
|
||||
* (0x800000), otherwise 0 (+ error 0xE0E01BF4) if it doesn't exist.
|
||||
*/
|
||||
QTM_MEMORY_BLOCK_CONVERSION_OFFSET = 21,
|
||||
|
||||
/**
|
||||
* Returns the base VA of the QTM static mem block reserved in the exheader, otherwise 0 (+
|
||||
* error 0xE0E01BF4) if it doesn't exist.
|
||||
*/
|
||||
QTM_MEMORY_ADDRESS = 22,
|
||||
|
||||
/**
|
||||
* Returns the size of the QTM static mem block reserved in the exheader, otherwise 0 (+ error
|
||||
* 0xE0E01BF4) if it doesn't exist.
|
||||
*/
|
||||
QTM_MEMORY_SIZE = 23,
|
||||
|
||||
// Custom values used by Luma3DS and 3GX plugins
|
||||
|
||||
/**
|
||||
* Returns the process name.
|
||||
*/
|
||||
LUMA_CUSTOM_PROCESS_NAME = 0x10000,
|
||||
|
||||
/**
|
||||
* Returns the process title ID.
|
||||
*/
|
||||
LUMA_CUSTOM_PROCESS_TITLE_ID = 0x10001,
|
||||
|
||||
/**
|
||||
* Returns the codeset text size.
|
||||
*/
|
||||
LUMA_CUSTOM_TEXT_SIZE = 0x10002,
|
||||
|
||||
/**
|
||||
* Returns the codeset rodata size.
|
||||
*/
|
||||
LUMA_CUSTOM_RODATA_SIZE = 0x10003,
|
||||
|
||||
/**
|
||||
* Returns the codeset data size.
|
||||
*/
|
||||
LUMA_CUSTOM_DATA_SIZE = 0x10004,
|
||||
|
||||
/**
|
||||
* Returns the codeset text vaddr.
|
||||
*/
|
||||
LUMA_CUSTOM_TEXT_ADDR = 0x10005,
|
||||
|
||||
/**
|
||||
* Returns the codeset rodata vaddr.
|
||||
*/
|
||||
LUMA_CUSTOM_RODATA_ADDR = 0x10006,
|
||||
|
||||
/**
|
||||
* Returns the codeset data vaddr.
|
||||
*/
|
||||
LUMA_CUSTOM_DATA_ADDR = 0x10007,
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Accepted by svcGetSystemInfo param with REGION_MEMORY_USAGE type. Selects a region to query
|
||||
* memory usage of.
|
||||
|
@ -121,6 +265,73 @@ enum class SystemInfoCitraInformation {
|
|||
BUILD_GIT_DESCRIPTION_PART2 = 41, // Git description (commit) last 7 characters.
|
||||
};
|
||||
|
||||
/**
|
||||
* Accepted by the custom svcControlProcess.
|
||||
*/
|
||||
enum class ControlProcessOP {
|
||||
/**
|
||||
* List all handles of the process, varg3 can be either 0 to fetch
|
||||
* all handles, or token of the type to fetch s32 count =
|
||||
* svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES,
|
||||
* (u32)&outBuf, 0) Returns how many handles were found
|
||||
*/
|
||||
PROCESSOP_GET_ALL_HANDLES = 0,
|
||||
|
||||
/**
|
||||
* Set the whole memory of the process with rwx access (in the mmu
|
||||
* table only) svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX,
|
||||
* 0, 0)
|
||||
*/
|
||||
PROCESSOP_SET_MMU_TO_RWX,
|
||||
|
||||
/**
|
||||
* Get the handle of an event which will be signaled
|
||||
* each time the memory layout of this process changes
|
||||
* svcControlProcess(handle,
|
||||
* PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
|
||||
* &eventHandleOut, 0)
|
||||
*/
|
||||
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
|
||||
|
||||
/**
|
||||
* Set a flag to be signaled when the process will be exited
|
||||
* svcControlProcess(handle, PROCESSOP_SIGNAL_ON_EXIT, 0, 0)
|
||||
*/
|
||||
PROCESSOP_SIGNAL_ON_EXIT,
|
||||
|
||||
/**
|
||||
* Get the physical address of the VAddr within the process
|
||||
* svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&PAOut,
|
||||
* VAddr)
|
||||
*/
|
||||
PROCESSOP_GET_PA_FROM_VA,
|
||||
|
||||
/*
|
||||
* Lock / Unlock the process's threads
|
||||
* svcControlProcess(handle, PROCESSOP_SCHEDULE_THREADS, lock,
|
||||
* threadPredicate) lock: 0 to unlock threads, any other value to
|
||||
* lock threads threadPredicate: can be NULL or a funcptr to a
|
||||
* predicate (typedef bool (*ThreadPredicate)(KThread *thread);)
|
||||
* The predicate must return true to operate on the thread
|
||||
*/
|
||||
PROCESSOP_SCHEDULE_THREADS,
|
||||
|
||||
/*
|
||||
* Lock / Unlock the process's threads
|
||||
* svcControlProcess(handle, PROCESSOP_SCHEDULE_THREADS, lock,
|
||||
* tlsmagicexclude) lock: 0 to unlock threads, any other value to
|
||||
* lock threads tlsmagicexclude: do not lock threads with this tls magic
|
||||
* value
|
||||
*/
|
||||
PROCESSOP_SCHEDULE_THREADS_WITHOUT_TLS_MAGIC,
|
||||
|
||||
/**
|
||||
* Disable any thread creation restrictions, such as priority value
|
||||
* or allowed cores
|
||||
*/
|
||||
PROCESSOP_DISABLE_CREATE_THREAD_RESTRICTIONS,
|
||||
};
|
||||
|
||||
class SVC : public SVCWrapper<SVC> {
|
||||
public:
|
||||
SVC(Core::System& system);
|
||||
|
@ -174,6 +385,7 @@ private:
|
|||
ResultCode GetThreadId(u32* thread_id, Handle handle);
|
||||
ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count);
|
||||
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
|
||||
ResultCode KernelSetState(u32 kernel_state, u32 varg1, u32 varg2);
|
||||
ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info,
|
||||
Handle process_handle, u32 addr);
|
||||
ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr);
|
||||
|
@ -196,6 +408,14 @@ private:
|
|||
ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle);
|
||||
ResultCode GetSystemInfo(s64* out, u32 type, s32 param);
|
||||
ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type);
|
||||
ResultCode GetThreadInfo(s64* out, Handle thread_handle, u32 type);
|
||||
ResultCode InvalidateInstructionCacheRange(u32 addr, u32 size);
|
||||
ResultCode InvalidateEntireInstructionCache();
|
||||
u32 ConvertVaToPa(u32 addr);
|
||||
ResultCode MapProcessMemoryEx(Handle dst_process_handle, u32 dst_address,
|
||||
Handle src_process_handle, u32 src_address, u32 size);
|
||||
ResultCode UnmapProcessMemoryEx(Handle process, u32 dst_address, u32 size);
|
||||
ResultCode ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, u32 varg3);
|
||||
|
||||
struct FunctionDef {
|
||||
using Func = void (SVC::*)();
|
||||
|
@ -205,7 +425,7 @@ private:
|
|||
const char* name;
|
||||
};
|
||||
|
||||
static const std::array<FunctionDef, 126> SVC_Table;
|
||||
static const std::array<FunctionDef, 180> SVC_Table;
|
||||
static const FunctionDef* GetSVCInfo(u32 func_num);
|
||||
};
|
||||
|
||||
|
@ -319,6 +539,8 @@ void SVC::ExitProcess() {
|
|||
thread->Stop();
|
||||
}
|
||||
|
||||
current_process->Exit();
|
||||
|
||||
// Kill the current thread
|
||||
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
|
||||
|
||||
|
@ -920,7 +1142,8 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
|||
std::shared_ptr<Process> current_process = kernel.GetCurrentProcess();
|
||||
|
||||
std::shared_ptr<ResourceLimit>& resource_limit = current_process->resource_limit;
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority &&
|
||||
!current_process->no_thread_restrictions) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
|
@ -945,7 +1168,7 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
|||
// process, exheader kernel-flags bitmask 0x2000 must be set (otherwise error 0xD9001BEA is
|
||||
// returned). When processorid==0x3 and the process is not a BASE mem-region process, error
|
||||
// 0xD9001BEA is returned. These are the only restriction checks done by the kernel for
|
||||
// processorid.
|
||||
// processorid. If this is implemented, make sure to check process->no_thread_restrictions.
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id);
|
||||
|
@ -1110,6 +1333,22 @@ ResultCode SVC::ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
|
|||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Sets the kernel state
|
||||
ResultCode SVC::KernelSetState(u32 kernel_state, u32 varg1, u32 varg2) {
|
||||
switch (static_cast<KernelState>(kernel_state)) {
|
||||
|
||||
// This triggers a hardware reboot on real console, since this doesn't make sense
|
||||
// on emulator, we shutdown instead.
|
||||
case KernelState::KERNEL_STATE_REBOOT:
|
||||
system.RequestShutdown();
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Unknown KernelSetState state={} varg1={} varg2={}", kernel_state,
|
||||
varg1, varg2);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
ResultCode SVC::QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info,
|
||||
Handle process_handle, u32 addr) {
|
||||
|
@ -1434,6 +1673,12 @@ ResultCode SVC::GetSystemInfo(s64* out, u32 type, s32 param) {
|
|||
case SystemInfoType::KERNEL_SPAWNED_PIDS:
|
||||
*out = 5;
|
||||
break;
|
||||
case SystemInfoType::NEW_3DS_INFO:
|
||||
// The actual subtypes are not implemented, homebrew just check
|
||||
// this doesn't return an error in n3ds to know the system type
|
||||
LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=65537 param={}", param);
|
||||
*out = 0;
|
||||
return (system.GetNumCores() == 4) ? RESULT_SUCCESS : ERR_INVALID_ENUM_VALUE;
|
||||
case SystemInfoType::CITRA_INFORMATION:
|
||||
switch ((SystemInfoCitraInformation)param) {
|
||||
case SystemInfoCitraInformation::IS_CITRA:
|
||||
|
@ -1501,9 +1746,9 @@ ResultCode SVC::GetProcessInfo(s64* out, Handle process_handle, u32 type) {
|
|||
if (process == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
case 2:
|
||||
switch (static_cast<ProcessInfoType>(type)) {
|
||||
case ProcessInfoType::PRIVATE_AND_SHARED_USED_MEMORY:
|
||||
case ProcessInfoType::PRIVATE_SHARED_SUPERVISOR_HANDLE_USED_MEMORY:
|
||||
// TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure
|
||||
// what's the difference between them.
|
||||
*out = process->memory_used;
|
||||
|
@ -1512,25 +1757,53 @@ ResultCode SVC::GetProcessInfo(s64* out, Handle process_handle, u32 type) {
|
|||
return ERR_MISALIGNED_SIZE;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case ProcessInfoType::SUPERVISOR_AND_HANDLE_USED_MEMORY:
|
||||
case ProcessInfoType::SUPERVISOR_AND_HANDLE_USED_MEMORY2:
|
||||
case ProcessInfoType::USED_HANDLE_COUNT:
|
||||
case ProcessInfoType::HIGHEST_HANDLE_COUNT:
|
||||
case ProcessInfoType::KPROCESS_0X234:
|
||||
case ProcessInfoType::THREAD_COUNT:
|
||||
case ProcessInfoType::MAX_THREAD_AMOUNT:
|
||||
// These are valid, but not implemented yet
|
||||
LOG_ERROR(Kernel_SVC, "unimplemented GetProcessInfo type={}", type);
|
||||
break;
|
||||
case 20:
|
||||
case ProcessInfoType::LINEAR_BASE_ADDR_OFFSET:
|
||||
*out = Memory::FCRAM_PADDR - process->GetLinearHeapAreaAddress();
|
||||
break;
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case ProcessInfoType::QTM_MEMORY_BLOCK_CONVERSION_OFFSET:
|
||||
case ProcessInfoType::QTM_MEMORY_ADDRESS:
|
||||
case ProcessInfoType::QTM_MEMORY_SIZE:
|
||||
// These return a different error value than higher invalid values
|
||||
LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type={}", type);
|
||||
return ERR_NOT_IMPLEMENTED;
|
||||
// Here start the custom ones, taken from Luma3DS for 3GX support
|
||||
case ProcessInfoType::LUMA_CUSTOM_PROCESS_NAME:
|
||||
// Get process name
|
||||
strncpy(reinterpret_cast<char*>(out), process->codeset->GetName().c_str(), 8);
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_PROCESS_TITLE_ID:
|
||||
// Get process TID
|
||||
*out = process->codeset->program_id;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_TEXT_SIZE:
|
||||
*out = process->codeset->CodeSegment().size;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_RODATA_SIZE:
|
||||
*out = process->codeset->RODataSegment().size;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_DATA_SIZE:
|
||||
*out = process->codeset->DataSegment().size;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_TEXT_ADDR:
|
||||
*out = process->codeset->CodeSegment().addr;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_RODATA_ADDR:
|
||||
*out = process->codeset->RODataSegment().addr;
|
||||
break;
|
||||
case ProcessInfoType::LUMA_CUSTOM_DATA_ADDR:
|
||||
*out = process->codeset->DataSegment().addr;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type={}", type);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
|
@ -1539,7 +1812,179 @@ ResultCode SVC::GetProcessInfo(s64* out, Handle process_handle, u32 type) {
|
|||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const std::array<SVC::FunctionDef, 126> SVC::SVC_Table{{
|
||||
ResultCode SVC::GetThreadInfo(s64* out, Handle thread_handle, u32 type) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X} type={}", thread_handle, type);
|
||||
|
||||
std::shared_ptr<Thread> thread =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Thread>(thread_handle);
|
||||
if (thread == nullptr) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0x10000:
|
||||
*out = static_cast<s64>(thread->GetTLSAddress());
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "unknown GetThreadInfo type={}", type);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SVC::InvalidateInstructionCacheRange(u32 addr, u32 size) {
|
||||
Core::GetRunningCore().InvalidateCacheRange(addr, size);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SVC::InvalidateEntireInstructionCache() {
|
||||
Core::GetRunningCore().ClearInstructionCache();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
u32 SVC::ConvertVaToPa(u32 addr) {
|
||||
auto vma = kernel.GetCurrentProcess()->vm_manager.FindVMA(addr);
|
||||
if (vma == kernel.GetCurrentProcess()->vm_manager.vma_map.end() ||
|
||||
vma->second.type != VMAType::BackingMemory) {
|
||||
return 0;
|
||||
}
|
||||
return kernel.memory.GetFCRAMOffset(vma->second.backing_memory.GetPtr() + addr -
|
||||
vma->second.base) +
|
||||
Memory::FCRAM_PADDR;
|
||||
}
|
||||
|
||||
ResultCode SVC::MapProcessMemoryEx(Handle dst_process_handle, u32 dst_address,
|
||||
Handle src_process_handle, u32 src_address, u32 size) {
|
||||
std::shared_ptr<Process> dst_process =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Process>(dst_process_handle);
|
||||
std::shared_ptr<Process> src_process =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Process>(src_process_handle);
|
||||
|
||||
if (dst_process == nullptr || src_process == nullptr) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (size & 0xFFF) {
|
||||
size = (size & ~0xFFF) + Memory::CITRA_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// Only linear memory supported
|
||||
auto vma = src_process->vm_manager.FindVMA(src_address);
|
||||
if (vma == src_process->vm_manager.vma_map.end() ||
|
||||
vma->second.type != VMAType::BackingMemory ||
|
||||
vma->second.meminfo_state != MemoryState::Continuous) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
u32 offset = src_address - vma->second.base;
|
||||
if (offset + size > vma->second.size) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto vma_res = dst_process->vm_manager.MapBackingMemory(
|
||||
dst_address,
|
||||
memory.GetFCRAMRef(vma->second.backing_memory.GetPtr() + offset -
|
||||
kernel.memory.GetFCRAMPointer(0)),
|
||||
size, Kernel::MemoryState::Continuous);
|
||||
|
||||
if (!vma_res.Succeeded()) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
dst_process->vm_manager.Reprotect(vma_res.Unwrap(), Kernel::VMAPermission::ReadWriteExecute);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SVC::UnmapProcessMemoryEx(Handle process, u32 dst_address, u32 size) {
|
||||
std::shared_ptr<Process> dst_process =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Process>(process);
|
||||
|
||||
if (dst_process == nullptr) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (size & 0xFFF) {
|
||||
size = (size & ~0xFFF) + Memory::CITRA_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// Only linear memory supported
|
||||
auto vma = dst_process->vm_manager.FindVMA(dst_address);
|
||||
if (vma == dst_process->vm_manager.vma_map.end() ||
|
||||
vma->second.type != VMAType::BackingMemory ||
|
||||
vma->second.meminfo_state != MemoryState::Continuous) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
dst_process->vm_manager.UnmapRange(dst_address, size);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SVC::ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, u32 varg3) {
|
||||
std::shared_ptr<Process> process =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Process>(process_handle);
|
||||
|
||||
if (process == nullptr) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
switch (static_cast<ControlProcessOP>(process_OP)) {
|
||||
case ControlProcessOP::PROCESSOP_SET_MMU_TO_RWX: {
|
||||
for (auto it = process->vm_manager.vma_map.cbegin();
|
||||
it != process->vm_manager.vma_map.cend(); it++) {
|
||||
if (it->second.meminfo_state != MemoryState::Free)
|
||||
process->vm_manager.Reprotect(it, Kernel::VMAPermission::ReadWriteExecute);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
case ControlProcessOP::PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT: {
|
||||
auto plgldr = Service::PLGLDR::GetService(system);
|
||||
if (!plgldr) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ResultVal<Handle> out = plgldr->GetMemoryChangedHandle(kernel);
|
||||
if (out.Failed()) {
|
||||
return out.Code();
|
||||
}
|
||||
|
||||
memory.Write32(varg2, out.Unwrap());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
case ControlProcessOP::PROCESSOP_SCHEDULE_THREADS_WITHOUT_TLS_MAGIC: {
|
||||
for (u32 i = 0; i < system.GetNumCores(); i++) {
|
||||
auto& thread_list = kernel.GetThreadManager(i).GetThreadList();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->owner_process.lock() != process) {
|
||||
continue;
|
||||
}
|
||||
if (memory.Read32(thread.get()->GetTLSAddress()) == varg3) {
|
||||
continue;
|
||||
}
|
||||
if (thread.get()->thread_id ==
|
||||
kernel.GetCurrentThreadManager().GetCurrentThread()->thread_id) {
|
||||
continue;
|
||||
}
|
||||
thread.get()->can_schedule = !varg2;
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
case ControlProcessOP::PROCESSOP_DISABLE_CREATE_THREAD_RESTRICTIONS: {
|
||||
process->no_thread_restrictions = varg2 == 1;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
case ControlProcessOP::PROCESSOP_GET_ALL_HANDLES:
|
||||
case ControlProcessOP::PROCESSOP_GET_PA_FROM_VA:
|
||||
case ControlProcessOP::PROCESSOP_SIGNAL_ON_EXIT:
|
||||
case ControlProcessOP::PROCESSOP_SCHEDULE_THREADS:
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Unknown ControlProcessOp type={}", process_OP);
|
||||
return ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, &SVC::Wrap<&SVC::ControlMemory>, "ControlMemory"},
|
||||
{0x02, &SVC::Wrap<&SVC::QueryMemory>, "QueryMemory"},
|
||||
|
@ -1584,7 +2029,7 @@ const std::array<SVC::FunctionDef, 126> SVC::SVC_Table{{
|
|||
{0x29, nullptr, "GetHandleInfo"},
|
||||
{0x2A, &SVC::Wrap<&SVC::GetSystemInfo>, "GetSystemInfo"},
|
||||
{0x2B, &SVC::Wrap<&SVC::GetProcessInfo>, "GetProcessInfo"},
|
||||
{0x2C, nullptr, "GetThreadInfo"},
|
||||
{0x2C, &SVC::Wrap<&SVC::GetThreadInfo>, "GetThreadInfo"},
|
||||
{0x2D, &SVC::Wrap<&SVC::ConnectToPort>, "ConnectToPort"},
|
||||
{0x2E, nullptr, "SendSyncRequest1"},
|
||||
{0x2F, nullptr, "SendSyncRequest2"},
|
||||
|
@ -1664,8 +2109,63 @@ const std::array<SVC::FunctionDef, 126> SVC::SVC_Table{{
|
|||
{0x79, nullptr, "SetResourceLimitValues"},
|
||||
{0x7A, nullptr, "AddCodeSegment"},
|
||||
{0x7B, nullptr, "Backdoor"},
|
||||
{0x7C, nullptr, "KernelSetState"},
|
||||
{0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState"},
|
||||
{0x7D, &SVC::Wrap<&SVC::QueryProcessMemory>, "QueryProcessMemory"},
|
||||
// Custom SVCs
|
||||
{0x7E, nullptr, "Unused"},
|
||||
{0x7F, nullptr, "Unused"},
|
||||
{0x80, nullptr, "CustomBackdoor"},
|
||||
{0x81, nullptr, "Unused"},
|
||||
{0x82, nullptr, "Unused"},
|
||||
{0x83, nullptr, "Unused"},
|
||||
{0x84, nullptr, "Unused"},
|
||||
{0x85, nullptr, "Unused"},
|
||||
{0x86, nullptr, "Unused"},
|
||||
{0x87, nullptr, "Unused"},
|
||||
{0x88, nullptr, "Unused"},
|
||||
{0x89, nullptr, "Unused"},
|
||||
{0x8A, nullptr, "Unused"},
|
||||
{0x8B, nullptr, "Unused"},
|
||||
{0x8C, nullptr, "Unused"},
|
||||
{0x8D, nullptr, "Unused"},
|
||||
{0x8E, nullptr, "Unused"},
|
||||
{0x8F, nullptr, "Unused"},
|
||||
{0x90, &SVC::Wrap<&SVC::ConvertVaToPa>, "ConvertVaToPa"},
|
||||
{0x91, nullptr, "FlushDataCacheRange"},
|
||||
{0x92, nullptr, "FlushEntireDataCache"},
|
||||
{0x93, &SVC::Wrap<&SVC::InvalidateInstructionCacheRange>, "InvalidateInstructionCacheRange"},
|
||||
{0x94, &SVC::Wrap<&SVC::InvalidateEntireInstructionCache>, "InvalidateEntireInstructionCache"},
|
||||
{0x95, nullptr, "Unused"},
|
||||
{0x96, nullptr, "Unused"},
|
||||
{0x97, nullptr, "Unused"},
|
||||
{0x98, nullptr, "Unused"},
|
||||
{0x99, nullptr, "Unused"},
|
||||
{0x9A, nullptr, "Unused"},
|
||||
{0x9B, nullptr, "Unused"},
|
||||
{0x9C, nullptr, "Unused"},
|
||||
{0x9D, nullptr, "Unused"},
|
||||
{0x9E, nullptr, "Unused"},
|
||||
{0x9F, nullptr, "Unused"},
|
||||
{0xA0, &SVC::Wrap<&SVC::MapProcessMemoryEx>, "MapProcessMemoryEx"},
|
||||
{0xA1, &SVC::Wrap<&SVC::UnmapProcessMemoryEx>, "UnmapProcessMemoryEx"},
|
||||
{0xA2, nullptr, "ControlMemoryEx"},
|
||||
{0xA3, nullptr, "ControlMemoryUnsafe"},
|
||||
{0xA4, nullptr, "Unused"},
|
||||
{0xA5, nullptr, "Unused"},
|
||||
{0xA6, nullptr, "Unused"},
|
||||
{0xA7, nullptr, "Unused"},
|
||||
{0xA8, nullptr, "Unused"},
|
||||
{0xA9, nullptr, "Unused"},
|
||||
{0xAA, nullptr, "Unused"},
|
||||
{0xAB, nullptr, "Unused"},
|
||||
{0xAC, nullptr, "Unused"},
|
||||
{0xAD, nullptr, "Unused"},
|
||||
{0xAE, nullptr, "Unused"},
|
||||
{0xAF, nullptr, "Unused"},
|
||||
{0xB0, nullptr, "ControlService"},
|
||||
{0xB1, nullptr, "CopyHandle"},
|
||||
{0xB2, nullptr, "TranslateHandle"},
|
||||
{0xB3, &SVC::Wrap<&SVC::ControlProcess>, "ControlProcess"},
|
||||
}};
|
||||
|
||||
const SVC::FunctionDef* SVC::GetSVCInfo(u32 func_num) {
|
||||
|
|
|
@ -72,8 +72,8 @@ void Thread::Acquire(Thread* thread) {
|
|||
}
|
||||
|
||||
Thread::Thread(KernelSystem& kernel, u32 core_id)
|
||||
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()), core_id(core_id),
|
||||
thread_manager(kernel.GetThreadManager(core_id)) {}
|
||||
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()),
|
||||
can_schedule(true), core_id(core_id), thread_manager(kernel.GetThreadManager(core_id)) {}
|
||||
Thread::~Thread() {}
|
||||
|
||||
Thread* ThreadManager::GetCurrentThread() const {
|
||||
|
@ -164,15 +164,29 @@ Thread* ThreadManager::PopNextReadyThread() {
|
|||
Thread* thread = GetCurrentThread();
|
||||
|
||||
if (thread && thread->status == ThreadStatus::Running) {
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
next = ready_queue.pop_first_better(thread->current_priority);
|
||||
if (!next) {
|
||||
// Otherwise just keep going with the current thread
|
||||
next = thread;
|
||||
}
|
||||
do {
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
next = ready_queue.pop_first_better(thread->current_priority);
|
||||
if (!next) {
|
||||
// Otherwise just keep going with the current thread
|
||||
next = thread;
|
||||
break;
|
||||
} else if (!next->can_schedule)
|
||||
unscheduled_ready_queue.push_back(next);
|
||||
} while (!next->can_schedule);
|
||||
} else {
|
||||
next = ready_queue.pop_first();
|
||||
do {
|
||||
next = ready_queue.pop_first();
|
||||
if (next && !next->can_schedule)
|
||||
unscheduled_ready_queue.push_back(next);
|
||||
} while (next && !next->can_schedule);
|
||||
}
|
||||
|
||||
while (!unscheduled_ready_queue.empty()) {
|
||||
auto t = std::move(unscheduled_ready_queue.back());
|
||||
ready_queue.push_back(t->current_priority, t);
|
||||
unscheduled_ready_queue.pop_back();
|
||||
}
|
||||
|
||||
return next;
|
||||
|
|
|
@ -148,6 +148,7 @@ private:
|
|||
|
||||
std::shared_ptr<Thread> current_thread;
|
||||
Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue;
|
||||
std::deque<Thread*> unscheduled_ready_queue;
|
||||
std::unordered_map<u64, Thread*> wakeup_callback_table;
|
||||
|
||||
/// Event type for the thread wake up event
|
||||
|
@ -289,6 +290,7 @@ public:
|
|||
|
||||
u32 thread_id;
|
||||
|
||||
bool can_schedule;
|
||||
ThreadStatus status;
|
||||
VAddr entry_point;
|
||||
VAddr stack_top;
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
|
@ -37,8 +39,8 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
VMManager::VMManager(Memory::MemorySystem& memory)
|
||||
: page_table(std::make_shared<Memory::PageTable>()), memory(memory) {
|
||||
VMManager::VMManager(Memory::MemorySystem& memory, Kernel::Process& proc)
|
||||
: page_table(std::make_shared<Memory::PageTable>()), memory(memory), process(proc) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
@ -383,6 +385,10 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
|||
memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler);
|
||||
break;
|
||||
}
|
||||
|
||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||
if (plgldr)
|
||||
plgldr->OnMemoryChanged(process, Core::System::GetInstance().Kernel());
|
||||
}
|
||||
|
||||
ResultVal<std::vector<std::pair<MemoryRef, u32>>> VMManager::GetBackingBlocksForRange(VAddr address,
|
||||
|
|
|
@ -131,7 +131,7 @@ public:
|
|||
std::map<VAddr, VirtualMemoryArea> vma_map;
|
||||
using VMAHandle = decltype(vma_map)::const_iterator;
|
||||
|
||||
explicit VMManager(Memory::MemorySystem& memory);
|
||||
explicit VMManager(Memory::MemorySystem& memory, Kernel::Process& proc);
|
||||
~VMManager();
|
||||
|
||||
/// Clears the address space map, re-initializing with a single free area.
|
||||
|
@ -254,6 +254,7 @@ private:
|
|||
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
|
||||
|
||||
Memory::MemorySystem& memory;
|
||||
Kernel::Process& process;
|
||||
|
||||
// When locked, ChangeMemoryState calls will be ignored, other modification calls will hit an
|
||||
// assert. VMManager locks itself after deserialization.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue