diff --git a/doc/mach.texi b/doc/mach.texi index d089224..6167b4b 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -1330,6 +1330,15 @@ which is conventionally used as a reply port by the recipient of the message. The field must carry a send right, a send-once right, @code{MACH_PORT_NULL}, or @code{MACH_PORT_DEAD}. +@item mach_port_t msgh_protected_payload +The @code{msgh_protected_payload} field carries a payload that is set +by the kernel during message delivery. The payload is an opaque +identifier that can be used by the receiver to lookup the associated +data structure. + +It is only valid in received messages. See @ref{Message Receive} for +further information. + @item mach_port_seqno_t msgh_seqno The @code{msgh_seqno} field provides a sequence number for the message. It is only valid in received messages; its value in sent messages is @@ -1417,6 +1426,7 @@ types are predefined: @item MACH_MSG_TYPE_STRING @item MACH_MSG_TYPE_STRING_C @item MACH_MSG_TYPE_PORT_NAME +@item MACH_MSG_TYPE_PROTECTED_PAYLOAD @end table The following predefined types specify port rights, and receive special @@ -1435,6 +1445,11 @@ should be used in preference to @code{MACH_MSG_TYPE_INTEGER_32}. @item MACH_MSG_TYPE_MAKE_SEND_ONCE @end table +The type @code{MACH_MSG_TYPE_PROTECTED_PAYLOAD} is used by the kernel +to indicate that a delivered message carries a payload in the +@code{msgh_protected_payload} field. See @ref{Message Receive} for +more information. + @item msgt_size : 8 The @code{msgt_size} field specifies the size of each datum, in bits. For example, the msgt_size of @code{MACH_MSG_TYPE_INTEGER_32} data is 32. @@ -1934,6 +1949,25 @@ loses the receive right after the message was dequeued from it, then right still exists, but isn't held by the caller, then @code{msgh_local_port} specifies @code{MACH_PORT_NULL}. +Servers usually associate some state with a receive right. To that +end, they might use a hash table to look up the state for the port a +message was sent to. To optimize this, a task may associate an opaque +@var{payload} with a receive right using the +@code{mach_port_set_protected_payload} function. Once this is done, +the kernel will set the @code{msgh_protected_payload} field to +@var{payload} when delivering a message to this right and indicate +this by setting the local part of @code{msgh_bits} to +@code{MACH_MSG_TYPE_PROTECTED_PAYLOAD}. + +The support for protected payloads was added to GNU Mach. To preserve +binary compatibility, the @code{msgh_local_port} and +@code{msgh_local_port} share the same location. This makes it +possible to add the payload information without increasing the size of +@code{mach_msg_header_t}. This is an implementation detail. Which +field is valid is determined by the local part of the +@code{msgh_bits}. Existing software is not affected. When a receive +right is transferred to another task, its payload is cleared. + Received messages are stamped with a sequence number, taken from the port from which the message was received. (Messages received from a port set are stamped with a sequence number from the appropriate member @@ -2715,6 +2749,41 @@ In addition to the normal diagnostic return codes from the call's server (normally the kernel), the call may return @code{mach_msg} return codes. @end deftypefun +@deftypefun kern_return_t mach_port_set_protected_payload (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) +The function @code{mach_port_set_protected_payload} sets the protected +payload associated with the right @var{name} to @var{payload}. +Section @ref{Message Receive} describes how setting a protected +payload affects the messages delivered to @var{name}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_set_protected_payload} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from +the call's server (normally the kernel), the call may return +@code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_clear_protected_payload (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) +The function @code{mach_port_clear_protected_payload} clears the +protected payload associated with the right @var{name}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_clear_protected_payload} call is actually an RPC +to @var{task}, normally a send right for a task port, but potentially +any send right. In addition to the normal diagnostic return codes +from the call's server (normally the kernel), the call may return +@code{mach_msg} return codes. +@end deftypefun @node Port Sets @subsection Port Sets diff --git a/include/mach/mach_port.defs b/include/mach/mach_port.defs index 769d892..c7e8526 100644 --- a/include/mach/mach_port.defs +++ b/include/mach/mach_port.defs @@ -349,3 +349,21 @@ skip; /* mach_port_create_act */ #endif /* MIGRATING_THREADS */ +/* + * Only valid for receive rights. + * Set the protected payload for this right to the given value. + */ + +routine mach_port_set_protected_payload( + task : ipc_space_t; + name : mach_port_name_t; + payload : natural_t); + +/* + * Only valid for receive rights. + * Clear the protected payload for this right. + */ + +routine mach_port_clear_protected_payload( + task : ipc_space_t; + name : mach_port_name_t); diff --git a/include/mach/message.h b/include/mach/message.h index f78e978..0a7297e 100644 --- a/include/mach/message.h +++ b/include/mach/message.h @@ -136,7 +136,10 @@ typedef struct { mach_msg_bits_t msgh_bits; mach_msg_size_t msgh_size; mach_port_t msgh_remote_port; - mach_port_t msgh_local_port; + union { + mach_port_t msgh_local_port; + unsigned long msgh_protected_payload; + }; mach_port_seqno_t msgh_seqno; mach_msg_id_t msgh_id; } mach_msg_header_t; @@ -253,7 +256,9 @@ typedef struct { #define MACH_MSG_TYPE_PORT_SEND MACH_MSG_TYPE_MOVE_SEND #define MACH_MSG_TYPE_PORT_SEND_ONCE MACH_MSG_TYPE_MOVE_SEND_ONCE -#define MACH_MSG_TYPE_LAST 22 /* Last assigned */ +#define MACH_MSG_TYPE_PROTECTED_PAYLOAD 23 + +#define MACH_MSG_TYPE_LAST 23 /* Last assigned */ /* * A dummy value. Mostly used to indicate that the actual value diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c index 0e43410..c57ed90 100644 --- a/ipc/ipc_kmsg.c +++ b/ipc/ipc_kmsg.c @@ -1802,9 +1802,17 @@ ipc_kmsg_copyout_header(msg, space, notify) } else ip_unlock(dest); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND)); - msg->msgh_local_port = dest_name; + if (! dest->ip_has_protected_payload) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS( + 0, MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = MACH_PORT_NULL; return MACH_MSG_SUCCESS; } @@ -1900,10 +1908,18 @@ ipc_kmsg_copyout_header(msg, space, notify) } else ip_unlock(dest); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, - MACH_MSG_TYPE_PORT_SEND)); - msg->msgh_local_port = dest_name; + if (! dest->ip_has_protected_payload) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = reply_name; return MACH_MSG_SUCCESS; } @@ -1935,9 +1951,18 @@ ipc_kmsg_copyout_header(msg, space, notify) dest_name = MACH_PORT_NULL; } - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND_ONCE)); - msg->msgh_local_port = dest_name; + if (! dest->ip_has_protected_payload) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PORT_SEND_ONCE)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = MACH_PORT_NULL; return MACH_MSG_SUCCESS; } @@ -2227,9 +2252,16 @@ ipc_kmsg_copyout_header(msg, space, notify) if (IP_VALID(reply)) ipc_port_release(reply); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(reply_type, dest_type)); - msg->msgh_local_port = dest_name; + if (! dest->ip_has_protected_payload) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, dest_type)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = dest->ip_protected_payload; + } msg->msgh_remote_port = reply_name; } diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c index 982bd4e..d9875d1 100644 --- a/ipc/ipc_object.c +++ b/ipc/ipc_object.c @@ -481,6 +481,7 @@ ipc_object_copyin_from_kernel( port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + port->ip_has_protected_payload = FALSE; ip_unlock(port); break; } diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c index d4ade8e..0789296 100644 --- a/ipc/ipc_port.c +++ b/ipc/ipc_port.c @@ -425,6 +425,44 @@ ipc_port_set_seqno(port, seqno) } /* + * Routine: ipc_port_set_protected_payload + * Purpose: + * Changes a port's protected payload. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_set_protected_payload(ipc_port_t port, unsigned long payload) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + port->ip_protected_payload = payload; + port->ip_has_protected_payload = TRUE; + imq_unlock(mqueue); +} + +/* + * Routine: ipc_port_clear_protected_payload + * Purpose: + * Clear a port's protected payload. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_clear_protected_payload(ipc_port_t port) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + port->ip_has_protected_payload = FALSE; + imq_unlock(mqueue); +} + + +/* * Routine: ipc_port_clear_receiver * Purpose: * Prepares a receive right for transmission/destruction. @@ -493,6 +531,8 @@ ipc_port_init( port->ip_seqno = 0; port->ip_msgcount = 0; port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT; + port->ip_has_protected_payload = FALSE; + port->ip_protected_payload = 0; ipc_mqueue_init(&port->ip_messages); ipc_thread_queue_init(&port->ip_blocked); @@ -615,6 +655,7 @@ ipc_port_destroy( /* make port be in limbo */ port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + port->ip_has_protected_payload = FALSE; ip_unlock(port); if (!ipc_port_check_circularity(port, pdrequest)) { @@ -1218,6 +1259,10 @@ ipc_port_print(port) indent += 2; + iprintf("flags "); + printf("has_protected_payload=%d", port->ip_has_protected_payload); + printf("\n"); + ipc_object_print(&port->ip_object); iprintf("receiver=0x%x", port->ip_receiver); printf(", receiver_name=0x%x\n", port->ip_receiver_name); @@ -1240,6 +1285,8 @@ ipc_port_print(port) printf(", sndrs=0x%x", port->ip_blocked.ithq_base); printf(", kobj=0x%x\n", port->ip_kobject); + iprintf("protected_payload=%p\n", (void *) port->ip_protected_payload); + indent -= 2; } diff --git a/ipc/ipc_port.h b/ipc/ipc_port.h index 27d2e49..4c7c742 100644 --- a/ipc/ipc_port.h +++ b/ipc/ipc_port.h @@ -71,6 +71,10 @@ typedef unsigned int ipc_port_timestamp_t; struct ipc_port { struct ipc_target ip_target; + /* Flags. */ + unsigned int ip_has_protected_payload:1; /* A pp has + been set. */ + /* This points to the ip_target above if this port isn't on a port set; otherwise it points to the port set's ips_target. */ struct ipc_target *ip_cur_target; @@ -96,6 +100,7 @@ struct ipc_port { mach_port_msgcount_t ip_msgcount; mach_port_msgcount_t ip_qlimit; struct ipc_thread_queue ip_blocked; + unsigned long ip_protected_payload; }; #define ip_object ip_target.ipt_object @@ -262,6 +267,12 @@ extern void ipc_port_set_seqno(ipc_port_t, mach_port_seqno_t); extern void +ipc_port_set_protected_payload(ipc_port_t, unsigned long); + +extern void +ipc_port_clear_protected_payload(ipc_port_t); + +extern void ipc_port_clear_receiver(ipc_port_t); extern void diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c index 41fe3de..6b6b590 100644 --- a/ipc/ipc_right.c +++ b/ipc/ipc_right.c @@ -1432,6 +1432,12 @@ ipc_right_copyin( port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + + /* + * Clear the protected payload field to retain + * the behavior of mach_msg. + */ + port->ip_has_protected_payload = FALSE; ip_unlock(port); *objectp = (ipc_object_t) port; @@ -1932,6 +1938,12 @@ ipc_right_copyout( port->ip_receiver_name = name; port->ip_receiver = space; + /* + * Clear the protected payload field to retain + * the behavior of mach_msg. + */ + port->ip_has_protected_payload = FALSE; + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); if (bits & MACH_PORT_TYPE_SEND) { diff --git a/ipc/mach_port.c b/ipc/mach_port.c index 13572a1..057278b 100644 --- a/ipc/mach_port.c +++ b/ipc/mach_port.c @@ -1564,3 +1564,76 @@ mach_port_set_syscall_right(task, name) } #endif #endif /* MIGRATING_THREADS */ + +/* + * Routine: mach_port_set_protected_payload [kernel call] + * Purpose: + * Changes a receive right's protected payload. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set protected payload. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_protected_payload( + ipc_space_t space, + mach_port_t name, + unsigned long payload) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_protected_payload(port, payload); + + ip_unlock(port); + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_clear_protected_payload [kernel call] + * Purpose: + * Clears a receive right's protected payload. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Clear protected payload. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_clear_protected_payload( + ipc_space_t space, + mach_port_t name) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_clear_protected_payload(port); + + ip_unlock(port); + return KERN_SUCCESS; +}