//                                        Ruler
//       1         2         3         4         5         6         7         8
//345678901234567890123456789012345678901234567890123456789012345678901234567890

/********************************************************************/
/*                                                                  */
/*   The standard layout.                                           */
/*                                                                  */
/*   The standard layout for 'cpp' files in this code is as         */
/*   follows:                                                       */
/*                                                                  */
/*      1. Include files.                                           */
/*      2. Constants local to the class.                            */
/*      3. Data structures local to the class.                      */
/*      4. Data initializations.                                    */
/*      5. Static functions.                                        */
/*      6. Class functions.                                         */
/*                                                                  */
/*   The constructor is typically the first function, class         */
/*   member functions appear in alphabetical order with the         */
/*   destructor appearing at the end of the file.  Any section      */
/*   or function this is not required is simply omitted.            */
/*                                                                  */
/********************************************************************/

#include "InterfacePCH.hpp"

#include "Common.hpp"
#include "RockallPhysicalBackEnd.hpp"
#if defined(_XBOX) || defined(_XENON)
#include <Xtl.h>
#endif


/********************************************************************/
/*                                                                  */
/*   Static member initialization.                                  */
/*                                                                  */
/*   Static member initialization sets the initial value for all    */
/*   static members.                                                */
/*                                                                  */
/********************************************************************/

#pragma init_seg(compiler)
ROCKALL_PHYSICAL_BACK_END ROCKALL_PHYSICAL_BACK_END::DefaultBaseClass(PAGE_READWRITE);
ROCKALL_PHYSICAL_BACK_END ROCKALL_PHYSICAL_BACK_END::DefaultBaseClassWC(PAGE_READWRITE | PAGE_WRITECOMBINE);

/********************************************************************/
/*                                                                  */
/*   Class constructor.                                             */
/*                                                                  */
/*   The may be a situation where the Rockall Back End needs a      */
/*   constructor but this is certainly not expected to be           */
/*   very common.                                                   */
/*                                                                  */
/********************************************************************/

ROCKALL_PHYSICAL_BACK_END::ROCKALL_PHYSICAL_BACK_END( DWORD protect ) : mProtect(protect)
{ /* void */ }

/********************************************************************/
/*                                                                  */
/*   Delete allocation area.                                        */
/*                                                                  */
/*   All memory requests are eventually sent back to the external   */
/*   deallocator.  This function can be overloaded so that memory   */
/*   can be provided from any source.  The default is to send       */
/*   it back to the operating system.                               */
/*                                                                  */
/********************************************************************/

void ROCKALL_PHYSICAL_BACK_END::DeleteArea( void *Memory,int Size,bool User )
{
#ifdef DEBUGGING
#ifdef ENABLE_ALLOCATION_STATISTICS
	//
	//  When we are debugging print out trace information.
	//  
	DebugPrint( "Delete\t 0x%08x %d bytes by %s request\n",Memory,Size, User?"user":"system" );
#endif
#endif
	
#if defined(_XBOX) || defined(_XENON)
	if (User)
	{
		XPhysicalFree(Memory);
	}
	else 
#endif
	if ( VirtualFree( Memory,0,MEM_RELEASE ) == NULL )
	{ 
	   Failure( "Delete fails in DeleteArea" ); 
	}
}


/********************************************************************/
/*                                                                  */
/*   New allocation area.                                           */
/*                                                                  */
/*   All memory requests are eventually sent to the new external    */
/*   allocator.  This function can be overloaded so that memory     */
/*   can be provided from any source.  The default is to get        */
/*   new memory from the operating system.                          */
/*                                                                  */
/********************************************************************/

void *ROCKALL_PHYSICAL_BACK_END::NewArea( int AlignMask,int Size,bool User )
{
	//
	//   When there is an alignment requirement greater
	//   than the natural alignment provided by the
	//   operating system we have to play various tricks
	//   to allocate a suitable block.  If not then we
	//   just do a normal allocation call.
	//
	//	 (If the request is for physical memory, we just go to OS and hope we can do it)
	//
#if defined(_XBOX) || defined(_XENON)	
	if (User)
	{
      REGISTER VOID *NewMemory;
      
   #ifdef USE_LARGE_PAGES 
      NewMemory = XPhysicalAlloc(Size, MAXULONG_PTR, AlignMask+1, mProtect | MEM_LARGE_PAGES);
   #else
      NewMemory = XPhysicalAlloc(Size, MAXULONG_PTR, AlignMask+1, mProtect);
   #endif

#ifdef DEBUGGING
#ifdef ENABLE_ALLOCATION_STATISTICS
      if ( NewMemory != NULL )
      {
         //
         //  When we are debugging output out trace
         //  information.
         //  
         DebugPrint( "New physical allocator memory allocated at\t 0x%08x of %d bytes (by %s request)\n",
            NewMemory, Size, User?"user":"system");
      }
#endif
#endif

      return ((void*) NewMemory);
	}
	else
#endif	
		
	if ( AlignMask > NaturalSize() )
	{
		REGISTER SBIT32 NewSize = (AlignMask + Size);
		assert(NewSize>0 && NewSize>=Size); //PREfix wants to ensure things are right

		//
		//   We need to allocate a block with an 
		//   alignment requirement greater than 
		//   the operating system default.  So we
		//   allocate a much larger block and
		//   release the parts we don't need.
		//
		while ( True )
		{
			REGISTER VOID *Reserved =
				(
				VirtualAlloc
				( 
				NULL,
				((DWORD) NewSize),
				MEM_RESERVE
#ifdef USE_LARGE_PAGES
				|MEM_LARGE_PAGES
#endif
				,PAGE_READWRITE 
				)
				);

			//
			//   Lets ensure we were able to find a suitable
			//   memory block.  If not then we exit.
			//
			if ( Reserved != NULL )
			{
				//
				//   We just want to return the parts of
				//   the block we don't need but 'NT' is  
				//   not smart enough.  So we free the  
				//   entire block.
				//
				if ( VirtualFree( Reserved,0,MEM_RELEASE ) )
				{
					REGISTER SNATIVE Address = ((SNATIVE) Reserved);
					REGISTER VOID *NewMemory;

					//
					//   Compute the base address of the part 
					//   of the block we really want to allocate.
					//
					Address = ((Address + AlignMask) & ~AlignMask);

					//
					//   Finally, lets reallocate the part of  
					//   the block we wanted but just released   
					//   and hope that nobody else got it before
					//   us.
					//
					NewMemory =
						(
						VirtualAlloc
						( 
						((LPVOID) Address),
						((DWORD) Size),
						MEM_RESERVE | MEM_COMMIT
#ifdef USE_LARGE_PAGES
						|MEM_LARGE_PAGES
#endif
						,PAGE_READWRITE 
						)
						);

					//
					//   If it all worked we can exit.
					//
					if ( NewMemory != NULL )
					{ 
#ifdef DEBUGGING
#ifdef ENABLE_ALLOCATION_STATISTICS
						//
						//  When we are debugging output 
						//  out trace information.
						//  
						DebugPrint
							( 
							"New\t\t at 0x%08x of %d bytes (by %s request)\n",
							NewMemory,
							Size,
							User?"user":"system"
							);

#endif
#endif
						return ((void*) NewMemory); 
					}
				}
				else
				{ return ((void*) AllocationFailure); }

			}
			else
			{ return ((void*) AllocationFailure); }
		}
	}
	else
	{
		REGISTER VOID *NewMemory;

		//
		//   We can allocate directly from the operating 
		//   system as the default alignment requirement 
		//   is enough for this case.
		//
		NewMemory =
			(
			VirtualAlloc
			( 
			NULL,
			((DWORD) Size),
			MEM_COMMIT
#ifdef USE_LARGE_PAGES
			|MEM_LARGE_PAGES
#endif
			,PAGE_READWRITE
			)
			);
#ifdef DEBUGGING
#ifdef ENABLE_ALLOCATION_STATISTICS

		if ( NewMemory != NULL )
		{
			//
			//  When we are debugging output out trace
			//  information.
			//  
			DebugPrint( "New physical allocator memory allocated at\t 0x%08x of %d bytes (by %s request)\n",
				NewMemory, Size, User?"user":"system");
		}
#endif
#endif

		return ((void*) NewMemory);
	}
}

/********************************************************************/
/*                                                                  */
/*   Class destructor.                                              */
/*                                                                  */
/*   The may be a situation where the Rockall Back End needs a      */
/*   destructor but this is certainly not expected to be            */
/*   very common.                                                   */
/*                                                                  */
/********************************************************************/

ROCKALL_PHYSICAL_BACK_END::~ROCKALL_PHYSICAL_BACK_END( void )
{ /* void */ }
