/* 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 || TABLE_DRIVEN_MARSHAL

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 || TABLE_DRIVEN_MARSHAL
    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 || TABLE_DRIVEN_MARSHAL
    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
}