text: /*********************************************************
   * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as published
   * by the Free Software Foundation version 2.1 and no later version.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
   *
   *********************************************************/
  
  #include "vmware.h"
  #include "str.h"
  #include "util.h"
  #include "userlock.h"
  #include "ulInt.h"
  #include "ulIntShared.h"
  #include "hashTable.h"
  #include "random.h"
  
  static Bool mxInPanic = FALSE;  // track when involved in a panic
  static Bool mxUserCollectLockingTree = FALSE;
  
  Bool (*MXUserTryAcquireForceFail)() = NULL;
  
  static MX_Rank (*MXUserMxCheckRank)(void) = NULL;
  static void (*MXUserMxLockLister)(void) = NULL;
  void (*MXUserMX_LockRec)(struct MX_MutexRec *lock) = NULL;
  void (*MXUserMX_UnlockRec)(struct MX_MutexRec *lock) = NULL;
  Bool (*MXUserMX_TryLockRec)(struct MX_MutexRec *lock) = NULL;
  Bool (*MXUserMX_IsLockedByCurThreadRec)(const struct MX_MutexRec *lock) = NULL;
  char *(*MXUserMX_NameRec)(const struct MX_MutexRec *lock) = NULL;
  static void (*MXUserMX_SetInPanic)(void) = NULL;
  static Bool (*MXUserMX_InPanic)(void) = NULL;
  
  #define	MXUSER_MAX_LOOP 5
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUser_IsLockingTreeAvailable
   *
   *      Is the lock tracking tree available for reporting?
   *
   * Results:
   *      TRUE  MXuser lock tree tracking is enabled
   *      FALSE MXUser lock tree tracking is disabled
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  Bool
  MXUser_IsLockingTreeAvailable(void)
  {
     return mxUserCollectLockingTree;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUser_LockingTreeCollection
   *
   *      Enable or disable locking tree data collection.
   *
   * Results:
   *      None
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUser_LockingTreeCollection(Bool enabled)  // IN:
  {
     mxUserCollectLockingTree = vmx86_devel && vmx86_debug && enabled;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserInternalSingleton --
   *
   *      A "singleton" function for the MXUser internal recursive lock.
   *
   *      Internal MXUser recursive locks have no statistics gathering or
   *      tracking abilities. They need to used with care and rarely.
   *
   * Results:
   *      NULL    Failure
   *      !NULL   A pointer to an initialized MXRecLock
   *
   * Side effects:
   *      Manifold.
   *
   *-----------------------------------------------------------------------------
   */
  
  MXRecLock *
  MXUserInternalSingleton(Atomic_Ptr *storage)  // IN:
  {
     MXRecLock *lock = Atomic_ReadPtr(storage);
  
     if (UNLIKELY(lock == NULL)) {
        MXRecLock *newLock = Util_SafeMalloc(sizeof *newLock);
  
        if (MXRecLockInit(newLock)) {
           lock = Atomic_ReadIfEqualWritePtr(storage, NULL, (void *) newLock);
  
           if (lock) {
              MXRecLockDestroy(newLock);
              free(newLock);
           } else {
              lock = Atomic_ReadPtr(storage);
           }
        } else {
           free(newLock);
           lock = Atomic_ReadPtr(storage);  // maybe another thread succeeded
        }
     }
  
     return lock;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserSydrome --
   *
   *      Generate the syndrome bits for this MXUser library.
   *
   *      Each MXUser library has unique syndrome bits enabling the run time
   *      detection of locks created with one copy of the MXUser library and
   *      passed to another copy of the MXUser library.
   *
   *      The syndrome bits are important as they prevent incompatible versions
   *      of the MXUser library from trashing each other.
   *
   *      The bits are generated by using a source of bits that is external to
   *      a program and its libraries. This way no code or data based scheme
   *      can be spoofed or aliased.
   *
   * Results:
   *      As above
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  static uint32
  MXUserSyndrome(void)
  {
     uint32 syndrome;
     static Atomic_uint32 syndromeMem;  // implicitly zero -- mbellon
  
     syndrome = Atomic_Read(&syndromeMem);
  
     if (syndrome == 0) {
  #if defined(_WIN32)
        syndrome = GetTickCount();
  #else
        syndrome = time(NULL) & 0xFFFFFFFF;
  #endif
  
        /*
         * Protect against a total failure.
         */
  
        if (syndrome == 0) {
           syndrome++;
        }
  
        /* blind write; if racing one thread or the other will do */
        Atomic_ReadIfEqualWrite(&syndromeMem, 0, syndrome);
  
        syndrome = Atomic_Read(&syndromeMem);
     }
  
     ASSERT(syndrome);
  
     return syndrome;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserGetSignature --
   *
   *      Return a signature appropriate for the specified object type.
   *
   * Results:
   *      As above
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  uint32
  MXUserGetSignature(MXUserObjectType objectType)  // IN:
  {
     uint32 signature;
  
     ASSERT((objectType >= 0) && (objectType < 16) &&
            (objectType != MXUSER_TYPE_NEVER_USE));
  
     /*
      * Use a random syndrome combined with a unique bit pattern mapping
      * of objectType to bits in a nibble. The random portion of the signature
      * can be used to catch multiple copies of lib/lock that are "leaking"
      * locks between them (which may be incompatible due to internal changes).
      */
  
     signature = (MXUserSyndrome() & 0x0FFFFFFF) | (objectType << 28);
  
     ASSERT(signature);
  
     return signature;
  }
  
  
  /*
   *---------------------------------------------------------------------
   *
   *  MXUser_SetInPanic --
   *
   *     Notify the locking system that a panic is occurring.
   *
   *  Results:
   *     Set the "in a panic" state in userland locks and, when possible,
   *     MX locks.
   *
   *  Side effects:
   *     None
   *
   *---------------------------------------------------------------------
   */
  
  void
  MXUser_SetInPanic(void)
  {
     mxInPanic = TRUE;
  
     if (MXUserMX_SetInPanic != NULL) {
        MXUserMX_SetInPanic();
     }
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserDumpAndPanic --
   *
   *      Dump a lock, print a message and die
   *
   * Results:
   *      A panic.
   *
   * Side effects:
   *      Manifold.
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserDumpAndPanic(MXUserHeader *header,  // IN:
                     const char *fmt,       // IN:
                     ...)                   // IN:
  {
     char *msg;
     va_list ap;
     static uint32 loopCounter = 0;  // Is panic looping through there?
  
     ASSERT((header != NULL) && (header->dumpFunc != NULL));
  
     if (++loopCounter > MXUSER_MAX_LOOP) {
        /*
         * Panic is looping through MXUser to here - no progress is being made.
         * Switch to panic mode in the hopes that this will allow some progress.
         */
  
        MXUser_SetInPanic();
     }
  
     (*header->dumpFunc)(header);
  
     va_start(ap, fmt);
     msg = Str_SafeVasprintf(NULL, fmt, ap);
     va_end(ap);
  
     Panic("%s", msg);
  }
  
  
  /*
   *---------------------------------------------------------------------
   *
   *  MXUser_InPanic --
   *
   *     Is the caller in the midst of a panic?
   *
   *  Results:
   *     TRUE   Yes
   *     FALSE  No
   *
   *  Side effects:
   *     None
   *
   *---------------------------------------------------------------------
   */
  
  Bool
  MXUser_InPanic(void)
  {
     return mxInPanic || (MXUserMX_InPanic != NULL && MXUserMX_InPanic());
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserInstallMxHooks --
   *
   *      The MX facility may notify the MXUser facility that it is place and
   *      that MXUser should check with it. This function should be called from
   *      MX_Init.
   *
   * Results:
   *      As Above.
   *
   * Side effects:
   *      None.
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserInstallMxHooks(void (*theLockListFunc)(void),
                       MX_Rank (*theRankFunc)(void),
                       void  (*theLockFunc)(struct MX_MutexRec *lock),
                       void  (*theUnlockFunc)(struct MX_MutexRec *lock),
                       Bool  (*theTryLockFunc)(struct MX_MutexRec *lock),
                       Bool  (*theIsLockedFunc)(const struct MX_MutexRec *lock),
                       char *(*theNameFunc)(const struct MX_MutexRec *lock),
                       void  (*theSetInPanicFunc)(void),
                       Bool  (*theInPanicFunc)(void))
  {
     /*
      * This function can be called more than once but the second and later
      * invocations must be attempting to install the same hook functions as
      * the first invocation.
      */
  
     if ((MXUserMxLockLister == NULL) &&
         (MXUserMxCheckRank == NULL) &&
         (MXUserMX_LockRec == NULL) &&
         (MXUserMX_UnlockRec == NULL) &&
         (MXUserMX_TryLockRec == NULL) &&
         (MXUserMX_IsLockedByCurThreadRec == NULL) &&
         (MXUserMX_NameRec == NULL) &&
         (MXUserMX_SetInPanic == NULL) &&
         (MXUserMX_InPanic == NULL)
         ) {
        MXUserMxLockLister = theLockListFunc;
        MXUserMxCheckRank = theRankFunc;
        MXUserMX_LockRec = theLockFunc;
        MXUserMX_UnlockRec = theUnlockFunc;
        MXUserMX_TryLockRec = theTryLockFunc;
        MXUserMX_IsLockedByCurThreadRec = theIsLockedFunc;
        MXUserMX_NameRec = theNameFunc;
        MXUserMX_SetInPanic = theSetInPanicFunc;
        MXUserMX_InPanic = theInPanicFunc;
     } else {
        ASSERT((MXUserMxLockLister == theLockListFunc) &&
               (MXUserMxCheckRank == theRankFunc) &&
               (MXUserMX_LockRec == theLockFunc) &&
               (MXUserMX_UnlockRec == theUnlockFunc) &&
               (MXUserMX_TryLockRec == theTryLockFunc) &&
               (MXUserMX_IsLockedByCurThreadRec == theIsLockedFunc) &&
               (MXUserMX_NameRec == theNameFunc) &&
               (MXUserMX_SetInPanic == theSetInPanicFunc) &&
               (MXUserMX_InPanic == theInPanicFunc)
              );
     }
  }
  
  #if defined(MXUSER_DEBUG)
  #define MXUSER_MAX_LOCKS_PER_THREAD (2 * MXUSER_MAX_REC_DEPTH)
  
  typedef struct MXUserPerThread {
     struct MXUserPerThread  *next;
     uint32                   locksHeld;
     MXUserHeader            *lockArray[MXUSER_MAX_LOCKS_PER_THREAD];
  } MXUserPerThread;
  
  static Atomic_Ptr perThreadLockMem;
  static MXUserPerThread *perThreadFreeList = NULL;
  
  static Atomic_Ptr hashTableMem;
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserAllocPerThread --
   *
   *     Allocate a perThread structure.
   *
   *     Memory is allocated for the specified thread as necessary. Use a
   *     victim cache in front of malloc to provide a slight performance
   *     advantage. The lock here is equivalent to the lock buried inside
   *     malloc but no complex calculations are necessary to perform an
   *     allocation most of the time.
   *
   *     The maximum size of the list will be roughly the maximum number of
   *     threads having taken locks at the same time - a bounded number less
   *     than or equal to the maximum of threads created.
   *
   * Results:
   *     As above.
   *
   * Side effects:
   *      Memory may be allocated.
   *
   *-----------------------------------------------------------------------------
   */
  
  static MXUserPerThread *
  MXUserAllocPerThread(void)
  {
     MXUserPerThread *perThread;
     MXRecLock *perThreadLock = MXUserInternalSingleton(&perThreadLockMem);
  
     ASSERT(perThreadLock);
  
     MXRecLockAcquire(perThreadLock,
                      NULL);          // non-stats
  
     if (perThreadFreeList == NULL) {
        perThread = Util_SafeMalloc(sizeof *perThread);
     } else {
        perThread = perThreadFreeList;
        perThreadFreeList = perThread->next;
     }
  
     MXRecLockRelease(perThreadLock);
  
     ASSERT(perThread);
  
     memset(perThread, 0, sizeof *perThread);  // ensure all zeros
  
     return perThread;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserFreePerThread --
   *
   *     Free a perThread structure.
   *
   *     The structure is placed on the free list -- for "later".
   *
   * Results:
   *     As above.
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  static void
  MXUserFreePerThread(MXUserPerThread *perThread)  // IN:
  {
     MXRecLock *perThreadLock;
  
     ASSERT(perThread);
     ASSERT(perThread->next == NULL);
  
     perThreadLock = MXUserInternalSingleton(&perThreadLockMem);
     ASSERT(perThreadLock);
  
     MXRecLockAcquire(perThreadLock,
                      NULL);          // non-stats
  
     perThread->next = perThreadFreeList;
     perThreadFreeList = perThread;
  
     MXRecLockRelease(perThreadLock);
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserGetPerThread --
   *
   *      Return a pointer to the per thread data for the specified thread.
   *
   *      Memory is allocated for the specified thread as necessary. This memory
   *      is never released since it it is highly likely a thread will use a
   *      lock and need to record data in the perThread.
   *
   * Results:
   *      NULL   mayAlloc was FALSE and the thread doesn't have a perThread
   *     !NULL   the perThread of the specified thread
   *
   * Side effects:
   *      Memory may be allocated.
   *
   *-----------------------------------------------------------------------------
   */
  
  static MXUserPerThread *
  MXUserGetPerThread(Bool mayAlloc)  // IN: alloc perThread if not present?
  {
     HashTable *hash;
     MXUserPerThread *perThread = NULL;
     void *tid = MXUserCastedThreadID();
  
     hash = HashTable_AllocOnce(&hashTableMem, 1024,
                                HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL);
  
     if (!HashTable_Lookup(hash, tid, (void **) &perThread)) {
        /* No entry for this tid was found, allocate one? */
  
        if (mayAlloc) {
           MXUserPerThread *newEntry = MXUserAllocPerThread();
  
           /*
            * Attempt to (racey) insert a perThread on behalf of the specified
            * thread. If yet another thread takes care of this first, clean up
            * the mess.
            */
  
           perThread = HashTable_LookupOrInsert(hash, tid, newEntry);
           ASSERT(perThread);
  
           if (perThread != newEntry) {
              MXUserFreePerThread(newEntry);
           }
        } else {
           perThread = NULL;
        }
     }
  
     return perThread;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserListLocks
   *
   *      Allow a caller to list, via warnings, the list of locks the caller
   *      has acquired. Ensure that no memory for lock tracking is allocated
   *      if no locks have been taken.
   *
   * Results:
   *      None
   *
   * Side effects:
   *      The list is printed.
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserListLocks(void)
  {
     MXUserPerThread *perThread = MXUserGetPerThread(FALSE);
  
     if (perThread != NULL) {
        uint32 i;
  
        for (i = 0; i < perThread->locksHeld; i++) {
           MXUserHeader *hdr = perThread->lockArray[i];
  
           Warning("\tMXUser lock %s (@0x%p) rank 0x%x\n", hdr->name, hdr,
                   hdr->rank);
        }
     }
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUser_IsCurThreadHoldingLocks --
   *
   *      Are any MXUser locks held by the calling thread?
   *
   * Results:
   *      TRUE   Yes
   *      FALSE  No
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  Bool
  MXUser_IsCurThreadHoldingLocks(void)
  {
     MXUserPerThread *perThread = MXUserGetPerThread(FALSE);
  
     return (perThread == NULL) ? FALSE : (perThread->locksHeld != 0);
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserThreadRank --
   *
   *      Return the highest rank held by the specified thread via MXUser locks.
   *
   * Results:
   *      As above
   *
   * Side effects:
   *      Can optionally determine if a lock has been locked before.
   *
   *-----------------------------------------------------------------------------
   */
  
  static MX_Rank
  MXUserThreadRank(MXUserPerThread *perThread,  // IN:
                   MXUserHeader *header,        // IN:
                   Bool *firstUse)              // OUT:
  {
     uint32 i;
     Bool foundOnce = TRUE;
     MX_Rank maxRank = RANK_UNRANKED;
  
     ASSERT(perThread);
  
     /*
      * Determine the maximum rank held. Note if the lock being acquired
      * was previously entered into the tracking system.
      */
  
     for (i = 0; i < perThread->locksHeld; i++) {
        MXUserHeader *chkHdr = perThread->lockArray[i];
  
        maxRank = MAX(chkHdr->rank, maxRank);
  
        if (chkHdr == header) {
           foundOnce = FALSE;
        }
     }
  
     if (firstUse) {
        *firstUse = foundOnce;
     }
  
     return maxRank;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserCurrentRank --
   *
   *      Return the highest rank held by the current thread via MXUser locks.
   *
   * Results:
   *      As above
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  MX_Rank
  MXUserCurrentRank(void)
  {
     MX_Rank maxRank;
     MXUserPerThread *perThread = MXUserGetPerThread(FALSE);
  
     if (perThread == NULL) {
        maxRank = RANK_UNRANKED;
     } else {
        maxRank = MXUserThreadRank(perThread, NULL, NULL);
     }
  
     return maxRank;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserAcquisitionTracking --
   *
   *      Perform the appropriate tracking for lock acquisition.
   *
   * Results:
   *      Panic when a rank violation is detected (checkRank is TRUE).
   *      Add a lock instance to perThread lock list.
   *
   * Side effects:
   *      Manifold.
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserAcquisitionTracking(MXUserHeader *header,  // IN:
                            Bool checkRank)        // IN:
  {
     MXUserPerThread *perThread = MXUserGetPerThread(TRUE);
  
     VERIFY(perThread->locksHeld < MXUSER_MAX_LOCKS_PER_THREAD);
  
     /*
      * Rank checking anyone?
      *
      * Rank checking is abandoned once we're in a panic situation. This will
      * improve the chances of obtaining a good log and/or coredump.
      */
  
     if (checkRank && (header->rank != RANK_UNRANKED) && !MXUser_InPanic()) {
        MX_Rank maxRank;
        Bool firstInstance = TRUE;
  
        /*
         * Determine the highest rank held by the calling thread. Check for
         * MX locks if they are present.
         */
  
        maxRank = MXUserThreadRank(perThread, header, &firstInstance);
  
        if (MXUserMxCheckRank) {
           maxRank = MAX(maxRank, (*MXUserMxCheckRank)());
        }
  
        /*
         * Perform rank checking when a lock is entered into the tracking
         * system for the first time. This works out well because:
         *
         * Recursive locks are rank checked only upon their first acquisition...
         * just like MX locks.
         *
         * Exclusive locks will have a second entry added into the tracking
         * system but will immediately panic due to the run time checking - no
         * (real) harm done.
         */
  
        if (firstInstance && (header->rank <= maxRank)) {
           Warning("%s: lock rank violation by thread %s\n", __FUNCTION__,
                   VThread_CurName());
           Warning("%s: locks held:\n", __FUNCTION__);
  
           if (MXUserMxLockLister) {
              (*MXUserMxLockLister)();
           }
  
           MXUserListLocks();
  
           MXUserDumpAndPanic(header, "%s: rank violation maxRank=0x%x\n",
                              __FUNCTION__, maxRank);
        }
     }
  
     /* Add a lock instance to the calling threads perThread information */
     perThread->lockArray[perThread->locksHeld++] = header;
  
     /*
      * Maintain the lock tracking tree when approporiate.
      */
  
     if (vmx86_devel && vmx86_debug && mxUserCollectLockingTree) {
        uint32 i;
        MXUserLockTreeNode *node = NULL;
  
        MXUserLockTreeAcquire();
  
        for (i = 0; i < perThread->locksHeld; i++) {
           header = perThread->lockArray[i];
  
           node = MXUserLockTreeAdd(node, header->name,
                                    header->bits.serialNumber, header->rank);
        }
  
        MXUserLockTreeRelease();
     }
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserReleaseTracking --
   *
   *      Perform the appropriate tracking for lock release.
   *
   * Results:
   *      A panic.
   *
   * Side effects:
   *      Manifold.
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserReleaseTracking(MXUserHeader *header)  // IN: lock, via its header
  {
     uint32 i;
     uint32 lastEntry;
     MXUserPerThread *perThread = MXUserGetPerThread(FALSE);
  
     /* MXUserAcquisitionTracking should have already created a perThread */
     if (UNLIKELY(perThread == NULL)) {
        MXUserDumpAndPanic(header, "%s: perThread not found! (thread 0x%p)\n",
                           __FUNCTION__, MXUserCastedThreadID());
     }
  
     /* Search the perThread for the argument lock */
     for (i = 0; i < perThread->locksHeld; i++) {
        if (perThread->lockArray[i] == header) {
           break;
        }
     }
  
     /* The argument lock had better be in the perThread */
     if (UNLIKELY(i >= perThread->locksHeld)) {
        MXUserDumpAndPanic(header,
                           "%s: lock not found! (thread 0x%p; count %u)\n",
                           __FUNCTION__, MXUserCastedThreadID(),
                           perThread->locksHeld);
     }
  
     /* Remove the argument lock from the perThread */
     lastEntry = perThread->locksHeld - 1;
  
     if (i < lastEntry) {
        uint32 j;
  
        for (j = i + 1; j < perThread->locksHeld; j++) {
           perThread->lockArray[i++] = perThread->lockArray[j];
        }
     }
  
     perThread->lockArray[lastEntry] = NULL;  // tidy up memory
     perThread->locksHeld--;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUser_TryAcquireFailureControl --
   *
   *      Should a TryAcquire operation fail, no matter "what", sometimes?
   *
   *      Failures occur statistically in debug builds to force our code down
   *      all of its paths.
   *
   * Results:
   *      Unknown
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUser_TryAcquireFailureControl(Bool (*func)(const char *name))  // IN:
  {
     MXUserTryAcquireForceFail = func;
  }
  
  
  /*
   *-----------------------------------------------------------------------------
   *
   * MXUserValidateHeader --
   *
   *      Validate an MXUser object header.
   *
   * Results:
   *      Return  All is well
   *      Panic   All is NOT well
   *
   * Side effects:
   *      None
   *
   *-----------------------------------------------------------------------------
   */
  
  void
  MXUserValidateHeader(MXUserHeader *header,         // IN:
                       MXUserObjectType objectType)  // IN:
  {
     uint32 expected = MXUserGetSignature(objectType);
  
     if (header->bits.badHeader == 1) {
        return; // No need to panic on a bad header repeatedly...
     }
  
     if (header->signature != expected) {
        header->bits.badHeader = 1;
  
        MXUserDumpAndPanic(header,
                          "%s: signature failure! expected 0x%X observed 0x%X\n",
                           __FUNCTION__, expected, header->signature);
  
     }
  
     if (header->bits.serialNumber == 0) {
        header->bits.badHeader = 1;
  
        MXUserDumpAndPanic(header, "%s: Invalid serial number!\n",
                           __FUNCTION__);
     }
  }
  #endif
  