diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2023-01-04 19:15:45 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-04 23:15:45 +0100 |
| commit | 08831eecf77cedd3c4192ebab5a9c485fb15d51e (patch) | |
| tree | 6d95b921a18e9cfa477579fcecb9d041e03d682e /Ryujinx.Horizon/Sm | |
| parent | c6a139a6e7e3ffe1591bc14dafafed60b9bef0dc (diff) | |
IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization
* Make types match on calls to AlignUp/AlignDown
* Formatting
* Address some PR feedback
* Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations
* Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory
* Implement EventType
* Address more PR feedback
* Log request processing errors since they are not normal
* Rename waitable to multiwait and add missing lock
* PR feedback
* Ac_K PR feedback
Diffstat (limited to 'Ryujinx.Horizon/Sm')
| -rw-r--r-- | Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs | 20 | ||||
| -rw-r--r-- | Ryujinx.Horizon/Sm/Impl/ServiceManager.cs | 197 | ||||
| -rw-r--r-- | Ryujinx.Horizon/Sm/ManagerService.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Horizon/Sm/SmMain.cs | 30 | ||||
| -rw-r--r-- | Ryujinx.Horizon/Sm/SmResult.cs | 19 | ||||
| -rw-r--r-- | Ryujinx.Horizon/Sm/UserService.cs | 66 |
6 files changed, 340 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs new file mode 100644 index 00000000..fed420aa --- /dev/null +++ b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Impl +{ + struct ServiceInfo + { + public ServiceName Name; + public ulong OwnerProcessId; + public int PortHandle; + + public void Free() + { + HorizonStatic.Syscall.CloseHandle(PortHandle); + + Name = ServiceName.Invalid; + OwnerProcessId = 0L; + PortHandle = 0; + } + } +} diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs new file mode 100644 index 00000000..cdf2d17f --- /dev/null +++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -0,0 +1,197 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Impl +{ + class ServiceManager + { + private const int MaxServicesCount = 256; + + private readonly ServiceInfo[] _services; + + public ServiceManager() + { + _services = new ServiceInfo[MaxServicesCount]; + } + + public Result GetService(out int handle, ulong processId, ServiceName name) + { + handle = 0; + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + int serviceIndex = GetServiceInfo(name); + + if (serviceIndex < 0) + { + return SfResult.RequestDeferredByUser; + } + + result = GetServiceImpl(out handle, ref _services[serviceIndex]); + + if (result == KernelResult.SessionCountExceeded) + { + return SmResult.OutOfSessions; + } + + return result; + } + + private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo) + { + return HorizonStatic.Syscall.ConnectToPort(out handle, serviceInfo.PortHandle); + } + + public Result RegisterService(out int handle, ulong processId, ServiceName name, int maxSessions, bool isLight) + { + handle = 0; + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + if (HasServiceInfo(name)) + { + return SmResult.AlreadyRegistered; + } + + return RegisterServiceImpl(out handle, processId, name, maxSessions, isLight); + } + + public Result RegisterServiceForSelf(out int handle, ServiceName name, int maxSessions) + { + return RegisterServiceImpl(out handle, Os.GetCurrentProcessId(), name, maxSessions, false); + } + + private Result RegisterServiceImpl(out int handle, ulong processId, ServiceName name, int maxSessions, bool isLight) + { + handle = 0; + + Result result = ValidateServiceName(name); + + if (!result.IsSuccess) + { + return result; + } + + if (HasServiceInfo(name)) + { + return SmResult.AlreadyRegistered; + } + + int freeServiceIndex = GetFreeService(); + + if (freeServiceIndex < 0) + { + return SmResult.OutOfServices; + } + + ref ServiceInfo freeService = ref _services[freeServiceIndex]; + + result = HorizonStatic.Syscall.CreatePort(out handle, out int clientPort, maxSessions, isLight, null); + + if (!result.IsSuccess) + { + return result; + } + + freeService.PortHandle = clientPort; + freeService.Name = name; + freeService.OwnerProcessId = processId; + + return Result.Success; + } + + public Result UnregisterService(ulong processId, ServiceName name) + { + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + int serviceIndex = GetServiceInfo(name); + + if (serviceIndex < 0) + { + return SmResult.NotRegistered; + } + + ref var serviceInfo = ref _services[serviceIndex]; + + if (serviceInfo.OwnerProcessId != processId) + { + return SmResult.NotAllowed; + } + + serviceInfo.Free(); + return Result.Success; + } + + private static Result ValidateServiceName(ServiceName name) + { + if (name[0] == 0) + { + return SmResult.InvalidServiceName; + } + + int nameLength = 1; + + for (; nameLength < name.Length; nameLength++) + { + if (name[nameLength] == 0) + { + break; + } + } + + while (nameLength < name.Length) + { + if (name[nameLength++] != 0) + { + return SmResult.InvalidServiceName; + } + } + + return Result.Success; + } + + private bool HasServiceInfo(ServiceName name) + { + return GetServiceInfo(name) != -1; + } + + private int GetFreeService() + { + return GetServiceInfo(ServiceName.Invalid); + } + + private int GetServiceInfo(ServiceName name) + { + for (int index = 0; index < MaxServicesCount; index++) + { + if (_services[index].Name == name) + { + return index; + } + } + + return -1; + } + } +} diff --git a/Ryujinx.Horizon/Sm/ManagerService.cs b/Ryujinx.Horizon/Sm/ManagerService.cs new file mode 100644 index 00000000..1719dcfd --- /dev/null +++ b/Ryujinx.Horizon/Sm/ManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sm +{ + partial class ManagerService : IServiceObject + { + } +} diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs new file mode 100644 index 00000000..8c37bece --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmMain.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; + +namespace Ryujinx.Horizon.Sm +{ + public class SmMain + { + private enum PortIndex + { + User, + Manager + } + + private const int MaxPortsCount = 2; + + private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0); + private readonly ServiceManager _serviceManager = new ServiceManager(); + + public void Main() + { + HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", 64).AbortOnFailure(); + + _serverManager.RegisterServer((int)PortIndex.User, smHandle); + _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure(); + _serverManager.RegisterServer((int)PortIndex.Manager, smmHandle); + _serverManager.ServiceRequests(); + } + } +} diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/Ryujinx.Horizon/Sm/SmResult.cs new file mode 100644 index 00000000..3063445d --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmResult.cs @@ -0,0 +1,19 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sm +{ + static class SmResult + { + private const int ModuleId = 21; + + public static Result OutOfProcess => new Result(ModuleId, 1); + public static Result InvalidClient => new Result(ModuleId, 2); + public static Result OutOfSessions => new Result(ModuleId, 3); + public static Result AlreadyRegistered => new Result(ModuleId, 4); + public static Result OutOfServices => new Result(ModuleId, 5); + public static Result InvalidServiceName => new Result(ModuleId, 6); + public static Result NotRegistered => new Result(ModuleId, 7); + public static Result NotAllowed => new Result(ModuleId, 8); + public static Result TooLargeAccessControl => new Result(ModuleId, 9); + } +} diff --git a/Ryujinx.Horizon/Sm/UserService.cs b/Ryujinx.Horizon/Sm/UserService.cs new file mode 100644 index 00000000..d3b4537b --- /dev/null +++ b/Ryujinx.Horizon/Sm/UserService.cs @@ -0,0 +1,66 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; + +namespace Ryujinx.Horizon.Sm +{ + partial class UserService : IServiceObject + { + private readonly ServiceManager _serviceManager; + + private ulong _clientProcessId; + private bool _initialized; + + public UserService(ServiceManager serviceManager) + { + _serviceManager = serviceManager; + } + + [CmifCommand(0)] + public Result Initialize([ClientProcessId] ulong clientProcessId) + { + _clientProcessId = clientProcessId; + _initialized = true; + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetService([MoveHandle] out int handle, ServiceName name) + { + if (!_initialized) + { + handle = 0; + + return SmResult.InvalidClient; + } + + return _serviceManager.GetService(out handle, _clientProcessId, name); + } + + [CmifCommand(2)] + public Result RegisterService([MoveHandle] out int handle, ServiceName name, int maxSessions, bool isLight) + { + if (!_initialized) + { + handle = 0; + + return SmResult.InvalidClient; + } + + return _serviceManager.RegisterService(out handle, _clientProcessId, name, maxSessions, isLight); + } + + [CmifCommand(3)] + public Result UnregisterService(ServiceName name) + { + if (!_initialized) + { + return SmResult.InvalidClient; + } + + return _serviceManager.UnregisterService(_clientProcessId, name); + } + } +} |
