/* * Copyright (c) 1999-2018 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * 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 "type.h" #include #include #include "mig_machine.h" #include "error.h" #include "alloc.h" #include "global.h" #include "routine.h" #include "write.h" u_int rtNumber = 0; static void rtSizeDelta(FILE *file, u_int mask, routine_t *rt); routine_t * rtAlloc(void) { routine_t *new; new = (routine_t *) calloc(1, sizeof *new); if (new == rtNULL) fatal("rtAlloc(): %s", strerror(errno)); new->rtNumber = rtNumber++; new->rtName = strNULL; new->rtErrorName = strNULL; new->rtUserName = strNULL; new->rtServerName = strNULL; return new; } void rtSkip(void) { rtNumber++; } argument_t * argAlloc(void) { extern void KPD_error(FILE *file, argument_t *arg); static argument_t prototype = { .argName = strNULL, .argNext = argNULL, .argKind = akNone, .argType = itNULL, .argKPD_Type = argKPD_NULL, .argKPD_Template = (void(*)(FILE *, argument_t *, boolean_t))KPD_error, .argKPD_Init = KPD_error, .argKPD_Pack = KPD_error, .argKPD_Extract = KPD_error, .argKPD_TypeCheck = KPD_error, .argVarName = strNULL, .argMsgField = strNULL, .argTTName = strNULL, .argPadName = strNULL, .argSuffix = strNULL, .argFlags = flNone, .argDeallocate = d_NO, .argCountInOut = FALSE, .argRoutine = rtNULL, .argCount = argNULL, .argSubCount = argNULL, .argCInOut = argNULL, .argPoly = argNULL, .argDealloc = argNULL, .argSameCount = argNULL, .argParent = argNULL, .argMultiplier = 1, .argRequestPos = 0, .argReplyPos = 0, .argByReferenceUser = FALSE, .argByReferenceServer = FALSE, .argTempOnStack = FALSE }; argument_t *new; new = (argument_t *) malloc(sizeof *new); if (new == argNULL) fatal("argAlloc(): %s", strerror(errno)); *new = prototype; return new; } routine_t * rtMakeRoutine(identifier_t name, argument_t *args) { routine_t *rt = rtAlloc(); rt->rtName = name; rt->rtKind = rkRoutine; rt->rtArgs = args; return rt; } routine_t * rtMakeSimpleRoutine(identifier_t name, argument_t *args) { routine_t *rt = rtAlloc(); rt->rtName = name; rt->rtKind = rkSimpleRoutine; rt->rtArgs = args; return rt; } char * rtRoutineKindToStr(routine_kind_t rk) { switch (rk) { case rkRoutine: return "Routine"; case rkSimpleRoutine: return "SimpleRoutine"; default: fatal("rtRoutineKindToStr(%d): not a routine_kind_t", rk); /*NOTREACHED*/ return strNULL; } } static void rtPrintArg(argument_t *arg) { ipc_type_t *it = arg->argType; if (!akCheck(arg->argKind, akbUserArg|akbServerArg) || (akIdent(arg->argKind) == akeCount) || (akIdent(arg->argKind) == akeDealloc) || (akIdent(arg->argKind) == akeNdrCode) || (akIdent(arg->argKind) == akePoly)) return; printf("\n\t"); switch (akIdent(arg->argKind)) { case akeRequestPort: printf("RequestPort"); break; case akeReplyPort: printf("ReplyPort"); break; case akeWaitTime: printf("WaitTime"); break; case akeSendTime: printf("SendTime"); break; case akeMsgOption: printf("MsgOption"); break; case akeMsgSeqno: printf("MsgSeqno\t"); break; case akeSecToken: printf("SecToken\t"); break; case akeAuditToken: printf("AuditToken\t"); break; case akeContextToken: printf("ContextToken\t"); break; case akeImplicit: printf("Implicit\t"); break; default: if (akCheck(arg->argKind, akbRequest)) { if (akCheck(arg->argKind, akbSend)) printf("In"); else printf("(In)"); } if (akCheck(arg->argKind, akbReply)) { if (akCheck(arg->argKind, akbReturn)) printf("Out"); else printf("(Out)"); } printf("\t"); } printf("\t%s: %s", arg->argName, it->itName); if (arg->argDeallocate == d_YES) printf(", Dealloc"); else if (arg->argDeallocate == d_MAYBE) printf(", Dealloc[]"); if (arg->argCountInOut) printf(", CountInOut"); if (arg->argFlags & flSameCount) printf(", SameCount"); if (arg->argFlags & flPhysicalCopy) printf(", PhysicalCopy"); if (arg->argFlags & flRetCode) printf(", PhysicalCopy"); if (arg->argFlags & flOverwrite) printf(", Overwrite"); if (arg->argFlags & flAuto) printf(", Auto"); if (arg->argFlags & flConst) printf(", Const"); } void rtPrintRoutine(routine_t *rt) { argument_t *arg; printf("%s (%d) %s(", rtRoutineKindToStr(rt->rtKind), rt->rtNumber, rt->rtName); for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) rtPrintArg(arg); printf(")\n"); printf("\n"); } /* * Determines appropriate value of msg-simple for the message. * One version for both In & Out. */ static void rtCheckSimple(argument_t *args, u_int mask, boolean_t *simple) { argument_t *arg; boolean_t MustBeComplex = FALSE; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { ipc_type_t *it = arg->argType; if (IS_KERN_PROC_DATA(it)) MustBeComplex = TRUE; } *simple = !MustBeComplex; } static void rtCheckFit(routine_t *rt, u_int mask, boolean_t *fitp, boolean_t *uselimp, u_int *knownp) { boolean_t uselim = FALSE; argument_t *arg; u_int size = sizeof(mach_msg_header_t); if (!rt->rtSimpleRequest) machine_alignment(size,sizeof(mach_msg_body_t)); for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { ipc_type_t *it = arg->argType; machine_alignment(size, it->itMinTypeSize); if (it->itNative) uselim = TRUE; else if (IS_VARIABLE_SIZED_UNTYPED(it)) { machine_alignment(size, it->itTypeSize); size += it->itPadSize; } } *knownp = size; if (MaxMessSizeOnStack == -1) { *fitp = TRUE; *uselimp = FALSE; } else if (size > MaxMessSizeOnStack) { *fitp = FALSE; *uselimp = FALSE; } else if (!uselim) { *fitp = TRUE; *uselimp = FALSE; } else if (UserTypeLimit == -1) { *fitp = FALSE; *uselimp = FALSE; } else if (size + UserTypeLimit > MaxMessSizeOnStack) { *fitp = FALSE; *uselimp = TRUE; } else { *fitp = TRUE; *uselimp = TRUE; } } static void rtFindHowMany(routine_t *rt) { argument_t *arg; int multiplier = 1; boolean_t test; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; if (IS_MULTIPLE_KPD(it)) { if (!it->itVarArray) multiplier = it->itKPD_Number; test = !it->itVarArray && !it->itElement->itVarArray; it = it->itElement; } else test = !it->itVarArray; if (akCheck(arg->argKind, akbSendKPD)) { if (it->itInLine) rt->rtCountPortsIn += it->itNumber * multiplier; else if (it->itPortType) { if (test) rt->rtCountOolPortsIn += it->itNumber * multiplier; } else { if (test) rt->rtCountOolIn += (it->itNumber * it->itSize + 7)/8 * multiplier; } } if (akCheckAll(arg->argKind, akbReturnKPD)) { if (it->itInLine) rt->rtCountPortsOut += it->itNumber * multiplier; else if (it->itPortType) { if (test) rt->rtCountOolPortsOut += it->itNumber * multiplier; } else { if (test) rt->rtCountOolOut += ((it->itNumber * it->itSize + 7)/8) * multiplier; } } } } boolean_t rtCheckMask(argument_t *args, u_int mask) { argument_t *arg; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) return TRUE; return FALSE; } boolean_t rtCheckMaskFunction(argument_t *args, u_int mask, boolean_t (*func)(argument_t *arg)) { argument_t *arg; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) if ((*func)(arg)) return TRUE; return FALSE; } int rtCountKPDs(argument_t *args, u_int mask) { argument_t *arg; int count = 0; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) count += arg->argType->itKPD_Number; return count; } int rtCountFlags(argument_t *args, u_int flag) { argument_t *arg; int count = 0; for (arg = args; arg != argNULL; arg = arg->argNext) if (arg->argFlags & flag) count++; return count; } int rtCountArgDescriptors(argument_t *args, int *argcount) { argument_t *arg; int count = 0; if (argcount) *argcount = 0; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, akbServerArg)) { if (RPCFixedArray(arg) || RPCPort(arg) || RPCVariableArray(arg) || RPCPortArray(arg)) { count++; if (argcount) (*argcount)++; } else { if (argcount) { if (arg->argType->itStruct && arg->argType->itNumber && (arg->argType->itSize >= 32)) *argcount += arg->argType->itNumber * (arg->argType->itSize / 32); else (*argcount)++; } } } return count; } int rtCountMask(argument_t *args, u_int mask) { argument_t *arg; int count = 0; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) count++; return count; } /* arg->argType may be NULL in this function */ static void rtDefaultArgKind(routine_t *rt, argument_t *arg) { if ((arg->argKind == akNone) && (rt->rtRequestPort == argNULL)) arg->argKind = akRequestPort; if (arg->argKind == akNone) arg->argKind = akIn; } /* * Initializes arg->argDeallocate, * arg->argCountInOut from arg->argFlags * and perform consistency check over the * flags. */ static ipc_flags_t rtProcessDeallocFlag(ipc_type_t *it, ipc_flags_t flags, arg_kind_t kind, dealloc_t *what, string_t name) { /* only one of flDealloc, flNotDealloc, flMaybeDealloc */ if (flags & flMaybeDealloc) { if (flags & (flDealloc|flNotDealloc)) { warn("%s: Dealloc and NotDealloc ignored with Dealloc[]", name); flags &= ~(flDealloc|flNotDealloc); } } if ((flags&(flDealloc|flNotDealloc)) == (flDealloc|flNotDealloc)) { warn("%s: Dealloc and NotDealloc cancel out", name); flags &= ~(flDealloc|flNotDealloc); } if (((IsKernelServer && akCheck(kind, akbReturn)) || (IsKernelUser && akCheck(kind, akbSend))) && (flags & flDealloc)) { /* * For a KernelServer interface and an Out argument, * or a KernelUser interface and an In argument, * we avoid a possible spurious warning about the deallocate bit. * For compatibility with Mach 2.5, the deallocate bit * may need to be enabled on some inline arguments. */ *what= d_YES; } else if (flags & (flMaybeDealloc|flDealloc)) { /* only give semantic warnings if the user specified something */ if (it->itInLine && !it->itPortType) { warn("%s: Dealloc is ignored: it is meaningless for that type of argument", name); flags &= ~(flMaybeDealloc|flDealloc); } else *what = (flags & flMaybeDealloc) ? d_MAYBE : d_YES; } return flags; } static void rtProcessSameCountFlag(argument_t *arg) { ipc_type_t *it = arg->argType; ipc_flags_t flags = arg->argFlags; string_t name = arg->argVarName; static argument_t *old_arg; if (flags & flSameCount) { if (!it->itVarArray) { warn("%s: SameCount is ignored - the argument is not variable", name); flags &= ~flSameCount; } if (old_arg) { if (old_arg->argParent) old_arg = old_arg->argParent; if (old_arg->argSameCount) old_arg = old_arg->argSameCount; if (!old_arg->argType->itVarArray) { warn("%s: SameCount is ignored - adjacent argument is not variable", name); flags &= ~flSameCount; } #define SAMECOUNT_MASK akeBITS|akbSend|akbReturn|akbRequest|akbReply|akbUserArg|akbServerArg if (akCheck(old_arg->argKind, SAMECOUNT_MASK) != akCheck(arg->argKind, SAMECOUNT_MASK) || old_arg->argCountInOut != arg->argCountInOut) { warn("%s: SameCount is ignored - inconsistencies with the adjacent argument\n", name); flags &= ~flSameCount; } arg->argSameCount = old_arg; } arg->argFlags = flags; } old_arg = arg; } static ipc_flags_t rtProcessCountInOutFlag(ipc_type_t *it, ipc_flags_t flags, arg_kind_t kind, boolean_t *what, string_t name) { if (flags & flCountInOut) { if (!akCheck(kind, akbReply)) { warn("%s: CountInOut is ignored: argument must be Out\n", name); flags &= ~flCountInOut; } else if (!it->itVarArray || !it->itInLine) { warn("%s: CountInOut is ignored: argument isn't variable or in-line\n", name); flags &= ~flCountInOut; } else *what = TRUE; } return flags; } static ipc_flags_t rtProcessPhysicalCopyFlag(ipc_type_t *it, ipc_flags_t flags, arg_kind_t kind, string_t name) { if (flags & flPhysicalCopy) { if (it->itInLine) { warn("%s: PhysicalCopy is ignored, argument copied inline anyway", name); flags &= ~flPhysicalCopy; } if (it->itPortType) { warn("%s: PhysicalCopy is ignored, it does not apply to ports and array of ports", name); flags &= ~flPhysicalCopy; } } return flags; } static void rtProcessRetCodeFlag(argument_t *thisarg) { ipc_type_t *it = thisarg->argType; ipc_flags_t flags = thisarg->argFlags; string_t name = thisarg->argVarName; routine_t *thisrout = thisarg->argRoutine; if (flags & flRetCode) { if (!it->itInLine || !it->itStruct || it->itSize != 32 || it->itNumber != 1) { warn("%s: RetCode is ignored - the type doesn't match a MIG RetCode", name); flags &= ~flRetCode; } else if (thisrout->rtKind != rkSimpleRoutine) { fatal("%s: RetCode is allowed only for SimpleRoutines", name); } else if (thisrout->rtRetCArg != argNULL) { warn("%s: RetCode is ignored - only one argument can be flagged as RetCode", name); flags &= ~flRetCode; } else { thisrout->rtRetCArg = thisarg; } thisarg->argFlags = flags; } } static ipc_flags_t rtProcessOverwriteFlag(ipc_type_t *it, ipc_flags_t flags, arg_kind_t kind, string_t name) { if (flags & flOverwrite) if (it->itInLine || it->itMigInLine || /* among In, Out, InOut, we want only the Out! */ !akCheck(kind, akbReturn) || akCheck(kind, akbSend)) { warn("%s: Overwrite is ignored - it must be Out AND Ool!", name); flags &= ~flOverwrite; } return flags; } static void rtDetectKPDArg(argument_t *arg) { ipc_type_t *it = arg->argType; char *string; if (IS_KERN_PROC_DATA(it)) { if (akCheck(arg->argKind, akbSendBody)) { arg->argKind = akRemFeature(arg->argKind, akbSendBody); arg->argKind = akAddFeature(arg->argKind, akbSendKPD); } if (akCheck(arg->argKind, akbReturnBody)) { arg->argKind = akRemFeature(arg->argKind, akbReturnBody); arg->argKind = akAddFeature(arg->argKind, akbReturnKPD); } if (it->itInLine) { string = "mach_msg_port_descriptor_t"; arg->argKPD_Type = MACH_MSG_PORT_DESCRIPTOR; } else if (it->itPortType) { string = "mach_msg_ool_ports_descriptor_t"; arg->argKPD_Type = MACH_MSG_OOL_PORTS_DESCRIPTOR; } else { string = "mach_msg_ool_descriptor_t"; arg->argKPD_Type = MACH_MSG_OOL_DESCRIPTOR; } it->itKPDType = string; } } static void rtAugmentArgKind(argument_t *arg) { ipc_type_t *it = arg->argType; /* akbVariable means variable-sized inline */ if (IS_VARIABLE_SIZED_UNTYPED(it)) { if (akCheckAll(arg->argKind, akbRequest|akbReply)) error("%s: Inline variable-sized arguments can't be InOut", arg->argName); arg->argKind = akAddFeature(arg->argKind, akbVariable); } if (IS_OPTIONAL_NATIVE(it)) arg->argKind = akAddFeature(arg->argKind, akbVariable); /* * Need to use a local variable in the following cases: * 1) There is a translate-out function & the argument is being * returned. We need to translate it before it hits the message. * 2) There is a translate-in function & the argument is * sent and returned. We need a local variable for its address. * 3) There is a destructor function, which will be used * (SendRcv and not ReturnSnd), and there is a translate-in * function whose value must be saved for the destructor. * 4) This is Complex KPD (array of KPD), and as such it has to * be copied to a local array in input and output * 5) Both poly and dealloc generate warnings compile time, because * we attempt to take address of bit-field structure member */ if ( ((it->itOutTrans != strNULL) && akCheck(arg->argKind, akbReturnSnd)) || ((it->itInTrans != strNULL) && akCheckAll(arg->argKind, akbSendRcv|akbReturnSnd)) || ((it->itDestructor != strNULL) && akCheck(arg->argKind, akbSendRcv) && !akCheck(arg->argKind, akbReturnSnd) && (it->itInTrans != strNULL)) || (IS_MULTIPLE_KPD(it)) || ((akIdent(arg->argKind) == akePoly) && akCheck(arg->argKind, akbReturnSnd)) || ((akIdent(arg->argKind) == akeDealloc) && akCheck(arg->argKind, akbReturnSnd)) ) { arg->argKind = akRemFeature(arg->argKind, akbReplyCopy); arg->argKind = akAddFeature(arg->argKind, akbVarNeeded); } } /* * The Suffix allows to handle KPDs as normal data. * it is used in InArgMsgField. */ static void rtSuffixExtArg(argument_t *args) { argument_t *arg; char *subindex; char string[MAX_STR_LEN]; for (arg = args; arg != argNULL; arg = arg->argNext) { if (akCheck(arg->argKind, akbSendKPD | akbReturnKPD)) { if (IS_MULTIPLE_KPD(arg->argType)) subindex = "[0]"; else subindex = ""; switch (arg->argKPD_Type) { case MACH_MSG_PORT_DESCRIPTOR: (void)sprintf(string, "%s.name", subindex); break; case MACH_MSG_OOL_DESCRIPTOR: case MACH_MSG_OOL_PORTS_DESCRIPTOR: (void)sprintf(string, "%s.address", subindex); break; default: error("Type of kernel processed data unknown\n"); } arg->argSuffix = strconcat(arg->argMsgField, string); /* see above the list of VarNeeded cases */ /* * argCount has been removed from the VarNeeded list, * because VarSize arrays have their Count in the untyped * section of the message, and because it is not possible * to move anything in-line/out-of-line */ } else if (akIdent(arg->argKind) == akePoly && akCheck(arg->argParent->argKind, akbSendKPD | akbReturnKPD)) { argument_t *par_arg = arg->argParent; if (IS_MULTIPLE_KPD(par_arg->argType)) subindex = "[0]"; else subindex = ""; switch (par_arg->argKPD_Type) { case MACH_MSG_PORT_DESCRIPTOR: case MACH_MSG_OOL_PORTS_DESCRIPTOR: (void)sprintf(string, "%s.disposition", subindex); arg->argSuffix = strconcat(par_arg->argMsgField, string); break; default: error("Type of kernel processed data inconsistent\n"); } } else if (akIdent(arg->argKind) == akeDealloc && akCheck(arg->argParent->argKind, akbSendKPD | akbReturnKPD)) { argument_t *par_arg = arg->argParent; if (IS_MULTIPLE_KPD(par_arg->argType)) subindex = "[0]"; else subindex = ""; switch (par_arg->argKPD_Type) { case MACH_MSG_OOL_DESCRIPTOR: case MACH_MSG_OOL_PORTS_DESCRIPTOR: (void)sprintf(string, "%s.deallocate", subindex); arg->argSuffix = strconcat(par_arg->argMsgField, string); break; default: error("Type of kernel processed data inconsistent\n"); } } } } /* arg->argType may be NULL in this function */ static void rtCheckRoutineArg(routine_t *rt, argument_t *arg) { switch (akIdent(arg->argKind)) { case akeRequestPort: if (rt->rtRequestPort != argNULL) warn("multiple RequestPort args in %s; %s won't be used", rt->rtName, rt->rtRequestPort->argName); rt->rtRequestPort = arg; break; case akeReplyPort: if (rt->rtReplyPort != argNULL) warn("multiple ReplyPort args in %s; %s won't be used", rt->rtName, rt->rtReplyPort->argName); rt->rtReplyPort = arg; break; case akeWaitTime: if (rt->rtWaitTime != argNULL) warn("multiple WaitTime/SendTime type args in %s; %s won't be used", rt->rtName, rt->rtWaitTime->argName); rt->rtWaitTime = arg; break; case akeSendTime: if (rt->rtWaitTime != argNULL) { if (akIdent(rt->rtWaitTime->argKind) == akeWaitTime) { warn("SendTime type argument after a WaitTime in %s; SendTime %s won't be used", rt->rtName, arg->argName); break; } else { warn("multiple SendTime type args in %s; %s won't be used", rt->rtName, rt->rtWaitTime->argName); } } rt->rtWaitTime = arg; break; case akeMsgOption: if (rt->rtMsgOption != argNULL) warn("multiple MsgOption args in %s; %s won't be used", rt->rtName, rt->rtMsgOption->argName); rt->rtMsgOption = arg; break; default: break; } } /* arg->argType may be NULL in this function */ static void rtSetArgDefaults(routine_t *rt, argument_t *arg) { arg->argRoutine = rt; if (arg->argVarName == strNULL) arg->argVarName = arg->argName; if (arg->argMsgField == strNULL) switch(akIdent(arg->argKind)) { case akeRequestPort: arg->argMsgField = "Head.msgh_request_port"; break; case akeReplyPort: arg->argMsgField = "Head.msgh_reply_port"; break; case akeNdrCode: arg->argMsgField = "NDR"; break; case akeSecToken: arg->argMsgField = "msgh_sender"; break; case akeAuditToken: arg->argMsgField = "msgh_audit"; break; case akeContextToken: arg->argMsgField = "msgh_context"; break; case akeMsgSeqno: arg->argMsgField = "msgh_seqno"; break; case akeImplicit: /* the field is set directly by Yacc */ break; default: arg->argMsgField = arg->argName; break; } if (arg->argTTName == strNULL) arg->argTTName = strconcat(arg->argName, "Template"); if (arg->argPadName == strNULL) arg->argPadName = strconcat(arg->argName, "Pad"); /* * The poly args for the request and reply ports have special defaults, * because their msg-type-name values aren't stored in normal fields. */ if ((rt->rtRequestPort != argNULL) && (rt->rtRequestPort->argPoly == arg) && (arg->argType != itNULL)) { arg->argMsgField = "Head.msgh_bits"; arg->argType->itInTrans = "MACH_MSGH_BITS_REQUEST"; } if ((rt->rtReplyPort != argNULL) && (rt->rtReplyPort->argPoly == arg) && (arg->argType != itNULL)) { arg->argMsgField = "Head.msgh_bits"; arg->argType->itInTrans = "MACH_MSGH_BITS_REPLY"; } } static void rtAddCountArg(argument_t *arg) { argument_t *count, *master; ipc_type_t *it = arg->argType; count = argAlloc(); if (IS_MULTIPLE_KPD(it) && it->itElement->itVarArray) { count->argName = strconcat(arg->argName, "Subs"); count->argType = itMakeSubCountType(it->itKPD_Number, it->itVarArray, arg->argVarName); count->argKind = akeSubCount; arg->argSubCount = count; } else { count->argName = strconcat(arg->argName, "Cnt"); count->argType = itMakeCountType(); count->argKind = akeCount; arg->argCount = count; if (arg->argParent != argNULL) { /* this is the case where we are at the second level of recursion: we want the Parent to access it through argCount */ arg->argParent->argCount = count; } } master = (arg->argParent != argNULL) ? arg->argParent : arg; if (IS_MULTIPLE_KPD(master->argType)) count->argMultiplier = 1; else count->argMultiplier = it->itElement->itNumber; count->argParent = arg; count->argNext = arg->argNext; arg->argNext = count; if (arg->argType->itString) { /* C String gets no Count argument on either side, but are still in the msg */ count->argKind = akAddFeature(count->argKind, akCheck(arg->argKind, akbSend) ? akbSendRcv : akbReturnRcv); count->argVarName = (char *)0; } else { /* * Count arguments have to be present on the message body (NDR encoded) * akbVariable has to be turned down, has it foul the algorithm * for detecting the in-line variable sized arrays */ count->argKind |= akAddFeature(akbUserArg|akbServerArg, (arg->argKind) & ~akeBITS); count->argKind = akRemFeature(count->argKind, akbVariable|akbVarNeeded); if (IS_VARIABLE_SIZED_UNTYPED(arg->argType)) /* * Count arguments for the above types are explicitly declared * BEFORE the variable (with those bits, they would come afterwards) */ count->argKind = akRemFeature(count->argKind, akbRequest|akbReply); } } static void rtAddCountInOutArg(argument_t *arg) { argument_t *count; /* * The user sees a single count variable. However, to get the * count passed from user to server for variable-sized inline OUT * arrays, we need two count arguments internally. This is * because the count value lives in different message fields (and * is scaled differently) in the request and reply messages. * * The two variables have the same name to simplify code generation. * * This variable has a null argParent field because it has akbRequest. * For example, see rtCheckVariable. */ count = argAlloc(); count->argName = strconcat(arg->argName, "Cnt"); count->argType = itMakeCountType(); count->argParent = argNULL; count->argNext = arg->argNext; arg->argNext = count; (count->argCInOut = arg->argCount)->argCInOut = count; count->argKind = akCountInOut; } static void rtAddPolyArg(argument_t *arg) { ipc_type_t *it = arg->argType; argument_t *poly; arg_kind_t akbsend, akbreturn; poly = argAlloc(); poly->argName = strconcat(arg->argName, "Poly"); poly->argType = itMakePolyType(); poly->argParent = arg; poly->argNext = arg->argNext; arg->argNext = poly; arg->argPoly = poly; /* * akbsend is bits added if the arg is In; * akbreturn is bits added if the arg is Out. * The mysterious business with KernelServer subsystems: * when packing Out arguments, they use OutNames instead * of InNames, and the OutName determines if they are poly-in * as well as poly-out. */ akbsend = akbSend; akbreturn = akbReturn; if (it->itInName == MACH_MSG_TYPE_POLYMORPHIC) { akbsend |= akbUserArg|akbSendSnd; if (!IsKernelServer) akbreturn |= akbServerArg|akbReturnSnd; } if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC) { akbsend |= akbServerArg|akbSendRcv; akbreturn |= akbUserArg|akbReturnRcv; if (IsKernelServer) akbreturn |= akbServerArg|akbReturnSnd; } poly->argKind = akPoly; if (akCheck(arg->argKind, akbSend)) poly->argKind = akAddFeature(poly->argKind, akCheck(arg->argKind, akbsend)); if (akCheck(arg->argKind, akbReturn)) poly->argKind = akAddFeature(poly->argKind, akCheck(arg->argKind, akbreturn)); } static void rtAddDeallocArg(argument_t *arg) { argument_t *dealloc; dealloc = argAlloc(); dealloc->argName = strconcat(arg->argName, "Dealloc"); dealloc->argType = itMakeDeallocType(); dealloc->argParent = arg; dealloc->argNext = arg->argNext; arg->argNext = dealloc; arg->argDealloc = dealloc; /* * Dealloc flag can only be associated to KPDs. */ dealloc->argKind = akeDealloc; if (akCheck(arg->argKind, akbSend)) dealloc->argKind = akAddFeature(dealloc->argKind, akCheck(arg->argKind, akbUserArg|akbSend|akbSendSnd)); if (akCheck(arg->argKind, akbReturn)) { dealloc->argKind = akAddFeature(dealloc->argKind, akCheck(arg->argKind, akbServerArg|akbReturn|akbReturnSnd)); dealloc->argByReferenceServer = TRUE; } } static void rtCheckRoutineArgs(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; rtDefaultArgKind(rt, arg); rtCheckRoutineArg(rt, arg); /* need to set argTTName before adding implicit args */ rtSetArgDefaults(rt, arg); /* the arg may not have a type (if there was some error in parsing it), in which case we don't want to do these steps. */ if (it != itNULL) { arg->argFlags = rtProcessDeallocFlag(it, arg->argFlags, arg->argKind, &arg->argDeallocate, arg->argVarName); arg->argFlags = rtProcessCountInOutFlag(it, arg->argFlags, arg->argKind, &arg->argCountInOut, arg->argVarName); rtProcessSameCountFlag(arg); arg->argFlags = rtProcessPhysicalCopyFlag(it, arg->argFlags, arg->argKind, arg->argVarName); rtProcessRetCodeFlag(arg); arg->argFlags = rtProcessOverwriteFlag(it, arg->argFlags, arg->argKind, arg->argVarName); rtAugmentArgKind(arg); /* args added here will get processed in later iterations */ /* order of args is 'arg poly countinout count dealloc' */ if (arg->argDeallocate == d_MAYBE) rtAddDeallocArg(arg); if (it->itVarArray || (IS_MULTIPLE_KPD(it) && it->itElement->itVarArray)) rtAddCountArg(arg); if (arg->argCountInOut) rtAddCountInOutArg(arg); if ((it->itInName == MACH_MSG_TYPE_POLYMORPHIC) || (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC)) rtAddPolyArg(arg); /* * Detects whether the arg has to become part of the * Kernel Processed Data section; if yes, define the proper * itUserKPDType, itServerKPDType */ rtDetectKPDArg(arg); } } } boolean_t rtCheckTrailerType(argument_t *arg) { if (akIdent(arg->argKind) == akeSecToken || akIdent(arg->argKind) == akeAuditToken || akIdent(arg->argKind) == akeContextToken ) itCheckTokenType(arg->argVarName, arg->argType); if (akIdent(arg->argKind) == akeMsgSeqno) itCheckIntType(arg->argVarName, arg->argType); /* * if the built-in are not used, we cannot match * the type/size of the desciption provided by the user * with the one defined in message.h. */ return TRUE; } static void rtCheckArgTypes(routine_t *rt) { if (rt->rtRequestPort == argNULL) error("%s %s doesn't have a server port argument", rtRoutineKindToStr(rt->rtKind), rt->rtName); if ((rt->rtRequestPort != argNULL) && (rt->rtRequestPort->argType != itNULL)) itCheckRequestPortType(rt->rtRequestPort->argName, rt->rtRequestPort->argType); if ((rt->rtReplyPort != argNULL) && (rt->rtReplyPort->argType != itNULL)) itCheckReplyPortType(rt->rtReplyPort->argName, rt->rtReplyPort->argType); if ((rt->rtWaitTime != argNULL) && (rt->rtWaitTime->argType != itNULL)) itCheckIntType(rt->rtWaitTime->argName, rt->rtWaitTime->argType); if ((rt->rtMsgOption != argNULL) && (rt->rtMsgOption->argType != itNULL)) itCheckIntType(rt->rtMsgOption->argName, rt->rtMsgOption->argType); if ((IsKernelServer && rt->rtServerImpl) || (IsKernelUser && rt->rtUserImpl)) fatal("Implicit data is not supported in the KernelUser and KernelServer modes"); /* rtCheckTrailerType will hit a fatal() if something goes wrong */ if (rt->rtServerImpl) rtCheckMaskFunction(rt->rtArgs, akbServerImplicit, rtCheckTrailerType); if (rt->rtUserImpl) rtCheckMaskFunction(rt->rtArgs, akbUserImplicit, rtCheckTrailerType); } /* * Check for arguments which are missing seemingly needed functions. * We make this check here instead of in itCheckDecl, because here * we can take into account what kind of argument the type is * being used with. * * These are warnings, not hard errors, because mig will generate * reasonable code in any case. The generated code will work fine * if the ServerType and TransType are really the same, even though * they have different names. */ static void rtCheckArgTrans(routine_t *rt) { argument_t *arg; /* the arg may not have a type (if there was some error in parsing it) */ for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; if ((it != itNULL) && !streql(it->itServerType, it->itTransType)) { if (akCheck(arg->argKind, akbSendRcv) && (it->itInTrans == strNULL)) warn("%s: argument has no in-translation function", arg->argName); if (akCheck(arg->argKind, akbReturnSnd) && (it->itOutTrans == strNULL)) warn("%s: argument has no out-translation function", arg->argName); } } } /* * Adds an implicit return-code argument. It exists in the reply message, * where it is the first piece of data (After the NDR format label).. */ static void rtAddRetCode(routine_t *rt) { argument_t *arg = argAlloc(); arg->argName = "RetCode"; arg->argType = itRetCodeType; arg->argKind = akRetCode; rt->rtRetCode = arg; arg->argNext = rt->rtArgs; rt->rtArgs = arg; } /* * Process the Return Code. * The MIG protocol says that RetCode != 0 are only sent through * mig_reply_error_t structures. Therefore, there is no need * for reserving a RetCode in a complex Reply message. */ static void rtProcessRetCode(routine_t *rt) { if (!rt->rtOneWay && !rt->rtSimpleReply) { argument_t *arg = rt->rtRetCode; arg->argKind = akRemFeature(arg->argKind, akbReply); /* we want the RetCode to be a local variable instead */ arg->argKind = akAddFeature(arg->argKind, akbVarNeeded); } if (rt->rtRetCArg != argNULL && !rt->rtSimpleRequest) { argument_t *arg = rt->rtRetCArg; arg->argKind = akeRetCode|akbUserArg|akbServerArg|akbSendRcv; } } /* * Adds an implicit NDR argument. It exists in the reply message, * where it is the first piece of data. */ static void rtAddNdrCode(routine_t *rt) { argument_t *arg = argAlloc(); arg->argName = "NDR_record"; arg->argType = itNdrCodeType; arg->argKind = akeNdrCode; rt->rtNdrCode = arg; /* add at beginning, so ndr-code is first in the reply message */ arg->argNext = rt->rtArgs; rt->rtArgs = arg; } /* * Process the NDR Code. * We stick a NDR format label iff there is untyped data */ static void rtProcessNdrCode(routine_t *rt) { argument_t *ndr = rt->rtNdrCode; argument_t *arg; boolean_t found; /* akbSendSnd|akbSendBody initialize the NDR format label */ #define ndr_send akbRequest|akbSend|akbSendSnd|akbSendBody /* akbReplyInit initializes the NDR format label */ #define ndr_rcv akbReply|akbReplyInit|akbReturn|akbReturnBody ndr->argKind = akAddFeature(ndr->argKind, ndr_send|ndr_rcv); for (found = FALSE, arg = ndr->argNext; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, akbSendRcv|akbSendBody) && !akCheck(arg->argKind, akbServerImplicit) && !arg->argType->itPortType && (!arg->argParent || akIdent(arg->argKind) == akeCount || akIdent(arg->argKind) == akeCountInOut)) { arg->argKind = akAddFeature(arg->argKind, akbSendNdr); found = TRUE; } if (!found) ndr->argKind = akRemFeature(ndr->argKind, ndr_send); found = FALSE; if (!rt->rtOneWay) for (arg = ndr->argNext; arg != argNULL; arg = arg->argNext) if ((arg == rt->rtRetCode && akCheck(arg->argKind, akbReply)) || (arg != rt->rtRetCode && akCheck(arg->argKind, akbReturnRcv|akbReturnBody) && !akCheck(arg->argKind, akbUserImplicit) && !arg->argType->itPortType && (!arg->argParent || akIdent(arg->argKind) == akeCount || akIdent(arg->argKind) == akeCountInOut))) { arg->argKind = akAddFeature(arg->argKind, akbReturnNdr); found = TRUE; } if (!found && !akCheck(rt->rtRetCode->argKind, akbReply)) ndr->argKind = akRemFeature(ndr->argKind, ndr_rcv); } /* * Adds a dummy WaitTime argument to the function. * This argument doesn't show up in any C argument lists; * it implements the global WaitTime statement. */ static void rtAddWaitTime(routine_t *rt, identifier_t name, arg_kind_t kind) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy WaitTime arg"; arg->argVarName = name; arg->argType = itWaitTimeType; arg->argKind = kind; rt->rtWaitTime = arg; /* add wait-time after msg-option, if possible */ if (rt->rtMsgOption != argNULL) loc = &rt->rtMsgOption->argNext; else loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * Adds a dummy MsgOption argument to the function. * This argument doesn't show up in any C argument lists; * it implements the global MsgOption statement. */ static void rtAddMsgOption(routine_t *rt, identifier_t name) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy MsgOption arg"; arg->argVarName = name; arg->argType = itMsgOptionType; arg->argKind = akeMsgOption; rt->rtMsgOption = arg; /* add msg-option after msg-seqno */ loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * Process the MsgOption Code. * We must add the information to post a receive with the right * Trailer options. */ static void rtProcessMsgOption(routine_t *rt) { argument_t *msgop = rt->rtMsgOption; argument_t *arg; boolean_t sectoken = FALSE; boolean_t audittoken = FALSE; boolean_t contexttoken = FALSE; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, akbReturn|akbUserImplicit)) { if (akIdent(arg->argKind) == akeSecToken) sectoken = TRUE; else if (akIdent(arg->argKind) == akeAuditToken) audittoken = TRUE; else if (akIdent(arg->argKind) == akeContextToken) contexttoken = TRUE; } if (contexttoken == TRUE) msgop->argVarName = strconcat(msgop->argVarName, "|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX)"); else if (audittoken == TRUE) msgop->argVarName = strconcat(msgop->argVarName, "|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)"); else if (sectoken == TRUE) msgop->argVarName = strconcat(msgop->argVarName, "|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)"); /* other implicit data received by the user will be handled here */ } static void rtProcessUseSpecialReplyPort(routine_t *rt) { if (IsKernelUser || IsKernelServer) { fatal("UseSpecialReplyPort option cannot be used with KernelUser / KernelServer\n"); } rt->rtMsgOption->argVarName = strconcat(rt->rtMsgOption->argVarName, "|__MigSpecialReplyPortMsgOption"); } /* * Adds a dummy reply port argument to the function. */ static void rtAddDummyReplyPort(routine_t *rt, ipc_type_t *type) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy ReplyPort arg"; arg->argVarName = "dummy ReplyPort arg"; arg->argType = type; arg->argKind = akeReplyPort; rt->rtReplyPort = arg; /* add the reply port after the request port */ if (rt->rtRequestPort != argNULL) loc = &rt->rtRequestPort->argNext; else loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * At least one overwrite keyword has been detected: * we tag all the OOL entries (ports + data) with * akbOverwrite which will tell us that we have to * fill a KPD entry in the message-template */ static void rtCheckOverwrite(routine_t *rt) { argument_t *arg; int howmany = rt->rtOverwrite; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; if (akCheck(arg->argKind, akbReturnKPD) && !it->itInLine) { /* among OUT args, we want OOL, OOL ports and MigInLine */ arg->argKind = akAddFeature(arg->argKind, akbOverwrite); if (arg->argFlags & flOverwrite) howmany--; if (!howmany) return; } } } /* * Initializes argRequestPos, argReplyPos, rtMaxRequestPos, rtMaxReplyPos, * rtNumRequestVar, rtNumReplyVar, and adds akbVarNeeded to those arguments * that need it because of variable-sized inline considerations. * * argRequestPos and argReplyPos get -1 if the value shouldn't be used. */ static void rtCheckVariable(routine_t *rt) { argument_t *arg; int NumRequestVar = 0; int NumReplyVar = 0; int MaxRequestPos = 0; int MaxReplyPos = 0; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { argument_t *parent = arg->argParent; /* * We skip KPDs. We have to make sure that the KPDs count * present in the message body follow the RequestPos/ReplyPos logic * The rest of the parameters are defaulted to have * Arg{Request, Reply}Pos = 0 */ if (parent == argNULL || akCheck(parent->argKind, akbSendKPD|akbReturnKPD)) { if (akCheckAll(arg->argKind, akbSend|akbSendBody)) { arg->argRequestPos = NumRequestVar; MaxRequestPos = NumRequestVar; if (akCheck(arg->argKind, akbVariable)) NumRequestVar++; } if (akCheckAll(arg->argKind, akbReturn|akbReturnBody)) { arg->argReplyPos = NumReplyVar; MaxReplyPos = NumReplyVar; if (akCheck(arg->argKind, akbVariable)) NumReplyVar++; } } else { arg->argRequestPos = parent->argRequestPos; arg->argReplyPos = parent->argReplyPos; } /* printf("Var %s Kind %x RequestPos %d\n", arg->argVarName, arg->argKind, arg->argRequestPos); printf("* Var %s Kind %x ReplyPos %d\n", arg->argVarName, arg->argKind, arg->argReplyPos); */ /* Out variables that follow a variable-sized field need VarNeeded or ReplyCopy; they can't be stored directly into the reply message. */ if (akCheckAll(arg->argKind, akbReturnSnd|akbReturnBody) && !akCheck(arg->argKind, akbReplyCopy|akbVarNeeded) && (arg->argReplyPos > 0)) arg->argKind = akAddFeature(arg->argKind, akbVarNeeded); } rt->rtNumRequestVar = NumRequestVar; rt->rtNumReplyVar = NumReplyVar; rt->rtMaxRequestPos = MaxRequestPos; rt->rtMaxReplyPos = MaxReplyPos; } /* * Adds akbDestroy where needed. */ static void rtCheckDestroy(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; if(akCheck(arg->argKind, akbSendRcv) && !akCheck(arg->argKind, akbReturnSnd) && (it->itDestructor != strNULL || IS_MIG_INLINE_EMUL(it))) { arg->argKind = akAddFeature(arg->argKind, akbDestroy); } if (argIsIn(arg) && akCheck(arg->argKind, akbSendKPD|akbReturnKPD) && arg->argKPD_Type == MACH_MSG_OOL_DESCRIPTOR && (arg->argFlags & flAuto)) arg->argKind = akAddFeature(arg->argKind, akbDestroy); } } /* * Sets ByReferenceUser and ByReferenceServer. */ static void rtAddByReference(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { ipc_type_t *it = arg->argType; if (akCheck(arg->argKind, akbReturnRcv) && it->itStruct) { arg->argByReferenceUser = TRUE; /* * A CountInOut arg itself is not akbReturnRcv, * so we need to set argByReferenceUser specially. */ if (arg->argCInOut != argNULL) arg->argCInOut->argByReferenceUser = TRUE; } if ((akCheck(arg->argKind, akbReturnSnd) || (akCheck(arg->argKind, akbServerImplicit) && akCheck(arg->argKind, akbReturnRcv) && akCheck(arg->argKind, akbSendRcv))) && it->itStruct) { arg->argByReferenceServer = TRUE; } } } /* * This procedure can be executed only when all the akb* and ake* have * been set properly (when rtAddCountArg is executed, akbVarNeeded * might not be set yet - see rtCheckVariable) */ void rtAddSameCount(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) if (arg->argFlags & flSameCount) { ipc_type_t *it = arg->argType; argument_t *tmp_count; argument_t *my_count = arg->argCount; argument_t *ref_count = arg->argSameCount->argCount; tmp_count = argAlloc(); *tmp_count = *ref_count; /* * if our count is a akbVarNeeded, we need to copy this * attribute to the master count! */ tmp_count->argKind = akeSameCount; ref_count->argKind = akAddFeature(ref_count->argKind, akCheck(my_count->argKind, akbVarNeeded)); tmp_count->argKind = akAddFeature(tmp_count->argKind, akCheck(my_count->argKind, akbVarNeeded)); tmp_count->argNext = my_count->argNext; tmp_count->argMultiplier = my_count->argMultiplier; tmp_count->argType = my_count->argType; tmp_count->argParent = arg; /* we don't need more */ arg->argCount = tmp_count; arg->argNext = tmp_count; /* for these args, Cnt is not akbRequest, and therefore size is embedded */ if (IS_VARIABLE_SIZED_UNTYPED(it)) it->itMinTypeSize = 0; tmp_count->argType->itMinTypeSize = 0; tmp_count->argType->itTypeSize = 0; } } void rtCheckRoutine(routine_t *rt) { /* Initialize random fields. */ rt->rtErrorName = ErrorProc; rt->rtOneWay = (rt->rtKind == rkSimpleRoutine); rt->rtServerName = strconcat(ServerPrefix, rt->rtName); rt->rtUserName = strconcat(UserPrefix, rt->rtName); rt->rtUseSpecialReplyPort = UseSpecialReplyPort && !rt->rtOneWay; rt->rtConsumeOnSendError = ConsumeOnSendError; /* Add implicit arguments. */ rtAddRetCode(rt); rtAddNdrCode(rt); /* Check out the arguments and their types. Add count, poly implicit args. Any arguments added after rtCheckRoutineArgs should have rtSetArgDefaults called on them. */ rtCheckRoutineArgs(rt); /* Add dummy WaitTime and MsgOption arguments, if the routine doesn't have its own args and the user specified global values. */ if (rt->rtReplyPort == argNULL) { if (rt->rtOneWay) rtAddDummyReplyPort(rt, itZeroReplyPortType); else rtAddDummyReplyPort(rt, itRealReplyPortType); } else if (akCheck(rt->rtReplyPort->argKind, akbUserArg)) { /* If an explicit ReplyPort is used, we can't assume it will be the SRP */ rt->rtUseSpecialReplyPort = FALSE; } if (rt->rtMsgOption == argNULL) { if (MsgOption == strNULL) rtAddMsgOption(rt, "MACH_MSG_OPTION_NONE"); else rtAddMsgOption(rt, MsgOption); } if (rt->rtWaitTime == argNULL) { if (WaitTime != strNULL) rtAddWaitTime(rt, WaitTime, akeWaitTime); else if (SendTime != strNULL) rtAddWaitTime(rt, SendTime, akeSendTime); } if (rt->rtWaitTime && rt->rtConsumeOnSendError == ConsumeOnSendErrorNone) { warn("%s %s specifies a SendTime/WaitTime which may leak resources, " "adopt \"ConsumeOnSendError Timeout\"", rtRoutineKindToStr(rt->rtKind), rt->rtName); } /* Now that all the arguments are in place, do more checking. */ rtCheckArgTypes(rt); rtCheckArgTrans(rt); if (rt->rtOneWay) { if (rtCheckMask(rt->rtArgs, akbReturn) || rt->rtUserImpl) error("%s %s has OUT argument", rtRoutineKindToStr(rt->rtKind), rt->rtName); } /* If there were any errors, don't bother calculating more info that is only used in code generation anyway. Therefore, the following functions don't have to worry about null types. */ if (mig_errors > 0) fatal("%d errors found. Abort.\n", mig_errors); rt->rtServerImpl = rtCountMask(rt->rtArgs, akbServerImplicit); rt->rtUserImpl = rtCountMask(rt->rtArgs, akbUserImplicit); /* * ASSUMPTION: * kernel cannot change a message from simple to complex, * therefore SimpleSendReply and SimpleRcvReply become SimpleReply */ rtCheckSimple(rt->rtArgs, akbRequest, &rt->rtSimpleRequest); rtCheckSimple(rt->rtArgs, akbReply, &rt->rtSimpleReply); rt->rtRequestKPDs = rtCountKPDs(rt->rtArgs, akbSendKPD); rt->rtReplyKPDs = rtCountKPDs(rt->rtArgs, akbReturnKPD); /* * Determine how many overwrite parameters we have: * # of Overwrite args -> rt->rtOverwrite * flOverwrite -> the arg has to be overwritten * akbOverwrite -> the arg has to be declared in the message-template * (only as a placeholder if !flOverwrite). */ if ((rt->rtOverwrite = rtCountFlags(rt->rtArgs, flOverwrite))) { rtCheckOverwrite(rt); rt->rtOverwriteKPDs = rtCountKPDs(rt->rtArgs, akbReturnKPD|akbOverwrite); if (IsKernelUser) fatal("Overwrite option(s) do not match with the KernelUser personality\n"); } rtCheckFit(rt, akbRequest, &rt->rtRequestFits, &rt->rtRequestUsedLimit, &rt->rtRequestSizeKnown); rtCheckFit(rt, akbReply, &rt->rtReplyFits, &rt->rtReplyUsedLimit, &rt->rtReplySizeKnown); rtCheckVariable(rt); rtCheckDestroy(rt); rtAddByReference(rt); rtSuffixExtArg(rt->rtArgs); rtAddSameCount(rt); rtProcessRetCode(rt); rtProcessNdrCode(rt); if (rt->rtUserImpl) rtProcessMsgOption(rt); if (rt->rtUseSpecialReplyPort) rtProcessUseSpecialReplyPort(rt); rt->rtNoReplyArgs = !rtCheckMask(rt->rtArgs, akbReturnSnd); if (UseEventLogger) /* some more info's are needed for Event logging/Stats */ rtFindHowMany(rt); } void rtMinRequestSize(FILE *file, routine_t *rt, char *str) { fprintf(file, "(mach_msg_size_t)(sizeof(%s)", str); rtSizeDelta(file, akbRequest, rt); fprintf(file, ")"); } void rtMinReplySize(FILE *file, routine_t *rt, char *str) { fprintf(file, "(mach_msg_size_t)(sizeof(%s)", str); rtSizeDelta(file, akbReply, rt); fprintf(file, ")"); } static void rtSizeDelta(FILE *file, u_int mask, routine_t *rt) { argument_t *arg; u_int min_size = sizeof(mach_msg_header_t); u_int max_size; boolean_t output = FALSE; if (!rt->rtSimpleRequest) machine_alignment(min_size, sizeof(mach_msg_body_t)); max_size = min_size; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { ipc_type_t *it = arg->argType; machine_alignment(min_size, it->itMinTypeSize); machine_alignment(max_size, it->itMinTypeSize); if (IS_VARIABLE_SIZED_UNTYPED(it)) { machine_alignment(max_size, it->itTypeSize); max_size += it->itPadSize; } if (IS_OPTIONAL_NATIVE(it)) { if (output) fprintf(file, " + "); else { output = TRUE; fprintf(file, " - ("); } fprintf(file, "_WALIGNSZ_(%s)", it->itUserType); } } if (min_size != max_size) { if (output) fprintf(file, " + "); else fprintf(file, " - "); fprintf(file, "%d", max_size - min_size); } if (output) fprintf(file, ")"); }