/*++

Copyright (c) 1993 Microsoft Corporation

Module Name:

    spmemory.c

Abstract:

    Memory allocation routines for text setup.

Author:

    Ted Miller (tedm) 29-July-1993

Revision History:

--*/



#include "spprecmp.h"
#pragma hdrstop

PVOID
SpMemAlloc(
    IN SIZE_T Size
    )
{
    return(SpMemAllocEx(Size,'pteS', PagedPool));
}

PVOID
SpMemAllocNonPagedPool(
    IN SIZE_T Size
    )
{
    return(SpMemAllocEx(Size,'pteS', NonPagedPool));
}

PVOID
SpMemAllocEx(
    IN SIZE_T Size,
    IN ULONG Tag,
    IN POOL_TYPE Type
    )

/*++

Routine Description:

    This function is guaranteed to succeed.

Arguments:

Return Value:

--*/

{
    PSIZE_T p;

    //
    // Add space for storing the size of the block.
    //
#if defined(SETUP_TEST_USERMODE)
    p = RtlAllocateHeap(RtlProcessHeap(), 0, Size + (2 * sizeof(SIZE_T)));
#else
    p = ExAllocatePoolWithTag(Type, Size + (2 * sizeof(SIZE_T)), Tag);
#endif

    if(!p) {

        SpOutOfMemory();
    }

    //
    // Store the size of the block, and return the address
    // of the user portion of the block.
    //
    *p = Tag;
    *(p + 1) = Size;

    return(p + 2);
}



PVOID
SpMemRealloc(
    IN PVOID Block,
    IN SIZE_T NewSize
    )

/*++

Routine Description:

    This function is guaranteed to succeed.

Arguments:

Return Value:

--*/

{
    PSIZE_T NewBlock;
    SIZE_T  OldSize;
    ULONG   OldTag;

    //
    // Get the size of the block being reallocated.
    //
    OldTag = (ULONG)((PSIZE_T)Block)[-2];
    OldSize = ((PSIZE_T)Block)[-1];

    //
    // Allocate a new block of the new size.
    //
    NewBlock = SpMemAllocEx(NewSize, OldTag, PagedPool);
    ASSERT(NewBlock);

    //
    // Copy the old block to the new block.
    //
    if (NewSize < OldSize) {
        RtlCopyMemory(NewBlock, Block, NewSize);
    } else {
        RtlCopyMemory(NewBlock, Block, OldSize);
    }

    //
    // Free the old block.
    //
    SpMemFree(Block);

    //
    // Return the address of the new block.
    //
    return(NewBlock);
}


VOID
SpMemFree(
    IN PVOID Block
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
extern PWSTR CommonStrings[11];
unsigned long i;

    if (Block == NULL)
        return;

    for( i = 0; i < sizeof(CommonStrings)/sizeof(PWSTR); i++ ) {
        if( (PWSTR)Block == CommonStrings[i] ) {
            return;
        }
    }

    //
    // Free the block at its real address.
    //
#if defined(SETUP_TEST_USERMODE)
    RtlFreeHeap(RtlProcessHeap(), 0, (PULONG_PTR)Block - 2);
#else
    ExFreePool((PULONG_PTR)Block - 2);
#endif
}


VOID
SpOutOfMemory(
    VOID
    )
{
    KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Out of memory\n"));

#if !defined(SETUP_TEST_USERMODE)

    if(VideoInitialized) {
        if(KbdLayoutInitialized) {

            ULONG ValidKeys[2] = { KEY_F3,0 };

            //
            // We run a high risk of getting into an infinite loop
            // here because SpStartScreen will result in a call to
            // SpMemAlloc(), which will fail and call SpOutOfMemory
            // again.  In order to get around this, we'll jettison
            // some memory that we won't need anymore (since we're
            // about to die).  These should give us enough memory
            // to display the messages below.
            //
            SpFreeBootVars();
            SpFreeArcNames();

            while(1) {
                SpStartScreen(SP_SCRN_OUT_OF_MEMORY,5,0,FALSE,TRUE,DEFAULT_ATTRIBUTE);

                SpDisplayStatusOptions(
                    DEFAULT_STATUS_ATTRIBUTE,
                    SP_STAT_F3_EQUALS_EXIT,
                    0
                    );

                if(SpWaitValidKey(ValidKeys,NULL,NULL) == KEY_F3) {
                    SpDone(0,FALSE,TRUE);
                }
            }
        } else {
            //
            // we haven't loaded the layout dll yet, so we can't prompt for a keypress to reboot
            //
            SpStartScreen(SP_SCRN_OUT_OF_MEMORY_RAW,5,0,FALSE,TRUE,DEFAULT_ATTRIBUTE);

            SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_KBD_HARD_REBOOT, 0);

            while(TRUE);    // Loop forever
        }
    } else {
        SpDisplayRawMessage(SP_SCRN_OUT_OF_MEMORY_RAW, 2);
        while(TRUE);    // loop forever
    }
#endif
}