2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/send.c
5 * PURPOSE: Local Procedure Call: Sending (Requests)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PUBLIC FUNCTIONS **********************************************************/
22 LpcRequestPort(IN PVOID PortObject
,
23 IN PPORT_MESSAGE LpcMessage
)
25 PLPCP_PORT_OBJECT Port
= PortObject
, QueuePort
, ConnectionPort
= NULL
;
27 PLPCP_MESSAGE Message
;
28 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
30 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", Port
, LpcMessage
);
32 /* Check if this is a non-datagram message */
33 if (LpcMessage
->u2
.s2
.Type
)
35 /* Get the message type */
36 MessageType
= LpcpGetMessageType(LpcMessage
);
39 if ((MessageType
< LPC_DATAGRAM
) || (MessageType
> LPC_CLIENT_DIED
))
42 return STATUS_INVALID_PARAMETER
;
45 /* Mark this as a kernel-mode message only if we really came from it */
46 if ((PreviousMode
== KernelMode
) &&
47 (LpcMessage
->u2
.s2
.Type
& LPC_KERNELMODE_MESSAGE
))
49 /* We did, this is a kernel mode message */
50 MessageType
|= LPC_KERNELMODE_MESSAGE
;
55 /* This is a datagram */
56 MessageType
= LPC_DATAGRAM
;
59 /* Can't have data information on this type of call */
60 if (LpcMessage
->u2
.s2
.DataInfoOffset
) return STATUS_INVALID_PARAMETER
;
62 /* Validate message sizes */
63 if (((ULONG
)LpcMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
64 ((ULONG
)LpcMessage
->u1
.s1
.TotalLength
<= (ULONG
)LpcMessage
->u1
.s1
.DataLength
))
67 return STATUS_PORT_MESSAGE_TOO_LONG
;
70 /* Allocate a new message */
71 Message
= LpcpAllocateFromPortZone();
72 if (!Message
) return STATUS_NO_MEMORY
;
74 /* Clear the context */
75 Message
->RepliedToThread
= NULL
;
76 Message
->PortContext
= NULL
;
78 /* Copy the message */
79 LpcpMoveMessage(&Message
->Request
,
83 &PsGetCurrentThread()->Cid
);
85 /* Acquire the LPC lock */
86 KeAcquireGuardedMutex(&LpcpLock
);
88 /* Check if this is anything but a connection port */
89 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
91 /* The queue port is the connected port */
92 QueuePort
= Port
->ConnectedPort
;
95 /* Check if this is a client port */
96 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
98 /* Then copy the context */
99 Message
->PortContext
= QueuePort
->PortContext
;
100 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
104 LpcpFreeToPortZone(Message
, 3);
105 return STATUS_PORT_DISCONNECTED
;
108 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
110 /* Any other kind of port, use the connection port */
111 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
115 LpcpFreeToPortZone(Message
, 3);
116 return STATUS_PORT_DISCONNECTED
;
120 /* If we have a connection port, reference it */
121 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
126 /* For connection ports, use the port itself */
127 QueuePort
= PortObject
;
130 /* Make sure we have a port */
133 /* Generate the Message ID and set it */
134 Message
->Request
.MessageId
= LpcpNextMessageId
++;
135 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
136 Message
->Request
.CallbackId
= 0;
138 /* No Message ID for the thread */
139 PsGetCurrentThread()->LpcReplyMessageId
= 0;
141 /* Insert the message in our chain */
142 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
144 /* Release the lock and release the semaphore */
145 KeEnterCriticalRegion();
146 KeReleaseGuardedMutex(&LpcpLock
);
147 LpcpCompleteWait(QueuePort
->MsgQueue
.Semaphore
);
149 /* If this is a waitable port, wake it up */
150 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
153 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
157 KeLeaveCriticalRegion();
158 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
159 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", QueuePort
, Message
);
160 return STATUS_SUCCESS
;
163 /* If we got here, then free the message and fail */
164 LpcpFreeToPortZone(Message
, 3);
165 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
166 return STATUS_PORT_DISCONNECTED
;
174 LpcRequestWaitReplyPort(IN PVOID PortObject
,
175 IN PPORT_MESSAGE LpcRequest
,
176 OUT PPORT_MESSAGE LpcReply
)
178 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
179 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
180 NTSTATUS Status
= STATUS_SUCCESS
;
181 PLPCP_MESSAGE Message
;
182 PETHREAD Thread
= PsGetCurrentThread();
184 PKSEMAPHORE Semaphore
;
188 Port
= (PLPCP_PORT_OBJECT
)PortObject
;
190 LPCTRACE(LPC_SEND_DEBUG
,
191 "Port: %p. Messages: %p/%p. Type: %lx\n",
195 LpcpGetMessageType(LpcRequest
));
197 /* Check if the thread is dying */
198 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
200 /* Check if this is an LPC Request */
201 if (LpcpGetMessageType(LpcRequest
) == LPC_REQUEST
)
203 /* Then it's a callback */
208 /* This is a kernel-mode message without a callback */
209 LpcRequest
->u2
.s2
.Type
|= LPC_REQUEST
;
213 /* Get the message type */
214 MessageType
= LpcRequest
->u2
.s2
.Type
;
216 /* Validate the length */
217 if (((ULONG
)LpcRequest
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
218 (ULONG
)LpcRequest
->u1
.s1
.TotalLength
)
221 return STATUS_INVALID_PARAMETER
;
224 /* Validate the message length */
225 if (((ULONG
)LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
226 ((ULONG
)LpcRequest
->u1
.s1
.TotalLength
<= (ULONG
)LpcRequest
->u1
.s1
.DataLength
))
229 return STATUS_PORT_MESSAGE_TOO_LONG
;
232 /* Allocate a message from the port zone */
233 Message
= LpcpAllocateFromPortZone();
236 /* Fail if we couldn't allocate a message */
237 return STATUS_NO_MEMORY
;
240 /* Check if this is a callback */
244 Semaphore
= NULL
; // we'd use the Thread Semaphore here
249 /* No callback, just copy the message */
250 LpcpMoveMessage(&Message
->Request
,
256 /* Acquire the LPC lock */
257 KeAcquireGuardedMutex(&LpcpLock
);
259 /* Right now clear the port context */
260 Message
->PortContext
= NULL
;
262 /* Check if this is a not connection port */
263 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
265 /* We want the connected port */
266 QueuePort
= Port
->ConnectedPort
;
269 /* We have no connected port, fail */
270 LpcpFreeToPortZone(Message
, 3);
271 return STATUS_PORT_DISCONNECTED
;
274 /* This will be the rundown port */
275 ReplyPort
= QueuePort
;
277 /* Check if this is a communication port */
278 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
280 /* Copy the port context and use the connection port */
281 Message
->PortContext
= QueuePort
->PortContext
;
282 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
286 LpcpFreeToPortZone(Message
, 3);
287 return STATUS_PORT_DISCONNECTED
;
290 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
291 LPCP_COMMUNICATION_PORT
)
293 /* Use the connection port for anything but communication ports */
294 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
298 LpcpFreeToPortZone(Message
, 3);
299 return STATUS_PORT_DISCONNECTED
;
303 /* Reference the connection port if it exists */
304 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
308 /* Otherwise, for a connection port, use the same port object */
309 QueuePort
= ReplyPort
= Port
;
312 /* No reply thread */
313 Message
->RepliedToThread
= NULL
;
314 Message
->SenderPort
= Port
;
316 /* Generate the Message ID and set it */
317 Message
->Request
.MessageId
= LpcpNextMessageId
++;
318 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
319 Message
->Request
.CallbackId
= 0;
321 /* Set the message ID for our thread now */
322 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
323 Thread
->LpcReplyMessage
= NULL
;
325 /* Insert the message in our chain */
326 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
327 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
328 LpcpSetPortToThread(Thread
, Port
);
330 /* Release the lock and get the semaphore we'll use later */
331 KeEnterCriticalRegion();
332 KeReleaseGuardedMutex(&LpcpLock
);
333 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
335 /* If this is a waitable port, wake it up */
336 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
339 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
343 /* Now release the semaphore */
344 LpcpCompleteWait(Semaphore
);
345 KeLeaveCriticalRegion();
347 /* And let's wait for the reply */
348 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
350 /* Acquire the LPC lock */
351 KeAcquireGuardedMutex(&LpcpLock
);
353 /* Get the LPC Message and clear our thread's reply data */
354 Message
= LpcpGetMessageFromThread(Thread
);
355 Thread
->LpcReplyMessage
= NULL
;
356 Thread
->LpcReplyMessageId
= 0;
358 /* Check if we have anything on the reply chain*/
359 if (!IsListEmpty(&Thread
->LpcReplyChain
))
361 /* Remove this thread and reinitialize the list */
362 RemoveEntryList(&Thread
->LpcReplyChain
);
363 InitializeListHead(&Thread
->LpcReplyChain
);
366 /* Release the lock */
367 KeReleaseGuardedMutex(&LpcpLock
);
369 /* Check if we got a reply */
370 if (Status
== STATUS_SUCCESS
)
372 /* Check if we have a valid message */
375 LPCTRACE(LPC_SEND_DEBUG
,
376 "Reply Messages: %p/%p\n",
378 (&Message
->Request
) + 1);
380 /* Move the message */
381 LpcpMoveMessage(LpcReply
,
383 (&Message
->Request
) + 1,
387 /* Check if this is an LPC request with data information */
388 if ((LpcpGetMessageType(&Message
->Request
) == LPC_REQUEST
) &&
389 (Message
->Request
.u2
.s2
.DataInfoOffset
))
391 /* Save the data information */
392 LpcpSaveDataInfoMessage(Port
, Message
, 0);
396 /* Otherwise, just free it */
397 LpcpFreeToPortZone(Message
, 0);
402 /* We don't have a reply */
403 Status
= STATUS_LPC_REPLY_LOST
;
408 /* The wait failed, free the message */
409 if (Message
) LpcpFreeToPortZone(Message
, 0);
413 LPCTRACE(LPC_SEND_DEBUG
,
414 "Port: %p. Status: %p\n",
418 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
427 NtRequestPort(IN HANDLE PortHandle
,
428 IN PPORT_MESSAGE LpcMessage
)
431 return STATUS_NOT_IMPLEMENTED
;
439 NtRequestWaitReplyPort(IN HANDLE PortHandle
,
440 IN PPORT_MESSAGE LpcRequest
,
441 IN OUT PPORT_MESSAGE LpcReply
)
443 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
444 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
446 PLPCP_MESSAGE Message
;
447 PETHREAD Thread
= PsGetCurrentThread();
449 PKSEMAPHORE Semaphore
;
452 LPCTRACE(LPC_SEND_DEBUG
,
453 "Handle: %lx. Messages: %p/%p. Type: %lx\n",
457 LpcpGetMessageType(LpcRequest
));
459 /* Check if the thread is dying */
460 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
462 /* Check if this is an LPC Request */
463 if (LpcpGetMessageType(LpcRequest
) == LPC_REQUEST
)
465 /* Then it's a callback */
468 else if (LpcpGetMessageType(LpcRequest
))
470 /* This is a not kernel-mode message */
471 return STATUS_INVALID_PARAMETER
;
475 /* This is a kernel-mode message without a callback */
476 LpcRequest
->u2
.s2
.Type
|= LPC_REQUEST
;
480 /* Get the message type */
481 MessageType
= LpcRequest
->u2
.s2
.Type
;
483 /* Validate the length */
484 if (((ULONG
)LpcRequest
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
485 (ULONG
)LpcRequest
->u1
.s1
.TotalLength
)
488 return STATUS_INVALID_PARAMETER
;
491 /* Reference the object */
492 Status
= ObReferenceObjectByHandle(PortHandle
,
498 if (!NT_SUCCESS(Status
)) return Status
;
500 /* Validate the message length */
501 if (((ULONG
)LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
502 ((ULONG
)LpcRequest
->u1
.s1
.TotalLength
<= (ULONG
)LpcRequest
->u1
.s1
.DataLength
))
505 ObDereferenceObject(Port
);
506 return STATUS_PORT_MESSAGE_TOO_LONG
;
509 /* Allocate a message from the port zone */
510 Message
= LpcpAllocateFromPortZone();
513 /* Fail if we couldn't allocate a message */
514 ObDereferenceObject(Port
);
515 return STATUS_NO_MEMORY
;
518 /* Check if this is a callback */
522 Semaphore
= NULL
; // we'd use the Thread Semaphore here
527 /* No callback, just copy the message */
530 LpcpMoveMessage(&Message
->Request
,
538 Status
= _SEH_GetExceptionCode();
542 if (!NT_SUCCESS(Status
))
544 LpcpFreeToPortZone(Message
, 0);
545 ObDereferenceObject(Port
);
549 /* Acquire the LPC lock */
550 KeAcquireGuardedMutex(&LpcpLock
);
552 /* Right now clear the port context */
553 Message
->PortContext
= NULL
;
555 /* Check if this is a not connection port */
556 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
558 /* We want the connected port */
559 QueuePort
= Port
->ConnectedPort
;
562 /* We have no connected port, fail */
563 LpcpFreeToPortZone(Message
, 3);
564 ObDereferenceObject(Port
);
565 return STATUS_PORT_DISCONNECTED
;
568 /* This will be the rundown port */
569 ReplyPort
= QueuePort
;
571 /* Check if this is a communication port */
572 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
574 /* Copy the port context and use the connection port */
575 Message
->PortContext
= QueuePort
->PortContext
;
576 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
580 LpcpFreeToPortZone(Message
, 3);
581 ObDereferenceObject(Port
);
582 return STATUS_PORT_DISCONNECTED
;
585 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
586 LPCP_COMMUNICATION_PORT
)
588 /* Use the connection port for anything but communication ports */
589 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
593 LpcpFreeToPortZone(Message
, 3);
594 ObDereferenceObject(Port
);
595 return STATUS_PORT_DISCONNECTED
;
599 /* Reference the connection port if it exists */
600 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
604 /* Otherwise, for a connection port, use the same port object */
605 QueuePort
= ReplyPort
= Port
;
608 /* No reply thread */
609 Message
->RepliedToThread
= NULL
;
610 Message
->SenderPort
= Port
;
612 /* Generate the Message ID and set it */
613 Message
->Request
.MessageId
= LpcpNextMessageId
++;
614 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
615 Message
->Request
.CallbackId
= 0;
617 /* Set the message ID for our thread now */
618 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
619 Thread
->LpcReplyMessage
= NULL
;
621 /* Insert the message in our chain */
622 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
623 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
624 LpcpSetPortToThread(Thread
, Port
);
626 /* Release the lock and get the semaphore we'll use later */
627 KeEnterCriticalRegion();
628 KeReleaseGuardedMutex(&LpcpLock
);
629 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
631 /* If this is a waitable port, wake it up */
632 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
635 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
639 /* Now release the semaphore */
640 LpcpCompleteWait(Semaphore
);
641 KeLeaveCriticalRegion();
643 /* And let's wait for the reply */
644 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
646 /* Acquire the LPC lock */
647 KeAcquireGuardedMutex(&LpcpLock
);
649 /* Get the LPC Message and clear our thread's reply data */
650 Message
= LpcpGetMessageFromThread(Thread
);
651 Thread
->LpcReplyMessage
= NULL
;
652 Thread
->LpcReplyMessageId
= 0;
654 /* Check if we have anything on the reply chain*/
655 if (!IsListEmpty(&Thread
->LpcReplyChain
))
657 /* Remove this thread and reinitialize the list */
658 RemoveEntryList(&Thread
->LpcReplyChain
);
659 InitializeListHead(&Thread
->LpcReplyChain
);
662 /* Release the lock */
663 KeReleaseGuardedMutex(&LpcpLock
);
665 /* Check if we got a reply */
666 if (Status
== STATUS_SUCCESS
)
668 /* Check if we have a valid message */
671 LPCTRACE(LPC_SEND_DEBUG
,
672 "Reply Messages: %p/%p\n",
674 (&Message
->Request
) + 1);
676 /* Move the message */
679 LpcpMoveMessage(LpcReply
,
681 (&Message
->Request
) + 1,
687 Status
= _SEH_GetExceptionCode();
691 /* Check if this is an LPC request with data information */
692 if ((LpcpGetMessageType(&Message
->Request
) == LPC_REQUEST
) &&
693 (Message
->Request
.u2
.s2
.DataInfoOffset
))
695 /* Save the data information */
696 LpcpSaveDataInfoMessage(Port
, Message
, 0);
700 /* Otherwise, just free it */
701 LpcpFreeToPortZone(Message
, 0);
706 /* We don't have a reply */
707 Status
= STATUS_LPC_REPLY_LOST
;
712 /* The wait failed, free the message */
713 if (Message
) LpcpFreeToPortZone(Message
, 0);
717 LPCTRACE(LPC_SEND_DEBUG
,
718 "Port: %p. Status: %p\n",
721 ObDereferenceObject(Port
);
722 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);