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/mach_types.defs b/include/mach/mach_types.defs index bfce6cb..85ad653 100644 --- a/include/mach/mach_types.defs +++ b/include/mach/mach_types.defs @@ -139,6 +139,9 @@ type memory_object_t = mach_port_t #ifdef MEMORY_OBJECT_INTRAN intran: MEMORY_OBJECT_INTRAN #endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: MEMORY_OBJECT_INTRAN_PAYLOAD +#endif #ifdef MEMORY_OBJECT_OUTTRAN outtran: MEMORY_OBJECT_OUTTRAN #endif 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..2190e30 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 (! ipc_port_flag_protected_payload(dest)) { + 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 (! ipc_port_flag_protected_payload(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; + } 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 (! ipc_port_flag_protected_payload(dest)) { + 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 (! ipc_port_flag_protected_payload(dest)) { + 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..db6ef01 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; + ipc_port_flag_protected_payload_clear(port); ip_unlock(port); break; } diff --git a/ipc/ipc_object.h b/ipc/ipc_object.h index adf5bca..b83bb5a 100644 --- a/ipc/ipc_object.h +++ b/ipc/ipc_object.h @@ -57,7 +57,9 @@ typedef struct ipc_object { #define IO_VALID(io) (((io) != IO_NULL) && ((io) != IO_DEAD)) #define IO_BITS_KOTYPE 0x0000ffff /* used by the object */ -#define IO_BITS_OTYPE 0x7fff0000 /* determines a cache */ +#define IO_BITS_OTYPE 0x3fff0000 /* determines a cache */ +/* The following masks are used to store attributes of ipc ports. */ +#define IO_BITS_PROTECTED_PAYLOAD 0x40000000 /* pp set? */ #define IO_BITS_ACTIVE 0x80000000U /* is object alive? */ #define io_active(io) ((int)(io)->io_bits < 0) /* hack */ diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c index d4ade8e..7ddbd1b 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; + ipc_port_flag_protected_payload_set(port); + 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); + ipc_port_flag_protected_payload_clear(port); + 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; + ipc_port_flag_protected_payload_clear(port); + 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; + ipc_port_flag_protected_payload_clear(port); ip_unlock(port); if (!ipc_port_check_circularity(port, pdrequest)) { @@ -1218,6 +1259,11 @@ ipc_port_print(port) indent += 2; + iprintf("flags "); + printf("has_protected_payload=%d", + ipc_port_flag_protected_payload(port)); + 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 +1286,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..125fefc 100644 --- a/ipc/ipc_port.h +++ b/ipc/ipc_port.h @@ -48,6 +48,7 @@ #include #include #include +#include #include "ipc_target.h" #include @@ -96,6 +97,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 +264,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 @@ -325,4 +333,23 @@ ipc_port_dealloc_special(ipc_port_t, ipc_space_t); #define ipc_port_release(port) \ ipc_object_release(&(port)->ip_object) +extern inline boolean_t +ipc_port_flag_protected_payload(const struct ipc_port *port) +{ + return !! (port->ip_target.ipt_object.io_bits + & IO_BITS_PROTECTED_PAYLOAD); +} + +extern inline void +ipc_port_flag_protected_payload_set(struct ipc_port *port) +{ + port->ip_target.ipt_object.io_bits |= IO_BITS_PROTECTED_PAYLOAD; +} + +extern inline void +ipc_port_flag_protected_payload_clear(struct ipc_port *port) +{ + port->ip_target.ipt_object.io_bits &= ~IO_BITS_PROTECTED_PAYLOAD; +} + #endif /* _IPC_IPC_PORT_H_ */ diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c index 41fe3de..dbeacff 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. + */ + ipc_port_flag_protected_payload_clear(port); 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. + */ + ipc_port_flag_protected_payload_clear(port); + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); if (bits & MACH_PORT_TYPE_SEND) { diff --git a/ipc/mach_msg.c b/ipc/mach_msg.c index 78247a4..783b2b4 100644 --- a/ipc/mach_msg.c +++ b/ipc/mach_msg.c @@ -1132,11 +1132,19 @@ mach_msg_trap(msg, option, send_size, rcv_size, rcv_name, time_out, notify) } else ip_unlock(dest_port); - kmsg->ikm_header.msgh_bits = - MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, - MACH_MSG_TYPE_PORT_SEND); + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PORT_SEND); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + dest_port->ip_protected_payload; + } kmsg->ikm_header.msgh_remote_port = reply_name; - kmsg->ikm_header.msgh_local_port = dest_name; goto fast_put; abort_request_copyout: @@ -1170,11 +1178,19 @@ mach_msg_trap(msg, option, send_size, rcv_size, rcv_name, time_out, notify) dest_name = MACH_PORT_NULL; } - kmsg->ikm_header.msgh_bits = - MACH_MSGH_BITS(0, - MACH_MSG_TYPE_PORT_SEND_ONCE); + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PORT_SEND_ONCE); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + dest_port->ip_protected_payload; + } kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; - kmsg->ikm_header.msgh_local_port = dest_name; goto fast_put; } @@ -1204,12 +1220,23 @@ mach_msg_trap(msg, option, send_size, rcv_size, rcv_name, time_out, notify) dest_name = MACH_PORT_NULL; } - kmsg->ikm_header.msgh_bits = - MACH_MSGH_BITS_COMPLEX | - MACH_MSGH_BITS(0, - MACH_MSG_TYPE_PORT_SEND_ONCE); + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS_COMPLEX + | MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PORT_SEND_ONCE); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS_COMPLEX + | MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + dest_port->ip_protected_payload; + } kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; - kmsg->ikm_header.msgh_local_port = dest_name; mr = ipc_kmsg_copyout_body( (vm_offset_t) (&kmsg->ikm_header + 1), 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; +}