/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//* Includes and Typedefs
#include "Tpm.h"
#include "Marshal.h"

#if TABLE_DRIVEN_DISPATCH

typedef TPM_RC(NoFlagFunction)(void *target, BYTE **buffer, INT32 *size);
typedef TPM_RC(FlagFunction)(void *target, BYTE **buffer, INT32 *size, BOOL flag);

typedef FlagFunction *UNMARSHAL_t;

typedef INT16(MarshalFunction)(void *source, BYTE **buffer, INT32 *size);
typedef MarshalFunction *MARSHAL_t;

typedef TPM_RC(COMMAND_NO_ARGS)(void);
typedef TPM_RC(COMMAND_IN_ARG)(void *in);
typedef TPM_RC(COMMAND_OUT_ARG)(void *out);
typedef TPM_RC(COMMAND_INOUT_ARG)(void *in, void *out);

typedef union COMMAND_t
{
    COMMAND_NO_ARGS         *noArgs;
    COMMAND_IN_ARG          *inArg;
    COMMAND_OUT_ARG         *outArg;
    COMMAND_INOUT_ARG       *inOutArg;
} COMMAND_t;

// This structure is used by ParseHandleBuffer() and CommandDispatcher(). The
// parameters in this structure are unique for each command. The parameters are:
// command      holds the address of the command processing function that is called
//              by Command Dispatcher
// inSize       This is the size of the command-dependent input structure. The
//              input structure holds the unmarshaled handles and command
//              parameters. If the command takes no arguments (handles or
//              parameters) then inSize will have a value of 0.
// outSize      This is the size of the command-dependent output structure. The
//              output structure holds the results of the command in an unmarshaled
//              form. When command processing is completed, these values are
//              marshaled into the output buffer. It is always the case that the
//              unmarshaled version of an output structure is larger then the
//              marshaled version. This is because the marshaled version contains
//              the exact same number of significant bytes but with padding removed.
// typesOffsets    This parameter points to the list of data types that are to be
//              marshaled or unmarshaled. The list of types follows the 'offsets'
//              array. The offsets array is variable sized so the typesOffset filed
//              is necessary for the handle and command processing to be able to
//              find the types that are being handled. The 'offsets' array may be
//              empty. The 'types' structure is described below.
// offsets      This is an array of offsets of each of the parameters in the
//              command or response. When processing the command parameters (not
//              handles) the list contains the offset of the next parameter. For
//              example, if the first command parameter has a size of 4 and there is
//              a second command parameter, then the offset would be 4, indicating
//              that the second parameter starts at 4. If the second parameter has
//              a size of 8, and there is a third parameter, then the second entry
//              in offsets is 12 (4 for the first parameter and 8 for the second).
//              An offset value of 0 in the list indicates the start of the response
//              parameter list. When CommandDispatcher hits this value, it will stop
//              unmarshaling the parameters and call 'command'. If a command has no
//              response parameters and only one command parameter, then offsets can
//              be an empty list.

typedef struct COMMAND_DESCRIPTOR_t
{
    COMMAND_t       command;        // Address of the command
    UINT16          inSize;         // Maximum size of the input structure
    UINT16          outSize;        // Maximum size of the output structure
    UINT16          typesOffset;    // address of the types field
    UINT16          offsets[1];
} COMMAND_DESCRIPTOR_t;

// The 'types' list is an encoded byte array. The byte value has two parts. The most
// significant bit is used when a parameter takes a flag and indicates if the flag
// should be SET or not. The remaining 7 bits are an index into an array of
// addresses of marshaling and unmarshaling functions.
// The array of functions is divided into 6 sections with a value assigned
// to denote the start of that section (and the end of the previous section). The
// defined offset values for each section are:
// 0                                unmarshaling for handles that do not take flags
// HANDLE_FIRST_FLAG_TYPE           unmarshaling for handles that take flags
// PARAMETER_FIRST_TYPE             unmarshaling for parameters that do not take flags
// PARAMETER_FIRST_FLAG_TYPE        unmarshaling for parameters that take flags
// PARAMETER_LAST_TYPE + 1          marshaling for handles
// RESPONSE_PARAMETER_FIRST_TYPE    marshaling for parameters
// RESPONSE_PARAMETER_LAST_TYPE     is the last value in the list of marshaling and
//                                  unmarshaling functions.
//
// The types list is constructed with a byte of 0xff at the end of the command
// parameters and with an 0xff at the end of the response parameters.

#if COMPRESSED_LISTS
#   define PAD_LIST 0
#else
#   define PAD_LIST 1
#endif
#define _COMMAND_TABLE_DISPATCH_
#include "CommandDispatchData.h"

#define TEST_COMMAND    TPM_CC_Startup

#define NEW_CC

#else

#include "Commands.h"

#endif

//* Marshal/Unmarshal Functions

//** ParseHandleBuffer()
// This is the table-driven version of the handle buffer unmarshaling code
TPM_RC
ParseHandleBuffer(
    COMMAND                 *command
    )
{
    TPM_RC                   result;
#if TABLE_DRIVEN_DISPATCH
    COMMAND_DESCRIPTOR_t    *desc;
    BYTE                    *types;
    BYTE                     type;
    BYTE                     dType;

    // Make sure that nothing strange has happened
    pAssert(command->index
            < sizeof(s_CommandDataArray) / sizeof(COMMAND_DESCRIPTOR_t *));
    // Get the address of the descriptor for this command
    desc = s_CommandDataArray[command->index];

    pAssert(desc != NULL);
    // Get the associated list of unmarshaling data types.
    types = &((BYTE *)desc)[desc->typesOffset];

//    if(s_ccAttr[commandIndex].commandIndex == TEST_COMMAND)
//        commandIndex = commandIndex;
    // No handles yet
    command->handleNum = 0;

    // Get the first type value
    for(type = *types++;
        // check each byte to make sure that we have not hit the start
        // of the parameters
    (dType = (type & 0x7F)) < PARAMETER_FIRST_TYPE;
    // get the next type
        type = *types++)
    {
#if TABLE_DRIVEN_MARSHAL
        marshalIndex_t      index;
        index = unmarshalArray[dType] | ((type & 0x80) ? NULL_FLAG : 0);
        result = Unmarshal(index, &(command->handles[command->handleNum]),
                           &command->parameterBuffer, &command->parameterSize);

#else
        // See if unmarshaling of this handle type requires a flag
        if(dType < HANDLE_FIRST_FLAG_TYPE)
        {
            // Look up the function to do the unmarshaling
            NoFlagFunction  *f = (NoFlagFunction *)unmarshalArray[dType];
            // call it
            result = f(&(command->handles[command->handleNum]),
                       &command->parameterBuffer,
                       &command->parameterSize);
        }
        else
        {
            //  Look up the function
            FlagFunction    *f = unmarshalArray[dType];

            // Call it setting the flag to the appropriate value
            result = f(&(command->handles[command->handleNum]),
                       &command->parameterBuffer,
                       &command->parameterSize, (type & 0x80) != 0);
        }
#endif

        // Got a handle
        // We do this first so that the match for the handle offset of the
        // response code works correctly.
        command->handleNum += 1;
        if(result != TPM_RC_SUCCESS)
            // if the unmarshaling failed, return the response code with the
            // handle indication set
            return result + TPM_RC_H + (command->handleNum * TPM_RC_1);
    }
#else
    BYTE            **handleBufferStart = &command->parameterBuffer;
    INT32           *bufferRemainingSize = &command->parameterSize;
    TPM_HANDLE      *handles = &command->handles[0];
    UINT32          *handleCount = &command->handleNum;
    *handleCount = 0;
    switch(command->code)
    {
#include "HandleProcess.h"
#undef handles
        default:
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }
#endif
    return TPM_RC_SUCCESS;
}

//** CommandDispatcher()
// Function to unmarshal the command parameters, call the selected action code, and
// marshal the response parameters.
TPM_RC
CommandDispatcher(
    COMMAND                 *command
    )
{
#if !TABLE_DRIVEN_DISPATCH
    TPM_RC       result;
    BYTE        **paramBuffer = &command->parameterBuffer;
    INT32       *paramBufferSize = &command->parameterSize;
    BYTE        **responseBuffer = &command->responseBuffer;
    INT32       *respParmSize = &command->parameterSize;
    INT32        rSize;
    TPM_HANDLE  *handles = &command->handles[0];
//
    command->handleNum = 0;                 // The command-specific code knows how
                                            // many handles there are. This is for
                                            // cataloging the number of response
                                            // handles
    MemoryIoBufferAllocationReset();        // Initialize so that allocation will
                                            // work properly
    switch(GetCommandCode(command->index))
    {
#include "CommandDispatcher.h"

        default:
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }
Exit:
    MemoryIoBufferZero();
    return result;
#else
    COMMAND_DESCRIPTOR_t    *desc;
    BYTE                    *types;
    BYTE                     type;
    UINT16                  *offsets;
    UINT16                   offset = 0;
    UINT32                   maxInSize;
    BYTE                    *commandIn;
    INT32                    maxOutSize;
    BYTE                    *commandOut;
    COMMAND_t                cmd;
    TPM_HANDLE              *handles;
    UINT32                   hasInParameters = 0;
    BOOL                     hasOutParameters = FALSE;
    UINT32                   pNum = 0;
    BYTE                     dType;     // dispatch type
    TPM_RC                   result;
//
    // Get the address of the descriptor for this command
    pAssert(command->index
            < sizeof(s_CommandDataArray) / sizeof(COMMAND_DESCRIPTOR_t *));
    desc = s_CommandDataArray[command->index];

    // Get the list of parameter types for this command
    pAssert(desc != NULL);
    types = &((BYTE *)desc)[desc->typesOffset];

    // Get a pointer to the list of parameter offsets
    offsets = &desc->offsets[0];
    // pointer to handles
    handles = command->handles;

    // Get the size required to hold all the unmarshaled parameters for this command
    maxInSize = desc->inSize;
    // and the size of the output parameter structure returned by this command
    maxOutSize = desc->outSize;

    MemoryIoBufferAllocationReset();
    // Get a buffer for the input parameters
    commandIn = MemoryGetInBuffer(maxInSize);
    // And the output parameters
    commandOut = (BYTE *)MemoryGetOutBuffer((UINT32)maxOutSize);

    // Get the address of the action code dispatch
    cmd = desc->command;

    // Copy any handles into the input buffer
    for(type = *types++; (type & 0x7F) < PARAMETER_FIRST_TYPE; type = *types++)
    {
        // 'offset' was initialized to zero so the first unmarshaling will always
        // be to the start of the data structure
        *(TPM_HANDLE *)&(commandIn[offset]) = *handles++;
        // This check is used so that we don't have to add an additional offset
        // value to the offsets list to correspond to the stop value in the
        // command parameter list.
        if(*types != 0xFF)
            offset = *offsets++;
//        maxInSize -= sizeof(TPM_HANDLE);
        hasInParameters++;
    }
    // Exit loop with type containing the last value read from types
    // maxInSize has the amount of space remaining in the command action input
    // buffer. Make sure that we don't have more data to unmarshal than is going to
    // fit.

    // type contains the last value read from types so it is not necessary to
    // reload it, which is good because *types now points to the next value
    for(; (dType = (type & 0x7F)) <= PARAMETER_LAST_TYPE; type = *types++)
    {
        pNum++;
#if TABLE_DRIVEN_MARSHAL
        {
            marshalIndex_t      index = unmarshalArray[dType];
            index |= (type & 0x80) ? NULL_FLAG : 0;
            result = Unmarshal(index, &commandIn[offset], &command->parameterBuffer, 
                               &command->parameterSize);
        }
#else
        if(dType < PARAMETER_FIRST_FLAG_TYPE)
        {
            NoFlagFunction      *f = (NoFlagFunction *)unmarshalArray[dType];
            result = f(&commandIn[offset], &command->parameterBuffer,
                       &command->parameterSize);
        }
        else
        {
            FlagFunction        *f = unmarshalArray[dType];
            result = f(&commandIn[offset], &command->parameterBuffer,
                       &command->parameterSize,
                       (type & 0x80) != 0);
        }
#endif
        if(result != TPM_RC_SUCCESS)
        {
            result += TPM_RC_P + (TPM_RC_1 * pNum);
            goto Exit;
        }
        // This check is used so that we don't have to add an additional offset
        // value to the offsets list to correspond to the stop value in the
        // command parameter list.
        if(*types != 0xFF)
            offset = *offsets++;
        hasInParameters++;
    }
    // Should have used all the bytes in the input
    if(command->parameterSize != 0)
    {
        result = TPM_RC_SIZE;
        goto Exit;
    }

    // The command parameter unmarshaling stopped when it hit a value that was out
    // of range for unmarshaling values and left *types pointing to the first
    // marshaling type. If that type happens to be the STOP value, then there
    // are no response parameters. So, set the flag to indicate if there are
    // output parameters.
    hasOutParameters = *types != 0xFF;

    // There are four cases for calling, with and without input parameters and with
    // and without output parameters.
    if(hasInParameters > 0)
    {
        if(hasOutParameters)
            result = cmd.inOutArg(commandIn, commandOut);
        else
            result = cmd.inArg(commandIn);
    }
    else
    {
        if(hasOutParameters)
            result = cmd.outArg(commandOut);
        else
            result = cmd.noArgs();
    }
    if(result != TPM_RC_SUCCESS)
       goto Exit;

    // Offset in the marshaled output structure
    offset = 0;

    // Process the return handles, if any
    command->handleNum = 0;

    // Could make this a loop to process output handles but there is only ever
    // one handle in the outputs (for now).
    type = *types++;
    if((dType = (type & 0x7F)) < RESPONSE_PARAMETER_FIRST_TYPE)
    {
        // The out->handle value was referenced as TPM_HANDLE in the
        // action code so it has to be properly aligned.
        command->handles[command->handleNum++] =
            *((TPM_HANDLE *)&(commandOut[offset]));
        maxOutSize -= sizeof(UINT32);
        type = *types++;
        offset = *offsets++;
    }
    // Use the size of the command action output buffer as the maximum for the
    // number of bytes that can get marshaled. Since the marshaling code has
    // no pointers to data, all of the data being returned has to be in the
    // command action output buffer. If we try to marshal more bytes than
    // could fit into the output buffer, we need to fail.
    for(;(dType = (type & 0x7F)) <= RESPONSE_PARAMETER_LAST_TYPE 
        && !g_inFailureMode; type = *types++)
    {
#if TABLE_DRIVEN_MARSHAL
        marshalIndex_t      index = marshalArray[dType];
        command->parameterSize += Marshal(index, &commandOut[offset], 
                                          &command->responseBuffer,
                                          &maxOutSize);
#else
        const MARSHAL_t     f = marshalArray[dType];

        command->parameterSize += f(&commandOut[offset], 
                                    &command->responseBuffer,
                                    &maxOutSize);
#endif
        offset = *offsets++;
    }
    result = (maxOutSize < 0) ? TPM_RC_FAILURE : TPM_RC_SUCCESS;
Exit:
    MemoryIoBufferZero();
    return result;
#endif
}