#include <mach/port.h>
#include <mach/kern_return.h>
#include <kern/task.h>
#include <kern/lock.h>
#include <kern/spl.h>
#include <kern/zalloc.h>
#include <kern/ipc_subsystem.h>
#include <kern/subsystem.h>
#include <kern/misc_protos.h>
#define SUBSYSTEM_MIN_SIZE 12
#define SUBSYSTEM_MAX_SIZE (2*1024*1024)
void
subsystem_init(
void)
{
}
kern_return_t
mach_subsystem_create(
register task_t parent_task,
user_subsystem_t user_subsys,
mach_msg_type_number_t user_subsysCount,
subsystem_t *subsystem_p)
{
int i;
subsystem_t new_subsystem;
kern_return_t kr;
boolean_t deallocate = FALSE;
vm_size_t size;
vm_offset_t offset;
int num_routines;
boolean_t bad_arg = FALSE;
if (parent_task == TASK_NULL)
return(KERN_INVALID_ARGUMENT);
if (user_subsysCount < SUBSYSTEM_MIN_SIZE ||
user_subsysCount > SUBSYSTEM_MAX_SIZE)
return(KERN_INVALID_ARGUMENT);
size = (vm_size_t)user_subsysCount + sizeof(struct subsystem) -
sizeof(struct rpc_subsystem);
new_subsystem = (subsystem_t) kalloc(size);
if (new_subsystem == 0)
return(KERN_RESOURCE_SHORTAGE);
new_subsystem->task = parent_task;
new_subsystem->ref_count = 1;
new_subsystem->size = size;
subsystem_lock_init(new_subsystem);
bcopy((char *)user_subsys, (char *)&(new_subsystem->user),
(int)user_subsysCount);
num_routines = new_subsystem->user.end - new_subsystem->user.start;
if (num_routines < 0 ||
(char *)&new_subsystem->user.routine[num_routines] >
(char *)&new_subsystem->user + (int)user_subsysCount
) {
kfree((vm_offset_t)new_subsystem, size);
return(KERN_INVALID_ADDRESS);
}
offset = (char *)&new_subsystem->user -
(char *)new_subsystem->user.base_addr;
for (i = 0; i < num_routines; i++) {
routine_descriptor_t routine = &new_subsystem->user.routine[i];
if (!routine->impl_routine)
continue;
routine->arg_descr = (routine_arg_descriptor_t)
((char *)routine->arg_descr + offset);
if (routine->argc > 1000000 ||
routine->argc < routine->descr_count) {
bad_arg = TRUE;
break;
}
if ((char *)&routine->arg_descr[0] <
(char *)&new_subsystem->user.routine[num_routines]
||
(char *)&routine->arg_descr[routine->descr_count] >
(char *)&new_subsystem->user + (int)user_subsysCount
) {
printf("Arg descr out of bounds: arg_descr=%x, &routine.num_routines=%x\n",
&routine->arg_descr[0], &new_subsystem->user.routine[num_routines]);
printf(" new_subsys->user + subsysCount = %x\n",
(char *)&new_subsystem->user + (int)user_subsysCount);
#if MACH_DEBUG && MACH_KDB
subsystem_print(new_subsystem);
#endif
bad_arg = TRUE;
break;
}
}
if (bad_arg) {
kfree((vm_offset_t)new_subsystem, size);
return(KERN_INVALID_ADDRESS);
}
new_subsystem->user.base_addr = (vm_address_t)&new_subsystem->user;
new_subsystem->user.subsystem = new_subsystem;
ipc_subsystem_init(new_subsystem);
task_lock(parent_task);
if (parent_task->active) {
parent_task->subsystem_count++;
queue_enter(&parent_task->subsystem_list, new_subsystem,
subsystem_t, subsystem_list);
} else
deallocate = TRUE;
task_unlock(parent_task);
if (deallocate) {
subsystem_deallocate(new_subsystem);
return(KERN_INVALID_TASK);
}
ipc_subsystem_enable(new_subsystem);
*subsystem_p = new_subsystem;
return(KERN_SUCCESS);
}
void
subsystem_reference(
register subsystem_t subsystem)
{
spl_t s;
if (subsystem == SUBSYSTEM_NULL)
return;
s = splsched();
subsystem_lock(subsystem);
subsystem->ref_count++;
subsystem_unlock(subsystem);
splx(s);
}
void
subsystem_deallocate(
subsystem_t subsystem)
{
task_t task;
spl_t s;
if (subsystem == SUBSYSTEM_NULL)
return;
s = splsched();
subsystem_lock(subsystem);
if (--subsystem->ref_count > 0) {
subsystem_unlock(subsystem);
splx(s);
return;
}
ipc_subsystem_disable(subsystem);
subsystem->ref_count = 1;
subsystem_unlock(subsystem);
splx(s);
task = subsystem->task;
task_lock(task);
s = splsched();
subsystem_lock(subsystem);
if (--subsystem->ref_count == 0) {
task->subsystem_count--;
queue_remove(&task->subsystem_list, subsystem, subsystem_t,
subsystem_list);
ipc_subsystem_terminate(subsystem);
subsystem_unlock(subsystem);
splx(s);
kfree((vm_offset_t) subsystem, subsystem->size);
task_unlock(task);
return;
}
ipc_subsystem_enable(subsystem);
subsystem_unlock(subsystem);
splx(s);
task_unlock(task);
}
#include <mach_kdb.h>
#if MACH_KDB
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#include <ddb/db_print.h>
#include <ddb/db_command.h>
#define printf kdbprintf
void rpc_subsystem_print(rpc_subsystem_t subsys);
void
subsystem_print(
subsystem_t subsystem)
{
extern int db_indent;
iprintf("subsystem 0x%x\n", subsystem);
db_indent += 2;
iprintf("ref %d size %x task %x port %x\n", subsystem->ref_count,
subsystem->size, subsystem->task, subsystem->ipc_self);
rpc_subsystem_print(&subsystem->user);
db_indent -=2;
}
struct flagnames {
char *name;
int bit;
} arg_type_names[] = {
"port", MACH_RPC_PORT, "array", MACH_RPC_ARRAY,
"variable", MACH_RPC_VARIABLE, "in", MACH_RPC_IN, "out", MACH_RPC_OUT,
"pointer", MACH_RPC_POINTER, "phys_copy", MACH_RPC_PHYSICAL_COPY,
"virt_copy", MACH_RPC_VIRTUAL_COPY, "deallocate", MACH_RPC_DEALLOCATE,
"onstack", MACH_RPC_ONSTACK, "bounded", MACH_RPC_BOUND,
};
void
rpc_subsystem_print(
rpc_subsystem_t subsys)
{
int i, num_routines;
iprintf("rpc_subsystem 0x%x\n", subsys);
db_indent += 2;
num_routines = subsys->end - subsys->start;
iprintf("start %d end %d (%d routines) maxsize %x base %x\n",
subsys->start, subsys->end, num_routines, subsys->maxsize,
subsys->base_addr);
for (i = 0; i < num_routines; i++) {
routine_descriptor_t routine = subsys->routine + i;
routine_arg_descriptor_t args = routine->arg_descr;
int j, type, disposition;
struct flagnames *n;
char *sep;
iprintf("%x #%d:", routine, subsys->start + i);
if (routine->impl_routine == 0) {
printf(" skip\n");
continue;
}
printf("\n");
db_indent += 2;
iprintf("impl ");
db_printsym((db_expr_t) routine->impl_routine, DB_STGY_PROC);
printf("\n");
iprintf("stub ");
db_printsym((db_expr_t) routine->stub_routine, DB_STGY_PROC);
printf("\n");
iprintf("argc %d descr_count %d max_reply %x\n",
routine->argc, routine->descr_count, routine->max_reply_msg);
for (j = 0; j < routine->descr_count; j++) {
iprintf("%x desc %d: size %d count %d offset %x type", &args[j], j,
args[j].size, args[j].count, args[j].offset);
sep = " ";
type = args[j].type;
for (n = arg_type_names; n->name != 0; n++) {
if (type & n->bit) {
printf("%s%s", sep, n->name);
sep = "|";
type &= ~n->bit;
}
}
#define NAME_MASK (3 << NAME_SHIFT)
#define ACTION_MASK (3 << ACTION_SHIFT)
#define DISPOSITION_MASK (NAME_MASK | ACTION_MASK)
disposition = type & DISPOSITION_MASK;
type &= ~DISPOSITION_MASK;
if (sep[0] != '|' || type != 0)
printf("%s%x", sep, type);
switch (disposition & ACTION_MASK) {
case MACH_RPC_MOVE: printf(" move"); break;
case MACH_RPC_COPY: printf(" copy"); break;
case MACH_RPC_MAKE: printf(" make"); break;
}
switch (disposition & NAME_MASK) {
case MACH_RPC_RECEIVE: printf(" receive"); break;
case MACH_RPC_SEND: printf(" send"); break;
case MACH_RPC_SEND_ONCE: printf(" send-once"); break;
}
printf("\n");
}
db_indent -= 2;
}
db_indent -= 2;
}
void
db_show_subsystem(
db_expr_t addr,
boolean_t have_addr,
db_expr_t count,
char *modif)
{
if (!have_addr || addr == 0) {
db_printf("No subsystem\n");
return;
}
if (db_option(modif, 'r'))
rpc_subsystem_print((rpc_subsystem_t) addr);
else
subsystem_print((subsystem_t) addr);
}
#endif