#include <mach_debug.h>
#include <mach_ipc_test.h>
#include <mach_machine_routines.h>
#include <norma_task.h>
#include <mach_rt.h>
#include <platforms.h>
#include <mach/mig.h>
#include <mach/port.h>
#include <mach/kern_return.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/notify.h>
#include <mach/ndr.h>
#include <mach/mach_vm_server.h>
#include <mach/mach_port_server.h>
#include <mach/mach_host_server.h>
#include <mach/host_priv_server.h>
#include <mach/host_security_server.h>
#include <mach/clock_server.h>
#include <mach/clock_priv_server.h>
#include <mach/ledger_server.h>
#include <mach/lock_set_server.h>
#include <default_pager/default_pager_object_server.h>
#include <mach/memory_object_server.h>
#include <mach/memory_object_control_server.h>
#include <mach/memory_object_default_server.h>
#include <mach/memory_object_name_server.h>
#include <mach/processor_server.h>
#include <mach/processor_set_server.h>
#include <mach/semaphore_server.h>
#include <mach/task_server.h>
#include <mach/vm_map_server.h>
#include <mach/thread_act_server.h>
#include <device/device_types.h>
#include <device/device_server.h>
#include <UserNotification/UNDReplyServer.h>
#if MACH_MACHINE_ROUTINES
#include <machine/machine_routines.h>
#endif
#if XK_PROXY
#include <uk_xkern/xk_uproxy_server.h>
#endif
#include <kern/ipc_tt.h>
#include <kern/ipc_mig.h>
#include <kern/ipc_kobject.h>
#include <kern/host_notify.h>
#include <kern/mk_timer.h>
#include <kern/misc_protos.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_labelh.h>
#include <kern/counters.h>
#include <vm/vm_protos.h>
#include <security/mac_mach_internal.h>
boolean_t
ipc_kobject_notify(
mach_msg_header_t *request_header,
mach_msg_header_t *reply_header);
typedef struct {
mach_msg_id_t num;
mig_routine_t routine;
int size;
#if MACH_COUNTERS
mach_counter_t callcount;
#endif
} mig_hash_t;
#define MAX_MIG_ENTRIES 1024
#define MIG_HASH(x) (x)
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
mig_hash_t mig_buckets[MAX_MIG_ENTRIES];
int mig_table_max_displ;
mach_msg_size_t mig_reply_size;
#if CONFIG_MACF
#include <mach/security_server.h>
#endif
const struct mig_subsystem *mig_e[] = {
(const struct mig_subsystem *)&mach_vm_subsystem,
(const struct mig_subsystem *)&mach_port_subsystem,
(const struct mig_subsystem *)&mach_host_subsystem,
(const struct mig_subsystem *)&host_priv_subsystem,
(const struct mig_subsystem *)&host_security_subsystem,
(const struct mig_subsystem *)&clock_subsystem,
(const struct mig_subsystem *)&clock_priv_subsystem,
(const struct mig_subsystem *)&processor_subsystem,
(const struct mig_subsystem *)&processor_set_subsystem,
(const struct mig_subsystem *)&is_iokit_subsystem,
(const struct mig_subsystem *)&memory_object_name_subsystem,
(const struct mig_subsystem *)&lock_set_subsystem,
(const struct mig_subsystem *)&ledger_subsystem,
(const struct mig_subsystem *)&semaphore_subsystem,
(const struct mig_subsystem *)&task_subsystem,
(const struct mig_subsystem *)&thread_act_subsystem,
(const struct mig_subsystem *)&vm_map_subsystem,
(const struct mig_subsystem *)&UNDReply_subsystem,
(const struct mig_subsystem *)&default_pager_object_subsystem,
#if XK_PROXY
(const struct mig_subsystem *)&do_uproxy_xk_uproxy_subsystem,
#endif
#if MACH_MACHINE_ROUTINES
(const struct mig_subsystem *)&MACHINE_SUBSYSTEM,
#endif
#if MCMSG && iPSC860
(const struct mig_subsystem *)&mcmsg_info_subsystem,
#endif
#if CONFIG_MACF
(const struct mig_subsystem *)&security_subsystem,
#endif
};
void
mig_init(void)
{
unsigned int i, n = sizeof(mig_e)/sizeof(const struct mig_subsystem *);
int howmany;
mach_msg_id_t j, pos, nentry, range;
for (i = 0; i < n; i++) {
range = mig_e[i]->end - mig_e[i]->start;
if (!mig_e[i]->start || range < 0)
panic("the msgh_ids in mig_e[] aren't valid!");
mig_reply_size = max(mig_reply_size, mig_e[i]->maxsize);
for (j = 0; j < range; j++) {
if (mig_e[i]->routine[j].stub_routine) {
nentry = j + mig_e[i]->start;
for (pos = MIG_HASH(nentry) % MAX_MIG_ENTRIES, howmany = 1;
mig_buckets[pos].num;
pos++, pos = pos % MAX_MIG_ENTRIES, howmany++) {
if (mig_buckets[pos].num == nentry) {
printf("message id = %d\n", nentry);
panic("multiple entries with the same msgh_id");
}
if (howmany == MAX_MIG_ENTRIES)
panic("the mig dispatch table is too small");
}
mig_buckets[pos].num = nentry;
mig_buckets[pos].routine = mig_e[i]->routine[j].stub_routine;
if (mig_e[i]->routine[j].max_reply_msg)
mig_buckets[pos].size = mig_e[i]->routine[j].max_reply_msg;
else
mig_buckets[pos].size = mig_e[i]->maxsize;
mig_table_max_displ = max(howmany, mig_table_max_displ);
}
}
}
printf("mig_table_max_displ = %d\n", mig_table_max_displ);
}
ipc_kmsg_t
ipc_kobject_server(
ipc_kmsg_t request)
{
mach_msg_size_t reply_size;
ipc_kmsg_t reply;
kern_return_t kr;
ipc_port_t *destp;
mach_msg_format_0_trailer_t *trailer;
register mig_hash_t *ptr;
{
register int key = request->ikm_header->msgh_id;
register int i = MIG_HASH(key);
register int max_iter = mig_table_max_displ;
do
ptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];
while (key != ptr->num && ptr->num && --max_iter);
if (!ptr->routine || key != ptr->num) {
ptr = (mig_hash_t *)0;
reply_size = mig_reply_size;
} else {
reply_size = ptr->size;
#if MACH_COUNTER
ptr->callcount++;
#endif
}
}
reply_size += MAX_TRAILER_SIZE;
reply = ipc_kmsg_alloc(reply_size);
if (reply == IKM_NULL) {
printf("ipc_kobject_server: dropping request\n");
ipc_kmsg_destroy(request);
return IKM_NULL;
}
{
#define InP ((mach_msg_header_t *) request->ikm_header)
#define OutP ((mig_reply_error_t *) reply->ikm_header)
OutP->NDR = NDR_record;
OutP->Head.msgh_size = sizeof(mig_reply_error_t);
OutP->Head.msgh_bits =
MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0);
OutP->Head.msgh_remote_port = InP->msgh_local_port;
OutP->Head.msgh_local_port = MACH_PORT_NULL;
OutP->Head.msgh_id = InP->msgh_id + 100;
#undef InP
#undef OutP
}
{
if (ptr) {
(*ptr->routine)(request->ikm_header, reply->ikm_header);
kernel_task->messages_received++;
}
else {
if (!ipc_kobject_notify(request->ikm_header, reply->ikm_header)){
#if MACH_IPC_TEST
printf("ipc_kobject_server: bogus kernel message, id=%d\n",
request->ikm_header->msgh_id);
#endif
_MIG_MSGID_INVALID(request->ikm_header->msgh_id);
((mig_reply_error_t *) reply->ikm_header)->RetCode
= MIG_BAD_ID;
}
else
kernel_task->messages_received++;
}
kernel_task->messages_sent++;
}
destp = (ipc_port_t *) &request->ikm_header->msgh_remote_port;
switch (MACH_MSGH_BITS_REMOTE(request->ikm_header->msgh_bits)) {
case MACH_MSG_TYPE_PORT_SEND:
ipc_port_release_send(*destp);
break;
case MACH_MSG_TYPE_PORT_SEND_ONCE:
ipc_port_release_sonce(*destp);
break;
default:
panic("ipc_kobject_server: strange destination rights");
}
*destp = IP_NULL;
if (!(reply->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
((mig_reply_error_t *) reply->ikm_header)->RetCode != KERN_SUCCESS)
kr = ((mig_reply_error_t *) reply->ikm_header)->RetCode;
else
kr = KERN_SUCCESS;
if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
ipc_kmsg_free(request);
} else {
request->ikm_header->msgh_local_port = MACH_PORT_NULL;
ipc_kmsg_destroy(request);
}
if (kr == MIG_NO_REPLY) {
ipc_kmsg_free(reply);
return IKM_NULL;
} else if (!IP_VALID((ipc_port_t)reply->ikm_header->msgh_remote_port)) {
ipc_kmsg_destroy(reply);
return IKM_NULL;
}
trailer = (mach_msg_format_0_trailer_t *)
((vm_offset_t)reply->ikm_header + (int)reply->ikm_header->msgh_size);
trailer->msgh_sender = KERNEL_SECURITY_TOKEN;
trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
return reply;
}
void
ipc_kobject_set(
ipc_port_t port,
ipc_kobject_t kobject,
ipc_kobject_type_t type)
{
ip_lock(port);
ipc_kobject_set_atomically(port, kobject, type);
#if CONFIG_MACF_MACH
mac_port_label_update_kobject (&port->ip_label, type);
#endif
ip_unlock(port);
}
void
ipc_kobject_set_atomically(
ipc_port_t port,
ipc_kobject_t kobject,
ipc_kobject_type_t type)
{
assert(type == IKOT_NONE || ip_active(port));
#if MACH_ASSERT
port->ip_spares[2] = (port->ip_bits & IO_BITS_KOTYPE);
#endif
port->ip_bits = (port->ip_bits &~ IO_BITS_KOTYPE) | type;
port->ip_kobject = kobject;
}
void
ipc_kobject_destroy(
ipc_port_t port)
{
switch (ip_kotype(port)) {
case IKOT_TIMER:
mk_timer_port_destroy(port);
break;
case IKOT_NAMED_ENTRY:
mach_destroy_memory_entry(port);
break;
case IKOT_HOST_NOTIFY:
host_notify_port_destroy(port);
break;
#if CONFIG_MACF_MACH
case IKOT_LABELH:
labelh_destroy(port);
break;
#endif
default:
break;
}
}
boolean_t
ipc_kobject_notify(
mach_msg_header_t *request_header,
mach_msg_header_t *reply_header)
{
ipc_port_t port = (ipc_port_t) request_header->msgh_remote_port;
((mig_reply_error_t *) reply_header)->RetCode = MIG_NO_REPLY;
switch (request_header->msgh_id) {
case MACH_NOTIFY_NO_SENDERS:
if(ip_kotype(port) == IKOT_NAMED_ENTRY) {
ip_lock(port);
port->ip_mscount = 0;
port->ip_messages.imq_seqno = 0;
ipc_port_destroy(port);
return TRUE;
}
if (ip_kotype(port) == IKOT_UPL) {
upl_no_senders(
request_header->msgh_remote_port,
(mach_port_mscount_t)
((mach_no_senders_notification_t *)
request_header)->not_count);
reply_header->msgh_remote_port = MACH_PORT_NULL;
return TRUE;
}
break;
case MACH_NOTIFY_PORT_DELETED:
case MACH_NOTIFY_PORT_DESTROYED:
case MACH_NOTIFY_SEND_ONCE:
case MACH_NOTIFY_DEAD_NAME:
break;
default:
return FALSE;
}
switch (ip_kotype(port)) {
#ifdef IOKIT
case IKOT_IOKIT_OBJECT:
case IKOT_IOKIT_CONNECT:
case IKOT_IOKIT_SPARE:
{
return iokit_notify(request_header);
}
#endif
default:
return FALSE;
}
}
#include <mach_kdb.h>
#if MACH_COUNTERS && MACH_KDB
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#define printf kdbprintf
extern void kobjserver_stats(void);
extern void bucket_stats_print(mig_hash_t *bucket);
extern void kobjserver_stats_clear(void);
void
kobjserver_stats_clear(void)
{
int i;
for (i = 0; i < MAX_MIG_ENTRIES; i++) {
mig_buckets[i].callcount = 0;
}
}
void
kobjserver_stats(void)
{
register unsigned int i, n = sizeof(mig_e)/sizeof(struct mig_subsystem);
register unsigned int howmany;
register mach_msg_id_t j, pos, nentry, range;
db_printf("Kobject server call counts:\n");
for (i = 0; i < n; i++) {
db_printf(" ");
db_printsym((vm_offset_t)mig_e[i], DB_STGY_ANY);
db_printf(":\n");
range = mig_e[i]->end - mig_e[i]->start;
if (!mig_e[i]->start || range < 0) continue;
for (j = 0; j < range; j++) {
nentry = j + mig_e[i]->start;
for (pos = MIG_HASH(nentry) % MAX_MIG_ENTRIES, howmany = 1;
mig_buckets[pos].num;
pos++, pos = pos % MAX_MIG_ENTRIES, howmany++) {
if (mig_buckets[pos].num == nentry)
bucket_stats_print(&mig_buckets[pos]);
}
}
}
}
void
bucket_stats_print(mig_hash_t *bucket)
{
if (bucket->callcount) {
db_printf(" ");
db_printsym((vm_offset_t)bucket->routine, DB_STGY_ANY);
db_printf(" (%d):\t%d\n", bucket->num, bucket->callcount);
}
}
#endif