//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//


#include <windows.h>
#include <tchar.h>
#include <tux.h>

#include "commonUtils.h"

#include "sysIntrOverloads.h"

#include "tuxInterrupts.h"

#include "interruptCmdLine.h"

#include "prettyPrinting.h"

/******************************************************************************/
/*                                                                            */
/*                                   Constants                                */
/*                                                                            */
/******************************************************************************/


/*
 * number of random values to test when we resort to random testing.
 * Lengthening probably won't increase test coverage that much but
 * will make the tests run longer.
 */
#define TEST_N_RANDOM_VALUES (10000)

/* 
 * when doing allocations for multiple IRQs, this tells the test how
 * many interations to run.
 */
#define MULT_ALLOC_TEST_IT (100)

/*
 * the maximium possible IRQ per SYSINTR.  We are setting this
 * impossibly high 
 */
#define MAX_POSSIBLE_IRQ_PER_SYSINTR (10)


/******************************************************************************/
/*                                                                            */
/*                                 Functions                                  */
/*                                                                            */
/******************************************************************************/

/******* helper ***************************************************************/



enum eReturnMaxSuccessfullyAllocatedIrq {FOUND_IT, MISSED_IT, FATAL_ERROR};

eReturnMaxSuccessfullyAllocatedIrq
findMaxSuccessfullyAllocatedIrq (DWORD dwIRQBegin, DWORD dwIRQEnd, 
				 DWORD * dwMax);
BOOL
findHowManySysIntrsAreFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
			    DWORD * dwNumFree, BOOL bPrintDebug);

BOOL
handleIrqRange (DWORD dwUserData, DWORD * pdwArgIRQBegin, DWORD * pdwArgIRQEnd,
		BOOL * pbAllowedToPass);

BOOL
getNumSysIntrsFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		    DWORD * pdwNumSysIntrsFree);

BOOL
checkNumSysIntrsFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		      DWORD dwNumSysIntrsFree);

__inline BOOL inKernelMode( void ) 
{
  return (int)inKernelMode < 0;
}
 

/******* For running tests ****************************************************/

BOOL
findMaxIrqPerSysIntr (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		      DWORD * pdwMaxIrqPerSysIntr, BOOL bPrint);

/***** Old model ******/

BOOL
OldModelStandardAllocate (DWORD dwIRQ, DWORD dwSysIntr);



enum enum_OldModelTestType {SIMPLE_ALLOCATE};

BOOL
setupAndRunUnallocatedSysintrsTest(enum_OldModelTestType type);

/***** new model ******/
BOOL
runAllocateAll_SingleIRQ (DWORD dwAllocationType, const TCHAR szTypeName[],
			  DWORD dwIRQBegin, DWORD dwIRQEnd,
			  BOOL bAllowedToFail, BOOL bAllowedToPass);

BOOL
runAllocateRand_MultIRQ (DWORD dwAllocationType, BOOL bAllowedToFail,
			 BOOL bAllowedToPass,
			 DWORD dwIRQBegin, DWORD dwIRQEnd, 
			 DWORD dwMaxIrqPerSysIntr);

BOOL
allocateRand_checkDuplicateIRQs (const DWORD *const dwIRQsOrig, DWORD dwLen,
				 DWORD dwAllocationType,
				 BOOL bAllowedToFail,
				 BOOL bAllowedToPass);

BOOL
runAllocateRandForceStatic_MultIRQ (DWORD dwIRQBegin, DWORD dwIRQEnd, 
				    DWORD dwMaxIrqPerSysIntr, 
				    BOOL bAllowedToPass);



/******************************************************************************/
/*                                                                            */
/*                                    Tests                                   */
/*                                                                            */
/******************************************************************************/

TESTPROCAPI
PrintUsage(
	   UINT uMsg,
	   TPPARAM tpParam,
	   LPFUNCTION_TABLE_ENTRY lpFTE)
{
  /* only supporting executing the test */
  if (uMsg != TPM_EXECUTE)
    {
      return (TPR_NOT_HANDLED);
    }
  
  printUsage ();

  return (TPR_PASS);
}

/******** Releasing SYSINTRs *********************************************/

/*
 * Only certain sysintrs are valid.  This function tries to free those
 * that aren't.
 *
 * ranges 0 - 7 are system intrs and should never be freed.
 * 8 - 71 are devices intrs and can be freed.
 * everything else is off limits
 *
 * We can't touch anything from 8 - 71 because we could crash the
 * system.
 *
 * Ideally, we would test everything that was not allowed in the range
 * of dwords, but this takes too long.  So, we get smart:
 * 
 * 0 - 7 because these are obvious
 * 72 to 256 because we expect errors close to the allowable values.  
 * -1 to -255 for the same reason.
 * SYSINTR_UNDEFINED for obvious reasons
 * Powers of two in case they are doing bit shifts and the like
 * 
 * We need more test cases, so we turn to a random generator between
 * the invalid numbers.  This will hopefully catch out of bounds
 * memory accesses and other funky things that the targeted tests
 * wouldn't hit.
 *
 * Note that we could make the random range > 256 to < (DWORD) -255.
 * This is confusing to explain and would have to be changed if folks
 * changed these special cases later.  For this reason we just do the
 * entire range > 72.
 */
TESTPROCAPI  freeWhatShouldNotBeFreed(
				      UINT uMsg, 
				      TPPARAM tpParam, 
				      LPFUNCTION_TABLE_ENTRY lpFTE) 
{

  INT returnVal = TPR_PASS;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      returnVal = TPR_NOT_HANDLED;
      goto cleanAndReturn;
    }

  breakIfUserDesires ();
    
  printDisclaimer ();
  Info (_T(""));

  Info (_T("This test confirms that the oal correctly handles SYSINTERS that"));
  Info (_T("can't be released.  This code calls IOCTL_HAL_RELEASE_SYSINTR with"));
  Info (_T("SYSINTR values that should not successfully be released.  The test"));
  Info (_T("passes if it receives false for each of these calls.  Any other"));
  Info (_T("return value constitutes an error."));
  Info (_T(""));
  Info (_T("The only SYSINTERS that can be released are those between 8 and 71,"));
  Info (_T("inclusive.  These are not touched, since releasing these could "));
  Info (_T("potentially crash the system."));
  Info (_T(""));	
  Info (_T("If this test fails, it is possible that the system could hang or"));
  Info (_T("crash.  Furthermore, the system could hang before the failure is"));
  Info (_T("reported in the logs."));
  Info (_T(""));

  Info (_T("Checking SYSINTR_UNDEFINED (0x%x)"), SYSINTR_UNDEFINED);
  
  if (releaseSysIntr (SYSINTR_UNDEFINED))
    {
      /* something is wrong.  All of these calls should fail. */
      Error (_T("Error.  We were able to release sysintr %u."), 
	     SYSINTR_UNDEFINED);
      returnVal = TPR_FAIL;
    }

  Info (_T("Checking [0, 8)"));

  /* try to free the special interrupts from [0, 8) */
  for (DWORD dwSysIntr = 0; dwSysIntr < 8; dwSysIntr++)
    {
      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}
    }

  Info (_T("Checking [72, 256]"));

  /* try to free the interrupts from [72, 256] */
  for (DWORD dwSysIntr = 72; dwSysIntr <= 256; dwSysIntr++)
    {
      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}
    }

  Info (_T("Checking boundaries of powers of two (plus/minus 1)"));

  /* try to free all interrupts on boardaries of two. */
  for (DWORD dwSysIntrBase = 256; dwSysIntrBase != 0;
       dwSysIntrBase <<= 1)
    {
      /* try one less than the power of two */
      DWORD dwSysIntr = dwSysIntrBase - 1;
      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}

      /* try power of two */
      dwSysIntr = dwSysIntrBase;
      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}

      /* try one greater */
      dwSysIntr = dwSysIntrBase + 1;
      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	} 
    }

  Info (_T("Checking (-256, -1]"));

  /*
   * check negative numbers from -1 to -255. There really correspond
   * to 0xffffffff, etc.
   */
  for (UINT iSysIntr = -1; iSysIntr > -256; iSysIntr--)
    {
      DWORD dwSysIntr = (DWORD) iSysIntr;

      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}
    }

  /* check random values in the invalid range */
  Info (_T("Checking %u randomly generated sysintrs between [72, 0xffffffff]."),
	TEST_N_RANDOM_VALUES);

  for (DWORD dwCount = 0; dwCount < TEST_N_RANDOM_VALUES; dwCount++)
    {
      DWORD dwSysIntr = getRandomInRange (72, 0xffffffff);

      if (releaseSysIntr (dwSysIntr))
	{
	  /* something is wrong.  All of these calls should fail. */
	  Error (_T("Error.  We were able to release sysintr %u."), dwSysIntr);
	  returnVal = TPR_FAIL;
	}
    }

  Info (_T("")); // blank line
  Info (_T("Test Complete"));
  Info (_T(""));

  if (returnVal == TPR_PASS)
    {
      Info (_T("Test passed"));
    }
  else
    {
      /* we don't use tpr_skip, so this is safe */
      Error (_T("Test failed"));
    }

 cleanAndReturn:

  return (returnVal);
}


/*
 * return how many free sysintrs are currently available.  Calls a helper
 * function that does the work.
 */
TESTPROCAPI  testFindNumFreeSysIntrs(
				      UINT uMsg, 
				      TPPARAM tpParam, 
				      LPFUNCTION_TABLE_ENTRY lpFTE) 
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      returnVal =  TPR_NOT_HANDLED;
      goto cleanAndReturn;
    }

  breakIfUserDesires ();
  
  printDisclaimer ();

  Info (_T("This test tries to find the number of free SYSINTRs on the"));
  Info (_T("the system."));

  /* the ranges supported by the test, inclusive */
  DWORD dwIRQ = TEST_IRQ_BEGIN;
  DWORD dwUpperBoundInclusive = TEST_IRQ_END;
  
  /* adjust the ranges if need be */
  if (!handleCmdLineIRQRange (&dwIRQ, &dwUpperBoundInclusive))
    {
      Error (_T("Couldn't parse command line."));
      goto cleanAndReturn;
    }

  DWORD dwNumSysIntrsFree1, dwNumSysIntrsFree2;

  if (!inKernelMode())
    {
      Error (_T("Must be in kernel mode to run this test."));
      goto cleanAndReturn;
    }

  if (!findHowManySysIntrsAreFree (dwIRQ, dwUpperBoundInclusive,
				   &dwNumSysIntrsFree1, TRUE))
    {
      goto cleanAndReturn;
    }

  if (!findHowManySysIntrsAreFree (dwIRQ, dwUpperBoundInclusive,
				   &dwNumSysIntrsFree2, TRUE))
    {
      goto cleanAndReturn;
    }

  if (dwNumSysIntrsFree2 != dwNumSysIntrsFree2)
    {
      Error (_T("Two successive calls yielded different numbers of SYSINTRs")
	     _T("free."));
      Error (_T("First got %u and then got %u."), 
	     dwNumSysIntrsFree1, dwNumSysIntrsFree2);
      goto cleanAndReturn;
    }

  Info (_T("There are %u SYSINTRs free."), dwNumSysIntrsFree1);

  returnVal = TPR_PASS;
  
 cleanAndReturn:

  return (returnVal);
}


/******** Old model (allocate ) ****************************/

/*
 * This test tries to force errors on unallocated sysintrs.
 *
 * IRQs are allocated until they can't be allocated anymore.  This
 * gives us a list of sysintrs that are unallocated.  We can then play
 * with these.
 *
 * This routines assumes (and confirms) that the interrupt routines
 * don't double allocate SYSINTRs.
 */
TESTPROCAPI  stressUnallocatedSysInters(
					   UINT uMsg, 
					   TPPARAM tpParam, 
					   LPFUNCTION_TABLE_ENTRY lpFTE)
{
  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      return (TPR_NOT_HANDLED);
    }
  
  printDisclaimer ();

  breakIfUserDesires ();

  Info (_T(""));
  Info (_T("This test works with the unallocated SYSINTRs on the system."));
  Info (_T("Multiple simultaneous allocations allow the test to develop"));
  Info (_T("a list of unallocated SYSINTRs.  These are then stressed with"));
  Info (_T("different types of calls."));
  Info (_T("If no SYSINTRs are currently available on the system this test"));
  Info (_T("will pass."));
  Info (_T(""));

  if (setupAndRunUnallocatedSysintrsTest (SIMPLE_ALLOCATE))
    {
      return (TPR_PASS);
    }
  else
    {
      return (TPR_FAIL);
    }

}

/* kern mode only */
BOOL
setupAndRunUnallocatedSysintrsTest(enum_OldModelTestType type)
{
  /*
   * Allows us to easily track the return values from the sysintr
   * calls.  We track the return value and the sysintr that was returned.
   *
   * Don't change the array length without changing the comparison code below
   */
  typedef struct
  {
    DWORD dwIRQ;		// irq
    DWORD dwRC;			// return code
  } sTrackSysIntrs;


  BOOL bRet = TRUE;

  DWORD dwIRQBegin = TEST_IRQ_BEGIN;
  DWORD dwIRQEnd = TEST_IRQ_END;

  if (!handleCmdLineIRQRange (&dwIRQBegin, &dwIRQEnd))
    {
      bRet = FALSE;
      goto cleanAndReturn;
    }

  sTrackSysIntrs pWhichSysIntrsToRelease[SYSINTR_MAXIMUM];

  /* initialize array */
  for (DWORD dwPos = 0; dwPos < SYSINTR_MAXIMUM; dwPos++)
    {
      pWhichSysIntrsToRelease[dwPos].dwRC = FALSE;
    }

  Info (_T("Finding which SYSINTRs are unallocated."));

  DWORD dwIRQ = dwIRQBegin;

  for (BOOL bJustRolled = FALSE;
       dwIRQ <= dwIRQEnd && !bJustRolled; dwIRQ++)
    {
      DWORD dwSysIntr;
      
      BOOL rc;

      if (!(rc = getSysIntr (dwIRQ, &dwSysIntr)))
	{
	  continue;
	}

      /* if out of range go to next irq */
      /* throw in second check (essentially a nop) to make it prefast clean */
      if (!checkSysIntrRange (dwSysIntr) || dwSysIntr >= SYSINTR_MAXIMUM)
	{
	  bRet = FALSE;
	  continue;
	}
	  
      /*
       * we are now sure that an array access wouldn't cause a buffer
       * overflow 
       */
      if (pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  Error (_T("SYSINTR %u has already been allocated to IRQ %u."),
		 dwSysIntr, pWhichSysIntrsToRelease[dwSysIntr].dwIRQ);
	  Error (_T("Routines just allocated it to IRQ %u as well."),
		 dwIRQ);
	  bRet = FALSE;
	  continue;
	}

      /* sysIntr is clear.  Record that we got it back. */
      pWhichSysIntrsToRelease[dwSysIntr].dwRC = TRUE;
      pWhichSysIntrsToRelease[dwSysIntr].dwIRQ = dwIRQ;

      /* if we just worked on MAX_DWORD then want to bail */
      bJustRolled = (dwIRQ == MAX_DWORD);

    } /* for */

  if (!bRet)
    {
      Error (_T("Error during allocation step.  Cannot continue."));
      goto cleanAndReturn;
    }

  /* 
   * now, we have a list of sysinters that we can play with that won't
   * crash the system.
   */

  Info (_T("The SYSINTRs that were just allocated by this test."));
  Info (_T(""));

  BOOL bFoundSysIntr = FALSE;

  /* print sysintr array for debugging */
  for (DWORD dwSysIntr = 0; dwSysIntr < SYSINTR_MAXIMUM; dwSysIntr++)
    {
      if (pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  Info (_T("SYSINTR %u  IRQ %u"), 
		dwSysIntr, pWhichSysIntrsToRelease[dwSysIntr].dwIRQ);
	  bFoundSysIntr = TRUE;
	}
    }

  if (!bFoundSysIntr)
    {
      Info (_T("No free SYSINTRs found.  This is odd, but not unheard of."));
      Info (_T("Passing the test because this isn't a definitive failure."));
      goto cleanAndReturn;
    }

  Info (_T(""));
 
  for (DWORD dwSysIntr = 0; dwPos < SYSINTR_MAXIMUM; dwSysIntr++)
    {
      /* only work on SYSINTERs that were successfully allocated */
      if (!pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  continue;
	}
      
      switch (type)
	{
	case SIMPLE_ALLOCATE:
	  bRet = OldModelStandardAllocate (pWhichSysIntrsToRelease[dwSysIntr].dwIRQ, dwSysIntr);
	  break;
	default:
	  IntErr (_T("test type is not defined."));
	  bRet = FALSE;
	  break;
	}
    }

  if (!bRet)
    {
      Error (_T("Above errors probably put the system into an unstable state."));
      Error (_T("Going to try to release SYSINTRs that we allocated anyway."));
    }

  Info (_T(""));
  Info (_T("Releasing the SYSINTRs allocated by the test."));
  Info (_T(""));

  /* release them - these should succeed */
  for (DWORD dwSysIntr = 0; dwSysIntr < SYSINTR_MAXIMUM; dwSysIntr++)
    {
      /* only work on SYSINTERs that were successfully allocated */
      if (!pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  continue;
	}

      if (!releaseSysIntr (dwSysIntr))
	{
	  Error (_T("Error!  Weren't able to release SYSINTR %u."), dwSysIntr);
	  bRet = FALSE;
	}
    }

 cleanAndReturn:

  if (bRet)
    {
      Info (_T("Test passed."));
      return (TPR_PASS);
    }
  else
    {
      Error (_T("Test failed."));
      return (TPR_FAIL);
    }
}


/*
 * for each sysinter:
 * - release it (should work)
 * - release it (double release should fail)
 * - reallocate that IRQ.  We should get the same sysinter (the
 * tables are full, we released one, there should be the only space
 * left).
 * - reallocate it again.  We should get failure, since there is
 * no room.
 */
BOOL
OldModelStandardAllocate (DWORD dwIRQ, DWORD dwSysIntr)
{
  BOOL returnVal = FALSE;

  Info (_T("  --- Working on SYSINTR %u ---"), dwSysIntr);

  Info (_T("Trying to release previously allocated sysinter %u."), 
	dwSysIntr);

  if (releaseSysIntr (dwSysIntr))
    {
      Info (_T("Successful!  SYSINTR %u is released."), dwSysIntr);
    }
  else
    {
      Error (_T("Error!  SYSINTR %u not released."), dwSysIntr);
      goto cleanAndReturn;
    }

  Info (_T("Trying to release sysinter %u again.  This should fail."),
	dwSysIntr);

  if (releaseSysIntr (dwSysIntr))
    {
      Error (_T("Error!  SYSINTR %u released a second time."), dwSysIntr);
      goto cleanAndReturn;
    }
  else
    {
      Info (_T("Successful!  SYSINTR %u not released."), dwSysIntr);
    }

  /* try to reallocate it */
  DWORD dwNewSysIntr;

  Info (_T("Trying to reallocate SYSINTR %u for irq %u"),
	dwSysIntr, dwIRQ);

  if (getSysIntr (dwIRQ, &dwNewSysIntr))
    {
      if (dwSysIntr == dwNewSysIntr)
	{
	  Info (_T("Successful!  Got the same SYSINTR back"));
	}
      else
	{
	  Error (_T("Error!  Expected SYSINTR %u and recieved SYSINTER %u"),
		 dwSysIntr, dwNewSysIntr);
	  goto cleanAndReturn;
	}

    }
  else
    {
      Error (_T("Error!  We should be able to get a SYSINTR for IRQ %u"),
	     dwIRQ);
      Error (_T("(we know that SYSINTR %u is free) but we couldn't ")
	     _T("allocate it."), dwSysIntr);
      goto cleanAndReturn;
    }

  Info (_T("Trying to reallocate IRQ %u again."), dwIRQ);

  /* reallocate.  Expect failure */
  if (getSysIntr (dwIRQ, &dwNewSysIntr))
    {
      Error (_T("Error!  There should be no room in the tables and yet"));
      Error (_T("we were able to allocate another IRQ."));
      goto cleanAndReturn;
    }
  else
    {
      Info (_T("Successful!  Couldn't allocate SYSINTR."));
    }

  returnVal = TRUE;

 cleanAndReturn:

  return (returnVal);
}


/********* Max IRQ per SYSINTR *********************************************/

/*
 * This test allocates a given set of irqs.  Successfully returned
 * sysintrs and immediately freed.
 *
 * irq range is support through command line options.
 */
TESTPROCAPI  testFindMaxIRQPerSysIntr(
				      UINT uMsg, 
				      TPPARAM tpParam, 
				      LPFUNCTION_TABLE_ENTRY lpFTE) 
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      returnVal =  TPR_NOT_HANDLED;
      goto cleanAndReturn;
    }

  breakIfUserDesires ();
  
  printDisclaimer ();

  Info (_T("This test tries to find the maximum allowed IRQ per Sysintr"));
  Info (_T("on this system.  Note that this assumes that this value is"));
  Info (_T("constant across all sysintrs.  If this isn't the case (say"));
  Info (_T("that the code uses a linked list implementation) this value"));
  Info (_T("might be incorrect."));

  /* the ranges supported by the test, inclusive */
  DWORD dwIRQ = TEST_IRQ_BEGIN;
  DWORD dwUpperBoundInclusive = TEST_IRQ_END;
  
  /* adjust the ranges if need be */
  if (!handleCmdLineIRQRange (&dwIRQ, &dwUpperBoundInclusive))
    {
      Error (_T("Couldn't parse command line."));
      goto cleanAndReturn;
    }

  DWORD dwNumSysIntrsFree;

  if (inKernelMode() && 
      !getNumSysIntrsFree (dwIRQ, dwUpperBoundInclusive, &dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  DWORD dwMaxIrqPerSysIntr;

  if (!findMaxIrqPerSysIntr (dwIRQ, dwUpperBoundInclusive, &dwMaxIrqPerSysIntr,
			     TRUE))
    {
      goto cleanAndReturn;
    }

  if (inKernelMode() &&
      !checkNumSysIntrsFree (dwIRQ, dwUpperBoundInclusive, dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  returnVal = TPR_PASS;
  
 cleanAndReturn:

  return (returnVal);
}

/*
 * start from the largest value and work down.  Return the first IRQ 
 * that we can successfully allocate.
 */
eReturnMaxSuccessfullyAllocatedIrq
findMaxSuccessfullyAllocatedIrq (DWORD dwIRQBegin, DWORD dwIRQEnd, 
				 DWORD * dwMax)
{
  if (dwIRQBegin > dwIRQEnd || !dwMax)
    {
      return (FATAL_ERROR);
    }

  /* assume couldn't find it */
  eReturnMaxSuccessfullyAllocatedIrq returnVal = MISSED_IT;

  DWORD dwIRQ = dwIRQEnd;
  DWORD dwLowerBoundInclusive = dwIRQBegin;

  /* 
   * use just rolled to catch the case in which 
   * dwUpperBoundInclusive == 0
   */
  for (BOOL bJustRolled = FALSE; 
       dwIRQ >= dwLowerBoundInclusive && !bJustRolled; dwIRQ--)
    {
      DWORD dwSysIntr;

      /* do the allocation.  If fail then we need to go to the next value */
      if (getSysIntr (SPECIAL_OAL_INTR_STANDARD, &dwIRQ, 1, &dwSysIntr))
	{
	  if (!checkSysIntrRange (dwSysIntr))
	    {
	      returnVal = FATAL_ERROR;
	      goto cleanAndReturn;
	    }

	  if (!releaseSysIntr (dwSysIntr))
	    {
	      Error (_T("Couldn't release sysintr %u"), dwSysIntr);
	      returnVal = FATAL_ERROR;
	      goto cleanAndReturn;
	    }

	  /* found first one that successfully allocates */
	  *dwMax = dwIRQ;
	  returnVal = FOUND_IT;
	  break;
	}

      /* if we just worked on 0 then want to bail */
      bJustRolled = (dwIRQ == 0);
    } 
  
 cleanAndReturn:

  return (returnVal);
  
}

/*
 * find the max IRQ per sysintr allowed on the system.  
 *
 * The first two values provide the irq range, inclusive.
 * pdwMaxIrqPerSysIntr returns the max IRQ per sysintr on the system.
 *
 * Function returns true if the max is found.  False if the parameters
 * are bad or it can't find the value.
 */
BOOL
findMaxIrqPerSysIntr (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		      DWORD * pdwMaxIrqPerSysIntr, BOOL bPrint)
{
  BOOL returnVal = FALSE;

  /* MAX_POSSIBLE_IRQ_PER_SYSINTR check comes from for loop */
  if ((dwIRQBegin > dwIRQEnd) || (!pdwMaxIrqPerSysIntr))
    {
      return (FALSE);
    }

  DWORD * pdwIRQBuffer = NULL;

  /*
   * reduce the dwIRQEnd value if we can.  This will help the random
   * guessing routine get better irq values.
   */
  {
    DWORD dwReducedIRQEnd;

    switch (findMaxSuccessfullyAllocatedIrq (dwIRQBegin, dwIRQEnd, 
					     &dwReducedIRQEnd))
      {
      case FOUND_IT:
	dwIRQEnd = dwReducedIRQEnd;
	if (bPrint)
	  {
	    Info (_T("Appears that the max IRQ value is %u.  Using this."), 
		  dwIRQEnd);
	  }
	break;
      case MISSED_IT:
	/* do nothing */
	break;
      case FATAL_ERROR:
	/* fatal error - bailing out */
	goto cleanAndReturn;
      default:
	IntErr (_T("Wrong return from findMaxSuccessfullyAllocatedIrq"));
	goto cleanAndReturn;
      }
  }	    

  DWORD dwMaxPossIRQPerSysIntr = MAX_POSSIBLE_IRQ_PER_SYSINTR;
  
  /* structure has two leading DWORDs and then the IRQ values */
  DWORD dwBufferSizeBytes = (2 + dwMaxPossIRQPerSysIntr) * sizeof (DWORD);

  if (dwBufferSizeBytes < dwMaxPossIRQPerSysIntr)
    {
      /* we overflowed */
      Error (_T("Overflowed when calculating needed memory."));
      goto cleanAndReturn;
    }

  pdwIRQBuffer = (DWORD *) malloc (dwBufferSizeBytes);

  if (!pdwIRQBuffer)
    {
      Error (_T("Couldn't get memory."));
      goto cleanAndReturn;
    }

  DWORD dwSupportedIRQs = 1;

  /* for each number of possible supported IRQs */
  for (; dwSupportedIRQs <= dwMaxPossIRQPerSysIntr; dwSupportedIRQs++)
    {
      DWORD dwNumTries = 100;

      if (bPrint)
	{
	  Info (_T("Testing %u IRQs."), dwSupportedIRQs);
	}

      BOOL bStillLooking = TRUE;
      /* try this many times to get a result */
      while ((dwNumTries != 0) && bStillLooking)
	{
	  /* load the irq array */
	  for (DWORD dwPos = 0; dwPos < dwSupportedIRQs; dwPos++)
	    {
	      pdwIRQBuffer[dwPos] = getRandomInRange (dwIRQBegin, dwIRQEnd);
	    }
	  
	  if (bPrint)
	    {
	      Info (_T("IRQ list is %s"), 
		    collateIRQs (pdwIRQBuffer, dwSupportedIRQs));
	    }

	  DWORD dwSysIntr;

	  if (getSysIntr (OAL_INTR_DYNAMIC, pdwIRQBuffer, 
			  dwSupportedIRQs, &dwSysIntr))
	    {
	      if (!checkSysIntrRange (dwSysIntr))
		{
		  goto cleanAndReturn;
		}

	      /* 
	       * successfully allocated, so we can support at least
	       * this many irqs 
	       */
	      bStillLooking = FALSE;

	      if (!releaseSysIntr (dwSysIntr))
		{
		  Error (_T("Couldn't release sysintr %u."), dwSysIntr);
		  goto cleanAndReturn;
		}
	    }
	  else
	    {
	      /* 
	       * it could be that we picked the wrong IRQs.  decrement
	       * vars and try again.
	       */
	      dwNumTries--;
	      if ((dwNumTries) && (bPrint))
		{
		  Info (_T("%u tries left for max value of %u."), 
			dwNumTries, dwSupportedIRQs);
		}
	    }
	} /* while */

      if (bStillLooking)
	{
	  /* 
	   * we ran out of tries and still didn't find it.  Since we
	   * are counting up this is the first value that didn't allow
	   * us to allocate this many IRQs.  It is the one that we are
	   * looking for.
	   */
	  Info (_T(""));
	  Info (_T("The max number of IRQs per SYSINTR is %u."), 
		dwSupportedIRQs - 1);

	  /*
	   * if dwSupportedIRQs is 1, then something is funky, since
	   * we should always be able to allocate at least one IRQ.
	   * this will never underflow.
	   */
	  *pdwMaxIrqPerSysIntr = dwSupportedIRQs - 1;

	  break;
	}

    } /* for */

  if (dwSupportedIRQs > dwMaxPossIRQPerSysIntr)
    {
      Error (_T("Couldn't find the max possible IRQ per Sysintr.  Tried ")
	     _T("[1, %u]."), dwMaxPossIRQPerSysIntr);
      goto cleanAndReturn;
    }
  

  returnVal = TRUE;
 cleanAndReturn:
  
  if (pdwIRQBuffer)
    {
      free (pdwIRQBuffer);
    }

  return (returnVal);
}



/********  Allocation Tests **********************************************/

/*
 * find the max successfully allocated IRQ.  Calls helper function above.
 */
TESTPROCAPI  testFindMaxSuccessfullyAllocatedIrq(
						 UINT uMsg, 
						 TPPARAM tpParam, 
						 LPFUNCTION_TABLE_ENTRY lpFTE)
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      returnVal =  TPR_NOT_HANDLED;
      goto cleanAndReturn;
    }

  breakIfUserDesires ();
  
  printDisclaimer ();

  Info (_T("This test tries to find the maximum allowed IRQ on the"));
  Info (_T("system.  This assumes that this value exists and that it"));
  Info (_T("is less than the starting point."));

  /* the ranges supported by the test, inclusive */
  DWORD dwIRQBegin = TEST_IRQ_BEGIN;
  DWORD dwIRQEnd = TEST_IRQ_END;
  
  if (lpFTE->dwUserData & IN_RANGE)
    {
      /* adjust the ranges if need be */
      if (!handleCmdLineIRQRange (&dwIRQBegin, &dwIRQEnd))
	{
	  Error (_T("Couldn't parse command line."));
	  goto cleanAndReturn;
	}
    }
  else
    {
      Info (_T("Ignoring user supplied IRQ params (if given).  Use the"));
      Info (_T("in range tests to confirm that the IOCTLs are returning"));
      Info (_T("the correct return values for IRQs within the OALs"));
      Info (_T("supported range."));
      Info (_T(""));
    }

  DWORD dwNumSysIntrsFree;

  if (inKernelMode() && 
      !getNumSysIntrsFree (dwIRQBegin, dwIRQEnd, &dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  DWORD dwMaxIrq;
  
  switch (findMaxSuccessfullyAllocatedIrq (dwIRQBegin, dwIRQEnd, 
					   &dwMaxIrq))
    {
    case FOUND_IT:
      Info (_T("Largest possible IRQ in [%u, %u] is %u."),
	    dwIRQBegin, dwIRQEnd, dwMaxIrq);
      break;
    case MISSED_IT:
      Info (_T("Largest possible IRQ in [%u, %u] is %u."),
	    dwIRQBegin, dwIRQEnd, dwIRQEnd);
      break;
    case FATAL_ERROR:
      Error (_T("Fatal error when finding largest IRQ."));
      goto cleanAndReturn;
      break;
    default:
      IntErr (_T("Wrong return from findMaxSuccessfullyAllocateIrq."));
      goto cleanAndReturn;
    }

  if (inKernelMode() &&
      !checkNumSysIntrsFree (dwIRQBegin, dwIRQEnd, dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  returnVal = TPR_PASS;
  
 cleanAndReturn:

  return (returnVal);
}

/*
 * This test allocates a given set of irqs.  Successfully returned
 * sysintrs and immediately freed.
 *
 * irq range is support through command line options.
 */
TESTPROCAPI  
allocateAll_SingleIRQ(
		      UINT uMsg, 
		      TPPARAM tpParam, 
		      LPFUNCTION_TABLE_ENTRY lpFTE) 
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      return (TPR_NOT_HANDLED);
    }

  breakIfUserDesires ();
  
  DWORD dwAllocationType;
  const TCHAR * szName;
  BOOL bAllowedToFail;
  BOOL bAllowedToPass = TRUE;

  printDisclaimer ();

  /* the ranges supported by the test, inclusive */
  DWORD dwIRQBegin = TEST_IRQ_BEGIN;
  DWORD dwIRQEnd = TEST_IRQ_END;

  if (!handleIrqRange (lpFTE->dwUserData, &dwIRQBegin, &dwIRQEnd,
		       &bAllowedToPass))
    {
      goto cleanAndReturn;
    }

  /*
   * this allows for us to both setup the szName and error check the
   * inbound params 
   */
  switch (lpFTE->dwUserData)
    {
    case (SPECIAL_OAL_INTR_STANDARD | IN_RANGE):
      dwAllocationType = SPECIAL_OAL_INTR_STANDARD;
      szName = _T("standard");
      bAllowedToFail = FALSE;
      break;
    case (OAL_INTR_STATIC | IN_RANGE):
      Info (_T("The static IRQs currently enabled on the system can be"));
      Info (_T("determined from the data below by looking for IRQs that"));
      Info (_T("the test can't allocate.  These IRQs are currently statically"));
      Info (_T("allocated."));
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case (OAL_INTR_DYNAMIC | IN_RANGE):
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      bAllowedToFail = FALSE;
      break;
    case (SPECIAL_OAL_INTR_STANDARD | MUST_FAIL):
      dwAllocationType = SPECIAL_OAL_INTR_STANDARD;
      szName = _T("standard");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_STATIC | MUST_FAIL:
      Info (_T("The static IRQs currently enabled on the system can be"));
      Info (_T("determined from the data below by looking for IRQs that"));
      Info (_T("the test can't allocate.  These IRQs are currently statically"));
      Info (_T("allocated."));
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_DYNAMIC | MUST_FAIL:
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      bAllowedToFail = TRUE;
      break;
    case (SPECIAL_OAL_INTR_STANDARD):
      dwAllocationType = SPECIAL_OAL_INTR_STANDARD;
      szName = _T("standard");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_STATIC:
      Info (_T("The static IRQs currently enabled on the system can be"));
      Info (_T("determined from the data below by looking for IRQs that"));
      Info (_T("the test can't allocate.  These IRQs are currently statically"));
      Info (_T("allocated."));
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_DYNAMIC:
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      bAllowedToFail = TRUE;
      break;
    default:
      Error (_T("Unsupported interrupt allocation type."));
      return (TPR_FAIL);
    }

  DWORD dwNumSysIntrsFree = 0;

  if (inKernelMode() && 
      !getNumSysIntrsFree (dwIRQBegin, dwIRQEnd, &dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  if (!runAllocateAll_SingleIRQ (dwAllocationType, szName, 
				 dwIRQBegin, dwIRQEnd,
				 bAllowedToFail, bAllowedToPass))
    {
      goto cleanAndReturn;
    }

  if (inKernelMode() &&
      !checkNumSysIntrsFree (dwIRQBegin, dwIRQEnd, dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  returnVal = TPR_PASS;

 cleanAndReturn:

  return (returnVal);
}
  

/*
 * This test allocates a given set of irqs.  Successfully returned
 * sysintrs are immediately freed.
 *
 * allocation type is one of the interrupt allocation schemes
 * specified in the documentation.  See #defines for more info.
 * szTypeName is the friendly human readable name for this allocation
 * scheme.  bAllowedToFail specifies whether the allocation is allowed
 * to fail for this test.  If this value is false then the allocation
 * must succeed or the test will fail.
 *
 * return true on success and false otherwise.
 */
BOOL
runAllocateAll_SingleIRQ (DWORD dwAllocationType, 
			  const TCHAR szTypeName[],
			  DWORD dwIRQBegin, DWORD dwIRQEnd,
			  BOOL bAllowedToFail, BOOL bAllowedToPass)
{
  BOOL returnVal = FALSE;

  Info (_T("This test tries to allocate SYSINTRs for the given set of IRQs."));
  Info (_T("These SYSINTRs are immediately freed."));
  Info (_T(""));
  Info (_T("These IRQs will be allocated as %s."), szTypeName);
  Info (_T(""));

  DWORD dwIRQ = dwIRQBegin;
  DWORD dwUpperBoundInclusive = dwIRQEnd;

  Info (_T(""));
  Info (_T("Checking [%u, %u]."), dwIRQ, dwUpperBoundInclusive);

  /* let the test run for as long as possible */
  BOOL bRet = TRUE;

  /* 
   * use just rolled to catch the case in which 
   * dwUpperBoundInclusive == MAX_DWORD 
   */
  for (BOOL bJustRolled = FALSE; 
       dwIRQ <= dwUpperBoundInclusive && !bJustRolled; dwIRQ++)
    {
      DWORD dwSysIntr;

      /* do the allocation.  If succeed then everything is fine */
      if (getSysIntr (dwAllocationType, &dwIRQ, 1, &dwSysIntr))
	{
	  Info (_T("Allocated SYSINTR %u to IRQ %u"), dwSysIntr, dwIRQ);

	  if (!bAllowedToPass)
	    {
      		  Error (_T("IOCTL call succeeded and it shouldn't have."));
		  bRet = FALSE;
	    }

	  if (!checkSysIntrRange (dwSysIntr))
	    {
	      bRet = FALSE;
	    }

	  if (!releaseSysIntr (dwSysIntr))
	    {
	      Error (_T("Couldn't release sysintr %u"), dwSysIntr);
	      bRet = FALSE;
	    }
	}
      else if (!bAllowedToFail)
	{
	  /* 
	   * this type of allocation should always succeed (for
	   * instance dynamic) 
	   */
	  Error (_T("Allocation failed for IRQ %u."), dwIRQ);
	  bRet = FALSE;
	}

      /* if we just worked on MAX_DWORD then want to bail */
      bJustRolled = (dwIRQ == MAX_DWORD);
    } 

  Info (_T(""));

  if (bRet)
    {
      Info (_T("Test passed."));
    }
  else
    {
      Info (_T("Test failed."));
      goto cleanAndReturn;
    }

  returnVal = TRUE;

 cleanAndReturn:

  return (returnVal);
}

/*
 * Test random allocations on multiple IRQs.
 */
TESTPROCAPI  
allocateRand_MultIRQ(
		     UINT uMsg, 
		     TPPARAM tpParam, 
		     LPFUNCTION_TABLE_ENTRY lpFTE) 
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
    { 
      return (TPR_NOT_HANDLED);
    }

  breakIfUserDesires ();
  
  DWORD dwAllocationType;
  /* underlying object can't change, but pointer can */
  const TCHAR * szName;
  BOOL bAllowedToFail;
  BOOL bAllowedToPass = TRUE;

  /* the ranges supported by the test, inclusive */
  DWORD dwIRQBegin = TEST_IRQ_BEGIN;
  DWORD dwIRQEnd = TEST_IRQ_END;

  if (!handleIrqRange (lpFTE->dwUserData, &dwIRQBegin, &dwIRQEnd,
		       &bAllowedToPass))
    {
      goto cleanAndReturn;
    }

  /*
   * this allows for us to both setup the szName and error check the
   * inbound params 
   */
  switch (lpFTE->dwUserData)
    {
    case (OAL_INTR_STATIC | IN_RANGE):
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case (OAL_INTR_DYNAMIC | IN_RANGE):
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      bAllowedToFail = FALSE;
      break;
    case OAL_INTR_FORCE_STATIC | IN_RANGE:
      dwAllocationType = OAL_INTR_FORCE_STATIC;
      szName = _T("OAL_INTR_FORCE_STATIC");
      bAllowedToFail = FALSE;	/* this value is not used */
      break;
    case OAL_INTR_STATIC | MUST_FAIL:
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_DYNAMIC | MUST_FAIL:
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      /* allowed to fail because could be out of irq range */
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_FORCE_STATIC | MUST_FAIL:
      dwAllocationType = OAL_INTR_FORCE_STATIC;
      szName = _T("OAL_INTR_FORCE_STATIC");
      bAllowedToFail = FALSE;	/* this value is not used */
      break;
    case OAL_INTR_STATIC:
      dwAllocationType = OAL_INTR_STATIC;
      szName = _T("OAL_INTR_STATIC");
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_DYNAMIC:
      dwAllocationType = OAL_INTR_DYNAMIC;
      szName = _T("OAL_INTR_DYNAMIC");
      /* allowed to fail because could be out of irq range */
      bAllowedToFail = TRUE;
      break;
    case OAL_INTR_FORCE_STATIC:
      dwAllocationType = OAL_INTR_FORCE_STATIC;
      szName = _T("OAL_INTR_FORCE_STATIC");
      bAllowedToFail = FALSE;	/* this value is not used */
      break;
    default:
      Error (_T("Unsupported interrupt allocation type."));
      return (TPR_FAIL);
    }

  DWORD dwNumSysIntrsFree = 0;

  if (inKernelMode() && 
      !getNumSysIntrsFree (dwIRQBegin, dwIRQEnd, &dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }


  Info (_T("Allocation type for this test is %s."), szName);

  printDisclaimer ();

  DWORD dwMaxIrqPerSysIntr;

  BOOL bMaxIrqOnCmdLine = FALSE;

  if (g_szDllCmdLine != NULL)
    {
      cTokenControl tokens;

      /* break up command line into tokens */
      if (!tokenize ((TCHAR *)g_szDllCmdLine, &tokens))
        {
          Error (_T("Couldn't parse command line."));
          goto cleanAndReturn;
        }

      if (getOptionDWord (&tokens, ARG_STR_MAX_IRQ_PER_SYSINTRS, 
			  &dwMaxIrqPerSysIntr))
        {
	  Info (_T("User specified max irq per sysintrs, which is %u."), 
		dwMaxIrqPerSysIntr);
	  bMaxIrqOnCmdLine = TRUE;
        }
    }

  if (!bMaxIrqOnCmdLine)
    {
      if (bAllowedToPass)
	{
	  /* guess if user doesn't tell us this */
	  Info (_T("Guessing how many IRQs can be allocated to each sysIntr."));
      
	  if (!findMaxIrqPerSysIntr (dwIRQBegin, dwIRQEnd, &dwMaxIrqPerSysIntr, 
				     FALSE))
	    {
	      goto cleanAndReturn;
	    }
	}
      else
	{
	  dwMaxIrqPerSysIntr = MAX_POSSIBLE_IRQ_PER_SYSINTR;
	  Info (_T("We are not allowed to pass, so determining the max number"));
	  Info (_T("of IRQs per SYSINTR will fail.  Using a default value of %u."),
		dwMaxIrqPerSysIntr);
	}
    }

  /* at this point dwMaxIrqPerSysIntr has been initialized */

  if (dwMaxIrqPerSysIntr <= 1)
    {
      Error (_T("This test is applicable only if the platform supports two or"));
       Error (_T("more irqs per sysintr. Skipping the test."));
	  returnVal = TPR_SKIP;
      goto cleanAndReturn;
    }

 if (dwAllocationType == OAL_INTR_FORCE_STATIC)
    {
      /* 
       * this is special cased because it has special contraints on which IRQs
       * it can play with.
       */
      if (!runAllocateRandForceStatic_MultIRQ (dwIRQBegin, dwIRQEnd, 
					       dwMaxIrqPerSysIntr,
					       bAllowedToPass))
	{
	  goto cleanAndReturn;
	}
    }
  else
    {
      if (!runAllocateRand_MultIRQ (dwAllocationType, bAllowedToFail,
				    bAllowedToPass,
				   dwIRQBegin, dwIRQEnd, dwMaxIrqPerSysIntr))
	{
	  goto cleanAndReturn;
	}
    }


  if (inKernelMode() &&
      !checkNumSysIntrsFree (dwIRQBegin, dwIRQEnd, dwNumSysIntrsFree))
    {
      goto cleanAndReturn;
    }

  returnVal = TPR_PASS;

 cleanAndReturn:

  return (returnVal);

}
  

BOOL
runAllocateRand_MultIRQ (DWORD dwAllocationType, BOOL bAllowedToFail,
			 BOOL bAllowedToPass,
			 DWORD dwIRQBegin, DWORD dwIRQEnd, 
			 DWORD dwMaxIrqPerSysIntr)
{
  BOOL returnVal = FALSE;
  
  /* dwMaxIrqPerSysIntr checks for loop overflow */
  if (dwIRQBegin > dwIRQEnd || dwMaxIrqPerSysIntr == 0 || 
      dwMaxIrqPerSysIntr == MAX_DWORD)
    {
      return (FALSE);
    }

  DWORD dwMaxIrqPerSysIntrArraySizeBytes;

  if (DWordMult (dwMaxIrqPerSysIntr, sizeof (DWORD), 
		 &dwMaxIrqPerSysIntrArraySizeBytes) != S_OK)
    {
      Error (_T("Overflow."));
      goto cleanAndReturn;
    }
      
  /* allocate array */
  DWORD * dwIRQs =  (DWORD *) malloc (dwMaxIrqPerSysIntrArraySizeBytes);
  
  if (!dwIRQs)
    {
      Error (_T("Couldn't allocate memory for IRQs."));
      goto cleanAndReturn;
    }

  Info (_T(""));
  Info (_T("Randomly selecting values between [%u, %u]."), dwIRQBegin, dwIRQEnd);

  /* let the test run for as long as possible */
  BOOL bRet = TRUE;

  for (DWORD dwIrqsPerSysIntrs = 1; dwIrqsPerSysIntrs <= dwMaxIrqPerSysIntr;
       dwIrqsPerSysIntrs++)
    {
      Info (_T(""));
      Info (_T("Working on Irqs per SYSINTRs = %u"), dwIrqsPerSysIntrs);

      /* run each for a given number of times */
      for (DWORD dwIt = 0; dwIt < MULT_ALLOC_TEST_IT; dwIt++)
	{
	  DWORD dwSysIntr;

	  /* load array with IRQs */
	  for (DWORD dwPos = 0; dwPos < dwIrqsPerSysIntrs; dwPos++)
	    {
	      /* duplicate IRQs are allowed */
	      dwIRQs[dwPos] = getRandomInRange (dwIRQBegin, dwIRQEnd);
	    }
	  
	  /* do the allocation.  If succeed then everything is fine */
	  if (getSysIntr (dwAllocationType, dwIRQs, dwIrqsPerSysIntrs, 
			  &dwSysIntr))
	    {
	      if (!bAllowedToPass)
		{
		  Error (_T("IOCTL call succeeded and it shouldn't have."));
		  Error (_T("IRQ(s) are: %s"), 
			 collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
		  bRet = FALSE;
		}

	      if (!checkSysIntrRange (dwSysIntr))
		{
		  bRet = FALSE;
		}

	      if (!releaseSysIntr (dwSysIntr))
		{
		  Error (_T("Couldn't release sysintr %u"), dwSysIntr);
		  Error (_T("IRQ(s) are: %s"), 
			 collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
		  bRet = FALSE;
		}
	      else
		{
		  Info (_T("Sucessfully allocated and freed SYSINTR %u. ")
			_T("IRQs %s"),
			dwSysIntr,
			collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
		}

	      if (!allocateRand_checkDuplicateIRQs (dwIRQs, dwIrqsPerSysIntrs,
						    dwAllocationType,
						    bAllowedToFail,
						    bAllowedToPass))
		{
		  bRet = FALSE;
		}

	    }
	  else if (!bAllowedToFail)
	    {
	      /* 
	       * this type of allocation should always succeed (for
	       * instance dynamic) 
	       */
	      Error (_T("Allocation failed."));
	      Error (_T("IRQ(s) are: %s"), 
		     collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
	      bRet = FALSE;
	    }
	}
    }
  
  returnVal = TRUE;

 cleanAndReturn:

  if (dwIRQs)
    {
      free (dwIRQs);
    }
  
  return (returnVal);
}

/*
 * call this with an array of IRQs.  Set up allowed to fail and allowed to pass
 * based from what you know about these IRQs (will their allocate always work?).
 *
 * It will take the first two irqs in the array and duplicate them alternately in 
 * a copy of the array of length dwLen.  The code then makes the call, and figures
 * out if the result is what the user wanted.
 *
 * If the result is correct, you get true.  Else, you get false.
 */
BOOL
allocateRand_checkDuplicateIRQs (const DWORD *const dwIRQsOrig, DWORD dwLen,
				 DWORD dwAllocationType,
				 BOOL bAllowedToFail,
				 BOOL bAllowedToPass)
{
  if (!dwIRQsOrig || dwLen <= 1 || (MAX_DWORD / sizeof (DWORD)) > dwLen)
    {
      return (FALSE);
    }

  BOOL returnVal = FALSE;

  /**** make local copy */
  DWORD * dwIRQs = (DWORD *) malloc (dwLen * sizeof (DWORD));
  
  if (!dwIRQs)
    {
      Error (_T("Couldn't allocate memory."));
      goto cleanAndReturn;
    }

  /* 
   * put duplicate IRQs in array.  Duplicate first two for how many 
   * spots we have.  Note that in the two IRQ case we special case
   * so as to still duplicate IRQs.
   */
  if (dwLen == 2)
    {
      dwIRQs[0] = dwIRQsOrig[0];
      dwIRQs[1] = dwIRQsOrig[0];
    }
  else
    {
      for (DWORD dwPos = 0; dwPos < dwLen; dwPos++)
	{
	  if (dwPos % 2)
	    {
	      dwIRQs[dwPos] = dwIRQsOrig[0];
	    }
	  else
	    {
	      dwIRQs[dwPos] = dwIRQsOrig[1];
	    }
	}
    }

  DWORD dwSysIntr;

  /* do the allocation.  If succeed then everything is fine */
  if (getSysIntr (dwAllocationType, dwIRQs, dwLen, &dwSysIntr))
    {
      if (!bAllowedToPass)
	{
	  Error (_T("IOCTL call succeeded and it shouldn't have."));
	  Error (_T("IRQ(s) are: %s"), 
		 collateIRQs (dwIRQs, dwLen));
	  goto cleanAndReturn;
	}

      if (!checkSysIntrRange (dwSysIntr))
	{
	  goto cleanAndReturn;
	}

      if (!releaseSysIntr (dwSysIntr))
	{
	  Error (_T("Couldn't release sysintr %u"), dwSysIntr);
	  Error (_T("IRQ(s) are: %s"), 
		 collateIRQs (dwIRQs, dwLen));
	  goto cleanAndReturn;
	}
      else
	{
	  Info (_T("Sucessfully allocated and freed SYSINTR %u. ")
		_T("IRQs %s"),
		dwSysIntr,
		collateIRQs (dwIRQs, dwLen));
	}
    }
  else if (!bAllowedToFail)
    {
      /* 
       * this type of allocation should always succeed (for
       * instance dynamic) 
       */
      Error (_T("Allocation failed."));
      Error (_T("IRQ(s) are: %s"), 
	     collateIRQs (dwIRQs, dwLen));
      goto cleanAndReturn;
    }
  
  returnVal = TRUE;

 cleanAndReturn:

  if (dwIRQs)
    {
      free (dwIRQs);
    }

  return (returnVal);
}

/* 
 * This is special cased since FORCE_STATIC is quite powerful
 */
BOOL
runAllocateRandForceStatic_MultIRQ (DWORD dwIRQBegin, DWORD dwIRQEnd, 
				   DWORD dwMaxIrqPerSysIntr, 
				    BOOL bAllowedToPass)
{
  BOOL returnVal = FALSE;

  if (dwIRQBegin > dwIRQEnd || dwMaxIrqPerSysIntr == 0 || 
      dwMaxIrqPerSysIntr == MAX_DWORD)
    {
      return (FALSE);
    }

  DWORD dwMaxIrqPerSysIntrArraySizeBytes;

  if (DWordMult (dwMaxIrqPerSysIntr, sizeof (DWORD), 
		 &dwMaxIrqPerSysIntrArraySizeBytes) != S_OK)
    {
      Error (_T("Overflow."));
      goto cleanAndReturn;
    }
      
  /* allocate array */
  DWORD * dwIRQs =  (DWORD *) malloc (dwMaxIrqPerSysIntrArraySizeBytes);
  

  if (!dwIRQs)
    {
      Error (_T("Couldn't allocate memory for IRQs."));
      goto cleanAndReturn;
    }

  Info (_T(""));
  Info (_T("Randomly selecting values between [%u, %u]."), dwIRQBegin, dwIRQEnd);

  /* let the test run for as long as possible */
  BOOL bRet = TRUE;

  for (DWORD dwIrqsPerSysIntrs = 1; dwIrqsPerSysIntrs <= dwMaxIrqPerSysIntr;
       dwIrqsPerSysIntrs++)
    {
      Info (_T(""));
      Info (_T("Working on Irqs per SYSINTRs = %u"), dwIrqsPerSysIntrs);

      /* how many irq sets we tested */
      DWORD dwTestedNIRQSets = 0;

      /* run each for a given number of times */
      for (DWORD dwIt = 0; dwIt < MULT_ALLOC_TEST_IT; dwIt++)
	{
	  DWORD dwSysIntrOrig, dwSysIntrForceStatic;

	  /* load array with IRQs */
	  for (DWORD dwPos = 0; dwPos < dwIrqsPerSysIntrs; dwPos++)
	    {
	      dwIRQs[dwPos] = getRandomInRange (dwIRQBegin, dwIRQEnd);
	    }
	  
	  /* do the allocation.  If succeed then everything is fine */
	  if (!getSysIntr (OAL_INTR_STATIC, dwIRQs, dwIrqsPerSysIntrs, 
			  &dwSysIntrOrig))
	    {
	      /*
	       * one of these IRQs is already marked as static.
	       * Forcing it as static again could crash the system, so
	       * skipping it. 
	       */
	      Info (_T("Can't force static on this irq set: %s"), 
		    collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
	      continue;
	    }

	  if (!bAllowedToPass)
	    {
	      Error (_T("We shouldn't have been able to force static and we")
		     _T("could."));
	      Error (_T("IRQs: %s"), collateIRQs (dwIRQs, dwIrqsPerSysIntrs));
	      goto cleanAndReturn;
	    }

	  if (!checkSysIntrRange (dwSysIntrOrig))
	    {
	      goto cleanAndReturn;
	    }

	  dwTestedNIRQSets++;

	  /* at this point we have a static map to this IRQ set */
	  
	  /* 
	   * force static should kick off this static map and give us another.
	   * this means that the sysintr will have changed.
	   * old sysintr now becomes dynamic.
	   */
	  if (!getSysIntr (OAL_INTR_FORCE_STATIC, dwIRQs, dwIrqsPerSysIntrs,
			  &dwSysIntrForceStatic))
	    {
	      /* we can't force static.  something is wrong. */
	      Error (_T("We can't force static.  IRQs: %s"), 
		     collateIRQs (dwIRQs, dwIrqsPerSysIntrs));

	      goto cleanAndReturn;
	    }

	  if (dwSysIntrForceStatic == dwSysIntrOrig)
	    {
	      Error (_T("The force static call should have allocated a ")
		     _T("new sysintr."));
	      Error (_T("It did not.  SYSINTR is %u."), dwSysIntrOrig);
	      goto cleanAndReturn;
	    }

	  /* we need to free all sysintrs allocated */

	  BOOL bPrintDebug = FALSE;

	  /* release the sysintrs */
	  if (!releaseSysIntr (dwSysIntrOrig))
	    {
	      Error (_T("Couldn't release sysintr %u"), dwSysIntrOrig);
	      bPrintDebug = TRUE;
	    }
	  
	  if (!releaseSysIntr (dwSysIntrForceStatic))
	    {
	      Error (_T("Couldn't release sysintr %u"), dwSysIntrForceStatic);
	      bPrintDebug = TRUE;
	    }

	  if (bPrintDebug)
	    {
	      Error (_T("Orig sysintr = %u"), dwSysIntrOrig);
	      Error (_T("Force static sysintr = %u"), dwSysIntrForceStatic);
	      Error (_T(""));
	      Error (_T("Couldn't release, so exiting..."));
	      goto cleanAndReturn;
	    }
	} /* for dwIt */

      /* 
       * if we are setup to fail for every case then we don't want to 
       * trigger this error condition.
       */
      if ((dwTestedNIRQSets < 1) && (bAllowedToPass))
	{
	  Info (_T(""));
	  Info (_T("Warning: Didn't find any IRQ sets to test for %u IRQs."), 
		 dwIrqsPerSysIntrs);
	  Info (_T("This can result when the set of IRQs being used for the"));
	  Info (_T("test is a good deal larger than valid set of IRQs on the"));
	  Info (_T("device.  Each randomly allocated set of IRQs will contain"));
	  Info (_T("one that is out of range, and the routines will not allow"));
	  Info (_T("the allocation to proceed."));
	  Info (_T("The test will pass in this case, since other test cases"));
	  Info (_T("cover this functionality.  This message just alerts you"));
	  Info (_T("that this condition was reached in the code."));
	}
      else if (bAllowedToPass)
	{
	  Info (_T(""));
	  Info (_T("Found %u IRQ sets to test for %u IRQs."), 
		dwTestedNIRQSets, dwIrqsPerSysIntrs);
	}

    } /* for number of sysinters in the array */

  if (!bRet)
    {
      goto cleanAndReturn;
    }

  returnVal = TRUE;

 cleanAndReturn:

  if (dwIRQs)
    {
      free (dwIRQs);
    }
  
  return (returnVal);
}


/******************************************************************************/
/*                                                                            */
/*                            Helper  Functions                               */
/*                                                                            */
/******************************************************************************/

/*
 * algorithm: Allocate every SYSINTR on the system.  When we are done,
 * count how many we allocated.  This was the number free to start with
 */
BOOL
findHowManySysIntrsAreFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
			    DWORD * pdwNumFree, BOOL bPrintDebug)
{
  if (!pdwNumFree || dwIRQBegin > dwIRQEnd)
    {
      return (FALSE);
    }

  typedef struct
  {
    DWORD dwRC;			// return code
  } sTrackSysIntrs;

  sTrackSysIntrs pWhichSysIntrsToRelease[SYSINTR_MAXIMUM];

  /* initialize array */
  for (DWORD dwPos = 0; dwPos < SYSINTR_MAXIMUM; dwPos++)
    {
      pWhichSysIntrsToRelease[dwPos].dwRC = FALSE;
    }

  DWORD dwIRQ = dwIRQBegin;

  BOOL bRet = TRUE;

  for (BOOL bJustRolled = FALSE;
       dwIRQ <= dwIRQEnd && !bJustRolled; dwIRQ++)
    {
      DWORD dwSysIntr;
      
      BOOL rc;

      if (!(rc = getSysIntr (dwIRQ, &dwSysIntr)))
	{
	  continue;
	}

      /* if out of range go to next irq */
      if (!checkSysIntrRange (dwSysIntr) || dwSysIntr >= SYSINTR_MAXIMUM)
	{
	  bRet = FALSE;
	  continue;
	}
	  
      /*
       * we are now sure that an array access wouldn't cause a buffer
       * overflow 
       */

      if (pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  Error (_T("SYSINTR %u has already been allocated to IRQ %u."),
		 dwSysIntr, dwIRQ);
	  Error (_T("Routines just allocated it to IRQ %u as well."),
		 dwIRQ);
	  bRet = FALSE;
	  continue;
	}

      /* sysIntr is clear.  Record that we got it back. */
      pWhichSysIntrsToRelease[dwSysIntr].dwRC = TRUE;

      if (bPrintDebug)
	{
	  Info (_T("Test allocated SYSINTR %u to IRQ %u"), dwSysIntr, dwIRQ);
	}

      /* if we just worked on MAX_DWORD then want to bail */
      bJustRolled = (dwIRQ == MAX_DWORD);

    } /* for */

  /* count how many sysintrs we allocated.*/
  DWORD dwCount = 0;

  for (DWORD dwSysIntr = 0; dwSysIntr < SYSINTR_MAXIMUM; dwSysIntr++)
    {
      if (pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  dwCount++;
	}
    }

  /* release them - these should succeed */
  for (DWORD dwSysIntr = 0; dwSysIntr < SYSINTR_MAXIMUM; dwSysIntr++)
    {
      /* only work on SYSINTERs that were successfully allocated */
      if (!pWhichSysIntrsToRelease[dwSysIntr].dwRC)
	{
	  continue;
	}

      if (!releaseSysIntr (dwSysIntr))
	{
	  Error (_T("Error!  Weren't able to release SYSINTR %u."), dwSysIntr);
	  bRet = FALSE;
	}
    }

  if (bRet)
    {
      *pdwNumFree = dwCount;
    }

  return (bRet);
}


/*
 * handle the different modes for the irq range.
 *
 * The irq range varies, depending on whether we are working IN_RANGE, and
 * whether we are given command line args.  If not, we want to guess which
 * values to use.
 *
 * This function is needed because everything is platform dependent.  The test 
 * has to guess at every value on the system before it can run.  some testers
 * will be able to tell it the values.  Other won't, and we have to support
 * this case as well.
 *
 * dwUserData is the parameter from the tux function able.  Everything else is
 * pointers to parameters used for the test.
 */
BOOL
handleIrqRange (DWORD dwUserData, DWORD * pdwArgIRQBegin, DWORD * pdwArgIRQEnd,
		BOOL * pbAllowedToPass)
{
  if (!pdwArgIRQBegin || !pdwArgIRQEnd || !pbAllowedToPass)
    {
      return (FALSE);
    }

  BOOL returnVal = FALSE;

  /* use these locally and copy over outbound args only if everything works */
  DWORD dwIRQBegin = *pdwArgIRQBegin;
  DWORD dwIRQEnd = *pdwArgIRQEnd;

  if (dwUserData & IN_RANGE)
    {
      DWORD dwIRQBeginOrig = dwIRQBegin;
      DWORD dwIRQEndOrig = dwIRQEnd;

      /* adjust the ranges if need be */
      if (!handleCmdLineIRQRange (&dwIRQBegin, &dwIRQEnd))
	{
	  Error (_T("Couldn't parse command line."));
	  goto cleanAndReturn;
	}

      if ((dwIRQBeginOrig == dwIRQBegin) && (dwIRQEndOrig == dwIRQEnd))
	{
	  Info (_T("Command line params were either not given or the same"));
	  Info (_T("as the defaults.  Guessing at the largest IRQ on the"));
	  Info (_T("system."));
	  Info (_T(""));

	  DWORD dwMaxIRQ;


	  switch (findMaxSuccessfullyAllocatedIrq (dwIRQBegin, dwIRQEnd,
						   &dwMaxIRQ))
	    {
	    case FATAL_ERROR:
	      Error (_T("Fatal error calculating largest possible IRQ."));
	      goto cleanAndReturn;
	    case MISSED_IT:
	      Info (_T("Couldn't find largest possible IRQ.  Using defaults."));
	      dwIRQBegin = dwIRQBeginOrig;
	      dwIRQEnd = dwIRQEndOrig;
	      break;
	    case FOUND_IT:
	      dwIRQBegin = dwIRQBeginOrig;
	      dwIRQEnd = dwMaxIRQ;
	      Info (_T("Successfully found largest possible IRQ.  New bounds ")
		    _T("are [%u, %u]."), dwIRQBegin, dwMaxIRQ);
	      break;
	    default:
	      IntErr (_T("Wrong return from findMaxSuccessfullyAllocatedIrq"));
	      goto cleanAndReturn;
	    }
	}      
    }
  else
    {
      Info (_T("Ignoring user supplied IRQ params (if given).  Use the"));
      Info (_T("in range tests to confirm that the IOCTLs are returning"));
      Info (_T("the correct return values for IRQs within the OALs"));
      Info (_T("supported range."));
      Info (_T(""));
    }

  if (dwUserData & MUST_FAIL)
    {
      Info (_T("No IOCTL calls are allowed to pass for this test."));
      Info (_T(""));
      *pbAllowedToPass = FALSE;
    }

  /* return correctly munged values to the user */
  *pdwArgIRQBegin = dwIRQBegin;
  *pdwArgIRQEnd = dwIRQEnd;

  returnVal = TRUE;

 cleanAndReturn:

  return (returnVal);
}


BOOL
getNumSysIntrsFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		    DWORD * pdwNumSysIntrsFree)
{
  if (dwIRQBegin > dwIRQEnd || !pdwNumSysIntrsFree || !inKernelMode())
    {
      Error (_T("bad args or not in kernel mode."));
      return (FALSE);
    }

  BOOL rc = findHowManySysIntrsAreFree (dwIRQBegin, dwIRQEnd, 
					pdwNumSysIntrsFree, FALSE);

  if (rc)
    {
      Info (_T("  ** Sanity check:  %u SYSINTRs free"), *pdwNumSysIntrsFree);
    }
  
  return (rc);
}

BOOL
checkNumSysIntrsFree (DWORD dwIRQBegin, DWORD dwIRQEnd, 
		      DWORD dwNumSysIntrsFree)
{
  if (dwIRQBegin > dwIRQEnd || !inKernelMode())
    {
      Error (_T("bad args or not in kernel mode."));
      return (FALSE);
    }

  DWORD dwNewNumSysIntrsFree;

  BOOL bRet = findHowManySysIntrsAreFree (dwIRQBegin, dwIRQEnd, 
					  &dwNewNumSysIntrsFree, FALSE);
  
  if (dwNewNumSysIntrsFree != dwNumSysIntrsFree)
    {
      Error (_T("We started with %u SYSINTRs free and ended up with %u ")
	     _T("free."), dwNumSysIntrsFree, dwNewNumSysIntrsFree);
      Error (_T("This sanity check failed.  Failing the test."));
      bRet = FALSE;
    }

  return (bRet);
}

