darling-bootstrap_cmds/migcom.tproj/routine.c
2023-01-28 13:25:04 -08:00

1845 lines
52 KiB
C

/*
* 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, ")");
}