/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    cmconfig.c

Abstract:

    This module is responsible to build the hardware tree of the
    registry data base.

--*/

#include "cmp.h"

//
// Title Index - Never used for Product 1, set to 0 for now.
//

#define TITLE_INDEX_VALUE 0


extern ULONG CmpTypeCount[];


#define EISA_ADAPTER_INDEX EisaAdapter
#define TURBOCHANNEL_ADAPTER_INDEX TcAdapter

//
// The following variables are used to cross-reference multifunction
// adapters to their corresponding NT interface type.
//

extern struct {
    PUCHAR  AscString;
    USHORT  InterfaceType;
    USHORT  Count;
} CmpMultifunctionTypes[];

extern USHORT CmpUnknownBusCount;


//
// CmpConfigurationData - A pointer to the area reserved for the purpose
//     of reconstructing Configuration Data.
//
// CmpConfigurationAreaSize - Record the size of the Configuration Data
//     area.

extern ULONG CmpConfigurationAreaSize;
extern PCM_FULL_RESOURCE_DESCRIPTOR CmpConfigurationData;

//
// Function prototypes for internal references
//

NTSTATUS
CmpSetupConfigurationTree(
     IN PCONFIGURATION_COMPONENT_DATA CurrentEntry,
     IN HANDLE ParentHandle,
     IN INTERFACE_TYPE InterfaceType,
     IN ULONG BusNumber
     );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,CmpInitializeHardwareConfiguration)
#pragma alloc_text(INIT,CmpSetupConfigurationTree)
#pragma alloc_text(INIT,CmpInitializeRegistryNode)
#endif


NTSTATUS
CmpInitializeHardwareConfiguration(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock
    )
/*++

Routine Description:

    This routine creates \\Registry\Machine\Hardware node in
    the registry and calls SetupTree routine to put the hardware
    information to the registry.

Arguments:

    LoaderBlock - supplies a pointer to the LoaderBlock passed in from the
                  OS Loader.

Returns:

    NTSTATUS code for success or reason of failure.

--*/
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE BaseHandle;
    PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;
    ULONG Disposition;

    ConfigurationRoot = (PCONFIGURATION_COMPONENT_DATA)LoaderBlock->ConfigurationRoot;

    //
    // Create \\Registry\Machine\Hardware\DeviceMap
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &CmRegistryMachineHardwareDeviceMapName,
        0,
        (HANDLE)NULL,
        NULL
        );
    ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

    Status = NtCreateKey(                   // Paht may already exist
                &BaseHandle,
                KEY_READ | KEY_WRITE,
                &ObjectAttributes,
                TITLE_INDEX_VALUE,
                NULL,
                0,
                &Disposition
                );

    if (!NT_SUCCESS(Status)) {
        return(Status);
    }

    NtClose(BaseHandle);

    ASSERT(Disposition == REG_CREATED_NEW_KEY);

    //
    // Create \\Registry\Machine\Hardware\Description and use the
    // returned handle as the BaseHandle to build the hardware tree.
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &CmRegistryMachineHardwareDescriptionName,
        0,
        (HANDLE)NULL,
        NULL
        );
    ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

    Status = NtCreateKey(                   // Path may already exist
                &BaseHandle,
                KEY_READ | KEY_WRITE,
                &ObjectAttributes,
                TITLE_INDEX_VALUE,
                NULL,
                0,
                &Disposition
                );

    if (!NT_SUCCESS(Status)) {
        return(Status);
    }

    ASSERT(Disposition == REG_CREATED_NEW_KEY);

    //
    // Allocate 16K bytes memory from paged pool for constructing
    // configuration data for controller component.
    // NOTE:  The configuration Data for controller component
    //    usually takes less than 100 bytes.  But on EISA machine, the
    //    EISA configuration information takes more than 10K and up to
    //    64K.  I believe 16K is the reasonable number to handler 99.9%
    //    of the machines.  Therefore, 16K is the initial value.
    //

    CmpConfigurationData = (PCM_FULL_RESOURCE_DESCRIPTOR)ExAllocatePool(
                                        PagedPool,
                                        CmpConfigurationAreaSize
                                        );

    if (CmpConfigurationData == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    //
    // Call SetupConfigurationTree routine to go over each component
    // of the tree and add component information to registry database.
    //

    if (ConfigurationRoot) {
        Status = CmpSetupConfigurationTree(ConfigurationRoot,
                                           BaseHandle,
                                           -1,
                                           (ULONG)-1);
    } else {
        Status = STATUS_SUCCESS;
    }

    ExFreePool((PVOID)CmpConfigurationData);
    NtClose(BaseHandle);
    return(Status);
}

NTSTATUS
CmpSetupConfigurationTree(
     IN PCONFIGURATION_COMPONENT_DATA CurrentEntry,
     IN HANDLE ParentHandle,
     IN INTERFACE_TYPE InterfaceType,
     IN ULONG BusNumber
     )
/*++

Routine Description:

    This routine traverses loader configuration tree and register
    the hardware information to the registry data base.

    Note to reduce the stack usage on machines with large number of PCI buses,
    we do not recursively process the sibling nodes.  We only recursively
    process the child trees.

Arguments:

    CurrentEntry - Supplies a pointer to a loader configuration
        tree or subtree.

    ParentHandle - Supplies the parent handle of CurrentEntry node.

    InterfaceType - Specify the Interface type of the bus that the
        CurrentEntry component resides.

    BusNumber - Specify the Bus Number of the bus that the CurrentEntry
        component resides.  If Bus number is -1, it means InterfaceType
        and BusNumber are meaningless for this component.

Returns:

    None.

--*/
{
    NTSTATUS Status;
    HANDLE NewHandle;
    USHORT i;
    CONFIGURATION_COMPONENT *Component;
    INTERFACE_TYPE LocalInterfaceType = InterfaceType;
    ULONG LocalBusNumber = BusNumber;
    USHORT DeviceIndexTable[NUMBER_TYPES];

    for (i = 0; i < NUMBER_TYPES; i++) {
        DeviceIndexTable[i] = 0;
    }

    //
    // Process current entry and its siblings
    //

    while (CurrentEntry) {

        //
        // Register current entry first before going down to its children
        //

        Component = &CurrentEntry->ComponentEntry;

        //
        // If the current component is a bus component, we will set up
        // its bus number and Interface type and use them to initialize
        // its subtree.
        //

        if (Component->Class == AdapterClass &&
            CurrentEntry->Parent->ComponentEntry.Class == SystemClass) {

            switch (Component->Type) {

            case EisaAdapter:
                LocalInterfaceType = Eisa;
                LocalBusNumber = CmpTypeCount[EISA_ADAPTER_INDEX]++;
                break;
            case TcAdapter:
                LocalInterfaceType = TurboChannel;
                LocalBusNumber = CmpTypeCount[TURBOCHANNEL_ADAPTER_INDEX]++;
                break;
            case MultiFunctionAdapter:

                //
                // Here we try to distinguish if the Multifunction adapter is
                // Isa, Mca, Internal bus and assign BusNumber based on
                // its interface type (bus type.)
                //

                if (Component->Identifier) {
                    for (i=0; CmpMultifunctionTypes[i].AscString; i++) {
                        if (_stricmp((PCHAR)CmpMultifunctionTypes[i].AscString,
                                    Component->Identifier) == 0) {
                                        break;
                        }
                    }

                    LocalInterfaceType = CmpMultifunctionTypes[i].InterfaceType;
                    LocalBusNumber = CmpMultifunctionTypes[i].Count++;
                }
                break;

            case ScsiAdapter:

                //
                // Set the bus type to internal.
                //

                LocalInterfaceType = Internal;
                LocalBusNumber = CmpTypeCount[ScsiAdapter]++;
                break;

            default:
                LocalInterfaceType = -1;
                LocalBusNumber = CmpUnknownBusCount++;
                break;
            }
        }

        //
        // Initialize and copy current component to hardware registry
        //

        Status = CmpInitializeRegistryNode(
                     CurrentEntry,
                     ParentHandle,
                     &NewHandle,
                     LocalInterfaceType,
                     LocalBusNumber,
                     DeviceIndexTable
                     );

        if (!NT_SUCCESS(Status)) {
            return(Status);
        }

        //
        // Once we are going one level down, we need to clear the TypeCount
        // table for everything under the current component class ...
        //

        if (CurrentEntry->Child) {

            //
            // Process the child entry of current entry
            //

            Status = CmpSetupConfigurationTree(CurrentEntry->Child,
                                               NewHandle,
                                               LocalInterfaceType,
                                               LocalBusNumber
                                               );
            if (!NT_SUCCESS(Status)) {
                NtClose(NewHandle);
                return(Status);
            }
        }
        NtClose(NewHandle);
        CurrentEntry = CurrentEntry->Sibling;
    }
    return(STATUS_SUCCESS);
}


NTSTATUS
CmpInitializeRegistryNode(
    IN PCONFIGURATION_COMPONENT_DATA CurrentEntry,
    IN HANDLE ParentHandle,
    OUT PHANDLE NewHandle,
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN PUSHORT DeviceIndexTable
    )

/*++

Routine Description:

    This routine creates a node for the current firmware component
    and puts component data to the data part of the node.

Arguments:

    CurrentEntry - Supplies a pointer to a configuration component.

    Handle - Supplies the parent handle of CurrentEntry node.

    NewHandle - Supplies a pointer to a HANDLE to receive the handle of
        the newly created node.

    InterfaceType - Specify the Interface type of the bus that the
        CurrentEntry component resides. (See BusNumber also)

    BusNumber - Specify the Bus Number of the bus that the CurrentEntry
        component resides on.  If Bus number is -1, it means InterfaceType
        and BusNumber are meaningless for this component.

Returns:

    None.

--*/
{

    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING KeyName;
    UNICODE_STRING ValueName;
    UNICODE_STRING ValueData;
    HANDLE Handle;
    HANDLE OldHandle;
    ANSI_STRING AnsiString;
    CHAR Buffer[12];
    WCHAR UnicodeBuffer[12];
    CONFIGURATION_COMPONENT *Component;
    ULONG Disposition;
    ULONG ConfigurationDataLength = 0;
    PCM_FULL_RESOURCE_DESCRIPTOR NewArea;

    Component = &CurrentEntry->ComponentEntry;

    //
    // If the component class is SystemClass, we set its Type to be
    // ArcSystem.  The reason is because the detection code sets
    // its type to MaximumType to indicate it is NOT ARC compatible.
    // Here, we are only interested in building a System Node.  So we
    // change its Type to ArcSystem to ease the setup.
    //

    if (Component->Class == SystemClass) {
        Component->Type = ArcSystem;
    }

    //
    // Create a new key to describe the Component.
    //
    // The type of the component will be used as the keyname of the
    // registry node.  The class is the class of the component.
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &(CmTypeName[Component->Type]),
        0,
        ParentHandle,
        NULL
        );
    ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

    Status = NtCreateKey(                   // Paht may already exist
                &Handle,
                KEY_READ | KEY_WRITE,
                &ObjectAttributes,
                0,
                NULL,
                0,
                &Disposition
                );

    if (!NT_SUCCESS(Status)) {
        return(Status);
    }

    //
    // If this component is NOT a SystemClass component, we will
    // create a subkey to identify the component's ordering.
    //

    if (Component->Class != SystemClass) {

        RtlIntegerToChar(
            DeviceIndexTable[Component->Type]++,
            10,
            12,
            Buffer
            );

        RtlInitAnsiString(
            &AnsiString,
            Buffer
            );

        KeyName.Buffer = (PWSTR)UnicodeBuffer;
        KeyName.Length = 0;
        KeyName.MaximumLength = sizeof(UnicodeBuffer);

        RtlAnsiStringToUnicodeString(
            &KeyName,
            &AnsiString,
            FALSE
            );

        OldHandle = Handle;

        InitializeObjectAttributes(
            &ObjectAttributes,
            &KeyName,
            0,
            OldHandle,
            NULL
            );
        ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

        Status = NtCreateKey(
                    &Handle,
                    KEY_READ | KEY_WRITE,
                    &ObjectAttributes,
                    0,
                    NULL,
                    0,
                    &Disposition
                    );

        NtClose(OldHandle);

        if (!NT_SUCCESS(Status)) {
            return(Status);
        }

        ASSERT(Disposition == REG_CREATED_NEW_KEY);
    }

    //
    // Create a value which describes the following component information:
    //     Flags, Cersion, Key, AffinityMask.
    //

    RtlInitUnicodeString(
        &ValueName,
        L"Component Information"
        );

    Status = NtSetValueKey(
                Handle,
                &ValueName,
                TITLE_INDEX_VALUE,
                REG_BINARY,
                &Component->Flags,
                FIELD_OFFSET(CONFIGURATION_COMPONENT, ConfigurationDataLength) -
                    FIELD_OFFSET(CONFIGURATION_COMPONENT, Flags)
                );

    if (!NT_SUCCESS(Status)) {
        NtClose(Handle);
        return(Status);
    }

    //
    // Create a value which describes the component identifier, if any.
    //

    if (Component->IdentifierLength) {

        RtlInitUnicodeString(
            &ValueName,
            L"Identifier"
            );

        RtlInitAnsiString(
            &AnsiString,
            Component->Identifier
            );

        Status = RtlAnsiStringToUnicodeString(
                    &ValueData,
                    &AnsiString,
                    TRUE
                    );

        if( NT_SUCCESS(Status) ) {
            Status = NtSetValueKey(
                        Handle,
                        &ValueName,
                        TITLE_INDEX_VALUE,
                        REG_SZ,
                        ValueData.Buffer,
                        ValueData.Length + sizeof( UNICODE_NULL )
                        );

            RtlFreeUnicodeString(&ValueData);
        }

        if (!NT_SUCCESS(Status)) {
            NtClose(Handle);
            return(Status);
        }
    }

    //
    // Create a value entry for component configuration data.
    //

    RtlInitUnicodeString(
        &ValueName,
        L"Configuration Data"
        );

    //
    // Create the configuration data based on CM_FULL_RESOURCE_DESCRIPTOR.
    //
    // Note the configuration data in firmware tree may be in the form of
    // CM_PARTIAL_RESOURCE_LIST or nothing.  In both cases, we need to
    // set up the registry configuration data to be in the form of
    // CM_FULL_RESOURCE_DESCRIPTOR.
    //

    if (CurrentEntry->ConfigurationData) {

        //
        // This component has configuration data, we copy the data
        // to our work area, add some more data items and copy the new
        // configuration data to the registry.
        //

        ConfigurationDataLength = Component->ConfigurationDataLength +
                      FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
                      PartialResourceList);

        //
        // Make sure our reserved area is big enough to hold the data.
        //

        if (ConfigurationDataLength > CmpConfigurationAreaSize) {

            //
            // If reserved area is not big enough, we resize our reserved
            // area.  If, unfortunately, the reallocation fails, we simply
            // loss the configuration data of this particular component.
            //

            NewArea = (PCM_FULL_RESOURCE_DESCRIPTOR)ExAllocatePool(
                                            PagedPool,
                                            ConfigurationDataLength
                                            );

            if (NewArea) {
                CmpConfigurationAreaSize = ConfigurationDataLength;
                ExFreePool(CmpConfigurationData);
                CmpConfigurationData = NewArea;
                RtlCopyMemory(
                    (PUCHAR)&CmpConfigurationData->PartialResourceList.Version,
                    CurrentEntry->ConfigurationData,
                    Component->ConfigurationDataLength
                    );
            } else {
                Component->ConfigurationDataLength = 0;
                CurrentEntry->ConfigurationData = NULL;
            }
        } else {
            RtlCopyMemory(
                (PUCHAR)&CmpConfigurationData->PartialResourceList.Version,
                CurrentEntry->ConfigurationData,
                Component->ConfigurationDataLength
                );
        }

    }

    if (CurrentEntry->ConfigurationData == NULL) {

        //
        // This component has NO configuration data (or we can't resize
        // our reserved area to hold the data), we simple add whatever
        // is required to set up a CM_FULL_RESOURCE_LIST.
        //

        CmpConfigurationData->PartialResourceList.Version = 0;
        CmpConfigurationData->PartialResourceList.Revision = 0;
        CmpConfigurationData->PartialResourceList.Count = 0;
        ConfigurationDataLength = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
                                               PartialResourceList) +
                                  FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST,
                                               PartialDescriptors);
    }

    //
    // Set up InterfaceType and BusNumber for the component.
    //

    CmpConfigurationData->InterfaceType = InterfaceType;
    CmpConfigurationData->BusNumber = BusNumber;

    //
    // Write the newly constructed configuration data to the hardware registry
    //

    Status = NtSetValueKey(
                Handle,
                &ValueName,
                TITLE_INDEX_VALUE,
                REG_FULL_RESOURCE_DESCRIPTOR,
                CmpConfigurationData,
                ConfigurationDataLength
                );

    if (!NT_SUCCESS(Status)) {
        NtClose(Handle);
        return(Status);
    }

    *NewHandle = Handle;
    return(STATUS_SUCCESS);
}

