From 4ef2af8c98354aea8a4a3758d798c6a2b7bf9321 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 26 Nov 2018 18:23:12 -0500 Subject: [PATCH 1/4] svc: Implement svcCreateResourceLimit() This function simply creates a ResourceLimit instance and attempts to create a handle for it within the current process' handle table. If the kernal fails to either create the ResourceLimit instance or create a handle for the ResourceLimit instance, it returns a failure code (OUT_OF_RESOURCE, and HANDLE_TABLE_FULL respectively). Finally, it exits by providing the output parameter with the handle value for the ResourceLimit instance and returning that it was successful. Note: We do not return OUT_OF_RESOURCE because, if yuzu runs out of available memory, then new will currently throw. We *could* allocate the kernel instance with std::nothrow, however this would be inconsistent with how all other kernel objects are currently allocated. --- src/core/hle/kernel/svc.cpp | 20 +++++++++++++++++++- src/core/hle/kernel/svc_wrap.h | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f287f7c97..4b7991c32 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1346,6 +1346,24 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { return RESULT_SUCCESS; } +static ResultCode CreateResourceLimit(Handle* out_handle) { + LOG_DEBUG(Kernel_SVC, "called"); + + auto& kernel = Core::System::GetInstance().Kernel(); + auto resource_limit = ResourceLimit::Create(kernel); + + auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit)); + if (handle.Failed()) { + return handle.Code(); + } + + *out_handle = *handle; + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -1482,7 +1500,7 @@ static const FunctionDef SVC_Table[] = { {0x7A, nullptr, "StartProcess"}, {0x7B, nullptr, "TerminateProcess"}, {0x7C, SvcWrap, "GetProcessInfo"}, - {0x7D, nullptr, "CreateResourceLimit"}, + {0x7D, SvcWrap, "CreateResourceLimit"}, {0x7E, nullptr, "SetResourceLimitLimitValue"}, {0x7F, nullptr, "CallSecureMonitor"}, }; diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 233a99fb0..fa1116624 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -43,6 +43,14 @@ void SvcWrap() { FuncReturn(func(static_cast(Param(0)), static_cast(Param(1))).raw); } +template +void SvcWrap() { + u32 param = 0; + const u32 retval = func(¶m).raw; + Core::CurrentArmInterface().SetReg(1, param); + FuncReturn(retval); +} + template void SvcWrap() { u32 param_1 = 0; From 1d6399c222e6c0478193160648cddf43b8d8eff9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 26 Nov 2018 18:48:07 -0500 Subject: [PATCH 2/4] svc: Implement svcGetResourceLimitLimitValue() This kernel service function retrieves the maximum allowable value for a provided resource category for a given resource limit instance. Given we already have the functionality added to the resource limit instance itself, it's sufficient to just hook it up. The error scenarios for this are: 1. If an invalid resource category type is provided, then ERR_INVALID_ENUM is returned. 2. If an invalid handle is provided, then ERR_INVALID_HANDLE is returned (bad thing goes in, bad thing goes out, as one would expect). If neither of the above error cases occur, then the out parameter is provided with the maximum limit value for the given category and success is returned. --- src/core/hle/kernel/resource_limit.h | 6 +++++- src/core/hle/kernel/svc.cpp | 29 +++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index bec065543..59dc11c22 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -14,7 +14,7 @@ namespace Kernel { class KernelCore; -enum class ResourceType { +enum class ResourceType : u32 { PhysicalMemory, Threads, Events, @@ -25,6 +25,10 @@ enum class ResourceType { ResourceTypeCount }; +constexpr bool IsValidResourceType(ResourceType type) { + return type < ResourceType::ResourceTypeCount; +} + class ResourceLimit final : public Object { public: /** diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4b7991c32..5e604411b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1364,6 +1364,33 @@ static ResultCode CreateResourceLimit(Handle* out_handle) { return RESULT_SUCCESS; } +static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, + u32 resource_type) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); + + const auto type = static_cast(resource_type); + if (!IsValidResourceType(type)) { + LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'.", resource_type); + return ERR_INVALID_ENUM_VALUE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + const auto resource_limit_object = + current_process->GetHandleTable().Get(resource_limit); + if (!resource_limit_object) { + LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", + resource_limit); + return ERR_INVALID_HANDLE; + } + + const s64 limit_value = resource_limit_object->GetMaxResourceValue(type); + *out_value = static_cast(limit_value); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -1423,7 +1450,7 @@ static const FunctionDef SVC_Table[] = { {0x2D, nullptr, "UnmapPhysicalMemory"}, {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, - {0x30, nullptr, "GetResourceLimitLimitValue"}, + {0x30, SvcWrap, "GetResourceLimitLimitValue"}, {0x31, nullptr, "GetResourceLimitCurrentValue"}, {0x32, SvcWrap, "SetThreadActivity"}, {0x33, SvcWrap, "GetThreadContext"}, From eb5596044dfb3ee862ca239f9cb08eb4b9b3903f Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 26 Nov 2018 19:14:29 -0500 Subject: [PATCH 3/4] svc: Implement svcGetResourceLimitCurrentValue() This kernel service function is essentially the exact same as svcGetResourceLimitLimitValue(), with the only difference being that it retrieves the current value for a given resource category using the provided resource limit handle, rather than retrieving the limiting value of that resource limit instance. Given these are exactly the same and only differ on returned values, we can extract the existing code for svcGetResourceLimitLimitValue() to handle both values. --- src/core/hle/kernel/svc.cpp | 65 ++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5e604411b..9d1b000b5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -105,6 +105,38 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add return RESULT_SUCCESS; } + +enum class ResourceLimitValueType { + CurrentValue, + LimitValue, +}; + +ResultVal RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, + ResourceLimitValueType value_type) { + const auto type = static_cast(resource_type); + if (!IsValidResourceType(type)) { + LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); + return ERR_INVALID_ENUM_VALUE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + const auto resource_limit_object = + current_process->GetHandleTable().Get(resource_limit); + if (!resource_limit_object) { + LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", + resource_limit); + return ERR_INVALID_HANDLE; + } + + if (value_type == ResourceLimitValueType::CurrentValue) { + return MakeResult(resource_limit_object->GetCurrentResourceValue(type)); + } + + return MakeResult(resource_limit_object->GetMaxResourceValue(type)); +} } // Anonymous namespace /// Set the process heap to a given Size. It can both extend and shrink the heap. @@ -1368,26 +1400,27 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim u32 resource_type) { LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); - const auto type = static_cast(resource_type); - if (!IsValidResourceType(type)) { - LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'.", resource_type); - return ERR_INVALID_ENUM_VALUE; + const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, + ResourceLimitValueType::LimitValue); + if (limit_value.Failed()) { + return limit_value.Code(); } - const auto& kernel = Core::System::GetInstance().Kernel(); - const auto* const current_process = kernel.CurrentProcess(); - ASSERT(current_process != nullptr); + *out_value = static_cast(*limit_value); + return RESULT_SUCCESS; +} - const auto resource_limit_object = - current_process->GetHandleTable().Get(resource_limit); - if (!resource_limit_object) { - LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", - resource_limit); - return ERR_INVALID_HANDLE; +static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, + u32 resource_type) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); + + const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, + ResourceLimitValueType::CurrentValue); + if (current_value.Failed()) { + return current_value.Code(); } - const s64 limit_value = resource_limit_object->GetMaxResourceValue(type); - *out_value = static_cast(limit_value); + *out_value = static_cast(*current_value); return RESULT_SUCCESS; } @@ -1451,7 +1484,7 @@ static const FunctionDef SVC_Table[] = { {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, SvcWrap, "GetResourceLimitLimitValue"}, - {0x31, nullptr, "GetResourceLimitCurrentValue"}, + {0x31, SvcWrap, "GetResourceLimitCurrentValue"}, {0x32, SvcWrap, "SetThreadActivity"}, {0x33, SvcWrap, "GetThreadContext"}, {0x34, SvcWrap, "WaitForAddress"}, From 5905162e364abe8312cd95dd04555aaefcfc2293 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 26 Nov 2018 19:51:09 -0500 Subject: [PATCH 4/4] svc: Implement svcSetResourceLimitLimitValue() The opposite of the getter functions, this function sets the limit value for a particular ResourceLimit resource category, with the restriction that the new limit value must be equal to or greater than the current resource value. If this is violated, then ERR_INVALID_STATE is returned. e.g. Assume: current[Events] = 10; limit[Events] = 20; a call to this service function lowering the limit value to 10 would be fine, however, attempting to lower it to 9 in this case would cause an invalid state error. --- src/core/hle/kernel/svc.cpp | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9d1b000b5..1f19d5576 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1424,6 +1424,41 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l return RESULT_SUCCESS; } +static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, + resource_type, value); + + const auto type = static_cast(resource_type); + if (!IsValidResourceType(type)) { + LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); + return ERR_INVALID_ENUM_VALUE; + } + + auto& kernel = Core::System::GetInstance().Kernel(); + auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + auto resource_limit_object = + current_process->GetHandleTable().Get(resource_limit); + if (!resource_limit_object) { + LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", + resource_limit); + return ERR_INVALID_HANDLE; + } + + const auto set_result = resource_limit_object->SetLimitValue(type, static_cast(value)); + if (set_result.IsError()) { + LOG_ERROR( + Kernel_SVC, + "Attempted to lower resource limit ({}) for category '{}' below its current value ({})", + resource_limit_object->GetMaxResourceValue(type), resource_type, + resource_limit_object->GetCurrentResourceValue(type)); + return set_result; + } + + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -1561,7 +1596,7 @@ static const FunctionDef SVC_Table[] = { {0x7B, nullptr, "TerminateProcess"}, {0x7C, SvcWrap, "GetProcessInfo"}, {0x7D, SvcWrap, "CreateResourceLimit"}, - {0x7E, nullptr, "SetResourceLimitLimitValue"}, + {0x7E, SvcWrap, "SetResourceLimitLimitValue"}, {0x7F, nullptr, "CallSecureMonitor"}, };