Syscall.cs
1 using Ryujinx.Common; 2 using Ryujinx.Common.Logging; 3 using Ryujinx.Cpu; 4 using Ryujinx.HLE.Exceptions; 5 using Ryujinx.HLE.HOS.Kernel.Common; 6 using Ryujinx.HLE.HOS.Kernel.Ipc; 7 using Ryujinx.HLE.HOS.Kernel.Memory; 8 using Ryujinx.HLE.HOS.Kernel.Process; 9 using Ryujinx.HLE.HOS.Kernel.Threading; 10 using Ryujinx.Horizon.Common; 11 using Ryujinx.Memory; 12 using System; 13 using System.Buffers; 14 using System.Threading; 15 16 namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall 17 { 18 [SvcImpl] 19 class Syscall : ISyscallApi 20 { 21 private readonly KernelContext _context; 22 23 public Syscall(KernelContext context) 24 { 25 _context = context; 26 } 27 28 // Process 29 30 [Svc(0x24)] 31 public Result GetProcessId(out ulong pid, int handle) 32 { 33 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 34 35 KProcess process = currentProcess.HandleTable.GetKProcess(handle); 36 37 if (process == null) 38 { 39 KThread thread = currentProcess.HandleTable.GetKThread(handle); 40 41 if (thread != null) 42 { 43 process = thread.Owner; 44 } 45 46 // TODO: KDebugEvent. 47 } 48 49 pid = process?.Pid ?? 0; 50 51 return process != null 52 ? Result.Success 53 : KernelResult.InvalidHandle; 54 } 55 56 public Result CreateProcess( 57 out int handle, 58 ProcessCreationInfo info, 59 ReadOnlySpan<uint> capabilities, 60 IProcessContextFactory contextFactory, 61 ThreadStart customThreadStart = null) 62 { 63 handle = 0; 64 65 if ((info.Flags & ~ProcessCreationFlags.All) != 0) 66 { 67 return KernelResult.InvalidEnumValue; 68 } 69 70 // TODO: Address space check. 71 72 if ((info.Flags & ProcessCreationFlags.PoolPartitionMask) > ProcessCreationFlags.PoolPartitionSystemNonSecure) 73 { 74 return KernelResult.InvalidEnumValue; 75 } 76 77 if ((info.CodeAddress & 0x1fffff) != 0) 78 { 79 return KernelResult.InvalidAddress; 80 } 81 82 if (info.CodePagesCount < 0 || info.SystemResourcePagesCount < 0) 83 { 84 return KernelResult.InvalidSize; 85 } 86 87 if (info.Flags.HasFlag(ProcessCreationFlags.EnableAliasRegionExtraSize)) 88 { 89 if ((info.Flags & ProcessCreationFlags.AddressSpaceMask) != ProcessCreationFlags.AddressSpace64Bit || 90 info.SystemResourcePagesCount <= 0) 91 { 92 return KernelResult.InvalidState; 93 } 94 95 // TODO: Check that we are in debug mode. 96 } 97 98 if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) && 99 !info.Flags.HasFlag(ProcessCreationFlags.IsApplication)) 100 { 101 return KernelResult.InvalidThread; 102 } 103 104 KHandleTable handleTable = KernelStatic.GetCurrentProcess().HandleTable; 105 106 KProcess process = new(_context); 107 108 using var _ = new OnScopeExit(process.DecrementReferenceCount); 109 110 KResourceLimit resourceLimit; 111 112 if (info.ResourceLimitHandle != 0) 113 { 114 resourceLimit = handleTable.GetObject<KResourceLimit>(info.ResourceLimitHandle); 115 116 if (resourceLimit == null) 117 { 118 return KernelResult.InvalidHandle; 119 } 120 } 121 else 122 { 123 resourceLimit = _context.ResourceLimit; 124 } 125 126 MemoryRegion memRegion = (info.Flags & ProcessCreationFlags.PoolPartitionMask) switch 127 { 128 ProcessCreationFlags.PoolPartitionApplication => MemoryRegion.Application, 129 ProcessCreationFlags.PoolPartitionApplet => MemoryRegion.Applet, 130 ProcessCreationFlags.PoolPartitionSystem => MemoryRegion.Service, 131 ProcessCreationFlags.PoolPartitionSystemNonSecure => MemoryRegion.NvServices, 132 _ => MemoryRegion.NvServices, 133 }; 134 135 Result result = process.Initialize( 136 info, 137 capabilities, 138 resourceLimit, 139 memRegion, 140 contextFactory, 141 customThreadStart); 142 143 if (result != Result.Success) 144 { 145 return result; 146 } 147 148 _context.Processes.TryAdd(process.Pid, process); 149 150 return handleTable.GenerateHandle(process, out handle); 151 } 152 153 public Result StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) 154 { 155 KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle); 156 157 if (process == null) 158 { 159 return KernelResult.InvalidHandle; 160 } 161 162 if ((uint)cpuCore >= KScheduler.CpuCoresCount || !process.IsCpuCoreAllowed(cpuCore)) 163 { 164 return KernelResult.InvalidCpuCore; 165 } 166 167 if ((uint)priority >= KScheduler.PrioritiesCount || !process.IsPriorityAllowed(priority)) 168 { 169 return KernelResult.InvalidPriority; 170 } 171 172 process.DefaultCpuCore = cpuCore; 173 174 Result result = process.Start(priority, mainThreadStackSize); 175 176 if (result != Result.Success) 177 { 178 return result; 179 } 180 181 process.IncrementReferenceCount(); 182 183 return Result.Success; 184 } 185 186 [Svc(0x5f)] 187 public Result FlushProcessDataCache(int processHandle, ulong address, ulong size) 188 { 189 // FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0. 190 // As we don't support (and don't actually need) to flush the cache, this is stubbed. 191 return Result.Success; 192 } 193 194 // IPC 195 196 [Svc(0x1f)] 197 public Result ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr) 198 { 199 handle = 0; 200 201 if (!KernelTransfer.UserToKernelString(out string name, namePtr, 12)) 202 { 203 return KernelResult.UserCopyFailed; 204 } 205 206 return ConnectToNamedPort(out handle, name); 207 } 208 209 public Result ConnectToNamedPort(out int handle, string name) 210 { 211 handle = 0; 212 213 if (name.Length > 11) 214 { 215 return KernelResult.MaximumExceeded; 216 } 217 218 KAutoObject autoObj = KAutoObject.FindNamedObject(_context, name); 219 220 if (autoObj is not KClientPort clientPort) 221 { 222 return KernelResult.NotFound; 223 } 224 225 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 226 227 Result result = currentProcess.HandleTable.ReserveHandle(out handle); 228 229 if (result != Result.Success) 230 { 231 return result; 232 } 233 234 result = clientPort.Connect(out KClientSession clientSession); 235 236 if (result != Result.Success) 237 { 238 currentProcess.HandleTable.CancelHandleReservation(handle); 239 240 return result; 241 } 242 243 currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession); 244 245 clientSession.DecrementReferenceCount(); 246 247 return result; 248 } 249 250 [Svc(0x21)] 251 public Result SendSyncRequest(int handle) 252 { 253 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 254 255 KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); 256 257 if (session == null) 258 { 259 return KernelResult.InvalidHandle; 260 } 261 262 return session.SendSyncRequest(); 263 } 264 265 [Svc(0x22)] 266 public Result SendSyncRequestWithUserBuffer( 267 [PointerSized] ulong messagePtr, 268 [PointerSized] ulong messageSize, 269 int handle) 270 { 271 if (!PageAligned(messagePtr)) 272 { 273 return KernelResult.InvalidAddress; 274 } 275 276 if (!PageAligned(messageSize) || messageSize == 0) 277 { 278 return KernelResult.InvalidSize; 279 } 280 281 if (messagePtr + messageSize <= messagePtr) 282 { 283 return KernelResult.InvalidMemState; 284 } 285 286 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 287 288 Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); 289 290 if (result != Result.Success) 291 { 292 return result; 293 } 294 295 KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); 296 297 if (session == null) 298 { 299 result = KernelResult.InvalidHandle; 300 } 301 else 302 { 303 result = session.SendSyncRequest(messagePtr, messageSize); 304 } 305 306 Result result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 307 308 if (result == Result.Success) 309 { 310 result = result2; 311 } 312 313 return result; 314 } 315 316 [Svc(0x23)] 317 public Result SendAsyncRequestWithUserBuffer( 318 out int doneEventHandle, 319 [PointerSized] ulong messagePtr, 320 [PointerSized] ulong messageSize, 321 int handle) 322 { 323 doneEventHandle = 0; 324 325 if (!PageAligned(messagePtr)) 326 { 327 return KernelResult.InvalidAddress; 328 } 329 330 if (!PageAligned(messageSize) || messageSize == 0) 331 { 332 return KernelResult.InvalidSize; 333 } 334 335 if (messagePtr + messageSize <= messagePtr) 336 { 337 return KernelResult.InvalidMemState; 338 } 339 340 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 341 342 Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); 343 344 if (result != Result.Success) 345 { 346 return result; 347 } 348 349 KResourceLimit resourceLimit = currentProcess.ResourceLimit; 350 351 if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Event, 1)) 352 { 353 currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 354 355 return KernelResult.ResLimitExceeded; 356 } 357 358 KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); 359 360 if (session == null) 361 { 362 result = KernelResult.InvalidHandle; 363 } 364 else 365 { 366 KEvent doneEvent = new(_context); 367 368 result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle); 369 370 if (result == Result.Success) 371 { 372 result = session.SendAsyncRequest(doneEvent.WritableEvent, messagePtr, messageSize); 373 374 if (result != Result.Success) 375 { 376 currentProcess.HandleTable.CloseHandle(doneEventHandle); 377 } 378 } 379 } 380 381 if (result != Result.Success) 382 { 383 resourceLimit?.Release(LimitableResource.Event, 1); 384 385 currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 386 } 387 388 return result; 389 } 390 391 [Svc(0x40)] 392 public Result CreateSession( 393 out int serverSessionHandle, 394 out int clientSessionHandle, 395 bool isLight, 396 [PointerSized] ulong namePtr) 397 { 398 return CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, null); 399 } 400 401 public Result CreateSession( 402 out int serverSessionHandle, 403 out int clientSessionHandle, 404 bool isLight, 405 string name) 406 { 407 serverSessionHandle = 0; 408 clientSessionHandle = 0; 409 410 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 411 412 KResourceLimit resourceLimit = currentProcess.ResourceLimit; 413 414 if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Session, 1)) 415 { 416 return KernelResult.ResLimitExceeded; 417 } 418 419 Result result; 420 421 if (isLight) 422 { 423 KLightSession session = new(_context); 424 425 result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); 426 427 if (result == Result.Success) 428 { 429 result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle); 430 431 if (result != Result.Success) 432 { 433 currentProcess.HandleTable.CloseHandle(serverSessionHandle); 434 435 serverSessionHandle = 0; 436 } 437 } 438 439 session.ServerSession.DecrementReferenceCount(); 440 session.ClientSession.DecrementReferenceCount(); 441 } 442 else 443 { 444 KSession session = new(_context); 445 446 result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); 447 448 if (result == Result.Success) 449 { 450 result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle); 451 452 if (result != Result.Success) 453 { 454 currentProcess.HandleTable.CloseHandle(serverSessionHandle); 455 456 serverSessionHandle = 0; 457 } 458 } 459 460 session.ServerSession.DecrementReferenceCount(); 461 session.ClientSession.DecrementReferenceCount(); 462 } 463 464 return result; 465 } 466 467 [Svc(0x41)] 468 public Result AcceptSession(out int sessionHandle, int portHandle) 469 { 470 sessionHandle = 0; 471 472 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 473 474 KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle); 475 476 if (serverPort == null) 477 { 478 return KernelResult.InvalidHandle; 479 } 480 481 Result result = currentProcess.HandleTable.ReserveHandle(out int handle); 482 483 if (result != Result.Success) 484 { 485 return result; 486 } 487 488 KAutoObject session; 489 490 if (serverPort.IsLight) 491 { 492 session = serverPort.AcceptIncomingLightConnection(); 493 } 494 else 495 { 496 session = serverPort.AcceptIncomingConnection(); 497 } 498 499 if (session != null) 500 { 501 currentProcess.HandleTable.SetReservedHandleObj(handle, session); 502 503 session.DecrementReferenceCount(); 504 505 sessionHandle = handle; 506 507 result = Result.Success; 508 } 509 else 510 { 511 currentProcess.HandleTable.CancelHandleReservation(handle); 512 513 result = KernelResult.NotFound; 514 } 515 516 return result; 517 } 518 519 [Svc(0x43)] 520 public Result ReplyAndReceive( 521 out int handleIndex, 522 [PointerSized] ulong handlesPtr, 523 int handlesCount, 524 int replyTargetHandle, 525 long timeout) 526 { 527 handleIndex = 0; 528 529 if ((uint)handlesCount > 0x40) 530 { 531 return KernelResult.MaximumExceeded; 532 } 533 534 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 535 536 ulong copySize = (ulong)((long)handlesCount * 4); 537 538 if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize)) 539 { 540 return KernelResult.UserCopyFailed; 541 } 542 543 if (handlesPtr + copySize < handlesPtr) 544 { 545 return KernelResult.UserCopyFailed; 546 } 547 548 int[] handles = new int[handlesCount]; 549 550 if (!KernelTransfer.UserToKernelArray<int>(handlesPtr, handles)) 551 { 552 return KernelResult.UserCopyFailed; 553 } 554 555 if (timeout > 0) 556 { 557 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 558 } 559 560 return ReplyAndReceive(out handleIndex, handles, replyTargetHandle, timeout); 561 } 562 563 public Result ReplyAndReceive(out int handleIndex, ReadOnlySpan<int> handles, int replyTargetHandle, long timeout) 564 { 565 handleIndex = 0; 566 567 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 568 569 KSynchronizationObject[] syncObjsArray = ArrayPool<KSynchronizationObject>.Shared.Rent(handles.Length); 570 571 Span<KSynchronizationObject> syncObjs = syncObjsArray.AsSpan(0, handles.Length); 572 573 for (int index = 0; index < handles.Length; index++) 574 { 575 KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]); 576 577 if (obj == null) 578 { 579 return KernelResult.InvalidHandle; 580 } 581 582 syncObjs[index] = obj; 583 } 584 585 Result result = Result.Success; 586 587 if (replyTargetHandle != 0) 588 { 589 KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle); 590 591 if (replyTarget == null) 592 { 593 result = KernelResult.InvalidHandle; 594 } 595 else 596 { 597 result = replyTarget.Reply(); 598 } 599 } 600 601 if (result == Result.Success) 602 { 603 if (timeout > 0) 604 { 605 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 606 } 607 608 while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == Result.Success) 609 { 610 KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); 611 612 if (session == null) 613 { 614 break; 615 } 616 617 if ((result = session.Receive()) != KernelResult.NotFound) 618 { 619 break; 620 } 621 } 622 } 623 624 ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray, true); 625 626 return result; 627 } 628 629 [Svc(0x44)] 630 public Result ReplyAndReceiveWithUserBuffer( 631 out int handleIndex, 632 [PointerSized] ulong messagePtr, 633 [PointerSized] ulong messageSize, 634 [PointerSized] ulong handlesPtr, 635 int handlesCount, 636 int replyTargetHandle, 637 long timeout) 638 { 639 handleIndex = 0; 640 641 if ((uint)handlesCount > 0x40) 642 { 643 return KernelResult.MaximumExceeded; 644 } 645 646 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 647 648 ulong copySize = (ulong)((long)handlesCount * 4); 649 650 if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize)) 651 { 652 return KernelResult.UserCopyFailed; 653 } 654 655 if (handlesPtr + copySize < handlesPtr) 656 { 657 return KernelResult.UserCopyFailed; 658 } 659 660 Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); 661 662 if (result != Result.Success) 663 { 664 return result; 665 } 666 667 int[] handles = new int[handlesCount]; 668 669 if (!KernelTransfer.UserToKernelArray<int>(handlesPtr, handles)) 670 { 671 currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 672 673 return KernelResult.UserCopyFailed; 674 } 675 676 KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount]; 677 678 for (int index = 0; index < handlesCount; index++) 679 { 680 KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]); 681 682 if (obj == null) 683 { 684 currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 685 686 return KernelResult.InvalidHandle; 687 } 688 689 syncObjs[index] = obj; 690 } 691 692 if (replyTargetHandle != 0) 693 { 694 KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle); 695 696 if (replyTarget == null) 697 { 698 result = KernelResult.InvalidHandle; 699 } 700 else 701 { 702 result = replyTarget.Reply(messagePtr, messageSize); 703 } 704 } 705 706 if (result == Result.Success) 707 { 708 if (timeout > 0) 709 { 710 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 711 } 712 713 while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == Result.Success) 714 { 715 KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); 716 717 if (session == null) 718 { 719 break; 720 } 721 722 if ((result = session.Receive(messagePtr, messageSize)) != KernelResult.NotFound) 723 { 724 break; 725 } 726 } 727 } 728 729 currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); 730 731 return result; 732 } 733 734 [Svc(0x70)] 735 public Result CreatePort( 736 out int serverPortHandle, 737 out int clientPortHandle, 738 int maxSessions, 739 bool isLight, 740 [PointerSized] ulong namePtr) 741 { 742 // The kernel doesn't use the name pointer, so we can just pass null as the name. 743 return CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, null); 744 } 745 746 public Result CreatePort( 747 out int serverPortHandle, 748 out int clientPortHandle, 749 int maxSessions, 750 bool isLight, 751 string name) 752 { 753 serverPortHandle = clientPortHandle = 0; 754 755 if (maxSessions < 1) 756 { 757 return KernelResult.MaximumExceeded; 758 } 759 760 KPort port = new(_context, maxSessions, isLight, name); 761 762 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 763 764 Result result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle); 765 766 if (result != Result.Success) 767 { 768 return result; 769 } 770 771 result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle); 772 773 if (result != Result.Success) 774 { 775 currentProcess.HandleTable.CloseHandle(clientPortHandle); 776 } 777 778 return result; 779 } 780 781 [Svc(0x71)] 782 public Result ManageNamedPort(out int handle, [PointerSized] ulong namePtr, int maxSessions) 783 { 784 handle = 0; 785 786 if (!KernelTransfer.UserToKernelString(out string name, namePtr, 12)) 787 { 788 return KernelResult.UserCopyFailed; 789 } 790 791 if (name.Length > 11) 792 { 793 return KernelResult.MaximumExceeded; 794 } 795 796 return ManageNamedPort(out handle, name, maxSessions); 797 } 798 799 public Result ManageNamedPort(out int handle, string name, int maxSessions) 800 { 801 handle = 0; 802 803 if (maxSessions < 0) 804 { 805 return KernelResult.MaximumExceeded; 806 } 807 808 if (maxSessions == 0) 809 { 810 return KAutoObject.RemoveName(_context, name); 811 } 812 813 KPort port = new(_context, maxSessions, false, null); 814 815 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 816 817 Result result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle); 818 819 if (result != Result.Success) 820 { 821 return result; 822 } 823 824 result = port.ClientPort.SetName(name); 825 826 if (result != Result.Success) 827 { 828 currentProcess.HandleTable.CloseHandle(handle); 829 } 830 831 return result; 832 } 833 834 [Svc(0x72)] 835 public Result ConnectToPort(out int clientSessionHandle, int clientPortHandle) 836 { 837 clientSessionHandle = 0; 838 839 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 840 841 KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle); 842 843 if (clientPort == null) 844 { 845 return KernelResult.InvalidHandle; 846 } 847 848 Result result = currentProcess.HandleTable.ReserveHandle(out int handle); 849 850 if (result != Result.Success) 851 { 852 return result; 853 } 854 855 KAutoObject session; 856 857 if (clientPort.IsLight) 858 { 859 result = clientPort.ConnectLight(out KLightClientSession clientSession); 860 861 session = clientSession; 862 } 863 else 864 { 865 result = clientPort.Connect(out KClientSession clientSession); 866 867 session = clientSession; 868 } 869 870 if (result != Result.Success) 871 { 872 currentProcess.HandleTable.CancelHandleReservation(handle); 873 874 return result; 875 } 876 877 currentProcess.HandleTable.SetReservedHandleObj(handle, session); 878 879 session.DecrementReferenceCount(); 880 881 clientSessionHandle = handle; 882 883 return result; 884 } 885 886 // Memory 887 888 [Svc(1)] 889 public Result SetHeapSize([PointerSized] out ulong address, [PointerSized] ulong size) 890 { 891 if ((size & 0xfffffffe001fffff) != 0) 892 { 893 address = 0; 894 895 return KernelResult.InvalidSize; 896 } 897 898 KProcess process = KernelStatic.GetCurrentProcess(); 899 900 return process.MemoryManager.SetHeapSize(size, out address); 901 } 902 903 [Svc(2)] 904 public Result SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) 905 { 906 if (!PageAligned(address)) 907 { 908 return KernelResult.InvalidAddress; 909 } 910 911 if (!PageAligned(size) || size == 0) 912 { 913 return KernelResult.InvalidSize; 914 } 915 916 if (address + size <= address) 917 { 918 return KernelResult.InvalidMemState; 919 } 920 921 if (permission != KMemoryPermission.None && (permission | KMemoryPermission.Write) != KMemoryPermission.ReadAndWrite) 922 { 923 return KernelResult.InvalidPermission; 924 } 925 926 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 927 928 if (!currentProcess.MemoryManager.InsideAddrSpace(address, size)) 929 { 930 return KernelResult.InvalidMemState; 931 } 932 933 return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission); 934 } 935 936 [Svc(3)] 937 public Result SetMemoryAttribute( 938 [PointerSized] ulong address, 939 [PointerSized] ulong size, 940 MemoryAttribute attributeMask, 941 MemoryAttribute attributeValue) 942 { 943 if (!PageAligned(address)) 944 { 945 return KernelResult.InvalidAddress; 946 } 947 948 if (!PageAligned(size) || size == 0) 949 { 950 return KernelResult.InvalidSize; 951 } 952 953 MemoryAttribute attributes = attributeMask | attributeValue; 954 955 const MemoryAttribute SupportedAttributes = MemoryAttribute.Uncached | MemoryAttribute.PermissionLocked; 956 957 if (attributes != attributeMask || 958 (attributes | SupportedAttributes) != SupportedAttributes) 959 { 960 return KernelResult.InvalidCombination; 961 } 962 963 // The permission locked attribute can't be unset. 964 if ((attributeMask & MemoryAttribute.PermissionLocked) != (attributeValue & MemoryAttribute.PermissionLocked)) 965 { 966 return KernelResult.InvalidCombination; 967 } 968 969 KProcess process = KernelStatic.GetCurrentProcess(); 970 971 if (!process.MemoryManager.InsideAddrSpace(address, size)) 972 { 973 return KernelResult.InvalidMemState; 974 } 975 976 Result result = process.MemoryManager.SetMemoryAttribute( 977 address, 978 size, 979 attributeMask, 980 attributeValue); 981 982 return result; 983 } 984 985 [Svc(4)] 986 public Result MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) 987 { 988 if (!PageAligned(src | dst)) 989 { 990 return KernelResult.InvalidAddress; 991 } 992 993 if (!PageAligned(size) || size == 0) 994 { 995 return KernelResult.InvalidSize; 996 } 997 998 if (src + size <= src || dst + size <= dst) 999 { 1000 return KernelResult.InvalidMemState; 1001 } 1002 1003 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1004 1005 if (!currentProcess.MemoryManager.InsideAddrSpace(src, size)) 1006 { 1007 return KernelResult.InvalidMemState; 1008 } 1009 1010 if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) || 1011 currentProcess.MemoryManager.InsideHeapRegion(dst, size) || 1012 currentProcess.MemoryManager.InsideAliasRegion(dst, size)) 1013 { 1014 return KernelResult.InvalidMemRange; 1015 } 1016 1017 KProcess process = KernelStatic.GetCurrentProcess(); 1018 1019 return process.MemoryManager.Map(dst, src, size); 1020 } 1021 1022 [Svc(5)] 1023 public Result UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) 1024 { 1025 if (!PageAligned(src | dst)) 1026 { 1027 return KernelResult.InvalidAddress; 1028 } 1029 1030 if (!PageAligned(size) || size == 0) 1031 { 1032 return KernelResult.InvalidSize; 1033 } 1034 1035 if (src + size <= src || dst + size <= dst) 1036 { 1037 return KernelResult.InvalidMemState; 1038 } 1039 1040 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1041 1042 if (!currentProcess.MemoryManager.InsideAddrSpace(src, size)) 1043 { 1044 return KernelResult.InvalidMemState; 1045 } 1046 1047 if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) || 1048 currentProcess.MemoryManager.InsideHeapRegion(dst, size) || 1049 currentProcess.MemoryManager.InsideAliasRegion(dst, size)) 1050 { 1051 return KernelResult.InvalidMemRange; 1052 } 1053 1054 KProcess process = KernelStatic.GetCurrentProcess(); 1055 1056 return process.MemoryManager.Unmap(dst, src, size); 1057 } 1058 1059 [Svc(6)] 1060 public Result QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address) 1061 { 1062 Result result = QueryMemory(out MemoryInfo info, out pageInfo, address); 1063 1064 if (result == Result.Success) 1065 { 1066 return KernelTransfer.KernelToUser(infoPtr, info) 1067 ? Result.Success 1068 : KernelResult.InvalidMemState; 1069 } 1070 1071 return result; 1072 } 1073 1074 public Result QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address) 1075 { 1076 KProcess process = KernelStatic.GetCurrentProcess(); 1077 1078 KMemoryInfo blockInfo = process.MemoryManager.QueryMemory(address); 1079 1080 info = new MemoryInfo( 1081 blockInfo.Address, 1082 blockInfo.Size, 1083 blockInfo.State & MemoryState.UserMask, 1084 blockInfo.Attribute, 1085 blockInfo.Permission & KMemoryPermission.UserMask, 1086 blockInfo.IpcRefCount, 1087 blockInfo.DeviceRefCount); 1088 1089 pageInfo = 0; 1090 1091 return Result.Success; 1092 } 1093 1094 [Svc(0x13)] 1095 public Result MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) 1096 { 1097 if (!PageAligned(address)) 1098 { 1099 return KernelResult.InvalidAddress; 1100 } 1101 1102 if (!PageAligned(size) || size == 0) 1103 { 1104 return KernelResult.InvalidSize; 1105 } 1106 1107 if (address + size <= address) 1108 { 1109 return KernelResult.InvalidMemState; 1110 } 1111 1112 if ((permission | KMemoryPermission.Write) != KMemoryPermission.ReadAndWrite) 1113 { 1114 return KernelResult.InvalidPermission; 1115 } 1116 1117 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1118 1119 KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle); 1120 1121 if (sharedMemory == null) 1122 { 1123 return KernelResult.InvalidHandle; 1124 } 1125 1126 if (currentProcess.MemoryManager.IsInvalidRegion(address, size) || 1127 currentProcess.MemoryManager.InsideHeapRegion(address, size) || 1128 currentProcess.MemoryManager.InsideAliasRegion(address, size)) 1129 { 1130 return KernelResult.InvalidMemRange; 1131 } 1132 1133 return sharedMemory.MapIntoProcess( 1134 currentProcess.MemoryManager, 1135 address, 1136 size, 1137 currentProcess, 1138 permission); 1139 } 1140 1141 [Svc(0x14)] 1142 public Result UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) 1143 { 1144 if (!PageAligned(address)) 1145 { 1146 return KernelResult.InvalidAddress; 1147 } 1148 1149 if (!PageAligned(size) || size == 0) 1150 { 1151 return KernelResult.InvalidSize; 1152 } 1153 1154 if (address + size <= address) 1155 { 1156 return KernelResult.InvalidMemState; 1157 } 1158 1159 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1160 1161 KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle); 1162 1163 if (sharedMemory == null) 1164 { 1165 return KernelResult.InvalidHandle; 1166 } 1167 1168 if (currentProcess.MemoryManager.IsInvalidRegion(address, size) || 1169 currentProcess.MemoryManager.InsideHeapRegion(address, size) || 1170 currentProcess.MemoryManager.InsideAliasRegion(address, size)) 1171 { 1172 return KernelResult.InvalidMemRange; 1173 } 1174 1175 return sharedMemory.UnmapFromProcess( 1176 currentProcess.MemoryManager, 1177 address, 1178 size, 1179 currentProcess); 1180 } 1181 1182 [Svc(0x15)] 1183 public Result CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) 1184 { 1185 handle = 0; 1186 1187 if (!PageAligned(address)) 1188 { 1189 return KernelResult.InvalidAddress; 1190 } 1191 1192 if (!PageAligned(size) || size == 0) 1193 { 1194 return KernelResult.InvalidSize; 1195 } 1196 1197 if (address + size <= address) 1198 { 1199 return KernelResult.InvalidMemState; 1200 } 1201 1202 if (permission > KMemoryPermission.ReadAndWrite || permission == KMemoryPermission.Write) 1203 { 1204 return KernelResult.InvalidPermission; 1205 } 1206 1207 KProcess process = KernelStatic.GetCurrentProcess(); 1208 1209 KResourceLimit resourceLimit = process.ResourceLimit; 1210 1211 if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.TransferMemory, 1)) 1212 { 1213 return KernelResult.ResLimitExceeded; 1214 } 1215 1216 void CleanUpForError() 1217 { 1218 resourceLimit?.Release(LimitableResource.TransferMemory, 1); 1219 } 1220 1221 if (!process.MemoryManager.InsideAddrSpace(address, size)) 1222 { 1223 CleanUpForError(); 1224 1225 return KernelResult.InvalidMemState; 1226 } 1227 1228 KTransferMemory transferMemory = new(_context); 1229 1230 Result result = transferMemory.Initialize(address, size, permission); 1231 1232 if (result != Result.Success) 1233 { 1234 CleanUpForError(); 1235 1236 return result; 1237 } 1238 1239 result = process.HandleTable.GenerateHandle(transferMemory, out handle); 1240 1241 transferMemory.DecrementReferenceCount(); 1242 1243 return result; 1244 } 1245 1246 [Svc(0x51)] 1247 public Result MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) 1248 { 1249 if (!PageAligned(address)) 1250 { 1251 return KernelResult.InvalidAddress; 1252 } 1253 1254 if (!PageAligned(size) || size == 0) 1255 { 1256 return KernelResult.InvalidSize; 1257 } 1258 1259 if (address + size <= address) 1260 { 1261 return KernelResult.InvalidMemState; 1262 } 1263 1264 if (permission > KMemoryPermission.ReadAndWrite || permission == KMemoryPermission.Write) 1265 { 1266 return KernelResult.InvalidPermission; 1267 } 1268 1269 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1270 1271 KTransferMemory transferMemory = currentProcess.HandleTable.GetObject<KTransferMemory>(handle); 1272 1273 if (transferMemory == null) 1274 { 1275 return KernelResult.InvalidHandle; 1276 } 1277 1278 if (currentProcess.MemoryManager.IsInvalidRegion(address, size) || 1279 currentProcess.MemoryManager.InsideHeapRegion(address, size) || 1280 currentProcess.MemoryManager.InsideAliasRegion(address, size)) 1281 { 1282 return KernelResult.InvalidMemRange; 1283 } 1284 1285 return transferMemory.MapIntoProcess( 1286 currentProcess.MemoryManager, 1287 address, 1288 size, 1289 currentProcess, 1290 permission); 1291 } 1292 1293 [Svc(0x52)] 1294 public Result UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) 1295 { 1296 if (!PageAligned(address)) 1297 { 1298 return KernelResult.InvalidAddress; 1299 } 1300 1301 if (!PageAligned(size) || size == 0) 1302 { 1303 return KernelResult.InvalidSize; 1304 } 1305 1306 if (address + size <= address) 1307 { 1308 return KernelResult.InvalidMemState; 1309 } 1310 1311 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1312 1313 KTransferMemory transferMemory = currentProcess.HandleTable.GetObject<KTransferMemory>(handle); 1314 1315 if (transferMemory == null) 1316 { 1317 return KernelResult.InvalidHandle; 1318 } 1319 1320 if (currentProcess.MemoryManager.IsInvalidRegion(address, size) || 1321 currentProcess.MemoryManager.InsideHeapRegion(address, size) || 1322 currentProcess.MemoryManager.InsideAliasRegion(address, size)) 1323 { 1324 return KernelResult.InvalidMemRange; 1325 } 1326 1327 return transferMemory.UnmapFromProcess( 1328 currentProcess.MemoryManager, 1329 address, 1330 size, 1331 currentProcess); 1332 } 1333 1334 [Svc(0x2c)] 1335 public Result MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) 1336 { 1337 if (!PageAligned(address)) 1338 { 1339 return KernelResult.InvalidAddress; 1340 } 1341 1342 if (!PageAligned(size) || size == 0) 1343 { 1344 return KernelResult.InvalidSize; 1345 } 1346 1347 if (address + size <= address) 1348 { 1349 return KernelResult.InvalidMemRange; 1350 } 1351 1352 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1353 1354 if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0) 1355 { 1356 return KernelResult.InvalidState; 1357 } 1358 1359 if (!currentProcess.MemoryManager.InsideAddrSpace(address, size) || 1360 currentProcess.MemoryManager.OutsideAliasRegion(address, size)) 1361 { 1362 return KernelResult.InvalidMemRange; 1363 } 1364 1365 KProcess process = KernelStatic.GetCurrentProcess(); 1366 1367 return process.MemoryManager.MapPhysicalMemory(address, size); 1368 } 1369 1370 [Svc(0x2d)] 1371 public Result UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) 1372 { 1373 if (!PageAligned(address)) 1374 { 1375 return KernelResult.InvalidAddress; 1376 } 1377 1378 if (!PageAligned(size) || size == 0) 1379 { 1380 return KernelResult.InvalidSize; 1381 } 1382 1383 if (address + size <= address) 1384 { 1385 return KernelResult.InvalidMemRange; 1386 } 1387 1388 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1389 1390 if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0) 1391 { 1392 return KernelResult.InvalidState; 1393 } 1394 1395 if (!currentProcess.MemoryManager.InsideAddrSpace(address, size) || 1396 currentProcess.MemoryManager.OutsideAliasRegion(address, size)) 1397 { 1398 return KernelResult.InvalidMemRange; 1399 } 1400 1401 KProcess process = KernelStatic.GetCurrentProcess(); 1402 1403 return process.MemoryManager.UnmapPhysicalMemory(address, size); 1404 } 1405 1406 [Svc(0x4b)] 1407 public Result CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size) 1408 { 1409 handle = 0; 1410 1411 if (!PageAligned(address)) 1412 { 1413 return KernelResult.InvalidAddress; 1414 } 1415 1416 if (!PageAligned(size) || size == 0) 1417 { 1418 return KernelResult.InvalidSize; 1419 } 1420 1421 if (size + address <= address) 1422 { 1423 return KernelResult.InvalidMemState; 1424 } 1425 1426 KCodeMemory codeMemory = new(_context); 1427 1428 using var _ = new OnScopeExit(codeMemory.DecrementReferenceCount); 1429 1430 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1431 1432 if (!currentProcess.MemoryManager.InsideAddrSpace(address, size)) 1433 { 1434 return KernelResult.InvalidMemState; 1435 } 1436 1437 Result result = codeMemory.Initialize(address, size); 1438 1439 if (result != Result.Success) 1440 { 1441 return result; 1442 } 1443 1444 return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle); 1445 } 1446 1447 [Svc(0x4c)] 1448 public Result ControlCodeMemory( 1449 int handle, 1450 CodeMemoryOperation op, 1451 ulong address, 1452 ulong size, 1453 KMemoryPermission permission) 1454 { 1455 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1456 1457 KCodeMemory codeMemory = currentProcess.HandleTable.GetObject<KCodeMemory>(handle); 1458 1459 // Newer versions of the kernel also returns an error here if the owner and process 1460 // where the operation will happen are the same. We do not return an error here 1461 // for homebrew because some of them requires this to be patched out to work (for JIT). 1462 if (codeMemory == null || (!currentProcess.AllowCodeMemoryForJit && codeMemory.Owner == currentProcess)) 1463 { 1464 return KernelResult.InvalidHandle; 1465 } 1466 1467 switch (op) 1468 { 1469 case CodeMemoryOperation.Map: 1470 if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable)) 1471 { 1472 return KernelResult.InvalidMemRange; 1473 } 1474 1475 if (permission != KMemoryPermission.ReadAndWrite) 1476 { 1477 return KernelResult.InvalidPermission; 1478 } 1479 1480 return codeMemory.Map(address, size, permission); 1481 1482 case CodeMemoryOperation.MapToOwner: 1483 if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly)) 1484 { 1485 return KernelResult.InvalidMemRange; 1486 } 1487 1488 if (permission != KMemoryPermission.Read && permission != KMemoryPermission.ReadAndExecute) 1489 { 1490 return KernelResult.InvalidPermission; 1491 } 1492 1493 return codeMemory.MapToOwner(address, size, permission); 1494 1495 case CodeMemoryOperation.Unmap: 1496 if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable)) 1497 { 1498 return KernelResult.InvalidMemRange; 1499 } 1500 1501 if (permission != KMemoryPermission.None) 1502 { 1503 return KernelResult.InvalidPermission; 1504 } 1505 1506 return codeMemory.Unmap(address, size); 1507 1508 case CodeMemoryOperation.UnmapFromOwner: 1509 if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly)) 1510 { 1511 return KernelResult.InvalidMemRange; 1512 } 1513 1514 if (permission != KMemoryPermission.None) 1515 { 1516 return KernelResult.InvalidPermission; 1517 } 1518 1519 return codeMemory.UnmapFromOwner(address, size); 1520 1521 default: 1522 return KernelResult.InvalidEnumValue; 1523 } 1524 } 1525 1526 [Svc(0x73)] 1527 public Result SetProcessMemoryPermission( 1528 int handle, 1529 ulong src, 1530 ulong size, 1531 KMemoryPermission permission) 1532 { 1533 if (!PageAligned(src)) 1534 { 1535 return KernelResult.InvalidAddress; 1536 } 1537 1538 if (!PageAligned(size) || size == 0) 1539 { 1540 return KernelResult.InvalidSize; 1541 } 1542 1543 if (permission != KMemoryPermission.None && 1544 permission != KMemoryPermission.Read && 1545 permission != KMemoryPermission.ReadAndWrite && 1546 permission != KMemoryPermission.ReadAndExecute) 1547 { 1548 return KernelResult.InvalidPermission; 1549 } 1550 1551 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1552 1553 KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); 1554 1555 if (targetProcess == null) 1556 { 1557 return KernelResult.InvalidHandle; 1558 } 1559 1560 if (targetProcess.MemoryManager.OutsideAddrSpace(src, size)) 1561 { 1562 return KernelResult.InvalidMemState; 1563 } 1564 1565 return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission); 1566 } 1567 1568 [Svc(0x74)] 1569 public Result MapProcessMemory( 1570 [PointerSized] ulong dst, 1571 int handle, 1572 ulong src, 1573 [PointerSized] ulong size) 1574 { 1575 if (!PageAligned(src) || !PageAligned(dst)) 1576 { 1577 return KernelResult.InvalidAddress; 1578 } 1579 1580 if (!PageAligned(size) || size == 0) 1581 { 1582 return KernelResult.InvalidSize; 1583 } 1584 1585 if (dst + size <= dst || src + size <= src) 1586 { 1587 return KernelResult.InvalidMemRange; 1588 } 1589 1590 KProcess dstProcess = KernelStatic.GetCurrentProcess(); 1591 KProcess srcProcess = dstProcess.HandleTable.GetObject<KProcess>(handle); 1592 1593 if (srcProcess == null) 1594 { 1595 return KernelResult.InvalidHandle; 1596 } 1597 1598 if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) || 1599 !dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory)) 1600 { 1601 return KernelResult.InvalidMemRange; 1602 } 1603 1604 KPageList pageList = new(); 1605 1606 Result result = srcProcess.MemoryManager.GetPagesIfStateEquals( 1607 src, 1608 size, 1609 MemoryState.MapProcessAllowed, 1610 MemoryState.MapProcessAllowed, 1611 KMemoryPermission.None, 1612 KMemoryPermission.None, 1613 MemoryAttribute.Mask, 1614 MemoryAttribute.None, 1615 pageList); 1616 1617 if (result != Result.Success) 1618 { 1619 return result; 1620 } 1621 1622 return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite); 1623 } 1624 1625 [Svc(0x75)] 1626 public Result UnmapProcessMemory( 1627 [PointerSized] ulong dst, 1628 int handle, 1629 ulong src, 1630 [PointerSized] ulong size) 1631 { 1632 if (!PageAligned(src) || !PageAligned(dst)) 1633 { 1634 return KernelResult.InvalidAddress; 1635 } 1636 1637 if (!PageAligned(size) || size == 0) 1638 { 1639 return KernelResult.InvalidSize; 1640 } 1641 1642 if (dst + size <= dst || src + size <= src) 1643 { 1644 return KernelResult.InvalidMemRange; 1645 } 1646 1647 KProcess dstProcess = KernelStatic.GetCurrentProcess(); 1648 KProcess srcProcess = dstProcess.HandleTable.GetObject<KProcess>(handle); 1649 1650 if (srcProcess == null) 1651 { 1652 return KernelResult.InvalidHandle; 1653 } 1654 1655 if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) || 1656 !dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory)) 1657 { 1658 return KernelResult.InvalidMemRange; 1659 } 1660 1661 Result result = dstProcess.MemoryManager.UnmapProcessMemory(dst, size, srcProcess.MemoryManager, src); 1662 1663 if (result != Result.Success) 1664 { 1665 return result; 1666 } 1667 1668 return Result.Success; 1669 } 1670 1671 [Svc(0x77)] 1672 public Result MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) 1673 { 1674 if (!PageAligned(dst) || !PageAligned(src)) 1675 { 1676 return KernelResult.InvalidAddress; 1677 } 1678 1679 if (!PageAligned(size) || size == 0) 1680 { 1681 return KernelResult.InvalidSize; 1682 } 1683 1684 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1685 1686 KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); 1687 1688 if (targetProcess == null) 1689 { 1690 return KernelResult.InvalidHandle; 1691 } 1692 1693 if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) || 1694 targetProcess.MemoryManager.OutsideAddrSpace(src, size) || 1695 targetProcess.MemoryManager.InsideAliasRegion(dst, size) || 1696 targetProcess.MemoryManager.InsideHeapRegion(dst, size)) 1697 { 1698 return KernelResult.InvalidMemRange; 1699 } 1700 1701 if (size + dst <= dst || size + src <= src) 1702 { 1703 return KernelResult.InvalidMemState; 1704 } 1705 1706 return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size); 1707 } 1708 1709 [Svc(0x78)] 1710 public Result UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) 1711 { 1712 if (!PageAligned(dst) || !PageAligned(src)) 1713 { 1714 return KernelResult.InvalidAddress; 1715 } 1716 1717 if (!PageAligned(size) || size == 0) 1718 { 1719 return KernelResult.InvalidSize; 1720 } 1721 1722 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1723 1724 KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); 1725 1726 if (targetProcess == null) 1727 { 1728 return KernelResult.InvalidHandle; 1729 } 1730 1731 if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) || 1732 targetProcess.MemoryManager.OutsideAddrSpace(src, size) || 1733 targetProcess.MemoryManager.InsideAliasRegion(dst, size) || 1734 targetProcess.MemoryManager.InsideHeapRegion(dst, size)) 1735 { 1736 return KernelResult.InvalidMemRange; 1737 } 1738 1739 if (size + dst <= dst || size + src <= src) 1740 { 1741 return KernelResult.InvalidMemState; 1742 } 1743 1744 return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); 1745 } 1746 1747 private static bool PageAligned(ulong address) 1748 { 1749 return (address & (KPageTableBase.PageSize - 1)) == 0; 1750 } 1751 1752 // System 1753 1754 [Svc(0x7b)] 1755 public Result TerminateProcess(int handle) 1756 { 1757 KProcess process = KernelStatic.GetCurrentProcess(); 1758 1759 process = process.HandleTable.GetObject<KProcess>(handle); 1760 1761 Result result; 1762 1763 if (process != null) 1764 { 1765 if (process == KernelStatic.GetCurrentProcess()) 1766 { 1767 result = Result.Success; 1768 process.DecrementToZeroWhileTerminatingCurrent(); 1769 } 1770 else 1771 { 1772 result = process.Terminate(); 1773 process.DecrementReferenceCount(); 1774 } 1775 } 1776 else 1777 { 1778 result = KernelResult.InvalidHandle; 1779 } 1780 1781 return result; 1782 } 1783 1784 [Svc(7)] 1785 public void ExitProcess() 1786 { 1787 KernelStatic.GetCurrentProcess().TerminateCurrentProcess(); 1788 } 1789 1790 [Svc(0x11)] 1791 public Result SignalEvent(int handle) 1792 { 1793 KProcess process = KernelStatic.GetCurrentProcess(); 1794 1795 KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle); 1796 1797 Result result; 1798 1799 if (writableEvent != null) 1800 { 1801 writableEvent.Signal(); 1802 1803 result = Result.Success; 1804 } 1805 else 1806 { 1807 result = KernelResult.InvalidHandle; 1808 } 1809 1810 return result; 1811 } 1812 1813 [Svc(0x12)] 1814 public Result ClearEvent(int handle) 1815 { 1816 Result result; 1817 1818 KProcess process = KernelStatic.GetCurrentProcess(); 1819 1820 KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle); 1821 1822 if (writableEvent == null) 1823 { 1824 KReadableEvent readableEvent = process.HandleTable.GetObject<KReadableEvent>(handle); 1825 1826 result = readableEvent?.Clear() ?? KernelResult.InvalidHandle; 1827 } 1828 else 1829 { 1830 result = writableEvent.Clear(); 1831 } 1832 1833 return result; 1834 } 1835 1836 [Svc(0x16)] 1837 public Result CloseHandle(int handle) 1838 { 1839 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1840 1841 return currentProcess.HandleTable.CloseHandle(handle) ? Result.Success : KernelResult.InvalidHandle; 1842 } 1843 1844 [Svc(0x17)] 1845 public Result ResetSignal(int handle) 1846 { 1847 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1848 1849 KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle); 1850 1851 Result result; 1852 1853 if (readableEvent != null) 1854 { 1855 result = readableEvent.ClearIfSignaled(); 1856 } 1857 else 1858 { 1859 KProcess process = currentProcess.HandleTable.GetKProcess(handle); 1860 1861 if (process != null) 1862 { 1863 result = process.ClearIfNotExited(); 1864 } 1865 else 1866 { 1867 result = KernelResult.InvalidHandle; 1868 } 1869 } 1870 1871 return result; 1872 } 1873 1874 [Svc(0x1e)] 1875 public ulong GetSystemTick() 1876 { 1877 return _context.TickSource.Counter; 1878 } 1879 1880 [Svc(0x26)] 1881 public void Break(ulong reason) 1882 { 1883 KThread currentThread = KernelStatic.GetCurrentThread(); 1884 1885 if ((reason & (1UL << 31)) == 0) 1886 { 1887 currentThread.PrintGuestStackTrace(); 1888 currentThread.PrintGuestRegisterPrintout(); 1889 1890 // As the process is exiting, this is probably caused by emulation termination. 1891 if (currentThread.Owner.State == ProcessState.Exiting) 1892 { 1893 return; 1894 } 1895 1896 // TODO: Debug events. 1897 currentThread.Owner.TerminateCurrentProcess(); 1898 1899 throw new GuestBrokeExecutionException(); 1900 } 1901 else 1902 { 1903 Logger.Debug?.Print(LogClass.KernelSvc, "Debugger triggered."); 1904 } 1905 } 1906 1907 [Svc(0x27)] 1908 public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size) 1909 { 1910 KProcess process = KernelStatic.GetCurrentProcess(); 1911 1912 string str = MemoryHelper.ReadAsciiString(process.CpuMemory, strPtr, (long)size); 1913 1914 Logger.Warning?.Print(LogClass.KernelSvc, str); 1915 } 1916 1917 [Svc(0x29)] 1918 public Result GetInfo(out ulong value, InfoType id, int handle, long subId) 1919 { 1920 value = 0; 1921 1922 switch (id) 1923 { 1924 case InfoType.CoreMask: 1925 case InfoType.PriorityMask: 1926 case InfoType.AliasRegionAddress: 1927 case InfoType.AliasRegionSize: 1928 case InfoType.HeapRegionAddress: 1929 case InfoType.HeapRegionSize: 1930 case InfoType.TotalMemorySize: 1931 case InfoType.UsedMemorySize: 1932 case InfoType.AslrRegionAddress: 1933 case InfoType.AslrRegionSize: 1934 case InfoType.StackRegionAddress: 1935 case InfoType.StackRegionSize: 1936 case InfoType.SystemResourceSizeTotal: 1937 case InfoType.SystemResourceSizeUsed: 1938 case InfoType.ProgramId: 1939 case InfoType.UserExceptionContextAddress: 1940 case InfoType.TotalNonSystemMemorySize: 1941 case InfoType.UsedNonSystemMemorySize: 1942 case InfoType.IsApplication: 1943 case InfoType.FreeThreadCount: 1944 case InfoType.AliasRegionExtraSize: 1945 { 1946 if (subId != 0) 1947 { 1948 return KernelResult.InvalidCombination; 1949 } 1950 1951 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 1952 1953 KProcess process = currentProcess.HandleTable.GetKProcess(handle); 1954 1955 if (process == null) 1956 { 1957 return KernelResult.InvalidHandle; 1958 } 1959 1960 switch (id) 1961 { 1962 case InfoType.CoreMask: 1963 value = process.Capabilities.AllowedCpuCoresMask; 1964 break; 1965 case InfoType.PriorityMask: 1966 value = process.Capabilities.AllowedThreadPriosMask; 1967 break; 1968 1969 case InfoType.AliasRegionAddress: 1970 value = process.MemoryManager.AliasRegionStart; 1971 break; 1972 case InfoType.AliasRegionSize: 1973 value = process.MemoryManager.AliasRegionEnd - process.MemoryManager.AliasRegionStart; 1974 break; 1975 1976 case InfoType.HeapRegionAddress: 1977 value = process.MemoryManager.HeapRegionStart; 1978 break; 1979 case InfoType.HeapRegionSize: 1980 value = process.MemoryManager.HeapRegionEnd - process.MemoryManager.HeapRegionStart; 1981 break; 1982 1983 case InfoType.TotalMemorySize: 1984 value = process.GetMemoryCapacity(); 1985 break; 1986 case InfoType.UsedMemorySize: 1987 value = process.GetMemoryUsage(); 1988 break; 1989 1990 case InfoType.AslrRegionAddress: 1991 value = process.MemoryManager.GetAddrSpaceBaseAddr(); 1992 break; 1993 case InfoType.AslrRegionSize: 1994 value = process.MemoryManager.GetAddrSpaceSize(); 1995 break; 1996 1997 case InfoType.StackRegionAddress: 1998 value = process.MemoryManager.StackRegionStart; 1999 break; 2000 case InfoType.StackRegionSize: 2001 value = process.MemoryManager.StackRegionEnd - process.MemoryManager.StackRegionStart; 2002 break; 2003 2004 case InfoType.SystemResourceSizeTotal: 2005 value = process.PersonalMmHeapPagesCount * KPageTableBase.PageSize; 2006 break; 2007 case InfoType.SystemResourceSizeUsed: 2008 if (process.PersonalMmHeapPagesCount != 0) 2009 { 2010 value = process.MemoryManager.GetMmUsedPages() * KPageTableBase.PageSize; 2011 } 2012 break; 2013 2014 case InfoType.ProgramId: 2015 value = process.TitleId; 2016 break; 2017 2018 case InfoType.UserExceptionContextAddress: 2019 value = process.UserExceptionContextAddress; 2020 break; 2021 2022 case InfoType.TotalNonSystemMemorySize: 2023 value = process.GetMemoryCapacityWithoutPersonalMmHeap(); 2024 break; 2025 case InfoType.UsedNonSystemMemorySize: 2026 value = process.GetMemoryUsageWithoutPersonalMmHeap(); 2027 break; 2028 2029 case InfoType.IsApplication: 2030 value = process.IsApplication ? 1UL : 0UL; 2031 break; 2032 2033 case InfoType.FreeThreadCount: 2034 if (process.ResourceLimit != null) 2035 { 2036 value = (ulong)(process.ResourceLimit.GetLimitValue(LimitableResource.Thread) - 2037 process.ResourceLimit.GetCurrentValue(LimitableResource.Thread)); 2038 } 2039 else 2040 { 2041 value = 0; 2042 } 2043 break; 2044 2045 case InfoType.AliasRegionExtraSize: 2046 value = process.MemoryManager.AliasRegionExtraSize; 2047 break; 2048 } 2049 break; 2050 } 2051 2052 case InfoType.DebuggerAttached: 2053 { 2054 if (handle != 0) 2055 { 2056 return KernelResult.InvalidHandle; 2057 } 2058 2059 if (subId != 0) 2060 { 2061 return KernelResult.InvalidCombination; 2062 } 2063 2064 value = KernelStatic.GetCurrentProcess().Debug ? 1UL : 0UL; 2065 break; 2066 } 2067 2068 case InfoType.ResourceLimit: 2069 { 2070 if (handle != 0) 2071 { 2072 return KernelResult.InvalidHandle; 2073 } 2074 2075 if (subId != 0) 2076 { 2077 return KernelResult.InvalidCombination; 2078 } 2079 2080 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2081 2082 if (currentProcess.ResourceLimit != null) 2083 { 2084 KHandleTable handleTable = currentProcess.HandleTable; 2085 KResourceLimit resourceLimit = currentProcess.ResourceLimit; 2086 2087 Result result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle); 2088 2089 if (result != Result.Success) 2090 { 2091 return result; 2092 } 2093 2094 value = (uint)resLimHandle; 2095 } 2096 break; 2097 } 2098 2099 case InfoType.IdleTickCount: 2100 { 2101 if (handle != 0) 2102 { 2103 return KernelResult.InvalidHandle; 2104 } 2105 2106 int currentCore = KernelStatic.GetCurrentThread().CurrentCore; 2107 2108 if (subId != -1 && subId != currentCore) 2109 { 2110 return KernelResult.InvalidCombination; 2111 } 2112 2113 value = (ulong)KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks); 2114 break; 2115 } 2116 2117 case InfoType.RandomEntropy: 2118 { 2119 if (handle != 0) 2120 { 2121 return KernelResult.InvalidHandle; 2122 } 2123 2124 if ((ulong)subId > 3) 2125 { 2126 return KernelResult.InvalidCombination; 2127 } 2128 2129 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2130 2131 value = currentProcess.RandomEntropy[subId]; 2132 break; 2133 } 2134 2135 case InfoType.ThreadTickCount: 2136 { 2137 if (subId < -1 || subId > 3) 2138 { 2139 return KernelResult.InvalidCombination; 2140 } 2141 2142 KThread thread = KernelStatic.GetCurrentProcess().HandleTable.GetKThread(handle); 2143 2144 if (thread == null) 2145 { 2146 return KernelResult.InvalidHandle; 2147 } 2148 2149 KThread currentThread = KernelStatic.GetCurrentThread(); 2150 2151 int currentCore = currentThread.CurrentCore; 2152 2153 if (subId != -1 && subId != currentCore) 2154 { 2155 return Result.Success; 2156 } 2157 2158 KScheduler scheduler = _context.Schedulers[currentCore]; 2159 2160 long timeDelta = PerformanceCounter.ElapsedTicks - scheduler.LastContextSwitchTime; 2161 2162 if (subId != -1) 2163 { 2164 value = (ulong)KTimeManager.ConvertHostTicksToTicks(timeDelta); 2165 } 2166 else 2167 { 2168 long totalTimeRunning = thread.TotalTimeRunning; 2169 2170 if (thread == currentThread) 2171 { 2172 totalTimeRunning += timeDelta; 2173 } 2174 2175 value = (ulong)KTimeManager.ConvertHostTicksToTicks(totalTimeRunning); 2176 } 2177 break; 2178 } 2179 2180 case InfoType.IsSvcPermitted: 2181 { 2182 if (handle != 0) 2183 { 2184 return KernelResult.InvalidHandle; 2185 } 2186 2187 if (subId != 0x36) 2188 { 2189 return KernelResult.InvalidCombination; 2190 } 2191 2192 value = KernelStatic.GetCurrentProcess().IsSvcPermitted((int)subId) ? 1UL : 0UL; 2193 break; 2194 } 2195 2196 case InfoType.MesosphereCurrentProcess: 2197 { 2198 if (handle != 0) 2199 { 2200 return KernelResult.InvalidHandle; 2201 } 2202 2203 if (subId != 0) 2204 { 2205 return KernelResult.InvalidCombination; 2206 } 2207 2208 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2209 KHandleTable handleTable = currentProcess.HandleTable; 2210 2211 Result result = handleTable.GenerateHandle(currentProcess, out int outHandle); 2212 2213 if (result != Result.Success) 2214 { 2215 return result; 2216 } 2217 2218 value = (uint)outHandle; 2219 break; 2220 } 2221 2222 default: 2223 return KernelResult.InvalidEnumValue; 2224 } 2225 2226 return Result.Success; 2227 } 2228 2229 [Svc(0x45)] 2230 public Result CreateEvent(out int wEventHandle, out int rEventHandle) 2231 { 2232 KEvent Event = new(_context); 2233 2234 KProcess process = KernelStatic.GetCurrentProcess(); 2235 2236 Result result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle); 2237 2238 if (result == Result.Success) 2239 { 2240 result = process.HandleTable.GenerateHandle(Event.ReadableEvent, out rEventHandle); 2241 2242 if (result != Result.Success) 2243 { 2244 process.HandleTable.CloseHandle(wEventHandle); 2245 } 2246 } 2247 else 2248 { 2249 rEventHandle = 0; 2250 } 2251 2252 return result; 2253 } 2254 2255 [Svc(0x65)] 2256 public Result GetProcessList(out int count, [PointerSized] ulong address, int maxCount) 2257 { 2258 count = 0; 2259 2260 if ((maxCount >> 28) != 0) 2261 { 2262 return KernelResult.MaximumExceeded; 2263 } 2264 2265 if (maxCount != 0) 2266 { 2267 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2268 2269 ulong copySize = (ulong)maxCount * 8; 2270 2271 if (address + copySize <= address) 2272 { 2273 return KernelResult.InvalidMemState; 2274 } 2275 2276 if (currentProcess.MemoryManager.OutsideAddrSpace(address, copySize)) 2277 { 2278 return KernelResult.InvalidMemState; 2279 } 2280 } 2281 2282 int copyCount = 0; 2283 2284 lock (_context.Processes) 2285 { 2286 foreach (KProcess process in _context.Processes.Values) 2287 { 2288 if (copyCount < maxCount) 2289 { 2290 if (!KernelTransfer.KernelToUser(address + (ulong)copyCount * 8, process.Pid)) 2291 { 2292 return KernelResult.UserCopyFailed; 2293 } 2294 } 2295 2296 copyCount++; 2297 } 2298 } 2299 2300 count = copyCount; 2301 2302 return Result.Success; 2303 } 2304 2305 [Svc(0x6f)] 2306 public Result GetSystemInfo(out long value, uint id, int handle, long subId) 2307 { 2308 value = 0; 2309 2310 if (id > 2) 2311 { 2312 return KernelResult.InvalidEnumValue; 2313 } 2314 2315 if (handle != 0) 2316 { 2317 return KernelResult.InvalidHandle; 2318 } 2319 2320 if (id < 2) 2321 { 2322 if ((ulong)subId > 3) 2323 { 2324 return KernelResult.InvalidCombination; 2325 } 2326 2327 KMemoryRegionManager region = _context.MemoryManager.MemoryRegions[subId]; 2328 2329 switch (id) 2330 { 2331 // Memory region capacity. 2332 case 0: 2333 value = (long)region.Size; 2334 break; 2335 2336 // Memory region free space. 2337 case 1: 2338 { 2339 ulong freePagesCount = region.GetFreePages(); 2340 2341 value = (long)(freePagesCount * KPageTableBase.PageSize); 2342 2343 break; 2344 } 2345 } 2346 } 2347 else /* if (Id == 2) */ 2348 { 2349 if ((ulong)subId > 1) 2350 { 2351 return KernelResult.InvalidCombination; 2352 } 2353 2354 switch (subId) 2355 { 2356 case 0: 2357 value = _context.PrivilegedProcessLowestId; 2358 break; 2359 case 1: 2360 value = _context.PrivilegedProcessHighestId; 2361 break; 2362 } 2363 } 2364 2365 return Result.Success; 2366 } 2367 2368 [Svc(0x30)] 2369 public Result GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource) 2370 { 2371 limitValue = 0; 2372 2373 if (resource >= LimitableResource.Count) 2374 { 2375 return KernelResult.InvalidEnumValue; 2376 } 2377 2378 KResourceLimit resourceLimit = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KResourceLimit>(handle); 2379 2380 if (resourceLimit == null) 2381 { 2382 return KernelResult.InvalidHandle; 2383 } 2384 2385 limitValue = resourceLimit.GetLimitValue(resource); 2386 2387 return Result.Success; 2388 } 2389 2390 [Svc(0x31)] 2391 public Result GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource) 2392 { 2393 limitValue = 0; 2394 2395 if (resource >= LimitableResource.Count) 2396 { 2397 return KernelResult.InvalidEnumValue; 2398 } 2399 2400 KResourceLimit resourceLimit = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KResourceLimit>(handle); 2401 2402 if (resourceLimit == null) 2403 { 2404 return KernelResult.InvalidHandle; 2405 } 2406 2407 limitValue = resourceLimit.GetCurrentValue(resource); 2408 2409 return Result.Success; 2410 } 2411 2412 [Svc(0x37)] 2413 public Result GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource) 2414 { 2415 peak = 0; 2416 2417 if (resource >= LimitableResource.Count) 2418 { 2419 return KernelResult.InvalidEnumValue; 2420 } 2421 2422 KResourceLimit resourceLimit = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KResourceLimit>(handle); 2423 2424 if (resourceLimit == null) 2425 { 2426 return KernelResult.InvalidHandle; 2427 } 2428 2429 peak = resourceLimit.GetPeakValue(resource); 2430 2431 return Result.Success; 2432 } 2433 2434 [Svc(0x7d)] 2435 public Result CreateResourceLimit(out int handle) 2436 { 2437 KResourceLimit limit = new(_context); 2438 2439 KProcess process = KernelStatic.GetCurrentProcess(); 2440 2441 return process.HandleTable.GenerateHandle(limit, out handle); 2442 } 2443 2444 [Svc(0x7e)] 2445 public Result SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue) 2446 { 2447 if (resource >= LimitableResource.Count) 2448 { 2449 return KernelResult.InvalidEnumValue; 2450 } 2451 2452 KResourceLimit resourceLimit = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KResourceLimit>(handle); 2453 2454 if (resourceLimit == null) 2455 { 2456 return KernelResult.InvalidHandle; 2457 } 2458 2459 return resourceLimit.SetLimitValue(resource, limitValue); 2460 } 2461 2462 // Thread 2463 2464 [Svc(8)] 2465 public Result CreateThread( 2466 out int handle, 2467 [PointerSized] ulong entrypoint, 2468 [PointerSized] ulong argsPtr, 2469 [PointerSized] ulong stackTop, 2470 int priority, 2471 int cpuCore) 2472 { 2473 return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null); 2474 } 2475 2476 public Result CreateThread( 2477 out int handle, 2478 ulong entrypoint, 2479 ulong argsPtr, 2480 ulong stackTop, 2481 int priority, 2482 int cpuCore, 2483 ThreadStart customThreadStart) 2484 { 2485 handle = 0; 2486 2487 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2488 2489 if (cpuCore == -2) 2490 { 2491 cpuCore = currentProcess.DefaultCpuCore; 2492 } 2493 2494 if ((uint)cpuCore >= KScheduler.CpuCoresCount || !currentProcess.IsCpuCoreAllowed(cpuCore)) 2495 { 2496 return KernelResult.InvalidCpuCore; 2497 } 2498 2499 if ((uint)priority >= KScheduler.PrioritiesCount || !currentProcess.IsPriorityAllowed(priority)) 2500 { 2501 return KernelResult.InvalidPriority; 2502 } 2503 2504 long timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100); 2505 2506 if (currentProcess.ResourceLimit != null && 2507 !currentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, timeout)) 2508 { 2509 return KernelResult.ResLimitExceeded; 2510 } 2511 2512 KThread thread = new(_context); 2513 2514 Result result = currentProcess.InitializeThread( 2515 thread, 2516 entrypoint, 2517 argsPtr, 2518 stackTop, 2519 priority, 2520 cpuCore, 2521 customThreadStart); 2522 2523 if (result == Result.Success) 2524 { 2525 KProcess process = KernelStatic.GetCurrentProcess(); 2526 2527 result = process.HandleTable.GenerateHandle(thread, out handle); 2528 } 2529 else 2530 { 2531 currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1); 2532 } 2533 2534 thread.DecrementReferenceCount(); 2535 2536 return result; 2537 } 2538 2539 [Svc(9)] 2540 public Result StartThread(int handle) 2541 { 2542 KProcess process = KernelStatic.GetCurrentProcess(); 2543 2544 KThread thread = process.HandleTable.GetKThread(handle); 2545 2546 if (thread != null) 2547 { 2548 thread.IncrementReferenceCount(); 2549 2550 Result result = thread.Start(); 2551 2552 if (result == Result.Success) 2553 { 2554 thread.IncrementReferenceCount(); 2555 } 2556 2557 thread.DecrementReferenceCount(); 2558 2559 return result; 2560 } 2561 else 2562 { 2563 return KernelResult.InvalidHandle; 2564 } 2565 } 2566 2567 [Svc(0xa)] 2568 public void ExitThread() 2569 { 2570 KThread currentThread = KernelStatic.GetCurrentThread(); 2571 2572 currentThread.Exit(); 2573 } 2574 2575 [Svc(0xb)] 2576 public void SleepThread(long timeout) 2577 { 2578 if (timeout < 1) 2579 { 2580 switch (timeout) 2581 { 2582 case 0: 2583 KScheduler.Yield(_context); 2584 break; 2585 case -1: 2586 KScheduler.YieldWithLoadBalancing(_context); 2587 break; 2588 case -2: 2589 KScheduler.YieldToAnyThread(_context); 2590 break; 2591 } 2592 } 2593 else 2594 { 2595 KernelStatic.GetCurrentThread().Sleep(timeout + KTimeManager.DefaultTimeIncrementNanoseconds); 2596 } 2597 } 2598 2599 [Svc(0xc)] 2600 public Result GetThreadPriority(out int priority, int handle) 2601 { 2602 KProcess process = KernelStatic.GetCurrentProcess(); 2603 2604 KThread thread = process.HandleTable.GetKThread(handle); 2605 2606 if (thread != null) 2607 { 2608 priority = thread.DynamicPriority; 2609 2610 return Result.Success; 2611 } 2612 else 2613 { 2614 priority = 0; 2615 2616 return KernelResult.InvalidHandle; 2617 } 2618 } 2619 2620 [Svc(0xd)] 2621 public Result SetThreadPriority(int handle, int priority) 2622 { 2623 // TODO: NPDM check. 2624 2625 KProcess process = KernelStatic.GetCurrentProcess(); 2626 2627 KThread thread = process.HandleTable.GetKThread(handle); 2628 2629 if (thread == null) 2630 { 2631 return KernelResult.InvalidHandle; 2632 } 2633 2634 thread.SetPriority(priority); 2635 2636 return Result.Success; 2637 } 2638 2639 [Svc(0xe)] 2640 public Result GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle) 2641 { 2642 KProcess process = KernelStatic.GetCurrentProcess(); 2643 2644 KThread thread = process.HandleTable.GetKThread(handle); 2645 2646 if (thread != null) 2647 { 2648 preferredCore = thread.PreferredCore; 2649 affinityMask = thread.AffinityMask; 2650 2651 return Result.Success; 2652 } 2653 else 2654 { 2655 preferredCore = 0; 2656 affinityMask = 0; 2657 2658 return KernelResult.InvalidHandle; 2659 } 2660 } 2661 2662 [Svc(0xf)] 2663 public Result SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask) 2664 { 2665 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2666 2667 if (preferredCore == -2) 2668 { 2669 preferredCore = currentProcess.DefaultCpuCore; 2670 2671 affinityMask = 1UL << preferredCore; 2672 } 2673 else 2674 { 2675 if ((currentProcess.Capabilities.AllowedCpuCoresMask | affinityMask) != 2676 currentProcess.Capabilities.AllowedCpuCoresMask) 2677 { 2678 return KernelResult.InvalidCpuCore; 2679 } 2680 2681 if (affinityMask == 0) 2682 { 2683 return KernelResult.InvalidCombination; 2684 } 2685 2686 if ((uint)preferredCore > 3) 2687 { 2688 if ((preferredCore | 2) != -1) 2689 { 2690 return KernelResult.InvalidCpuCore; 2691 } 2692 } 2693 else if ((affinityMask & (1UL << preferredCore)) == 0) 2694 { 2695 return KernelResult.InvalidCombination; 2696 } 2697 } 2698 2699 KProcess process = KernelStatic.GetCurrentProcess(); 2700 2701 KThread thread = process.HandleTable.GetKThread(handle); 2702 2703 if (thread == null) 2704 { 2705 return KernelResult.InvalidHandle; 2706 } 2707 2708 return thread.SetCoreAndAffinityMask(preferredCore, affinityMask); 2709 } 2710 2711 [Svc(0x10)] 2712 public int GetCurrentProcessorNumber() 2713 { 2714 return KernelStatic.GetCurrentThread().CurrentCore; 2715 } 2716 2717 [Svc(0x25)] 2718 public Result GetThreadId(out ulong threadUid, int handle) 2719 { 2720 KProcess process = KernelStatic.GetCurrentProcess(); 2721 2722 KThread thread = process.HandleTable.GetKThread(handle); 2723 2724 if (thread != null) 2725 { 2726 threadUid = thread.ThreadUid; 2727 2728 return Result.Success; 2729 } 2730 else 2731 { 2732 threadUid = 0; 2733 2734 return KernelResult.InvalidHandle; 2735 } 2736 } 2737 2738 [Svc(0x32)] 2739 public Result SetThreadActivity(int handle, bool pause) 2740 { 2741 KProcess process = KernelStatic.GetCurrentProcess(); 2742 2743 KThread thread = process.HandleTable.GetObject<KThread>(handle); 2744 2745 if (thread == null) 2746 { 2747 return KernelResult.InvalidHandle; 2748 } 2749 2750 if (thread.Owner != process) 2751 { 2752 return KernelResult.InvalidHandle; 2753 } 2754 2755 if (thread == KernelStatic.GetCurrentThread()) 2756 { 2757 return KernelResult.InvalidThread; 2758 } 2759 2760 return thread.SetActivity(pause); 2761 } 2762 2763 [Svc(0x33)] 2764 public Result GetThreadContext3([PointerSized] ulong address, int handle) 2765 { 2766 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2767 KThread currentThread = KernelStatic.GetCurrentThread(); 2768 2769 KThread thread = currentProcess.HandleTable.GetObject<KThread>(handle); 2770 2771 if (thread == null) 2772 { 2773 return KernelResult.InvalidHandle; 2774 } 2775 2776 if (thread.Owner != currentProcess) 2777 { 2778 return KernelResult.InvalidHandle; 2779 } 2780 2781 if (currentThread == thread) 2782 { 2783 return KernelResult.InvalidThread; 2784 } 2785 2786 Result result = thread.GetThreadContext3(out ThreadContext context); 2787 2788 if (result == Result.Success) 2789 { 2790 return KernelTransfer.KernelToUser(address, context) 2791 ? Result.Success 2792 : KernelResult.InvalidMemState; 2793 } 2794 2795 return result; 2796 } 2797 2798 // Thread synchronization 2799 2800 [Svc(0x18)] 2801 public Result WaitSynchronization(out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, long timeout) 2802 { 2803 handleIndex = 0; 2804 2805 if ((uint)handlesCount > KThread.MaxWaitSyncObjects) 2806 { 2807 return KernelResult.MaximumExceeded; 2808 } 2809 2810 KThread currentThread = KernelStatic.GetCurrentThread(); 2811 2812 if (handlesCount != 0) 2813 { 2814 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2815 2816 if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr) 2817 { 2818 return KernelResult.UserCopyFailed; 2819 } 2820 2821 long handlesSize = handlesCount * 4; 2822 2823 if (handlesPtr + (ulong)handlesSize <= handlesPtr) 2824 { 2825 return KernelResult.UserCopyFailed; 2826 } 2827 2828 if (handlesPtr + (ulong)handlesSize - 1 > currentProcess.MemoryManager.AddrSpaceEnd - 1) 2829 { 2830 return KernelResult.UserCopyFailed; 2831 } 2832 2833 Span<int> handles = new Span<int>(currentThread.WaitSyncHandles)[..handlesCount]; 2834 2835 if (!KernelTransfer.UserToKernelArray(handlesPtr, handles)) 2836 { 2837 return KernelResult.UserCopyFailed; 2838 } 2839 2840 return WaitSynchronization(out handleIndex, handles, timeout); 2841 } 2842 2843 return WaitSynchronization(out handleIndex, ReadOnlySpan<int>.Empty, timeout); 2844 } 2845 2846 public Result WaitSynchronization(out int handleIndex, ReadOnlySpan<int> handles, long timeout) 2847 { 2848 handleIndex = 0; 2849 2850 if ((uint)handles.Length > KThread.MaxWaitSyncObjects) 2851 { 2852 return KernelResult.MaximumExceeded; 2853 } 2854 2855 KThread currentThread = KernelStatic.GetCurrentThread(); 2856 2857 var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects)[..handles.Length]; 2858 2859 if (handles.Length != 0) 2860 { 2861 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2862 2863 int processedHandles = 0; 2864 2865 for (; processedHandles < handles.Length; processedHandles++) 2866 { 2867 KSynchronizationObject syncObj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[processedHandles]); 2868 2869 if (syncObj == null) 2870 { 2871 break; 2872 } 2873 2874 syncObjs[processedHandles] = syncObj; 2875 2876 syncObj.IncrementReferenceCount(); 2877 } 2878 2879 if (processedHandles != handles.Length) 2880 { 2881 // One or more handles are invalid. 2882 for (int index = 0; index < processedHandles; index++) 2883 { 2884 currentThread.WaitSyncObjects[index].DecrementReferenceCount(); 2885 } 2886 2887 return KernelResult.InvalidHandle; 2888 } 2889 } 2890 2891 if (timeout > 0) 2892 { 2893 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 2894 } 2895 2896 Result result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex); 2897 2898 if (result == KernelResult.PortRemoteClosed) 2899 { 2900 result = Result.Success; 2901 } 2902 2903 for (int index = 0; index < handles.Length; index++) 2904 { 2905 currentThread.WaitSyncObjects[index].DecrementReferenceCount(); 2906 } 2907 2908 return result; 2909 } 2910 2911 [Svc(0x19)] 2912 public Result CancelSynchronization(int handle) 2913 { 2914 KProcess process = KernelStatic.GetCurrentProcess(); 2915 2916 KThread thread = process.HandleTable.GetKThread(handle); 2917 2918 if (thread == null) 2919 { 2920 return KernelResult.InvalidHandle; 2921 } 2922 2923 thread.CancelSynchronization(); 2924 2925 return Result.Success; 2926 } 2927 2928 [Svc(0x1a)] 2929 public Result ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle) 2930 { 2931 if (IsPointingInsideKernel(mutexAddress)) 2932 { 2933 return KernelResult.InvalidMemState; 2934 } 2935 2936 if (IsAddressNotWordAligned(mutexAddress)) 2937 { 2938 return KernelResult.InvalidAddress; 2939 } 2940 2941 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2942 2943 return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle); 2944 } 2945 2946 [Svc(0x1b)] 2947 public Result ArbitrateUnlock([PointerSized] ulong mutexAddress) 2948 { 2949 if (IsPointingInsideKernel(mutexAddress)) 2950 { 2951 return KernelResult.InvalidMemState; 2952 } 2953 2954 if (IsAddressNotWordAligned(mutexAddress)) 2955 { 2956 return KernelResult.InvalidAddress; 2957 } 2958 2959 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2960 2961 return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress); 2962 } 2963 2964 [Svc(0x1c)] 2965 public Result WaitProcessWideKeyAtomic( 2966 [PointerSized] ulong mutexAddress, 2967 [PointerSized] ulong condVarAddress, 2968 int handle, 2969 long timeout) 2970 { 2971 if (IsPointingInsideKernel(mutexAddress)) 2972 { 2973 return KernelResult.InvalidMemState; 2974 } 2975 2976 if (IsAddressNotWordAligned(mutexAddress)) 2977 { 2978 return KernelResult.InvalidAddress; 2979 } 2980 2981 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2982 2983 if (timeout > 0) 2984 { 2985 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 2986 } 2987 2988 return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic( 2989 mutexAddress, 2990 condVarAddress, 2991 handle, 2992 timeout); 2993 } 2994 2995 [Svc(0x1d)] 2996 public Result SignalProcessWideKey([PointerSized] ulong address, int count) 2997 { 2998 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 2999 3000 currentProcess.AddressArbiter.SignalProcessWideKey(address, count); 3001 3002 return Result.Success; 3003 } 3004 3005 [Svc(0x34)] 3006 public Result WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout) 3007 { 3008 if (IsPointingInsideKernel(address)) 3009 { 3010 return KernelResult.InvalidMemState; 3011 } 3012 3013 if (IsAddressNotWordAligned(address)) 3014 { 3015 return KernelResult.InvalidAddress; 3016 } 3017 3018 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 3019 3020 if (timeout > 0) 3021 { 3022 timeout += KTimeManager.DefaultTimeIncrementNanoseconds; 3023 } 3024 3025 return type switch 3026 { 3027 ArbitrationType.WaitIfLessThan 3028 => currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, false, timeout), 3029 ArbitrationType.DecrementAndWaitIfLessThan 3030 => currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, true, timeout), 3031 ArbitrationType.WaitIfEqual 3032 => currentProcess.AddressArbiter.WaitForAddressIfEqual(address, value, timeout), 3033 _ => KernelResult.InvalidEnumValue, 3034 }; 3035 } 3036 3037 [Svc(0x35)] 3038 public Result SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count) 3039 { 3040 if (IsPointingInsideKernel(address)) 3041 { 3042 return KernelResult.InvalidMemState; 3043 } 3044 3045 if (IsAddressNotWordAligned(address)) 3046 { 3047 return KernelResult.InvalidAddress; 3048 } 3049 3050 KProcess currentProcess = KernelStatic.GetCurrentProcess(); 3051 3052 return type switch 3053 { 3054 SignalType.Signal 3055 => currentProcess.AddressArbiter.Signal(address, count), 3056 SignalType.SignalAndIncrementIfEqual 3057 => currentProcess.AddressArbiter.SignalAndIncrementIfEqual(address, value, count), 3058 SignalType.SignalAndModifyIfEqual 3059 => currentProcess.AddressArbiter.SignalAndModifyIfEqual(address, value, count), 3060 _ => KernelResult.InvalidEnumValue, 3061 }; 3062 } 3063 3064 [Svc(0x36)] 3065 public Result SynchronizePreemptionState() 3066 { 3067 KernelStatic.GetCurrentThread().SynchronizePreemptionState(); 3068 3069 return Result.Success; 3070 } 3071 3072 // Not actual syscalls, used by HLE services and such. 3073 3074 public IExternalEvent GetExternalEvent(int handle) 3075 { 3076 KWritableEvent writableEvent = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KWritableEvent>(handle); 3077 3078 if (writableEvent == null) 3079 { 3080 return null; 3081 } 3082 3083 return new ExternalEvent(writableEvent); 3084 } 3085 3086 public IVirtualMemoryManager GetMemoryManagerByProcessHandle(int handle) 3087 { 3088 return KernelStatic.GetCurrentProcess().HandleTable.GetKProcess(handle).CpuMemory; 3089 } 3090 3091 public ulong GetTransferMemoryAddress(int handle) 3092 { 3093 KTransferMemory transferMemory = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KTransferMemory>(handle); 3094 3095 if (transferMemory == null) 3096 { 3097 return 0; 3098 } 3099 3100 return transferMemory.Address; 3101 } 3102 3103 private static bool IsPointingInsideKernel(ulong address) 3104 { 3105 return (address + 0x1000000000) < 0xffffff000; 3106 } 3107 3108 private static bool IsAddressNotWordAligned(ulong address) 3109 { 3110 return (address & 3) != 0; 3111 } 3112 } 3113 }