public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) { KThread serverThread = KernelContext.Scheduler.GetCurrentThread(); KProcess serverProcess = serverThread.Owner; KernelContext.CriticalSection.Enter(); if (_parent.ClientSession.State != ChannelState.Open) { KernelContext.CriticalSection.Leave(); return(KernelResult.PortRemoteClosed); } if (_activeRequest != null || !DequeueRequest(out KSessionRequest request)) { KernelContext.CriticalSection.Leave(); return(KernelResult.NotFound); } if (request.ClientThread == null) { KernelContext.CriticalSection.Leave(); return(KernelResult.PortRemoteClosed); } KThread clientThread = request.ClientThread; KProcess clientProcess = clientThread.Owner; KernelContext.CriticalSection.Leave(); _activeRequest = request; request.ServerProcess = serverProcess; Message clientMsg = new Message(request); Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize); MessageHeader clientHeader = GetClientMessageHeader(clientMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg); KernelResult serverResult = KernelResult.NotFound; KernelResult clientResult = KernelResult.Success; void CleanUpForError() { if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success) { request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager); } CloseAllHandles(serverMsg, clientHeader, serverProcess); KernelContext.CriticalSection.Enter(); _activeRequest = null; if (_requests.Count != 0) { Signal(); } KernelContext.CriticalSection.Leave(); WakeClientThread(request, clientResult); } if (clientHeader.ReceiveListType < 2 && clientHeader.ReceiveListOffset > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } else if (clientHeader.ReceiveListType == 2 && clientHeader.ReceiveListOffset + 8 > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } else if (clientHeader.ReceiveListType > 2 && clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords) { CleanUpForError(); return(KernelResult.InvalidCombination); } if (clientHeader.MessageSizeInWords * 4 > clientMsg.Size) { CleanUpForError(); return(KernelResult.CmdBufferTooSmall); } ulong[] receiveList = GetReceiveList( serverMsg, serverHeader.ReceiveListType, serverHeader.ReceiveListOffset); serverProcess.CpuMemory.Write(serverMsg.Address + 0, clientHeader.Word0); serverProcess.CpuMemory.Write(serverMsg.Address + 4, clientHeader.Word1); uint offset; // Copy handles. if (clientHeader.HasHandles) { if (clientHeader.MoveHandlesCount != 0) { CleanUpForError(); return(KernelResult.InvalidCombination); } serverProcess.CpuMemory.Write(serverMsg.Address + 8, clientHeader.Word2); offset = 3; if (clientHeader.HasPid) { serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, clientProcess.Pid); offset += 2; } for (int index = 0; index < clientHeader.CopyHandlesCount; index++) { int newHandle = 0; int handle = KernelContext.Memory.Read <int>(clientMsg.DramAddress + offset * 4); if (clientResult == KernelResult.Success && handle != 0) { clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle); } serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, newHandle); offset++; } for (int index = 0; index < clientHeader.MoveHandlesCount; index++) { int newHandle = 0; int handle = KernelContext.Memory.Read <int>(clientMsg.DramAddress + offset * 4); if (handle != 0) { if (clientResult == KernelResult.Success) { clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle); } else { clientProcess.HandleTable.CloseHandle(handle); } } serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, newHandle); offset++; } if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } } else { offset = 2; } // Copy pointer/receive list buffers. uint recvListDstOffset = 0; for (int index = 0; index < clientHeader.PointerBuffersCount; index++) { ulong pointerDesc = KernelContext.Memory.Read <ulong>(clientMsg.DramAddress + offset * 4); PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc); if (descriptor.BufferSize != 0) { clientResult = GetReceiveListAddress( descriptor, serverMsg, serverHeader.ReceiveListType, clientHeader.MessageSizeInWords, receiveList, ref recvListDstOffset, out ulong recvListBufferAddress); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess( recvListBufferAddress, descriptor.BufferSize, descriptor.BufferAddress, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, MemoryPermission.Read, MemoryAttribute.Uncached, MemoryAttribute.None); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } descriptor.BufferAddress = recvListBufferAddress; } else { descriptor.BufferAddress = 0; } serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, descriptor.Pack()); offset += 2; } // Copy send, receive and exchange buffers. uint totalBuffersCount = clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount + clientHeader.ExchangeBuffersCount; for (int index = 0; index < totalBuffersCount; index++) { ulong clientDescAddress = clientMsg.DramAddress + offset * 4; uint descWord0 = KernelContext.Memory.Read <uint>(clientDescAddress + 0); uint descWord1 = KernelContext.Memory.Read <uint>(clientDescAddress + 4); uint descWord2 = KernelContext.Memory.Read <uint>(clientDescAddress + 8); bool isSendDesc = index < clientHeader.SendBuffersCount; bool isExchangeDesc = index >= clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount; bool notReceiveDesc = isSendDesc || isExchangeDesc; bool isReceiveDesc = !notReceiveDesc; MemoryPermission permission = index >= clientHeader.SendBuffersCount ? MemoryPermission.ReadAndWrite : MemoryPermission.Read; uint sizeHigh4 = (descWord2 >> 24) & 0xf; ulong bufferSize = descWord0 | (ulong)sizeHigh4 << 32; ulong dstAddress = 0; if (bufferSize != 0) { ulong bufferAddress; bufferAddress = descWord2 >> 28; bufferAddress |= ((descWord2 >> 2) & 7) << 4; bufferAddress = (bufferAddress << 32) | descWord1; MemoryState state = IpcMemoryStates[(descWord2 + 1) & 3]; clientResult = serverProcess.MemoryManager.MapBufferFromClientProcess( bufferSize, bufferAddress, clientProcess.MemoryManager, permission, state, notReceiveDesc, out dstAddress); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } if (isSendDesc) { clientResult = request.BufferDescriptorTable.AddSendBuffer(bufferAddress, dstAddress, bufferSize, state); } else if (isReceiveDesc) { clientResult = request.BufferDescriptorTable.AddReceiveBuffer(bufferAddress, dstAddress, bufferSize, state); } else /* if (isExchangeDesc) */ { clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state); } if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } } descWord1 = (uint)dstAddress; descWord2 &= 3; descWord2 |= sizeHigh4 << 24; descWord2 |= (uint)(dstAddress >> 34) & 0x3ffffffc; descWord2 |= (uint)(dstAddress >> 4) & 0xf0000000; ulong serverDescAddress = serverMsg.Address + offset * 4; serverProcess.CpuMemory.Write(serverDescAddress + 0, descWord0); serverProcess.CpuMemory.Write(serverDescAddress + 4, descWord1); serverProcess.CpuMemory.Write(serverDescAddress + 8, descWord2); offset += 3; } // Copy raw data. if (clientHeader.RawDataSizeInWords != 0) { ulong copySrc = clientMsg.Address + offset * 4; ulong copyDst = serverMsg.Address + offset * 4; ulong copySize = clientHeader.RawDataSizeInWords * 4; if (serverMsg.IsCustom || clientMsg.IsCustom) { MemoryPermission permission = clientMsg.IsCustom ? MemoryPermission.None : MemoryPermission.Read; clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess( copyDst, copySize, copySrc, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, permission, MemoryAttribute.Uncached, MemoryAttribute.None); } else { copySrc = clientProcess.MemoryManager.GetDramAddressFromVa(copySrc); copyDst = serverProcess.MemoryManager.GetDramAddressFromVa(copyDst); KernelContext.Memory.Copy(copyDst, copySrc, copySize); } if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } } return(KernelResult.Success); }
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) { KThread serverThread = KernelContext.Scheduler.GetCurrentThread(); KProcess serverProcess = serverThread.Owner; KernelContext.CriticalSection.Enter(); if (_activeRequest == null) { KernelContext.CriticalSection.Leave(); return(KernelResult.InvalidState); } KSessionRequest request = _activeRequest; _activeRequest = null; if (_requests.Count != 0) { Signal(); } KernelContext.CriticalSection.Leave(); KThread clientThread = request.ClientThread; KProcess clientProcess = clientThread.Owner; Message clientMsg = new Message(request); Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize); MessageHeader clientHeader = GetClientMessageHeader(clientMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg); KernelResult clientResult = KernelResult.Success; KernelResult serverResult = KernelResult.Success; void CleanUpForError() { CloseAllHandles(clientMsg, serverHeader, clientProcess); CancelRequest(request, clientResult); } if (clientHeader.ReceiveListType < 2 && clientHeader.ReceiveListOffset > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } else if (clientHeader.ReceiveListType == 2 && clientHeader.ReceiveListOffset + 8 > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } else if (clientHeader.ReceiveListType > 2 && clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size) { CleanUpForError(); return(KernelResult.InvalidCombination); } if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords) { CleanUpForError(); return(KernelResult.InvalidCombination); } if (serverHeader.MessageSizeInWords * 4 > clientMsg.Size) { CleanUpForError(); return(KernelResult.CmdBufferTooSmall); } if (serverHeader.SendBuffersCount != 0 || serverHeader.ReceiveBuffersCount != 0 || serverHeader.ExchangeBuffersCount != 0) { CleanUpForError(); return(KernelResult.InvalidCombination); } // Read receive list. ulong[] receiveList = GetReceiveList( clientMsg, clientHeader.ReceiveListType, clientHeader.ReceiveListOffset); // Copy receive and exchange buffers. clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } // Copy header. KernelContext.Memory.Write(clientMsg.DramAddress + 0, serverHeader.Word0); KernelContext.Memory.Write(clientMsg.DramAddress + 4, serverHeader.Word1); // Copy handles. uint offset; if (serverHeader.HasHandles) { offset = 3; KernelContext.Memory.Write(clientMsg.DramAddress + 8, serverHeader.Word2); if (serverHeader.HasPid) { KernelContext.Memory.Write(clientMsg.DramAddress + offset * 4, serverProcess.Pid); offset += 2; } for (int index = 0; index < serverHeader.CopyHandlesCount; index++) { int newHandle = 0; int handle = serverProcess.CpuMemory.Read <int>(serverMsg.Address + offset * 4); if (handle != 0) { GetCopyObjectHandle(serverThread, clientProcess, handle, out newHandle); } KernelContext.Memory.Write(clientMsg.DramAddress + offset * 4, newHandle); offset++; } for (int index = 0; index < serverHeader.MoveHandlesCount; index++) { int newHandle = 0; int handle = serverProcess.CpuMemory.Read <int>(serverMsg.Address + offset * 4); if (handle != 0) { if (clientResult == KernelResult.Success) { clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle); } else { serverProcess.HandleTable.CloseHandle(handle); } } KernelContext.Memory.Write(clientMsg.DramAddress + offset * 4, newHandle); offset++; } } else { offset = 2; } // Copy pointer/receive list buffers. uint recvListDstOffset = 0; for (int index = 0; index < serverHeader.PointerBuffersCount; index++) { ulong pointerDesc = serverProcess.CpuMemory.Read <ulong>(serverMsg.Address + offset * 4); PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc); if (descriptor.BufferSize != 0) { clientResult = GetReceiveListAddress( descriptor, clientMsg, clientHeader.ReceiveListType, serverHeader.MessageSizeInWords, receiveList, ref recvListDstOffset, out ulong recvListBufferAddress); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess( recvListBufferAddress, descriptor.BufferSize, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, MemoryPermission.Read, MemoryAttribute.Uncached, MemoryAttribute.None, descriptor.BufferAddress); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } } offset += 2; } // Set send, receive and exchange buffer descriptors to zero. uint totalBuffersCount = serverHeader.SendBuffersCount + serverHeader.ReceiveBuffersCount + serverHeader.ExchangeBuffersCount; for (int index = 0; index < totalBuffersCount; index++) { ulong dstDescAddress = clientMsg.DramAddress + offset * 4; KernelContext.Memory.Write(dstDescAddress + 0, 0); KernelContext.Memory.Write(dstDescAddress + 4, 0); KernelContext.Memory.Write(dstDescAddress + 8, 0); offset += 3; } // Copy raw data. if (serverHeader.RawDataSizeInWords != 0) { ulong copyDst = clientMsg.Address + offset * 4; ulong copySrc = serverMsg.Address + offset * 4; ulong copySize = serverHeader.RawDataSizeInWords * 4; if (serverMsg.IsCustom || clientMsg.IsCustom) { MemoryPermission permission = clientMsg.IsCustom ? MemoryPermission.None : MemoryPermission.Read; clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess( copyDst, copySize, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, permission, MemoryAttribute.Uncached, MemoryAttribute.None, copySrc); } else { copyDst = clientProcess.MemoryManager.GetDramAddressFromVa(copyDst); copySrc = serverProcess.MemoryManager.GetDramAddressFromVa(copySrc); KernelContext.Memory.Copy(copyDst, copySrc, copySize); } } // Unmap buffers from server. clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager); if (clientResult != KernelResult.Success) { CleanUpForError(); return(serverResult); } WakeClientThread(request, clientResult); return(serverResult); }
private KernelResult GetReceiveListAddress( PointerBufferDesc descriptor, Message message, uint recvListType, uint messageSizeInWords, ulong[] receiveList, ref uint dstOffset, out ulong address) { ulong recvListBufferAddress = address = 0; if (recvListType == 0) { return(KernelResult.OutOfResource); } else if (recvListType == 1 || recvListType == 2) { ulong recvListBaseAddr; ulong recvListEndAddr; if (recvListType == 1) { recvListBaseAddr = message.Address + messageSizeInWords * 4; recvListEndAddr = message.Address + message.Size; } else /* if (recvListType == 2) */ { ulong packed = receiveList[0]; recvListBaseAddr = packed & 0x7fffffffff; uint size = (uint)(packed >> 48); if (size == 0) { return(KernelResult.OutOfResource); } recvListEndAddr = recvListBaseAddr + size; } recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10); ulong endAddress = recvListBufferAddress + descriptor.BufferSize; dstOffset = (uint)endAddress - (uint)recvListBaseAddr; if (recvListBufferAddress + descriptor.BufferSize <= recvListBufferAddress || recvListBufferAddress + descriptor.BufferSize > recvListEndAddr) { return(KernelResult.OutOfResource); } } else /* if (recvListType > 2) */ { if (descriptor.ReceiveIndex >= receiveList.Length) { return(KernelResult.OutOfResource); } ulong packed = receiveList[descriptor.ReceiveIndex]; recvListBufferAddress = packed & 0x7fffffffff; uint size = (uint)(packed >> 48); if (recvListBufferAddress == 0 || size == 0 || size < descriptor.BufferSize) { return(KernelResult.OutOfResource); } } address = recvListBufferAddress; return(KernelResult.Success); }