diff options
Diffstat (limited to 'mig/user.c')
-rw-r--r-- | mig/user.c | 1332 |
1 files changed, 1332 insertions, 0 deletions
diff --git a/mig/user.c b/mig/user.c new file mode 100644 index 0000000..a5ca5fb --- /dev/null +++ b/mig/user.c @@ -0,0 +1,1332 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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 + * 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 the + * rights to redistribute these changes. + */ + +#include <assert.h> + +#include "write.h" +#include "error.h" +#include "utils.h" +#include "global.h" +#include "mig_string.h" +#include "cpu.h" + +/************************************************************* + * Writes the standard includes. The subsystem specific + * includes are in <SubsystemName>.h and writen by + * header:WriteHeader. Called by WriteProlog. + *************************************************************/ +static void +WriteIncludes(FILE *file) +{ + if (IsKernelServer) + { + /* + * We want to get the user-side definitions of types + * like task_t, ipc_space_t, etc. in mach/mach_types.h. + */ + + fprintf(file, "#undef\tKERNEL\n"); + + if (InternalHeaderFileName != strNULL) + { + register char *cp; + + /* Strip any leading path from InternalHeaderFileName. */ + cp = strrchr(InternalHeaderFileName, '/'); + if (cp == 0) + cp = InternalHeaderFileName; + else + cp++; /* skip '/' */ + fprintf(file, "#include \"%s\"\n", cp); + } + } + + if (UserHeaderFileName != strNULL) + { + register char *cp; + + /* Strip any leading path from UserHeaderFileName. */ + cp = strrchr(UserHeaderFileName, '/'); + if (cp == 0) + cp = UserHeaderFileName; + else + cp++; /* skip '/' */ + fprintf(file, "#include \"%s\"\n", cp); + } + + 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/notify.h>\n"); + fprintf(file, "#include <mach/mach_types.h>\n"); + fprintf(file, "#include <mach/mig_errors.h>\n"); + fprintf(file, "#include <mach/mig_support.h>\n"); + fprintf(file, "#include <mach/msg_type.h>\n"); + fprintf(file, "/* LINTLIBRARY */\n"); + fprintf(file, "\n"); +} + +static void +WriteGlobalDecls(FILE *file) +{ + if (RCSId != strNULL) + WriteRCSDecl(file, strconcat(SubsystemName, "_user"), RCSId); + + fprintf(file, "#define msgh_request_port\tmsgh_remote_port\n"); + fprintf(file, "#define msgh_reply_port\t\tmsgh_local_port\n"); + fprintf(file, "\n"); +} + +/************************************************************* + * Writes the standard #includes, #defines, and + * RCS declaration. Called by WriteUser. + *************************************************************/ +static void +WriteProlog(FILE *file) +{ + WriteIncludes(file); + WriteBogusDefines(file); + WriteGlobalDecls(file); +} + +/*ARGSUSED*/ +static void +WriteEpilog(FILE *file) +{ +} + +static const_string_t +WriteHeaderPortType(const argument_t *arg) +{ + if (arg->argType->itInName == MACH_MSG_TYPE_POLYMORPHIC) + return arg->argPoly->argVarName; + else + return arg->argType->itInNameStr; +} + +static void +WriteRequestHead(FILE *file, const routine_t *rt) +{ + if (rt->rtMaxRequestPos > 0) + fprintf(file, "\tInP = &Mess.In;\n"); + + if (rt->rtSimpleFixedRequest) { + fprintf(file, "\tInP->Head.msgh_bits ="); + if (!rt->rtSimpleSendRequest) + fprintf(file, " MACH_MSGH_BITS_COMPLEX|"); + fprintf(file, "\n"); + fprintf(file, "\t\tMACH_MSGH_BITS(%s, %s);\n", + WriteHeaderPortType(rt->rtRequestPort), + WriteHeaderPortType(rt->rtUReplyPort)); + } else { + fprintf(file, "\tInP->Head.msgh_bits = msgh_simple ?\n"); + fprintf(file, "\t\tMACH_MSGH_BITS(%s, %s) :\n", + WriteHeaderPortType(rt->rtRequestPort), + WriteHeaderPortType(rt->rtUReplyPort)); + fprintf(file, "\t\t(MACH_MSGH_BITS_COMPLEX|\n"); + fprintf(file, "\t\t MACH_MSGH_BITS(%s, %s));\n", + WriteHeaderPortType(rt->rtRequestPort), + WriteHeaderPortType(rt->rtUReplyPort)); + } + + fprintf(file, "\t/* msgh_size passed as argument */\n"); + + /* + * KernelUser stubs need to cast the request and reply ports + * from ipc_port_t to mach_port_t. + */ + + if (IsKernelUser) + fprintf(file, "\tInP->%s = (mach_port_t) %s;\n", + rt->rtRequestPort->argMsgField, + rt->rtRequestPort->argVarName); + else + fprintf(file, "\tInP->%s = %s;\n", + rt->rtRequestPort->argMsgField, + rt->rtRequestPort->argVarName); + + if (akCheck(rt->rtUReplyPort->argKind, akbUserArg)) { + if (IsKernelUser) + fprintf(file, "\tInP->%s = (mach_port_t) %s;\n", + rt->rtUReplyPort->argMsgField, + rt->rtUReplyPort->argVarName); + else + fprintf(file, "\tInP->%s = %s;\n", + rt->rtUReplyPort->argMsgField, + rt->rtUReplyPort->argVarName); + } else if (rt->rtOneWay || IsKernelUser) + fprintf(file, "\tInP->%s = MACH_PORT_NULL;\n", + rt->rtUReplyPort->argMsgField); + else + fprintf(file, "\tInP->%s = %smig_get_reply_port();\n", + rt->rtUReplyPort->argMsgField, SubrPrefix); + + fprintf(file, "\tInP->Head.msgh_seqno = 0;\n"); + fprintf(file, "\tInP->Head.msgh_id = %d;\n", rt->rtNumber + SubsystemBase); +} + +/************************************************************* + * Writes declarations for the message types, variables + * and return variable if needed. Called by WriteRoutine. + *************************************************************/ +static void +WriteVarDecls(FILE *file, const routine_t *rt) +{ + fprintf(file, "\tunion {\n"); + fprintf(file, "\t\tRequest In;\n"); + if (!rt->rtOneWay) + fprintf(file, "\t\tReply Out;\n"); + fprintf(file, "\t} Mess;\n"); + fprintf(file, "\n"); + + fprintf(file, "\tregister Request *InP = &Mess.In;\n"); + if (!rt->rtOneWay) + fprintf(file, "\tregister Reply *OutP = &Mess.Out;\n"); + fprintf(file, "\n"); + + if (!rt->rtOneWay || rt->rtProcedure) + fprintf(file, "\tmach_msg_return_t msg_result;\n"); + + if (!rt->rtSimpleFixedRequest) + fprintf(file, "\tboolean_t msgh_simple = %s;\n", + strbool(rt->rtSimpleSendRequest)); + else if (!rt->rtOneWay && + !(rt->rtSimpleCheckReply && rt->rtSimpleReceiveReply)) { + fprintf(file, "#if\tTypeCheck\n"); + fprintf(file, "\tboolean_t msgh_simple;\n"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + } + + if (rt->rtNumRequestVar > 0) + fprintf(file, "\tunsigned int msgh_size;\n"); + else if (!rt->rtOneWay && !rt->rtNoReplyArgs) + { + fprintf(file, "#if\tTypeCheck\n"); + fprintf(file, "\tunsigned int msgh_size;\n"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + } + + /* if either request or reply is variable, we need msgh_size_delta */ + if ((rt->rtMaxRequestPos > 0) || + (rt->rtMaxReplyPos > 0)) + fprintf(file, "\tunsigned int msgh_size_delta;\n"); + + fprintf(file, "\n"); +} + +/************************************************************* + * Writes code to call the user provided error procedure + * when a MIG error occurs. Called by WriteMsgSend, + * WriteMsgCheckReceive, WriteMsgSendReceive, WriteCheckIdentity, + * WriteRetCodeCheck, WriteTypeCheck, WritePackArgValue. + *************************************************************/ +static void +WriteMsgError(FILE *file, const routine_t *rt, const char *error_msg) +{ + if (rt->rtProcedure) + fprintf(file, "\t\t{ %s(%s); return; }\n", rt->rtErrorName, error_msg); + else if (rt->rtReturn != rt->rtRetCode) + { + fprintf(file, "\t\t{ %s(%s); ", rt->rtErrorName, error_msg); + if (rt->rtNumReplyVar > 0) + fprintf(file, "OutP = &Mess.Out; "); + fprintf(file, "return OutP->%s; }\n", rt->rtReturn->argMsgField); + } + else + fprintf(file, "\t\treturn %s;\n", error_msg); +} + +/************************************************************* + * Writes the send call when there is to be no subsequent + * receive. Called by WriteRoutine for SimpleProcedures + * or SimpleRoutines + *************************************************************/ +static void +WriteMsgSend(FILE *file, const routine_t *rt) +{ + const char *MsgResult = (rt->rtProcedure) + ? "msg_result =" + : "return"; + + char SendSize[24]; + + if (rt->rtNumRequestVar == 0) + sprintf(SendSize, "%d", rt->rtRequestSize); + else + strcpy(SendSize, "msgh_size"); + + if (IsKernelUser) + { + fprintf(file, "\t%s %smach_msg_send_from_kernel(", + MsgResult, SubrPrefix); + fprintf(file, "&InP->Head, %s);\n", SendSize); + } + else + { + fprintf(file, "\t%s %smach_msg(&InP->Head, MACH_SEND_MSG|%s, %s, 0,", + MsgResult, + SubrPrefix, + rt->rtMsgOption->argVarName, + SendSize); + fprintf(file, + " MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n" + ); + } + + if (rt->rtProcedure) + { + fprintf(file, "\tif (msg_result != MACH_MSG_SUCCESS)\n"); + WriteMsgError(file, rt, "msg_result"); + } +} + +/************************************************************* + * Writes to code to check for error returns from receive. + * Called by WriteMsgSendReceive and WriteMsgRPC + *************************************************************/ +static void +WriteMsgCheckReceive(FILE *file, const routine_t *rt, const char *success) +{ + fprintf(file, "\tif (msg_result != %s) {\n", success); + if (!akCheck(rt->rtUReplyPort->argKind, akbUserArg) && !IsKernelUser) + { + /* If we aren't using a user-supplied reply port, + then deallocate the reply port on any message transmission + errors. */ + fprintf(file, "\t\t%smig_dealloc_reply_port(%s);\n", + SubrPrefix, "InP->Head.msgh_reply_port"); + } + WriteMsgError(file, rt, "msg_result"); + fprintf(file, "\t}\n"); + + /* + * If not using a user supplied reply port, tell the port + * allocator we're done with the port. + */ + if (!akCheck(rt->rtUReplyPort->argKind, akbUserArg) && !IsKernelUser) + { + fprintf(file, "\t%smig_put_reply_port(InP->Head.msgh_reply_port);\n", + SubrPrefix); + } +} + +/************************************************************* + * Writes the send and receive calls and code to check + * for errors. Normally the rpc code is generated instead + * although, the subsytem can be compiled with the -R option + * which will cause this code to be generated. Called by + * WriteRoutine if UseMsgRPC option is false. + *************************************************************/ +static void +WriteMsgSendReceive(FILE *file, const routine_t *rt) +{ + char SendSize[24]; + + if (rt->rtNumRequestVar == 0) + sprintf(SendSize, "%d", rt->rtRequestSize); + else + strcpy(SendSize, "msgh_size"); + + fprintf(file, "\tmsg_result = %smach_msg(&InP->Head, MACH_SEND_MSG|%s, %s, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n", + SubrPrefix, + rt->rtMsgOption->argVarName, + SendSize); + + fprintf(file, "\tif (msg_result != MACH_MSG_SUCCESS)\n"); + WriteMsgError(file, rt, "msg_result"); + fprintf(file, "\n"); + + fprintf(file, "\tmsg_result = %smach_msg(&OutP->Head, MACH_RCV_MSG|%s%s, 0, sizeof(Reply), InP->Head.msgh_local_port, %s, MACH_PORT_NULL);\n", + SubrPrefix, + rt->rtMsgOption->argVarName, + rt->rtWaitTime != argNULL ? "|MACH_RCV_TIMEOUT" : "", + rt->rtWaitTime != argNULL ? rt->rtWaitTime->argVarName : "MACH_MSG_TIMEOUT_NONE"); + WriteMsgCheckReceive(file, rt, "MACH_MSG_SUCCESS"); + fprintf(file, "\n"); +} + +/************************************************************* + * Writes the rpc call and the code to check for errors. + * This is the default code to be generated. Called by WriteRoutine + * for all routine types except SimpleProcedure and SimpleRoutine. + *************************************************************/ +static void +WriteMsgRPC(FILE *file, const routine_t *rt) +{ + char SendSize[24]; + + if (rt->rtNumRequestVar == 0) + sprintf(SendSize, "%d", rt->rtRequestSize); + else + strcpy(SendSize, "msgh_size"); + + if (IsKernelUser) + fprintf(file, "\tmsg_result = %smach_msg_rpc_from_kernel(&InP->Head, %s, sizeof(Reply));\n", + SubrPrefix, + SendSize); + else + fprintf(file, "\tmsg_result = %smach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|%s%s, %s, sizeof(Reply), InP->Head.msgh_reply_port, %s, MACH_PORT_NULL);\n", + SubrPrefix, + rt->rtMsgOption->argVarName, + rt->rtWaitTime != argNULL ? "|MACH_RCV_TIMEOUT" : "", + SendSize, + rt->rtWaitTime != argNULL? rt->rtWaitTime->argVarName : "MACH_MSG_TIMEOUT_NONE"); + WriteMsgCheckReceive(file, rt, "MACH_MSG_SUCCESS"); + fprintf(file, "\n"); +} + +/************************************************************* + * Sets the correct value of the dealloc flag and calls + * Utils:WritePackMsgType to fill in the ipc msg type word(s) + * in the request message. Called by WriteRoutine for each + * argument that is to be sent in the request message. + *************************************************************/ +static void +WritePackArgType(FILE *file, const argument_t *arg) +{ + WritePackMsgType(file, arg->argType, + arg->argType->itIndefinite ? d_NO : arg->argDeallocate, + arg->argLongForm, TRUE, + "InP->%s", "%s", arg->argTTName); + fprintf(file, "\n"); +} + +/************************************************************* + * Writes code to copy an argument into the request message. + * Called by WriteRoutine for each argument that is to placed + * in the request message. + *************************************************************/ +static void +WritePackArgValue(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + register const char *ref = arg->argByReferenceUser ? "*" : ""; + + 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, + "\tInP->%s = %smig_strncpy(InP->%s, %s, %d);\n", + arg->argCount->argMsgField, + SubrPrefix, + arg->argMsgField, + arg->argVarName, + it->itNumber); + } + else { + + /* + * Copy in variable-size inline array with memcpy, + * after checking that number of elements doesn`t + * exceed declared maximum. + */ + register const argument_t *count = arg->argCount; + register const char *countRef = count->argByReferenceUser ? "*" :""; + register const ipc_type_t *btype = it->itElement; + + /* Note btype->itNumber == count->argMultiplier */ + + fprintf(file, "\tif (%s%s > %d) {\n", + countRef, count->argVarName, + it->itNumber/btype->itNumber); + if (it->itIndefinite) { + fprintf(file, "\t\tInP->%s%s.msgt_inline = FALSE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + if (arg->argDeallocate == d_YES) + fprintf(file, "\t\tInP->%s%s.msgt_deallocate = TRUE;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + else if (arg->argDeallocate == d_MAYBE) + fprintf(file, "\t\tInP->%s%s.msgt_deallocate = %s%s;\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + arg->argDealloc->argByReferenceUser ? "*" : "", + arg->argDealloc->argVarName); + fprintf(file, "\t\t*((%s **)InP->%s) = %s%s;\n", + FetchUserType(btype), + arg->argMsgField, + ref, arg->argVarName); + if (!arg->argRoutine->rtSimpleFixedRequest) + fprintf(file, "\t\tmsgh_simple = FALSE;\n"); + } + else + WriteMsgError(file, arg->argRoutine, "MIG_ARRAY_TOO_LARGE"); + + fprintf(file, "\t}\n\telse {\n"); + + fprintf(file, "\t\tmemcpy(InP->%s, %s%s, ", arg->argMsgField, + ref, arg->argVarName); + if (btype->itTypeSize > 1) + fprintf(file, "%d * ", btype->itTypeSize); + fprintf(file, "%s%s);\n", + countRef, count->argVarName); + fprintf(file, "\t}\n"); + } + } + else if (arg->argMultiplier > 1) + WriteCopyType(file, it, "InP->%s", "/* %s */ %d * %s%s", + arg->argMsgField, arg->argMultiplier, + ref, arg->argVarName); + else + WriteCopyType(file, it, "InP->%s", "/* %s */ %s%s", + arg->argMsgField, ref, arg->argVarName); + fprintf(file, "\n"); +} + +static void +WriteAdjustMsgSimple(FILE *file, register const argument_t *arg) +{ + if (!arg->argRoutine->rtSimpleFixedRequest) + { + register const char *ref = arg->argByReferenceUser ? "*" : ""; + + fprintf(file, "\tif (MACH_MSG_TYPE_PORT_ANY(%s%s))\n", + ref, arg->argVarName); + fprintf(file, "\t\tmsgh_simple = FALSE;\n"); + fprintf(file, "\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, "(InP->%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, "InP->%s", count->argMsgField); + else + /* get count from argument */ + fprintf(file, "%s%s", + count->argByReferenceUser ? "*" : "", + 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 *)", + FetchUserType(ptype->itElement)); + } +} + +/* + * Adjust message size and advance request pointer. + * Called after packing a variable-length argument that + * has more arguments following. + */ +static void +WriteAdjustMsgSize(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argType; + + /* There are more In arguments. We need to adjust msgh_size + and advance InP, 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 (arg->argRequestPos == 0) + /* First variable-length argument. The previous msgh_size value + is the minimum request size. */ + + fprintf(file, "\tmsgh_size = %d + msgh_size_delta;\n", + arg->argRoutine->rtRequestSize); + else + fprintf(file, "\tmsgh_size += msgh_size_delta;\n"); + + fprintf(file, + "\tInP = (Request *) ((char *) InP + 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 In arguments. If this is the only variable In + argument, the previous msgh_size value is the minimum + request size. */ + + if (arg->argRequestPos == 0) { + fprintf(file, "\tmsgh_size = %d + (", + arg->argRoutine->rtRequestSize); + WriteArgSize(file, arg); + fprintf(file, ");\n"); + } + else { + fprintf(file, "\tmsgh_size += "); + WriteArgSize(file, arg); + fprintf(file, ";\n"); + } +} + +static void +WriteInitializeCount(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argCInOut->argParent->argType; + register const ipc_type_t *btype = ptype->itElement; + + fprintf(file, "\tif (%s%s < %d)\n", + arg->argByReferenceUser ? "*" : "", + arg->argVarName, + ptype->itNumber/btype->itNumber); + fprintf(file, "\t\tInP->%s = %s%s;\n", + arg->argMsgField, + arg->argByReferenceUser ? "*" : "", + arg->argVarName); + fprintf(file, "\telse\n"); + fprintf(file, "\t\tInP->%s = %d;\n", + arg->argMsgField, ptype->itNumber/btype->itNumber); + fprintf(file, "\n"); +} + +/* + * Called for every argument. Responsible for packing that + * argument into the request message. + */ +static void +WritePackArg(FILE *file, register const argument_t *arg) +{ + if (akCheck(arg->argKind, akbRequest)) + WritePackArgType(file, arg); + + if ((akIdent(arg->argKind) == akePoly) && + akCheckAll(arg->argKind, akbSendSnd|akbUserArg)) + WriteAdjustMsgSimple(file, arg); + + if ((akIdent(arg->argKind) == akeCountInOut) && + akCheck(arg->argKind, akbSendSnd)) + WriteInitializeCount(file, arg); + else if (akCheckAll(arg->argKind, akbSendSnd|akbSendBody)) + WritePackArgValue(file, arg); +} + +/* + * Generate code to fill in all of the request arguments and their + * message types. + */ +static void +WriteRequestArgs(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 request argument was variable-length and the + * request position will change. + */ + if (lastVarArg != argNULL && + lastVarArg->argRequestPos < arg->argRequestPos) + { + WriteAdjustMsgSize(file, lastVarArg); + lastVarArg = argNULL; + } + + /* + * Copy the argument + */ + WritePackArg(file, arg); + + /* + * Remember whether this was variable-length. + */ + if (akCheckAll(arg->argKind, akbSendSnd|akbSendBody|akbVariable)) + lastVarArg = arg; + } + + /* + * Finish the message size. + */ + if (lastVarArg != argNULL) + WriteFinishMsgSize(file, lastVarArg); +} + +/************************************************************* + * Writes code to check that the return msgh_id is correct and that + * the size of the return message is correct. Called by + * WriteRoutine. + *************************************************************/ +static void +WriteCheckIdentity(FILE *file, const routine_t *rt) +{ + fprintf(file, "\tif (OutP->Head.msgh_id != %d) {\n", + rt->rtNumber + SubsystemBase + 100); + fprintf(file, "\t\tif (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)\n"); + WriteMsgError(file, rt, "MIG_SERVER_DIED"); + fprintf(file, "\t\telse {\n"); + fprintf(file, "\t\t\t%smig_dealloc_reply_port(%s);\n\t", + SubrPrefix,"InP->Head.msgh_reply_port"); + WriteMsgError(file, rt, "MIG_REPLY_MISMATCH"); + fprintf(file, "\t\t}\n\t}\n"); + fprintf(file, "\n"); + fprintf(file, "#if\tTypeCheck\n"); + + if (rt->rtSimpleCheckReply && rt->rtSimpleReceiveReply) + { + /* Expecting a simple message. We can factor out the check for + a simple message, since the error reply message is also simple. + */ + + if (!rt->rtNoReplyArgs) + fprintf(file, "\tmsgh_size = OutP->Head.msgh_size;\n\n"); + + fprintf(file, + "\tif ((OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||\n"); + if (rt->rtNoReplyArgs) + fprintf(file, "\t (OutP->Head.msgh_size != %d))\n", + rt->rtReplySize); + else { + fprintf(file, "\t ((msgh_size %s %d) &&\n", + (rt->rtNumReplyVar > 0) ? "<" : "!=", + rt->rtReplySize); + fprintf(file, "\t ((msgh_size != sizeof(mig_reply_header_t)) ||\n"); + fprintf(file, "\t (OutP->RetCode == KERN_SUCCESS))))\n"); + } + } + else { + /* Expecting a complex message, or may vary at run time. */ + + fprintf(file, "\tmsgh_size = OutP->Head.msgh_size;\n"); + fprintf(file, "\tmsgh_simple = !(OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);\n"); + fprintf(file, "\n"); + + fprintf(file, "\tif (((msgh_size %s %d)", + (rt->rtNumReplyVar > 0) ? "<" : "!=", + rt->rtReplySize); + + if (rt->rtSimpleCheckReply) + /* if rtSimpleReceiveReply was true, then we would have + executed the code above. So we know that the message + is complex. */ + fprintf(file, " || msgh_simple"); + fprintf(file, ") &&\n"); + + fprintf(file, "\t ((msgh_size != sizeof(mig_reply_header_t)) ||\n"); + fprintf(file, "\t !msgh_simple ||\n"); + fprintf(file, "\t (OutP->RetCode == KERN_SUCCESS)))\n"); + } + WriteMsgError(file, rt, "MIG_TYPE_ERROR"); + fprintf(file, "#endif\t/* TypeCheck */\n"); + fprintf(file, "\n"); +} + +/************************************************************* + * Write code to generate error handling code if the RetCode + * argument of a Routine is not KERN_SUCCESS. + *************************************************************/ +static void +WriteRetCodeCheck(FILE *file, const routine_t *rt) +{ + fprintf(file, "\tif (OutP->RetCode != KERN_SUCCESS)\n"); + WriteMsgError(file, rt, "OutP->RetCode"); + fprintf(file, "\n"); +} + +/************************************************************* + * Writes code to check that the type of each of the arguments + * in the reply message is what is expected. Called by + * WriteRoutine for each argument in the reply message. + *************************************************************/ +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, akbReplyQC)) + { + fprintf(file, "\tif (* (int *) &OutP->%s != * (int *) &%sCheck)\n", + arg->argTTName, arg->argVarName); + } + else + { + fprintf(file, "\tif ("); + if (!it->itIndefinite) { + fprintf(file, "(OutP->%s%s.msgt_inline != %s) ||\n\t ", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + strbool(it->itInLine)); + } + fprintf(file, "(OutP->%s%s.msgt_longform != %s) ||\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : "", + strbool(arg->argLongForm)); + if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC) + { + if (!rt->rtSimpleCheckReply) + fprintf(file, "\t (MACH_MSG_TYPE_PORT_ANY(OutP->%s.msgt%s_name) && msgh_simple) ||\n", + arg->argTTName, + arg->argLongForm ? "l" : ""); + } + else + fprintf(file, "\t (OutP->%s.msgt%s_name != %s) ||\n", + arg->argTTName, + arg->argLongForm ? "l" : "", + it->itOutNameStr); + if (!it->itVarArray) + fprintf(file, "\t (OutP->%s.msgt%s_number != %d) ||\n", + arg->argTTName, + arg->argLongForm ? "l" : "", + it->itNumber); + fprintf(file, "\t (OutP->%s.msgt%s_size != %d))\n", + arg->argTTName, + arg->argLongForm ? "l" : "", + it->itSize); + } + WriteMsgError(file, rt, "MIG_TYPE_ERROR"); + 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, "(OutP->%s%s.msgt_inline) ? ", + arg->argTTName, arg->argLongForm ? ".msgtl_header" : ""); + } + + if (btype->itTypeSize % 4 != 0) + fprintf(file, "("); + + if (multiplier > 1) + fprintf(file, "%d * ", multiplier); + + fprintf(file, "OutP->%s", 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 *)", FetchUserType(btype)); +} + +static void +WriteCheckMsgSize(FILE *file, register const argument_t *arg) +{ + register const routine_t *rt = arg->argRoutine; + + /* If there aren't any more Out args after this, then + we can use the msgh_size_delta value directly in + the TypeCheck conditional. */ + + if (arg->argReplyPos == rt->rtMaxReplyPos) + { + fprintf(file, "#if\tTypeCheck\n"); + fprintf(file, "\tif (msgh_size != %d + (", + rt->rtReplySize); + WriteCheckArgSize(file, arg); + fprintf(file, "))\n"); + + WriteMsgError(file, rt, "MIG_TYPE_ERROR"); + 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->argReplyPos+1 == rt->rtNumReplyVar; + + /* 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->rtReplySize); + else + fprintf(file, "\tif (msgh_size < %d + msgh_size_delta)\n", + rt->rtReplySize); + WriteMsgError(file, rt, "MIG_TYPE_ERROR"); + + if (!LastVarArg) + fprintf(file, "\tmsgh_size -= msgh_size_delta;\n"); + + fprintf(file, "#endif\t/* TypeCheck */\n"); + } + fprintf(file, "\n"); +} + +/************************************************************* + * Write code to copy an argument from the reply message + * to the parameter. Called by WriteRoutine for each argument + * in the reply message. + *************************************************************/ +static void +WriteExtractArgValue(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *argType = arg->argType; + register const char *ref = arg->argByReferenceUser ? "*" : ""; + + if (argType->itInLine && argType->itVarArray) { + + if (argType->itString) { + /* + * Copy out variable-size C string with mig_strncpy. + */ + fprintf(file, "\t(void) %smig_strncpy(%s%s, OutP->%s, %d);\n", + SubrPrefix, + ref, + arg->argVarName, + arg->argMsgField, + argType->itNumber); + } + else if (argType->itIndefinite) { + /* + * If data was returned out-of-line, + * change user`s pointer to point to it. + * If data was returned in-line but doesn`t fit, + * allocate a new buffer, copy the data to it, + * and change user`s pointer to point to it. + * If data was returned in-line and fits, + * copy to buffer. + */ + const argument_t *count = arg->argCount; + const char *countRef = count->argByReferenceUser ? "*" : ""; + const ipc_type_t *btype = argType->itElement; + + fprintf(file, "\tif (!OutP->%s%s.msgt_inline)\n", + arg->argTTName, + arg->argLongForm ? ".msgtl_header" : ""); + fprintf(file, "\t %s%s = *((%s **)OutP->%s);\n", + ref, arg->argVarName, + FetchUserType(btype), arg->argMsgField); + fprintf(file, "\telse if (OutP->%s", count->argMsgField); + if (btype->itNumber > 1) + fprintf(file, " / %d", btype->itNumber); + fprintf(file, " > %s%s) {\n", countRef, count->argVarName); + fprintf(file, "\t %smig_allocate((vm_offset_t *)%s,\n\t\t", + SubrPrefix, arg->argVarName); /* no ref! */ + if (btype->itTypeSize != btype->itNumber) + fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber); + fprintf(file, "OutP->%s);\n", count->argMsgField); + fprintf(file, "\t memcpy(%s%s, OutP->%s, ", ref, arg->argVarName, + arg->argMsgField); + if (btype->itTypeSize != btype->itNumber) + fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber); + fprintf(file, "OutP->%s);\n", count->argMsgField); + fprintf(file, "\t}\n"); + fprintf(file, "\telse {\n"); + + fprintf(file, "\t memcpy(%s%s, OutP->%s, ", ref, arg->argVarName, + arg->argMsgField); + if (btype->itTypeSize != btype->itNumber) + fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber); + fprintf(file, "OutP->%s);\n", count->argMsgField); + fprintf(file, "\t}\n"); + } + else { + + /* + * Copy out variable-size inline array with memcpy, + * after checking that number of elements doesn`t + * exceed user`s maximum. + */ + register const argument_t *count = arg->argCount; + register const char *countRef = count->argByReferenceUser ? "*" :""; + register const ipc_type_t *btype = argType->itElement; + + /* Note count->argMultiplier == btype->itNumber */ + + fprintf(file, "\tif (OutP->%s", count->argMsgField); + if (btype->itNumber > 1) + fprintf(file, " / %d", btype->itNumber); + fprintf(file, " > %s%s) {\n", + countRef, count->argVarName); + + /* + * If number of elements is too many for user receiving area, + * fill user`s area as much as possible. Return the correct + * number of elements. + */ + fprintf(file, "\t\tmemcpy(%s%s, OutP->%s, ", ref, arg->argVarName, + arg->argMsgField); + if (btype->itTypeSize > 1) + fprintf(file, "%d * ", btype->itTypeSize); + fprintf(file, "%s%s);\n", + countRef, count->argVarName); + + fprintf(file, "\t\t%s%s = OutP->%s", + countRef, count->argVarName, count->argMsgField); + if (btype->itNumber > 1) + fprintf(file, " / %d", btype->itNumber); + fprintf(file, ";\n"); + WriteMsgError(file,arg->argRoutine, "MIG_ARRAY_TOO_LARGE"); + + fprintf(file, "\t}\n\telse {\n"); + + fprintf(file, "\t\tmemcpy(%s%s, OutP->%s, ", ref, arg->argVarName, + arg->argMsgField); + if (btype->itTypeSize != btype->itNumber) + fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber); + fprintf(file, "OutP->%s);\n", count->argMsgField); + fprintf(file, "\t}\n"); + } + } + else if (arg->argMultiplier > 1) + WriteCopyType(file, argType, + "%s%s", "/* %s%s */ OutP->%s / %d", + ref, arg->argVarName, arg->argMsgField, + arg->argMultiplier); + else + WriteCopyType(file, argType, + "%s%s", "/* %s%s */ OutP->%s", + ref, arg->argVarName, arg->argMsgField); + fprintf(file, "\n"); +} + +static void +WriteExtractArg(FILE *file, register const argument_t *arg) +{ + register const routine_t *rt = arg->argRoutine; + + if (akCheck(arg->argKind, akbReply)) + WriteTypeCheck(file, arg); + + if (akCheckAll(arg->argKind, akbVariable|akbReply)) + WriteCheckMsgSize(file, arg); + + /* Now that the RetCode is type-checked, check its value. + Must abort immediately if it isn't KERN_SUCCESS, because + in that case the reply message is truncated. */ + + if (arg == rt->rtRetCode) + WriteRetCodeCheck(file, rt); + + if (akCheckAll(arg->argKind, akbReturnRcv)) + WriteExtractArgValue(file, arg); +} + +static void +WriteAdjustReplyMsgPtr(FILE *file, register const argument_t *arg) +{ + register const ipc_type_t *ptype = arg->argType; + + fprintf(file, + "\tOutP = (Reply *) ((char *) OutP + msgh_size_delta - %d);\n\n", + ptype->itTypeSize + ptype->itPadSize); +} + +static void +WriteReplyArgs(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 reply argument was + * variable-length and the reply position will change. + */ + if (lastVarArg != argNULL && + lastVarArg->argReplyPos < arg->argReplyPos) + { + WriteAdjustReplyMsgPtr(file, lastVarArg); + lastVarArg = argNULL; + } + + /* + * Copy the argument + */ + WriteExtractArg(file, arg); + + /* + * Remember whether this was variable-length. + */ + if (akCheckAll(arg->argKind, akbReturnRcv|akbVariable)) + lastVarArg = arg; + } +} + +/************************************************************* + * Writes code to return the return value. Called by WriteRoutine + * for routines and functions. + *************************************************************/ +static void +WriteReturnValue(FILE *file, const routine_t *rt) +{ + if (rt->rtReturn == rt->rtRetCode) + /* If returning RetCode, we have already checked that it is + KERN_SUCCESS */ + fprintf(file, "\treturn KERN_SUCCESS;\n"); + + else + { + if (rt->rtNumReplyVar > 0) + fprintf(file, "\tOutP = &Mess.Out;\n"); + + fprintf(file, "\treturn OutP->%s;\n", rt->rtReturn->argMsgField); + } +} + +/************************************************************* + * Writes the elements of the message type declaration: the + * msg_type structure, the argument itself and any padding + * that is required to make the argument a multiple of 4 bytes. + * Called by WriteRoutine for all the arguments in the request + * message first and then the reply message. + *************************************************************/ +static void +WriteFieldDecl(FILE *file, const argument_t *arg) +{ + WriteFieldDeclPrim(file, arg, FetchUserType); +} + +static void +WriteStubDecl(FILE *file, register const routine_t *rt) +{ + fprintf(file, "\n"); + fprintf(file, "/* %s %s */\n", rtRoutineKindToStr(rt->rtKind), rt->rtName); + fprintf(file, "mig_external %s %s\n", ReturnTypeStr(rt), rt->rtUserName); + fprintf(file, "(\n"); + WriteList(file, rt->rtArgs, WriteUserVarDecl, akbUserArg, ",\n", "\n"); + fprintf(file, ")\n"); + fprintf(file, "{\n"); +} + +/************************************************************* + * Writes all the code comprising a routine body. Called by + * WriteUser for each routine. + *************************************************************/ +static void +WriteRoutine(FILE *file, register const routine_t *rt) +{ + /* write the stub's declaration */ + + WriteStubDecl(file, rt); + + /* typedef of structure for Request and Reply messages */ + + WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbRequest, "Request"); + if (!rt->rtOneWay) + WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbReply, "Reply"); + + /* declarations for local vars: Union of Request and Reply messages, + InP, OutP and return value */ + + WriteVarDecls(file, rt); + + /* declarations and initializations of the mach_msg_type_t variables + for each argument */ + + WriteList(file, rt->rtArgs, WriteTypeDeclIn, akbRequest, "\n", "\n"); + if (!rt->rtOneWay) + WriteList(file, rt->rtArgs, WriteCheckDecl, akbReplyQC, "\n", "\n"); + + /* fill in all the request message types and then arguments */ + + WriteRequestArgs(file, rt); + + /* fill in request message head */ + + WriteRequestHead(file, rt); + fprintf(file, "\n"); + + /* Write the send/receive or rpc call */ + + if (rt->rtOneWay) + WriteMsgSend(file, rt); + else + { + if (UseMsgRPC) + WriteMsgRPC(file, rt); + else + WriteMsgSendReceive(file, rt); + + /* Check the values that are returned in the reply message */ + + WriteCheckIdentity(file, rt); + + /* If the reply message has no Out parameters or return values + other than the return code, we can type-check it and + return it directly. */ + + if (rt->rtNoReplyArgs) + { + WriteTypeCheck(file, rt->rtRetCode); + + fprintf(file, "\treturn OutP->RetCode;\n"); + } + else { + WriteReplyArgs(file, rt); + + /* return the return value, if any */ + + if (rt->rtProcedure) + fprintf(file, "\t/* Procedure - no return needed */\n"); + else + WriteReturnValue(file, rt); + } + } + + fprintf(file, "}\n"); +} + +/************************************************************* + * Writes out the xxxUser.c file. Called by mig.c + *************************************************************/ +void +WriteUser(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 skUImport: + WriteImport(file, stat->stFileName); + break; + case skSImport: + break; + default: + fatal("WriteUser(): bad statement_kind_t (%d)", + (int) stat->stKind); + } + WriteEpilog(file); +} + +/************************************************************* + * Writes out individual .c user files for each routine. Called by mig.c + *************************************************************/ +void +WriteUserIndividual(const statement_t *stats) +{ + register const statement_t *stat; + + for (stat = stats; stat != stNULL; stat = stat->stNext) + switch (stat->stKind) + { + case skRoutine: + { + FILE *file; + register char *filename; + + filename = strconcat(UserFilePrefix, + strconcat(stat->stRoutine->rtName, ".c")); + file = fopen(filename, "w"); + if (file == NULL) + fatal("fopen(%s): %s", filename, + unix_error_string(errno)); + WriteProlog(file); + + { + /* Write all the imports. */ + const statement_t *s; + for (s = stats; s != stNULL; s = s->stNext) + switch (s->stKind) + { + case skImport: + case skUImport: + WriteImport(file, s->stFileName); + break; + } + } + + WriteRoutine(file, stat->stRoutine); + WriteEpilog(file); + fclose(file); + strfree(filename); + } + break; + case skImport: + case skUImport: + break; + case skSImport: + break; + default: + fatal("WriteUserIndividual(): bad statement_kind_t (%d)", + (int) stat->stKind); + } +} |