diff options
Diffstat (limited to 'mig/server.c')
-rw-r--r-- | mig/server.c | 1322 |
1 files changed, 1322 insertions, 0 deletions
diff --git a/mig/server.c b/mig/server.c new file mode 100644 index 0000000..07e4964 --- /dev/null +++ b/mig/server.c @@ -0,0 +1,1322 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include <assert.h> + +#include "write.h" +#include "utils.h" +#include "global.h" +#include "error.h" +#include "cpu.h" + +static void +WriteIncludes(FILE *file) +{ + fprintf(file, "#define EXPORT_BOOLEAN\n"); + fprintf(file, "#include <mach/boolean.h>\n"); + fprintf(file, "#include <mach/kern_return.h>\n"); + fprintf(file, "#include <mach/message.h>\n"); + fprintf(file, "#include <mach/mig_errors.h>\n"); + fprintf(file, "#include <mach/mig_support.h>\n"); + if (IsKernelServer) + fprintf(file, "#include <ipc/ipc_port.h>\n"); + fprintf(file, "\n"); +} + +static void +WriteGlobalDecls(FILE *file) +{ + if (RCSId != strNULL) + WriteRCSDecl(file, strconcat(SubsystemName, "_server"), RCSId); + + /* Used for locations in the request message, *not* reply message. + Reply message locations aren't dependent on IsKernelServer. */ + + if (IsKernelServer) + { + fprintf(file, "#define msgh_request_port\tmsgh_remote_port\n"); + fprintf(file, "#define MACH_MSGH_BITS_REQUEST(bits)"); + fprintf(file, "\tMACH_MSGH_BITS_REMOTE(bits)\n"); + fprintf(file, "#define msgh_reply_port\t\tmsgh_local_port\n"); + fprintf(file, "#define MACH_MSGH_BITS_REPLY(bits)"); + fprintf(file, "\tMACH_MSGH_BITS_LOCAL(bits)\n"); + } + else + { + fprintf(file, "#define msgh_request_port\tmsgh_local_port\n"); + fprintf(file, "#define MACH_MSGH_BITS_REQUEST(bits)"); + fprintf(file, "\tMACH_MSGH_BITS_LOCAL(bits)\n"); + fprintf(file, "#define msgh_reply_port\t\tmsgh_remote_port\n"); + fprintf(file, "#define MACH_MSGH_BITS_REPLY(bits)"); + fprintf(file, "\tMACH_MSGH_BITS_REMOTE(bits)\n"); + } + fprintf(file, "\n"); +} + +static void +WriteProlog(FILE *file) +{ + fprintf(file, "/* Module %s */\n", SubsystemName); + fprintf(file, "\n"); + + WriteIncludes(file); + WriteBogusDefines(file); + WriteGlobalDecls(file); +} + + +static void +WriteSymTabEntries(FILE *file, const statement_t *stats) +{ + register const statement_t *stat; + register u_int current = 0; + + for (stat = stats; stat != stNULL; stat = stat->stNext) + if (stat->stKind == skRoutine) { + register num = stat->stRoutine->rtNumber; + const char *name = stat->stRoutine->rtName; + + while (++current <= num) + fprintf(file,"\t\t\t{ \"\", 0, 0 },\n"); + fprintf(file, "\t{ \"%s\", %d, _X%s },\n", + name, + SubsystemBase + current - 1, + name); + } + while (++current <= rtNumber) + fprintf(file,"\t{ \"\", 0, 0 },\n"); +} + +static void +WriteArrayEntries(FILE *file, const statement_t *stats) +{ + register u_int current = 0; + register const statement_t *stat; + + for (stat = stats; stat != stNULL; stat = stat->stNext) + if (stat->stKind == skRoutine) + { + register const routine_t *rt = stat->stRoutine; + + while (current++ < rt->rtNumber) + fprintf(file, "\t\t0,\n"); + fprintf(file, "\t\t_X%s,\n", rt->rtName); + } + while (current++ < rtNumber) + fprintf(file, "\t\t\t0,\n"); +} + +static void +WriteEpilog(FILE *file, const statement_t *stats) +{ + fprintf(file, "\n"); + + /* + * First, the symbol table + */ + fprintf(file, "static mig_routine_t %s_routines[] = {\n", ServerDemux); + + WriteArrayEntries(file, stats); + + fprintf(file, "};\n"); + fprintf(file, "\n"); + + /* + * Then, the server routine + */ + fprintf(file, "mig_external boolean_t %s\n", ServerDemux); + fprintf(file, "\t(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)\n"); + + fprintf(file, "{\n"); + fprintf(file, "\tregister mach_msg_header_t *InP = InHeadP;\n"); + + fprintf(file, "\tregister mig_reply_header_t *OutP = (mig_reply_header_t *) OutHeadP;\n"); + + fprintf(file, "\n"); + + WriteStaticDecl(file, itRetCodeType, + itRetCodeType->itDeallocate, itRetCodeType->itLongForm, + !IsKernelServer, "RetCodeType"); + fprintf(file, "\n"); + + fprintf(file, "\tregister mig_routine_t routine;\n"); + fprintf(file, "\n"); + + fprintf(file, "\tOutP->Head.msgh_bits = "); + fprintf(file, "MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InP->msgh_bits), 0);\n"); + fprintf(file, "\tOutP->Head.msgh_size = sizeof *OutP;\n"); + fprintf(file, "\tOutP->Head.msgh_remote_port = InP->msgh_reply_port;\n"); + fprintf(file, "\tOutP->Head.msgh_local_port = MACH_PORT_NULL;\n"); + fprintf(file, "\tOutP->Head.msgh_seqno = 0;\n"); + fprintf(file, "\tOutP->Head.msgh_id = InP->msgh_id + 100;\n"); + fprintf(file, "\n"); + WritePackMsgType(file, itRetCodeType, + itRetCodeType->itDeallocate, itRetCodeType->itLongForm, + !IsKernelServer, "OutP->RetCodeType", "RetCodeType"); + fprintf(file, "\n"); + + fprintf(file, "\tif ((InP->msgh_id > %d) || (InP->msgh_id < %d) ||\n", + SubsystemBase + rtNumber - 1, SubsystemBase); + fprintf(file, "\t ((routine = %s_routines[InP->msgh_id - %d]) == 0)) {\n", + ServerDemux, SubsystemBase); + fprintf(file, "\t\tOutP->RetCode = MIG_BAD_ID;\n"); + fprintf(file, "\t\treturn FALSE;\n"); + fprintf(file, "\t}\n"); + + /* Call appropriate routine */ + fprintf(file, "\t(*routine) (InP, &OutP->Head);\n"); + fprintf(file, "\treturn TRUE;\n"); + fprintf(file, "}\n"); + fprintf(file, "\n"); + + /* + * Then, the <subsystem>_server_routine routine + */ + fprintf(file, "mig_external mig_routine_t %s_routine\n", ServerDemux); + fprintf(file, "\t(const mach_msg_header_t *InHeadP)\n"); + + fprintf(file, "{\n"); + fprintf(file, "\tregister int msgh_id;\n"); + fprintf(file, "\n"); + fprintf(file, "\tmsgh_id = InHeadP->msgh_id - %d;\n", SubsystemBase); + fprintf(file, "\n"); + fprintf(file, "\tif ((msgh_id > %d) || (msgh_id < 0))\n", + rtNumber - 1); + fprintf(file, "\t\treturn 0;\n"); + fprintf(file, "\n"); + fprintf(file, "\treturn %s_routines[msgh_id];\n", ServerDemux); + fprintf(file, "}\n"); + fprintf(file, "\n"); + + /* symtab */ + + if (GenSymTab) { + fprintf(file,"\nmig_symtab_t _%sSymTab[] = {\n",SubsystemName); + WriteSymTabEntries(file,stats); + fprintf(file,"};\n"); + fprintf(file,"int _%sSymTabBase = %d;\n",SubsystemName,SubsystemBase); + fprintf(file,"int _%sSymTabEnd = %d;\n",SubsystemName,SubsystemBase+rtNumber); + } +} + +/* + * Returns the return type of the server-side work function. + * Suitable for "extern %s serverfunc()". + */ +static const char * +ServerSideType(const routine_t *rt) +{ + if (rt->rtServerReturn == argNULL) + return "void"; + else + return rt->rtServerReturn->argType->itTransType; +} + +static void +WriteLocalVarDecl(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + if (it->itInLine && it->itVarArray) + { + register const ipc_type_t *btype = it->itElement; + + fprintf(file, "\t%s %s[%d]", btype->itTransType, + arg->argVarName, it->itNumber/btype->itNumber); + } + else + fprintf(file, "\t%s %s", it->itTransType, arg->argVarName); +} + +static void +WriteLocalPtrDecl(FILE *file, register const argument_t *arg) +{ + fprintf(file, "\t%s *%sP", + FetchServerType(arg->argType->itElement), + arg->argVarName); +} + +static void +WriteServerArgDecl(FILE *file, const argument_t *arg) +{ + fprintf(file, "%s %s%s", + arg->argType->itTransType, + arg->argByReferenceServer ? "*" : "", + arg->argVarName); +} + +/* + * Writes the local variable declarations which are always + * present: InP, OutP, the server-side work function. + */ +static void +WriteVarDecls(FILE *file, const routine_t *rt) +{ + int i; + boolean_t NeedMsghSize = FALSE; + boolean_t NeedMsghSizeDelta = FALSE; + + fprintf(file, "\tregister Request *In0P = (Request *) InHeadP;\n"); + for (i = 1; i <= rt->rtMaxRequestPos; i++) + fprintf(file, "\tregister Request *In%dP;\n", i); + fprintf(file, "\tregister Reply *OutP = (Reply *) OutHeadP;\n"); + + fprintf(file, "\tmig_external %s %s\n", + ServerSideType(rt), rt->rtServerName); + fprintf(file, "\t\t("); + WriteList(file, rt->rtArgs, WriteServerArgDecl, akbServerArg, ", ", ""); + fprintf(file, ");\n"); + fprintf(file, "\n"); + + if (!rt->rtSimpleFixedReply) + fprintf(file, "\tboolean_t msgh_simple;\n"); + else if (!rt->rtSimpleCheckRequest) + { + fprintf(file, "#if\tTypeCheck\n"); + fprintf(file, "\tboolean_t msgh_simple;\n"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + fprintf(file, "\n"); + } + + /* if either request or reply is variable, we may need + msgh_size_delta and msgh_size */ + + if (rt->rtNumRequestVar > 0) + NeedMsghSize = TRUE; + if (rt->rtMaxRequestPos > 0) + NeedMsghSizeDelta = TRUE; + + if (rt->rtNumReplyVar > 1) + NeedMsghSize = TRUE; + if (rt->rtMaxReplyPos > 0) + NeedMsghSizeDelta = TRUE; + + if (NeedMsghSize) + fprintf(file, "\tunsigned int msgh_size;\n"); + if (NeedMsghSizeDelta) + fprintf(file, "\tunsigned int msgh_size_delta;\n"); + + if (NeedMsghSize || NeedMsghSizeDelta) + fprintf(file, "\n"); +} + +static void +WriteMsgError(FILE *file, const char *error_msg) +{ + fprintf(file, "\t\t{ OutP->RetCode = %s; return; }\n", error_msg); +} + +static void +WriteReplyInit(FILE *file, const routine_t *rt) +{ + boolean_t printed_nl = FALSE; + + if (rt->rtSimpleFixedReply) + { + if (!rt->rtSimpleSendReply) /* complex reply message */ + { + printed_nl = TRUE; + fprintf(file, "\n"); + fprintf(file, + "\tOutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;\n"); + } + } + else + { + printed_nl = TRUE; + fprintf(file, "\n"); + fprintf(file, "\tmsgh_simple = %s;\n", + strbool(rt->rtSimpleSendReply)); + } + + if (rt->rtNumReplyVar == 0) + { + if (!printed_nl) + fprintf(file, "\n"); + fprintf(file, "\tOutP->Head.msgh_size = %d;\n", rt->rtReplySize); + } +} + +static void +WriteReplyHead(FILE *file, const routine_t *rt) +{ + if ((!rt->rtSimpleFixedReply) || + (rt->rtNumReplyVar > 1)) + { + fprintf(file, "\n"); + if (rt->rtMaxReplyPos > 0) + fprintf(file, "\tOutP = (Reply *) OutHeadP;\n"); + } + + if (!rt->rtSimpleFixedReply) + { + fprintf(file, "\tif (!msgh_simple)\n"); + fprintf(file, + "\t\tOutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;\n"); + } + if (rt->rtNumReplyVar > 1) + fprintf(file, "\tOutP->Head.msgh_size = msgh_size;\n"); +} + +static void +WriteCheckHead(FILE *file, const routine_t *rt) +{ + fprintf(file, "#if\tTypeCheck\n"); + if (rt->rtNumRequestVar > 0) + fprintf(file, "\tmsgh_size = In0P->Head.msgh_size;\n"); + if (!rt->rtSimpleCheckRequest) + fprintf(file, "\tmsgh_simple = !(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);\n"); + + if (rt->rtNumRequestVar > 0) + fprintf(file, "\tif ((msgh_size < %d)", + rt->rtRequestSize); + else + fprintf(file, "\tif ((In0P->Head.msgh_size != %d)", + rt->rtRequestSize); + + if (rt->rtSimpleCheckRequest) + fprintf(file, " ||\n\t %s(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)", + rt->rtSimpleReceiveRequest ? "" : "!"); + fprintf(file, ")\n"); + WriteMsgError(file, "MIG_BAD_ARGUMENTS"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + fprintf(file, "\n"); +} + +static void +WriteTypeCheck(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + register const routine_t *rt = arg->argRoutine; + + fprintf(file, "#if\tTypeCheck\n"); + if (akCheck(arg->argKind, akbRequestQC)) + fprintf(file, "\tif (* (int *) &In%dP->%s != * (int *) &%sCheck)\n", + arg->argRequestPos, arg->argTTName, arg->argVarName); + else + { + fprintf(file, "\tif ("); + if (!it->itIndefinite) { + fprintf(file, "(In%dP->%s%s.msgt_inline != %s) ||\n\t ", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + strbool(it->itInLine)); + } + fprintf(file, "(In%dP->%s%s.msgt_longform != %s) ||\n", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + strbool(arg->argLongForm)); + if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC) + { + if (!rt->rtSimpleCheckRequest) + fprintf(file, "\t (MACH_MSG_TYPE_PORT_ANY(In%dP->%s.msgt%s_name) && msgh_simple) ||\n", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? "l" : ""); + } + else + fprintf(file, "\t (In%dP->%s.msgt%s_name != %s) ||\n", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? "l" : "", + it->itOutNameStr); + if (!it->itVarArray) + fprintf(file, "\t (In%dP->%s.msgt%s_number != %d) ||\n", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? "l" : "", + it->itNumber); + fprintf(file, "\t (In%dP->%s.msgt%s_size != %d))\n", + arg->argRequestPos, arg->argTTName, + arg->argLongForm ? "l" : "", + it->itSize); + } + WriteMsgError(file, "MIG_BAD_ARGUMENTS"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + fprintf(file, "\n"); +} + +static void +WriteCheckArgSize(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argType; + register const ipc_type_t *btype = ptype->itElement; + const argument_t *count = arg->argCount; + int multiplier = btype->itTypeSize / btype->itNumber; + + if (ptype->itIndefinite) { + /* + * Check descriptor. If out-of-line, use standard size. + */ + fprintf(file, "(In%dP->%s%s.msgt_inline) ? ", + arg->argRequestPos, + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + } + + if (btype->itTypeSize % 4 != 0) + fprintf(file, "("); + + if (multiplier > 1) + fprintf(file, "%d * ", multiplier); + + fprintf(file, "In%dP->%s", arg->argRequestPos, count->argMsgField); + + /* If the base type size of the data field isn`t a multiple of 4, + we have to round up. */ + if (btype->itTypeSize % 4 != 0) + fprintf(file, " + 3) & ~3"); + + if (ptype->itIndefinite) { + fprintf(file, " : sizeof(%s *)", FetchServerType(btype)); + } +} + +static void +WriteCheckMsgSize(FILE *file, register const argument_t *arg) +{ + register const routine_t *rt = arg->argRoutine; + + /* If there aren't any more In args after this, then + we can use the msgh_size_delta value directly in + the TypeCheck conditional. */ + + if (arg->argRequestPos == rt->rtMaxRequestPos) + { + fprintf(file, "#if\tTypeCheck\n"); + fprintf(file, "\tif (msgh_size != %d + (", rt->rtRequestSize); + WriteCheckArgSize(file, arg); + fprintf(file, "))\n"); + + WriteMsgError(file, "MIG_BAD_ARGUMENTS"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + } + else + { + /* If there aren't any more variable-sized arguments after this, + then we must check for exact msg-size and we don't need to + update msgh_size. */ + + boolean_t LastVarArg = arg->argRequestPos+1 == rt->rtNumRequestVar; + + /* calculate the actual size in bytes of the data field. note + that this quantity must be a multiple of four. hence, if + the base type size isn't a multiple of four, we have to + round up. note also that btype->itNumber must + divide btype->itTypeSize (see itCalculateSizeInfo). */ + + fprintf(file, "\tmsgh_size_delta = "); + WriteCheckArgSize(file, arg); + fprintf(file, ";\n"); + fprintf(file, "#if\tTypeCheck\n"); + + /* Don't decrement msgh_size until we've checked that + it won't underflow. */ + + if (LastVarArg) + fprintf(file, "\tif (msgh_size != %d + msgh_size_delta)\n", + rt->rtRequestSize); + else + fprintf(file, "\tif (msgh_size < %d + msgh_size_delta)\n", + rt->rtRequestSize); + WriteMsgError(file, "MIG_BAD_ARGUMENTS"); + + if (!LastVarArg) + fprintf(file, "\tmsgh_size -= msgh_size_delta;\n"); + + fprintf(file, "#endif\t/* TypeCheck */\n"); + } + fprintf(file, "\n"); +} + +static const char * +InArgMsgField(register const argument_t *arg) +{ + static char buffer[100]; + + /* + * Inside the kernel, the request and reply port fields + * really hold ipc_port_t values, not mach_port_t values. + * Hence we must cast the values. + */ + + if (IsKernelServer && + ((akIdent(arg->argKind) == akeRequestPort) || + (akIdent(arg->argKind) == akeReplyPort))) + sprintf(buffer, "(ipc_port_t) In%dP->%s", + arg->argRequestPos, arg->argMsgField); + else + sprintf(buffer, "In%dP->%s", + arg->argRequestPos, arg->argMsgField); + + return buffer; +} + +static void +WriteExtractArgValue(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + if (arg->argMultiplier > 1) + WriteCopyType(file, it, "%s", "/* %s */ %s / %d", + arg->argVarName, InArgMsgField(arg), arg->argMultiplier); + else if (it->itInTrans != strNULL) + WriteCopyType(file, it, "%s", "/* %s */ %s(%s)", + arg->argVarName, it->itInTrans, InArgMsgField(arg)); + else + WriteCopyType(file, it, "%s", "/* %s */ %s", + arg->argVarName, InArgMsgField(arg)); + fprintf(file, "\n"); +} + +static void +WriteInitializeCount(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argParent->argType; + register const ipc_type_t *btype = ptype->itElement; + + /* + * Initialize 'count' argument for variable-length inline OUT parameter + * with maximum allowed number of elements. + */ + + fprintf(file, "\t%s = %d;\n", arg->argVarName, + ptype->itNumber/btype->itNumber); + + /* + * If the user passed in a count, then we use the minimum. + * We can't let the user completely override our maximum, + * or the user might convince the server to overwrite the buffer. + */ + + if (arg->argCInOut != argNULL) { + const char *msgfield = InArgMsgField(arg->argCInOut); + + fprintf(file, "\tif (%s < %s)\n", msgfield, arg->argVarName); + fprintf(file, "\t\t%s = %s;\n", arg->argVarName, msgfield); + } + + fprintf(file, "\n"); +} + +static void +WriteInitializePtr(FILE *file, register const argument_t *arg) +{ + if (akCheck(arg->argKind, akbVarNeeded)) + fprintf(file, "\t%sP = %s;\n", + arg->argVarName, arg->argVarName); + else + fprintf(file, "\t%sP = OutP->%s;\n", + arg->argVarName, arg->argMsgField); +} + +static void +WriteTypeCheckArg(FILE *file, register const argument_t *arg) +{ + if (akCheck(arg->argKind, akbRequest)) { + WriteTypeCheck(file, arg); + + if (akCheck(arg->argKind, akbVariable)) + WriteCheckMsgSize(file, arg); + } +} + +static void +WriteAdjustRequestMsgPtr(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argType; + + fprintf(file, + "\tIn%dP = (Request *) ((char *) In%dP + msgh_size_delta - %d);\n\n", + arg->argRequestPos+1, arg->argRequestPos, + ptype->itTypeSize + ptype->itPadSize); +} + +static void +WriteTypeCheckRequestArgs(FILE *file, register const routine_t *rt) +{ + register const argument_t *arg; + register const argument_t *lastVarArg; + + lastVarArg = argNULL; + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { + + /* + * Advance message pointer if the last request argument was + * variable-length and the request position will change. + */ + if (lastVarArg != argNULL && + lastVarArg->argRequestPos < arg->argRequestPos) + { + WriteAdjustRequestMsgPtr(file, lastVarArg); + lastVarArg = argNULL; + } + + /* + * Type-check the argument. + */ + WriteTypeCheckArg(file, arg); + + /* + * Remember whether this was variable-length. + */ + if (akCheckAll(arg->argKind, akbVariable|akbRequest)) + lastVarArg = arg; + } +} + +static void +WriteExtractArg(FILE *file, register const argument_t *arg) +{ + if (akCheckAll(arg->argKind, akbSendRcv|akbVarNeeded)) + WriteExtractArgValue(file, arg); + + if ((akIdent(arg->argKind) == akeCount) && + akCheck(arg->argKind, akbReturnSnd)) + { + register ipc_type_t *ptype = arg->argParent->argType; + + if (ptype->itInLine && ptype->itVarArray) + WriteInitializeCount(file, arg); + } + + if (akCheckAll(arg->argKind, akbReturnSnd|akbPointer)) + WriteInitializePtr(file, arg); +} + +static void +WriteServerCallArg(FILE *file, register const argument_t *arg) +{ + const ipc_type_t *it = arg->argType; + boolean_t NeedClose = FALSE; + + if (arg->argByReferenceServer) + fprintf(file, "&"); + + if ((it->itInTrans != strNULL) && + akCheck(arg->argKind, akbSendRcv) && + !akCheck(arg->argKind, akbVarNeeded)) + { + fprintf(file, "%s(", it->itInTrans); + NeedClose = TRUE; + } + + if (akCheck(arg->argKind, akbPointer)) + fprintf(file, "%sP", arg->argVarName); + else if (akCheck(arg->argKind, akbVarNeeded)) + fprintf(file, "%s", arg->argVarName); + else if (akCheck(arg->argKind, akbSendRcv)) { + if (akCheck(arg->argKind, akbIndefinite)) { + fprintf(file, "(In%dP->%s%s.msgt_inline) ", + arg->argRequestPos, + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + fprintf(file, "? %s ", InArgMsgField(arg)); + fprintf(file, ": *((%s **)%s)", + FetchServerType(arg->argType->itElement), + InArgMsgField(arg)); + } + else + fprintf(file, "%s", InArgMsgField(arg)); + } + else + fprintf(file, "OutP->%s", arg->argMsgField); + + if (NeedClose) + fprintf(file, ")"); + + if (!arg->argByReferenceServer && (arg->argMultiplier > 1)) + fprintf(file, " / %d", arg->argMultiplier); +} + +static void +WriteDestroyArg(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + if (akCheck(arg->argKind, akbIndefinite)) { + /* + * Deallocate only if out-of-line. + */ + argument_t *count = arg->argCount; + ipc_type_t *btype = it->itElement; + int multiplier = btype->itTypeSize / btype->itNumber; + + fprintf(file, "\tif (!In%dP->%s%s.msgt_inline)\n", + arg->argRequestPos, + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + fprintf(file, "\t\t%smig_deallocate(* (vm_offset_t *) %s, ", + SubrPrefix, InArgMsgField(arg)); + if (multiplier > 1) + fprintf(file, "%d * ", multiplier); + fprintf(file, " %s);\n", InArgMsgField(count)); + } else { + if (akCheck(arg->argKind, akbVarNeeded)) + fprintf(file, "\t%s(%s);\n", it->itDestructor, arg->argVarName); + else + fprintf(file, "\t%s(%s);\n", it->itDestructor, + InArgMsgField(arg)); + } +} + +static void +WriteDestroyPortArg(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + /* + * If a translated port argument occurs in the body of a request + * message, and the message is successfully processed, then the + * port right should be deallocated. However, the called function + * didn't see the port right; it saw the translation. So we have + * to release the port right for it. + */ + + if ((it->itInTrans != strNULL) && + (it->itOutName == MACH_MSG_TYPE_PORT_SEND)) + { + fprintf(file, "\n"); + fprintf(file, "\tif (IP_VALID(%s))\n", InArgMsgField(arg)); + fprintf(file, "\t\t%sipc_port_release_send(%s);\n", + SubrPrefix, InArgMsgField(arg)); + } +} + +/* + * Check whether WriteDestroyPortArg would generate any code for arg. + */ +static boolean_t +CheckDestroyPortArg(register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + if ((it->itInTrans != strNULL) && + (it->itOutName == MACH_MSG_TYPE_PORT_SEND)) + { + return TRUE; + } + return FALSE; +} + +static void +WriteServerCall(FILE *file, const routine_t *rt) +{ + boolean_t NeedClose = FALSE; + + fprintf(file, "\t"); + if (rt->rtServerReturn != argNULL) + { + const argument_t *arg = rt->rtServerReturn; + const ipc_type_t *it = arg->argType; + + fprintf(file, "OutP->%s = ", arg->argMsgField); + if (it->itOutTrans != strNULL) + { + fprintf(file, "%s(", it->itOutTrans); + NeedClose = TRUE; + } + } + fprintf(file, "%s(", rt->rtServerName); + WriteList(file, rt->rtArgs, WriteServerCallArg, akbServerArg, ", ", ""); + if (NeedClose) + fprintf(file, ")"); + fprintf(file, ");\n"); +} + +static void +WriteGetReturnValue(FILE *file, register const routine_t *rt) +{ + if (rt->rtServerReturn != rt->rtRetCode) + fprintf(file, "\tOutP->%s = KERN_SUCCESS;\n", + rt->rtRetCode->argMsgField); +} + +static void +WriteCheckReturnValue(FILE *file, register const routine_t *rt) +{ + if (rt->rtServerReturn == rt->rtRetCode) + { + fprintf(file, "\tif (OutP->%s != KERN_SUCCESS)\n", + rt->rtRetCode->argMsgField); + fprintf(file, "\t\treturn;\n"); + } +} + +static void +WritePackArgType(FILE *file, register const argument_t *arg) +{ + fprintf(file, "\n"); + + WritePackMsgType(file, arg->argType, + arg->argType->itIndefinite ? d_NO : arg->argDeallocate, + arg->argLongForm, !IsKernelServer, + "OutP->%s", "%s", arg->argTTName); +} + +static void +WritePackArgValue(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + + fprintf(file, "\n"); + + if (it->itInLine && it->itVarArray) { + + if (it->itString) { + /* + * Copy variable-size C string with mig_strncpy. + * Save the string length (+ 1 for trailing 0) + * in the argument`s count field. + */ + fprintf(file, + "\tOutP->%s = %smig_strncpy(OutP->%s, %s, %d);\n", + arg->argCount->argMsgField, + SubrPrefix, + arg->argMsgField, + arg->argVarName, + it->itNumber); + } + else { + register argument_t *count = arg->argCount; + register ipc_type_t *btype = it->itElement; + + /* Note btype->itNumber == count->argMultiplier */ + + if (it->itIndefinite) { + /* + * If we are packing argument, it must be from + * a local variable. + */ + fprintf(file, "\tif (%sP != %s) {\n", + arg->argVarName, + arg->argVarName); + fprintf(file, "\t\tOutP->%s%s.msgt_inline = FALSE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + if (arg->argDeallocate == d_YES) + fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = TRUE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + else if (arg->argDeallocate == d_MAYBE) + fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = %s;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + arg->argDealloc->argVarName); + fprintf(file, "\t\t*((%s **)OutP->%s) = %sP;\n", + FetchServerType(btype), + arg->argMsgField, + arg->argVarName); + if (!arg->argRoutine->rtSimpleFixedReply) + fprintf(file, "\t\tmsgh_simple = FALSE;\n"); + fprintf(file, "\t}\n\telse {\n\t"); + } + fprintf(file, "\tmemcpy(OutP->%s, %s, ", + arg->argMsgField, arg->argVarName); + if (btype->itTypeSize > 1) + fprintf(file, "%d * ", + btype->itTypeSize); + fprintf(file, "%s);\n", + count->argVarName); + if (it->itIndefinite) + fprintf(file, "\t}\n"); + } + } + else if (arg->argMultiplier > 1) + WriteCopyType(file, it, "OutP->%s", "/* %s */ %d * %s", + arg->argMsgField, + arg->argMultiplier, + arg->argVarName); + else if (it->itOutTrans != strNULL) + WriteCopyType(file, it, "OutP->%s", "/* %s */ %s(%s)", + arg->argMsgField, it->itOutTrans, arg->argVarName); + else + WriteCopyType(file, it, "OutP->%s", "/* %s */ %s", + arg->argMsgField, arg->argVarName); +} + +static void +WriteCopyArgValue(FILE *file, register const argument_t *arg) +{ + fprintf(file, "\n"); + WriteCopyType(file, arg->argType, "/* %d */ OutP->%s", "In%dP->%s", + arg->argRequestPos, arg->argMsgField); +} + +static void +WriteAdjustMsgSimple(FILE *file, register const argument_t *arg) +{ + /* akbVarNeeded must be on */ + + if (!arg->argRoutine->rtSimpleFixedReply) + { + fprintf(file, "\n"); + fprintf(file, "\tif (MACH_MSG_TYPE_PORT_ANY(%s))\n", arg->argVarName); + fprintf(file, "\t\tmsgh_simple = FALSE;\n"); + } +} + +static void +WriteAdjustMsgCircular(FILE *file, register const argument_t *arg) +{ + fprintf(file, "\n"); + + if (arg->argType->itOutName == MACH_MSG_TYPE_POLYMORPHIC) + fprintf(file, "\tif (%s == MACH_MSG_TYPE_PORT_RECEIVE)\n", + arg->argPoly->argVarName); + + /* + * The carried port right can be accessed in OutP->XXXX. Normally + * the server function stuffs it directly there. If it is InOut, + * then it has already been copied into the reply message. + * If the server function deposited it into a variable (perhaps + * because the reply message is variable-sized) then it has already + * been copied into the reply message. Note we must use InHeadP + * (or In0P->Head) and OutHeadP to access the message headers, + * because of the variable-sized messages. + */ + + fprintf(file, "\tif (IP_VALID((ipc_port_t) InHeadP->msgh_reply_port) &&\n"); + fprintf(file, "\t IP_VALID((ipc_port_t) OutP->%s) &&\n", arg->argMsgField); + fprintf(file, "\t %sipc_port_check_circularity((ipc_port_t) OutP->%s, (ipc_port_t) InHeadP->msgh_reply_port))\n", + SubrPrefix, arg->argMsgField); + fprintf(file, "\t\tOutHeadP->msgh_bits |= MACH_MSGH_BITS_CIRCULAR;\n"); +} + +/* + * Calculate the size of a variable-length message field. + */ +static void +WriteArgSize(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argType; + register int bsize = ptype->itElement->itTypeSize; + register const argument_t *count = arg->argCount; + + if (ptype->itIndefinite) { + /* + * Check descriptor. If out-of-line, use standard size. + */ + fprintf(file, "(OutP->%s%s.msgt_inline) ? ", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + } + + if (bsize % 4 != 0) + fprintf(file, "("); + + if (bsize > 1) + fprintf(file, "%d * ", bsize); + if (ptype->itString) + /* get count from descriptor in message */ + fprintf(file, "OutP->%s", count->argMsgField); + else + /* get count from argument */ + fprintf(file, "%s", count->argVarName); + + /* + * If the base type size is not a multiple of sizeof(int) [4], + * we have to round up. + */ + if (bsize % 4 != 0) + fprintf(file, " + 3) & ~3"); + + if (ptype->itIndefinite) { + fprintf(file, " : sizeof(%s *)", + FetchServerType(ptype->itElement)); + } +} + +/* + * Adjust message size and advance reply pointer. + * Called after packing a variable-length argument that + * has more arguments following. + */ +static void +WriteAdjustMsgSize(FILE *file, register const argument_t *arg) +{ + register routine_t *rt = arg->argRoutine; + register ipc_type_t *ptype = arg->argType; + + /* There are more Out arguments. We need to adjust msgh_size + and advance OutP, so we save the size of the current field + in msgh_size_delta. */ + + fprintf(file, "\tmsgh_size_delta = "); + WriteArgSize(file, arg); + fprintf(file, ";\n"); + + if (rt->rtNumReplyVar == 1) + /* We can still address the message header directly. Fill + in the size field. */ + + fprintf(file, "\tOutP->Head.msgh_size = %d + msgh_size_delta;\n", + rt->rtReplySize); + else + if (arg->argReplyPos == 0) + /* First variable-length argument. The previous msgh_size value + is the minimum reply size. */ + + fprintf(file, "\tmsgh_size = %d + msgh_size_delta;\n", + rt->rtReplySize); + else + fprintf(file, "\tmsgh_size += msgh_size_delta;\n"); + + fprintf(file, + "\tOutP = (Reply *) ((char *) OutP + msgh_size_delta - %d);\n", + ptype->itTypeSize + ptype->itPadSize); +} + +/* + * Calculate the size of the message. Called after the + * last argument has been packed. + */ +static void +WriteFinishMsgSize(FILE *file, register const argument_t *arg) +{ + /* No more Out arguments. If this is the only variable Out + argument, we can assign to msgh_size directly. */ + + if (arg->argReplyPos == 0) { + fprintf(file, "\tOutP->Head.msgh_size = %d + (", + arg->argRoutine->rtReplySize); + WriteArgSize(file, arg); + fprintf(file, ");\n"); + } + else { + fprintf(file, "\tmsgh_size += "); + WriteArgSize(file, arg); + fprintf(file, ";\n"); + } +} + +static void +WritePackArg(FILE *file, register const argument_t *arg) +{ + if (akCheck(arg->argKind, akbReplyInit)) + WritePackArgType(file, arg); + + if ((akIdent(arg->argKind) == akePoly) && + akCheck(arg->argKind, akbReturnSnd)) + WriteAdjustMsgSimple(file, arg); + + if (akCheckAll(arg->argKind, akbReturnSnd|akbVarNeeded)) + WritePackArgValue(file, arg); + else if (akCheckAll(arg->argKind, akbReturnSnd|akbVariable)) { + register const ipc_type_t *it = arg->argType; + + if (it->itString) { + /* Need to call strlen to calculate the size of the argument. */ + fprintf(file, "\tOutP->%s = strlen(OutP->%s) + 1;\n", + arg->argCount->argMsgField, arg->argMsgField); + } else if (it->itIndefinite) { + /* + * We know that array is in reply message. + */ + fprintf(file, "\tif (%sP != OutP->%s) {\n", + arg->argVarName, + arg->argMsgField); + fprintf(file, "\t\tOutP->%s%s.msgt_inline = FALSE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + if (arg->argDeallocate == d_YES) + fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = TRUE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + else if (arg->argDeallocate == d_MAYBE) + fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = %s;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + arg->argDealloc->argVarName); + fprintf(file, "\t\t*((%s **)OutP->%s) = %sP;\n", + FetchServerType(it->itElement), + arg->argMsgField, + arg->argVarName); + if (!arg->argRoutine->rtSimpleFixedReply) + fprintf(file, "\t\tmsgh_simple = FALSE;\n"); + fprintf(file, "\t}\n"); + } + } + + if (akCheck(arg->argKind, akbReplyCopy)) + WriteCopyArgValue(file, arg); + + /* + * If this is a KernelServer, and the reply message contains + * a receive right, we must check for the possibility of a + * port/message circularity. If queueing the reply message + * would cause a circularity, we mark the reply message + * with the circular bit. + */ + + if (IsKernelServer && + akCheck(arg->argKind, akbReturnSnd) && + ((arg->argType->itOutName == MACH_MSG_TYPE_PORT_RECEIVE) || + (arg->argType->itOutName == MACH_MSG_TYPE_POLYMORPHIC))) + WriteAdjustMsgCircular(file, arg); +} + +/* + * Handle reply arguments - fill in message types and copy arguments + * that need to be copied. + */ +static void +WritePackReplyArgs(FILE *file, register const routine_t *rt) +{ + register const argument_t *arg; + register const argument_t *lastVarArg; + + lastVarArg = argNULL; + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { + + /* + * Adjust message size and advance message pointer if + * the last reply argument was variable-length and the + * request position will change. + */ + if (lastVarArg != argNULL && + lastVarArg->argReplyPos < arg->argReplyPos) + { + WriteAdjustMsgSize(file, lastVarArg); + lastVarArg = argNULL; + } + + /* + * Copy the argument + */ + WritePackArg(file, arg); + + /* + * Remember whether this was variable-length. + */ + if (akCheckAll(arg->argKind, akbReturnSnd|akbVariable)) + lastVarArg = arg; + } + + /* + * Finish the message size. + */ + if (lastVarArg != argNULL) + WriteFinishMsgSize(file, lastVarArg); +} + +static void +WriteFieldDecl(FILE *file, const argument_t *arg) +{ + WriteFieldDeclPrim(file, arg, FetchServerType); +} + +static void +WriteRoutine(FILE *file, register const routine_t *rt) +{ + fprintf(file, "\n"); + + fprintf(file, "/* %s %s */\n", rtRoutineKindToStr(rt->rtKind), rt->rtName); + fprintf(file, "mig_internal void _X%s\n", rt->rtName); + fprintf(file, "\t(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)\n"); + + fprintf(file, "{\n"); + WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbRequest, "Request"); + WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbReply, "Reply"); + + WriteVarDecls(file, rt); + + WriteList(file, rt->rtArgs, WriteCheckDecl, akbRequestQC, "\n", "\n"); + WriteList(file, rt->rtArgs, + IsKernelServer ? WriteTypeDeclOut : WriteTypeDeclIn, + akbReplyInit, "\n", "\n"); + + WriteList(file, rt->rtArgs, WriteLocalVarDecl, + akbVarNeeded, ";\n", ";\n\n"); + WriteList(file, rt->rtArgs, WriteLocalPtrDecl, + akbPointer, ";\n", ";\n\n"); + + WriteCheckHead(file, rt); + + WriteTypeCheckRequestArgs(file, rt); + WriteList(file, rt->rtArgs, WriteExtractArg, akbNone, "", ""); + + WriteServerCall(file, rt); + WriteGetReturnValue(file, rt); + + WriteReverseList(file, rt->rtArgs, WriteDestroyArg, akbDestroy, "", ""); + + /* + * For one-way routines, it doesn`t make sense to check the return + * code, because we return immediately afterwards. However, + * kernel servers may want to deallocate port arguments - and the + * deallocation must not be done if the return code is not KERN_SUCCESS. + */ + if (rt->rtOneWay || rt->rtNoReplyArgs) + { + if (IsKernelServer) + { + if (rtCheckMaskFunction(rt->rtArgs, akbSendBody|akbSendRcv, + CheckDestroyPortArg)) + { + WriteCheckReturnValue(file, rt); + } + WriteReverseList(file, rt->rtArgs, WriteDestroyPortArg, + akbSendBody|akbSendRcv, "", ""); + } + } + else + { + WriteCheckReturnValue(file, rt); + + if (IsKernelServer) + WriteReverseList(file, rt->rtArgs, WriteDestroyPortArg, + akbSendBody|akbSendRcv, "", ""); + + WriteReplyInit(file, rt); + WritePackReplyArgs(file, rt); + WriteReplyHead(file, rt); + } + + fprintf(file, "}\n"); +} + +void +WriteServer(FILE *file, const statement_t *stats) +{ + register const statement_t *stat; + + WriteProlog(file); + for (stat = stats; stat != stNULL; stat = stat->stNext) + switch (stat->stKind) + { + case skRoutine: + WriteRoutine(file, stat->stRoutine); + break; + case skImport: + case skSImport: + WriteImport(file, stat->stFileName); + break; + case skUImport: + break; + default: + fatal("WriteServer(): bad statement_kind_t (%d)", + (int) stat->stKind); + } + WriteEpilog(file, stats); +} |