2026-09-25 15:39:00

Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie
2026-09-25 15:39:00 +00:00
parent bebc19da32
commit 9e59b4132a
7 changed files with 226 additions and 166 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Design Overview
Modern game consoles require heavy power to be emulated appropriately. This is why the emulator uses an approach known as HLE (High-Level-Emulation), in a nutshell: Instead of accurately emulating every subsystem that forms part of a component, emulate the resulting visible I/O interface instead.
Modern game consoles require heavy power to emulate. This is why the emulator uses an approach known as HLE (High-Level-Emulation), in a nutshell: Instead of accurately emulating every subsystem that forms part of a component, emulate the resulting visible output of said interface instead.
For example, take a disk write, instead of emulating a proper SD card we instead use the C++ standard library for I/O. Additionally we use the abstractions provided by the `fs` service to "lie" to programs about certain SD card properties. Notably this includes making up sizes for the fake SD card, giving "realistic" values or expected outputs for a given card, and so on. And instead of writing to an actual SD card, the emulator simply writes to a file.
+1 -1
View File
@@ -21,7 +21,7 @@ This contains documentation created by developers, build instructions, guideline
- Subsystems:
- **[Design Overview](./DesignOverview.md)**
- **[Dynarmic](./dynarmic/README.md)**
- **[HOS Kernel](./HosKernel.md)**
- **[Subsystem: HLE](./SubsystemHLE.md)**
- **[Settings](./Settings.md)**
## Policies
+42 -1
View File
@@ -1,4 +1,6 @@
# HOS Kernel
# Subsystem: HLE
## HOS Kernel
In brief, the HOS kernel is a microkernel, all services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack.
@@ -29,3 +31,42 @@ Every process keeps it's own tracking of the following structures:
The emulator willingly restricts itself to only use 4 threads (to emulate 4 cores), this is because most existing applications do not benefit greatly from the added core count, and in fact can be detrimental due to extra contention. This translates equitatively to about 4 `ArmInterface` slots for each process, these are then redirected to whatever is the last `pc` of the last thread running on the core is meant to be; proceed to run it, then when returning (due to halt or interruption), proceed to reschedule the thread.
The scheduler as-is isn't 100% faithful to the original (for example the original is cooperative and not preemptive), and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings).
## Services
Consult [SwitchBrew](https://switchbrew.org/) for per-service methods, and implementation details.
All services are instatiated implicitly using `ServiceFramework`. To register a new interface or service, you can inherit from said class, providing additionally a template parameter that references `Self`, for example:
```c++
// Follows as:
// class MyInterface [final] : public ServiceFramework<MyInterface> { ... };
// [final] is optional, but should be used whenever there is no intention of this class itself being inherited.
class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> {
public:
// ctor, you can pass extra parameters here (if so required)
explicit IFloatingRegistrationRequest(Core::System& system_)
: ServiceFramework{system_, "IFloatingRegistrationRequest"}
{}
// Define here your functions and methods, please order them.
// Use FindRequestTipc for TIPC handlers.
FunctionInfoBase const* FindRequest(u32 key) override {
return HandlerTableGenerateWithFind(key,
FunctionInfo{0, nullptr, "GetSessionId"},
FunctionInfo{12, nullptr, "GetAccountId"},
FunctionInfo{13, nullptr, "GetLinkedNintendoAccountId"},
FunctionInfo{14, nullptr, "GetNickname"},
FunctionInfo{15, nullptr, "GetProfileImage"},
FunctionInfo{16, nullptr, "GetProfileLargeImage", MakeVersionGate({18,0,0})},
FunctionInfo{21, nullptr, "LoadIdTokenCache"},
FunctionInfo{100, nullptr, "RegisterUser"},
FunctionInfo{101, nullptr, "RegisterUserWithUid"},
FunctionInfo{102, nullptr, "RegisterNetworkServiceAccountAsync", MakeVersionGate({4,0,0})},
FunctionInfo{103, nullptr, "RegisterNetworkServiceAccountWithUidAsync", MakeVersionGate({4,0,0})},
FunctionInfo{110, nullptr, "SetSystemProgramIdentification"},
FunctionInfo{111, nullptr, "EnsureIdTokenCacheAsync"}
);
}
};
```
+105 -109
View File
@@ -75,41 +75,6 @@ class IManagerForSystemService final : public ServiceFramework<IManagerForSystem
public:
explicit IManagerForSystemService(Core::System& system_, Common::UUID uuid)
: ServiceFramework{system_, "IManagerForSystemService"}, account_id{uuid} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IManagerForSystemService::CheckAvailability>, "CheckAvailability"},
{1, D<&IManagerForSystemService::GetAccountId>, "GetAccountId"},
{2, nullptr, "EnsureIdTokenCacheAsync"},
{3, D<&IManagerForSystemService::LoadIdTokenCacheDeprecated>, "LoadIdTokenCacheDeprecated"}, // 19.0.0+
{4, D<&IManagerForSystemService::LoadIdTokenCache>, "LoadIdTokenCache"}, // 19.0.0+
{100, nullptr, "SetSystemProgramIdentification"},
{101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
{110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
{111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
{112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
{113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
{120, nullptr, "GetNintendoAccountId"},
{121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
{130, nullptr, "GetNintendoAccountUserResourceCache"},
{131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
{132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
{133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
{134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
{135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
{136, nullptr, "GetNintendoAccountUserResourceCache"}, // 19.0.0+
{140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
{141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
{142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
{143, D<&IManagerForSystemService::GetNetworkServiceLicenseCacheEx>, "GetNetworkServiceLicenseCacheEx"}, // 15.0.0+
{150, nullptr, "CreateAuthorizationRequest"},
{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
{170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+
{180, nullptr, "GetRequestForNintendoAccountReauthentication"} // 18.0.0+
};
// clang-format on
RegisterHandlers(functions);
}
private:
@@ -143,6 +108,39 @@ private:
R_SUCCEED();
}
FunctionInfoBase const* FindRequest(u32 key) override {
return HandlerTableGenerateWithFind(key,
FunctionInfo{0, D<&IManagerForSystemService::CheckAvailability>, "CheckAvailability"},
FunctionInfo{1, D<&IManagerForSystemService::GetAccountId>, "GetAccountId"},
FunctionInfo{2, nullptr, "EnsureIdTokenCacheAsync"},
FunctionInfo{3, D<&IManagerForSystemService::LoadIdTokenCacheDeprecated>, "LoadIdTokenCacheDeprecated"}, // 19.0.0+
FunctionInfo{4, D<&IManagerForSystemService::LoadIdTokenCache>, "LoadIdTokenCache"}, // 19.0.0+
FunctionInfo{100, nullptr, "SetSystemProgramIdentification"},
FunctionInfo{101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
FunctionInfo{110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
FunctionInfo{111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
FunctionInfo{112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
FunctionInfo{113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
FunctionInfo{120, nullptr, "GetNintendoAccountId"},
FunctionInfo{121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
FunctionInfo{130, nullptr, "GetNintendoAccountUserResourceCache"},
FunctionInfo{131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
FunctionInfo{132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
FunctionInfo{133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
FunctionInfo{134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
FunctionInfo{135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
FunctionInfo{136, nullptr, "GetNintendoAccountUserResourceCache"}, // 19.0.0+
FunctionInfo{140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
FunctionInfo{141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
FunctionInfo{142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
FunctionInfo{143, D<&IManagerForSystemService::GetNetworkServiceLicenseCacheEx>, "GetNetworkServiceLicenseCacheEx"}, // 15.0.0+
FunctionInfo{150, nullptr, "CreateAuthorizationRequest"},
FunctionInfo{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
FunctionInfo{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
FunctionInfo{170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+
FunctionInfo{180, nullptr, "GetRequestForNintendoAccountReauthentication"} // 18.0.0+
);
}
Common::UUID account_id;
};
@@ -151,25 +149,24 @@ class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegi
public:
explicit IFloatingRegistrationRequest(Core::System& system_, Common::UUID)
: ServiceFramework{system_, "IFloatingRegistrationRequest"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetSessionId"},
{12, nullptr, "GetAccountId"},
{13, nullptr, "GetLinkedNintendoAccountId"},
{14, nullptr, "GetNickname"},
{15, nullptr, "GetProfileImage"},
{16, nullptr, "GetProfileLargeImage"}, // 18.0.0+
{21, nullptr, "LoadIdTokenCache"},
{100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync
{101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync
{102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+
{103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+
{110, nullptr, "SetSystemProgramIdentification"},
{111, nullptr, "EnsureIdTokenCacheAsync"},
};
// clang-format on
}
RegisterHandlers(functions);
FunctionInfoBase const* FindRequest(u32 key) override {
return HandlerTableGenerateWithFind(key,
FunctionInfo{0, nullptr, "GetSessionId"},
FunctionInfo{12, nullptr, "GetAccountId"},
FunctionInfo{13, nullptr, "GetLinkedNintendoAccountId"},
FunctionInfo{14, nullptr, "GetNickname"},
FunctionInfo{15, nullptr, "GetProfileImage"},
FunctionInfo{16, nullptr, "GetProfileLargeImage", MakeVersionGate({18,0,0})},
FunctionInfo{21, nullptr, "LoadIdTokenCache"},
FunctionInfo{100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync
FunctionInfo{101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync
FunctionInfo{102, nullptr, "RegisterNetworkServiceAccountAsync", MakeVersionGate({4,0,0})},
FunctionInfo{103, nullptr, "RegisterNetworkServiceAccountWithUidAsync", MakeVersionGate({4,0,0})},
FunctionInfo{110, nullptr, "SetSystemProgramIdentification"},
FunctionInfo{111, nullptr, "EnsureIdTokenCacheAsync"}
);
}
};
@@ -177,62 +174,6 @@ class IAdministrator final : public ServiceFramework<IAdministrator> {
public:
explicit IAdministrator(Core::System& system_, Common::UUID)
: ServiceFramework{system_, "IAdministrator"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CheckAvailability"},
{1, nullptr, "GetAccountId"},
{2, nullptr, "EnsureIdTokenCacheAsync"},
{3, nullptr, "LoadIdTokenCache"},
{100, nullptr, "SetSystemProgramIdentification"},
{101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
{110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
{111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
{112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
{113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
{120, nullptr, "GetNintendoAccountId"},
{121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
{130, nullptr, "GetNintendoAccountUserResourceCache"},
{131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
{132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
{133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
{134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+
{135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
{140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
{141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
{142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
{143, nullptr, "GetNetworkServiceLicenseCacheEx"},
{150, nullptr, "CreateAuthorizationRequest"},
{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
{170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+
{180, nullptr, "GetRequestForNintendoAccountReauthentication"}, // 18.0.0+
{200, nullptr, "IsRegistered"},
{201, nullptr, "RegisterAsync"},
{202, nullptr, "UnregisterAsync"},
{203, nullptr, "DeleteRegistrationInfoLocally"},
{220, nullptr, "SynchronizeProfileAsync"},
{221, nullptr, "UploadProfileAsync"},
{222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
{250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"},
{251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
{252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
{255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
{256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"},
{260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
{261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
{280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"},
{290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+
{300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+
{400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+
{401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+
{900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+
{901, nullptr, "ImportAsyncForWin"}, // 9.0.0+
{997, nullptr, "DebugUnlinkNintendoAccountAsync"},
{998, nullptr, "DebugSetAvailabilityErrorDetail"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
@@ -241,6 +182,61 @@ private:
rb.Push(ResultSuccess);
rb.Push(false);
}
FunctionInfoBase const* FindRequest(u32 key) override {
return HandlerTableGenerateWithFind(key,
FunctionInfo{0, nullptr, "CheckAvailability"},
FunctionInfo{1, nullptr, "GetAccountId"},
FunctionInfo{2, nullptr, "EnsureIdTokenCacheAsync"},
FunctionInfo{3, nullptr, "LoadIdTokenCache"},
FunctionInfo{100, nullptr, "SetSystemProgramIdentification"},
FunctionInfo{101, nullptr, "RefreshNotificationTokenAsync", MakeVersionGate({7,0,0})},
FunctionInfo{110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
FunctionInfo{111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
FunctionInfo{112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
FunctionInfo{113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
FunctionInfo{120, nullptr, "GetNintendoAccountId"},
FunctionInfo{121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
FunctionInfo{130, nullptr, "GetNintendoAccountUserResourceCache"},
FunctionInfo{131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
FunctionInfo{132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
FunctionInfo{133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
FunctionInfo{134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+
FunctionInfo{135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
FunctionInfo{140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
FunctionInfo{141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
FunctionInfo{142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
FunctionInfo{143, nullptr, "GetNetworkServiceLicenseCacheEx"},
FunctionInfo{150, nullptr, "CreateAuthorizationRequest"},
FunctionInfo{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
FunctionInfo{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
FunctionInfo{170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+
FunctionInfo{180, nullptr, "GetRequestForNintendoAccountReauthentication"}, // 18.0.0+
FunctionInfo{200, nullptr, "IsRegistered"},
FunctionInfo{201, nullptr, "RegisterAsync"},
FunctionInfo{202, nullptr, "UnregisterAsync"},
FunctionInfo{203, nullptr, "DeleteRegistrationInfoLocally"},
FunctionInfo{220, nullptr, "SynchronizeProfileAsync"},
FunctionInfo{221, nullptr, "UploadProfileAsync"},
FunctionInfo{222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
FunctionInfo{250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"},
FunctionInfo{251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
FunctionInfo{252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
FunctionInfo{255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
FunctionInfo{256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"},
FunctionInfo{260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
FunctionInfo{261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
FunctionInfo{280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"},
FunctionInfo{290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+
FunctionInfo{300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+
FunctionInfo{400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+
FunctionInfo{401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+
FunctionInfo{900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+
FunctionInfo{901, nullptr, "ImportAsyncForWin"}, // 9.0.0+
FunctionInfo{997, nullptr, "DebugUnlinkNintendoAccountAsync"},
FunctionInfo{998, nullptr, "DebugSetAvailabilityErrorDetail"}
);
}
};
class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
+4 -29
View File
@@ -47,22 +47,7 @@ ServiceFrameworkBase::~ServiceFrameworkBase() {
const auto guard = ServiceFrameworkBase::LockService();
}
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers.reserve(handlers.size() + n);
for (std::size_t i = 0; i < n; ++i)
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
}
void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers_tipc.reserve(handlers_tipc.size() + n);
for (std::size_t i = 0; i < n; ++i)
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]);
}
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
const FunctionInfoBase* info) {
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info) {
auto cmd_buf = ctx.CommandBuffer();
std::string function_name = info == nullptr ? "<unknown>" : info->name;
@@ -82,16 +67,10 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
}
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
const auto command = ctx.GetCommand();
auto it = handlers.find(command);
const bool is_cmd_read = command == 0;
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
if (info == nullptr || info->handler_callback == nullptr)
return ReportUnimplementedFunction(ctx, info);
const bool is_cmd_read = ctx.GetCommand() == 0;
auto const info = FindRequest(ctx.GetCommand());
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx);
if (is_i_storage && is_cmd_read) {
const auto* const process = ctx.GetThread().GetOwnerProcess();
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
@@ -101,11 +80,7 @@ void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
}
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
auto it = handlers_tipc.find(ctx.GetCommand());
FunctionInfoBase const* info = it == handlers_tipc.end() ? nullptr : &it->second;
if (info == nullptr || info->handler_callback == nullptr)
return ReportUnimplementedFunction(ctx, info);
auto const info = FindRequestTipc(ctx.GetCommand());
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx);
}
+72 -24
View File
@@ -7,7 +7,10 @@
#pragma once
#include <cstddef>
#include <memory>
#include <mutex>
#include <tuple>
#include "frozen/map.h"
#include "common/container/unordered_map.h"
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
@@ -77,30 +80,37 @@ protected:
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() noexcept {
return std::unique_lock{lock_service};
}
private:
template <typename T>
friend class ServiceFramework;
static constexpr u32 MakeVersionGate(std::tuple<u32, u32, u32> since, std::tuple<u32, u32, u32> until = {0, 0, 0}) {
auto const [s_maj, s_min, s_pat] = since;
auto const [u_maj, u_min, u_pat] = until;
return (u_pat << 0) | (u_min << 4) | (u_maj << 8)
| (s_pat << 12) | (s_min << 16) | (s_maj << 20);
}
struct FunctionInfoBase {
u32 expected_header;
HandlerFnP<ServiceFrameworkBase> handler_callback;
const char* name;
u32 version_gating;
};
private:
template <typename T>
friend class ServiceFramework;
using InvokerFn = void(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member,
HLERequestContext& ctx);
using InvokerFn = void(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member, HLERequestContext& ctx);
explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_);
explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_);
~ServiceFrameworkBase() override;
virtual FunctionInfoBase const* FindRequest(u32 key) = 0;
virtual FunctionInfoBase const* FindRequestTipc(u32 key) = 0;
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info);
protected:
::Common::unordered_map<u32, FunctionInfoBase> handlers;
::Common::unordered_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
std::mutex lock_service;
/// System context that the service operates under.
@@ -143,25 +153,60 @@ protected:
/// @param expected_header_ request header in the command buffer which will trigger dispatch to this handler
/// @param handler_callback_ member function in this service which will be called to handle the request
/// @param name_ human-friendly name for the request. Used mostly for logging purposes.
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{expected_header_, HandlerFnP<ServiceFrameworkBase>(handler_callback_), name_} {}
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_, u32 version_gating_ = 0)
: FunctionInfoBase{expected_header_, HandlerFnP<ServiceFrameworkBase>(handler_callback_), name_, version_gating_}
{}
};
using FunctionInfo = FunctionInfoTyped<Self>;
/**
* Initializes the handler with no functions installed.
*
* @param system_ The system context to construct this service under.
* @param service_name_ Name of the service.
* @param max_sessions_ Maximum number of sessions that can be connected to this service at the
* same time.
*/
template<typename ...Ts>
requires (std::same_as<Ts, FunctionInfo> && ...)
static FunctionInfoBase const* HandlerTableGenerateWithFind(u32 key, Ts... args) {
static auto const map = frozen::map<u32, FunctionInfo, sizeof...(args)>{
{args.expected_header, FunctionInfo(args)}...
};
auto const it = map.find(key);
return it != map.end() ? std::addressof(it->second) : nullptr;
}
/// @brief Initializes the handler with no functions installed.
/// @param system_ The system context to construct this service under.
/// @param service_name_ Name of the service.
/// @param max_sessions_ Maximum number of sessions that can be connected to this service at the
/// same time.
explicit ServiceFramework(Core::System& system_, const char* service_name_, u32 max_sessions_ = ServerSessionCountMax)
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker)
{}
FunctionInfoBase const* FindRequest(u32 key) override {
auto it = handlers.find(key);
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
return !(info == nullptr || info->handler_callback == nullptr) ? info : nullptr;
}
FunctionInfoBase const* FindRequestTipc(u32 key) override {
auto it = handlers_tipc.find(key);
FunctionInfoBase const* info = it == handlers_tipc.end() ? nullptr : &it->second;
return !(info == nullptr || info->handler_callback == nullptr) ? info : nullptr;
}
constexpr void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers.reserve(handlers.size() + n);
for (std::size_t i = 0; i < n; ++i)
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
}
constexpr void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers_tipc.reserve(handlers_tipc.size() + n);
for (std::size_t i = 0; i < n; ++i)
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]);
}
/// Registers handlers in the service.
template <typename T = Self, std::size_t N>
void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
constexpr void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlers(functions, N);
}
@@ -170,13 +215,13 @@ protected:
* overload in order to avoid needing to specify the array size.
*/
template <typename T = Self>
void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
constexpr void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBase(functions, n);
}
/// Registers handlers in the service.
template <typename T = Self, std::size_t N>
void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
constexpr void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlersTipc(functions, N);
}
@@ -185,7 +230,7 @@ protected:
* overload in order to avoid needing to specify the array size.
*/
template <typename T = Self>
void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
constexpr void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBaseTipc(functions, n);
}
@@ -217,6 +262,9 @@ private:
// Cast back up to our original types and call the member function
(static_cast<Self*>(object)->*HandlerFnP<Self>(member))(ctx);
}
::Common::unordered_map<u32, FunctionInfoBase> handlers;
::Common::unordered_map<u32, FunctionInfoBase> handlers_tipc;
};
} // namespace Service
+1 -1
View File
@@ -8,7 +8,7 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
## Binaries
- `maxwell-spirv`: Converts Maxwell shaders (dumped from `.ash` files) into SPIR-V code (emitted into STDOUT).
- `maxwell-spirv`: Converts Maxwell shaders (dumped from `.ash` files) into SPIR-V code sent to stdout.
- `maxwell-disas`: Dumb raw Maxwell disassembler.
- `maxwell-ir`: Dump generated IR of Maxwell shaders.