|
|
GNU Mach 1.0
/*
* 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 [email protected]
* 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);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.