/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    eisac.c

Abstract:

    This module implements routines to get EISA configuration information.

Author:

    Shie-Lin Tzong (shielint) 10-June-1991

Environment:

    16-bit real mode.


Revision History:

    John Vert (jvert) 5-Sep-1991
        Moved into the SU module of portable bootloader

--*/
#include "su.h"
#include "eisa.h"

//
// HACKHACK - John Vert (jvert) 12-Sep-1991
//      We have to initialize this or else it gets stuck in our BSS section
//      which is right in the middle of the osloader.exe header
//
extern BTEISA_FUNCTION_INFORMATION FunctionInformation;


BOOLEAN
FindFunctionInformation (
    IN UCHAR SlotFlags,
    IN UCHAR FunctionFlags,
    OUT PBTEISA_FUNCTION_INFORMATION Buffer,
    IN BOOLEAN FromBeginning
    )

/*++

Routine Description:

    This routine finds function information that matches the specified
    flags.  It starts, either where it left off last time, or at the
    beginning (slot 0, function 0)

Arguments:

    Flags - Flags to check against EISA function and slot information.

    Buffer - pointer to buffer to store EISA information in.

    FromBeginning - if TRUE, search starts at slot 0, function 0.
                    else continue from where it left off last time.

Return Value:

    TRUE - If the operation is success (Buffer is filled in.)
    FALSE - Request fails.

    Notes: The buffer is always changed, reguardless of the success
    of the function.  When failure is returned, the info is invalid.

--*/

{
    static UCHAR Slot=0;
    static UCHAR Function=0;
    BTEISA_SLOT_INFORMATION  SlotInformation;
    UCHAR Flags;
    UCHAR ReturnCode;

    if (FromBeginning) {
        Slot = 0;
        Function = 0;
    }
    BtGetEisaSlotInformation(&SlotInformation, Slot);
    while (SlotInformation.ReturnCode != EISA_INVALID_SLOT) {

        //
        // insure that the slot is not empty, and all of the flags are set.
        // the flags are tested by performing the following logic:
        //
        // -- (RequestSlotFlags XOR (SlotFlags AND RequestSlotFlags)) --
        //
        // if all the requested flags are set, the result will be zero
        //

        if ((SlotInformation.ReturnCode != EISA_EMPTY_SLOT) &&
            (!(SlotFlags ^ (SlotInformation.FunctionInformation & SlotFlags)))) {

            while (SlotInformation.NumberFunctions > Function) {
                ReturnCode = BtGetEisaFunctionInformation(Buffer, Slot, Function);
                Function++;

                //
                // if function call succeeded
                //

                if (!ReturnCode){

                    Flags = Buffer->FunctionFlags;

                    //
                    // Function Enable/Disable bit reversed.
                    //

                    Flags |= (~Flags & EISA_FUNCTION_ENABLED);

                    //
                    // insure that all the function flags are set.
                    // the flags are tested by performing the following logic:
                    //
                    // -- (ReqFuncFlags XOR (FuncFlags AND ReqFuncFlags)) --
                    //
                    // if all the requested flags are set, the result will
                    // be zero
                    //

                    if (!(FunctionFlags ^ (Flags & FunctionFlags))) {
                        return TRUE;
                    }
                }

            }
        }
        Slot++;
        Function = 0;
        BtGetEisaSlotInformation(&SlotInformation, Slot);
    }

    Slot = 0;
    Function = 0;
    return FALSE;
}

VOID
InsertDescriptor (
    ULONG Address,
    ULONG Size
    )

/*++

Routine Description:

    This routine inserts a descriptor into the correct place in the
    memory descriptor list.

Arguments:

    Address - Starting address of the memory block.

    Size - Size of the memory block to be inserted.

Return Value:

    None.

--*/

{
    MEMORY_LIST_ENTRY _far *CurrentEntry;

#ifdef DEBUG1
    BlPrint("Inserting descriptor %lx at %lx\n",Size,Address);
        _asm {
            push    ax
            mov     ax, 0
            int     16h
            pop     ax
        }
#endif
    //
    // Search the spot to insert the new descriptor.
    //

    CurrentEntry = MemoryDescriptorList;

    while (CurrentEntry->BlockSize > 0) {
        //
        // Check to see if this memory descriptor is contiguous with
        // the current one.  If so, coalesce them.  (yes, some machines
        // will return memory descriptors that look like this.  Compaq
        // Prosignia machines)
        //
        if (Address+Size == CurrentEntry->BlockBase) {
#ifdef DEBUG1
            BlPrint("  coalescing with descriptor at %lx (%lx)\n",
                    CurrentEntry->BlockBase,
                    CurrentEntry->BlockSize);
#endif
            CurrentEntry->BlockBase = Address;
            CurrentEntry->BlockSize += Size;
#ifdef DEBUG1
            BlPrint("  new descriptor at %lx (%lx)\n",
                    CurrentEntry->BlockBase,
                    CurrentEntry->BlockSize);
#endif
            break;
        }
        if (Address == (CurrentEntry->BlockBase + CurrentEntry->BlockSize)) {
#ifdef DEBUG1
            BlPrint("  coalescing with descriptor at %lx (%lx)\n",
                    CurrentEntry->BlockBase,
                    CurrentEntry->BlockSize);
#endif
            CurrentEntry->BlockSize += Size;
#ifdef DEBUG1
            BlPrint("  new descriptor at %lx (%lx)\n",
                    CurrentEntry->BlockBase,
                    CurrentEntry->BlockSize);
#endif
            break;
        }

        CurrentEntry++;
    }

    if (CurrentEntry->BlockSize == 0) {
        //
        // If CurrentEntry->BlockSize == 0, we  have reached the end of the list
        // So, insert the new descriptor here, and create a new end-of-list entry
        //
        CurrentEntry->BlockBase = Address;
        CurrentEntry->BlockSize = Size;

        ++CurrentEntry;
        //
        // Create a new end-of-list marker
        //
        CurrentEntry->BlockBase = 0L;
        CurrentEntry->BlockSize = 0L;
    }
#ifdef DEBUG1
    //
    // Wait for a keypress
    //
        _asm {
            push    ax
            mov     ax, 0
            int     16h
            pop     ax
        }
#endif

}

ULONG
EisaConstructMemoryDescriptors (
    VOID
    )

/*++

Routine Description:

    This routine gets the information EISA memory function above 16M
    and creates entries in the memory Descriptor array for them.

Arguments:

    None.

Return Value:

    Number of pages of usable memory.

--*/

{
    BOOLEAN Success;
    PBTEISA_MEMORY_CONFIGURATION MemoryConfiguration;
    ULONG Address;
    ULONG EndAddress;
    ULONG Size;
    ULONG MemorySize=0;
    ULONG IsaMemUnder1Mb=0xffffffff;
    MEMORY_LIST_ENTRY _far *CurrentEntry;

    //
    // HACKHACK John Vert (jvert) 5-Mar-1993
    //
    // See if there is already a memory descriptor for the 640k under
    // 1Mb.  If so, we will believe it instead of the EISA routine.  This
    // is because many EISA routines will always return 640k, even if
    // the disk parameter table is in the last 1k.  The ISA routines will
    // always account for the disk parameter tables.  If we believe the
    // EISA routines, we can overwrite the disk parameter tables, causing
    // much grief.
    //
    CurrentEntry = MemoryDescriptorList;
    while (CurrentEntry->BlockSize > 0) {
        if (CurrentEntry->BlockBase == 0) {
            //
            // found a descriptor starting at zero with a size > 0, so
            // this is the one we want to override the EISA information.
            //
            IsaMemUnder1Mb = CurrentEntry->BlockSize;
            break;
        }
        ++CurrentEntry;
    }

    //
    // Initialize the first entry in the list to zero (end-of-list)
    //

    MemoryDescriptorList->BlockSize = 0;
    MemoryDescriptorList->BlockBase = 0;

    Success = FindFunctionInformation(
                              EISA_HAS_MEMORY_ENTRY,
                              EISA_FUNCTION_ENABLED | EISA_HAS_MEMORY_ENTRY,
                              &FunctionInformation,
                              TRUE
                              );

    //
    // while there are more memory functions, and more free descriptors
    //

    while (Success) {

        MemoryConfiguration = &FunctionInformation.EisaMemory[0];

        do {

            //
            // Get physical address of the memory.
            // Note: physical address is stored divided by 100h
            //

            Address = (((ULONG)MemoryConfiguration->AddressHighByte << 16)
                      + MemoryConfiguration->AddressLowWord) * 0x100;

            //
            // Get the size of the memory block.
            // Note: Size is stored divided by 400h with the value of 0
            //       meaning a size of 64M
            //

            if (MemoryConfiguration->MemorySize) {
                Size = ((ULONG)MemoryConfiguration->MemorySize) * 0x400;
            } else {
                Size = (_64MEGB);
            }

#ifdef DEBUG1
            BlPrint("EISA memory at %lx  Size=%lx  Type=%x ",
                    Address,
                    Size,
                    MemoryConfiguration->ConfigurationByte);

            if ((MemoryConfiguration->ConfigurationByte.Type == EISA_SYSTEM_MEMORY) &&
                (MemoryConfiguration->ConfigurationByte.ReadWrite == EISA_MEMORY_TYPE_RAM) ) {

                BlPrint("  (USED BY NT)\n");
            } else {
                BlPrint("  (not used)\n");
            }
#endif

            //
            // Compute end address to determine if any part of the block
            // is above 16M
            //

            EndAddress = Address + Size;

            //
            // If it is SYSTEM memory and RAM, add the descriptor to the list.
            //

            if ((MemoryConfiguration->ConfigurationByte.Type == EISA_SYSTEM_MEMORY) &&
                (MemoryConfiguration->ConfigurationByte.ReadWrite == EISA_MEMORY_TYPE_RAM) ) {

                if (Address==0) {
                    //
                    // This is the descriptor for the memory under 1Mb.
                    // Compare it with the ISA routine's result, and see
                    // if the ISA one is smaller.  If it is, use the ISA
                    // answer.
                    //
                    if (Size > IsaMemUnder1Mb) {
                        Size = IsaMemUnder1Mb;
                    }
                }
                InsertDescriptor(Address, Size);
                MemorySize += (Size >> 12);
            }

        } while (MemoryConfiguration++->ConfigurationByte.MoreEntries);

        Success = FindFunctionInformation(
                                  EISA_HAS_MEMORY_ENTRY,
                                  EISA_FUNCTION_ENABLED | EISA_HAS_MEMORY_ENTRY,
                                  &FunctionInformation,
                                  FALSE
                                  );
    }
#ifdef DEBUG1
    //
    // Wait for a keypress
    //
        _asm {
            push    ax
            mov     ax, 0
            int     16h
            pop     ax
        }
#endif
    return(MemorySize);
}

BOOLEAN
Int15E820 (
    E820Frame       *Frame
    );


BOOLEAN
ConstructMemoryDescriptors (
    VOID
    )
/*++

Routine Description:

Arguments:


Return Value:


--*/
{
    ULONG           BAddr, EAddr;
    E820Frame       Frame;

    //
    // Initialize the first entry in the list to zero (end-of-list)
    //

    MemoryDescriptorList->BlockSize = 0;
    MemoryDescriptorList->BlockBase = 0;

    //
    // Any entries returned for E820?
    //

    Frame.Key = 0;
    Frame.Size = sizeof (Frame.Descriptor);
    Int15E820 (&Frame);
    if (Frame.ErrorFlag  ||  Frame.Size < sizeof (Frame.Descriptor)) {
        return FALSE;
    }

    //
    // Found memory in table, use the reported memory
    //

    Frame.Key = 0;
    do {
        Frame.Size = sizeof (Frame.Descriptor);
        Int15E820 (&Frame);
        if (Frame.ErrorFlag  ||  Frame.Size < sizeof (Frame.Descriptor)) {
            break ;
        }

#ifdef DEBUG1
        BlPrint("E820: %lx  %lx:%lx %lx:%lx %lx %lx\n",
            Frame.Size,
            Frame.Descriptor.BaseAddrHigh,  Frame.Descriptor.BaseAddrLow,
            Frame.Descriptor.SizeHigh,      Frame.Descriptor.SizeLow,
            Frame.Descriptor.MemoryType,
            Frame.Key
            );

            _asm {
                push    ax
                mov     ax, 0
                int     16h
                pop     ax
            }
#endif

        BAddr = Frame.Descriptor.BaseAddrLow;
        EAddr = Frame.Descriptor.BaseAddrLow + Frame.Descriptor.SizeLow - 1;

        //
        // All the processors we have right now only support 32 bits
        // If the upper 32 bits of the Base Address is non-zero, then
        // this range is entirely above the 4g mark and can be ignored
        //

        if (Frame.Descriptor.BaseAddrHigh == 0) {

            if (EAddr < BAddr) {
                //
                // address wrapped - truncate the Ending address to
                // 32 bits of address space
                //

                EAddr = 0xFFFFFFFF;
            }

            //
            // Based upon the address range descriptor type, find the
            // available memory and add it to the descriptor list
            //

            switch (Frame.Descriptor.MemoryType) {
                case 1:
                    //
                    // This is a memory descriptor
                    //

                    InsertDescriptor (BAddr, EAddr - BAddr + 1);
                    break;
            }
        }

    } while (Frame.Key) ;

    return TRUE;
}



