summaryrefslogtreecommitdiffstats
path: root/bootstrap_cmds/migcom.tproj/routine.c
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /bootstrap_cmds/migcom.tproj/routine.c
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'bootstrap_cmds/migcom.tproj/routine.c')
-rw-r--r--bootstrap_cmds/migcom.tproj/routine.c1844
1 files changed, 1844 insertions, 0 deletions
diff --git a/bootstrap_cmds/migcom.tproj/routine.c b/bootstrap_cmds/migcom.tproj/routine.c
new file mode 100644
index 0000000..f4c0778
--- /dev/null
+++ b/bootstrap_cmds/migcom.tproj/routine.c
@@ -0,0 +1,1844 @@
+/*
+ * 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 <mach/message.h>
+#include <mach/kern_return.h>
+#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, ")");
+}
+