/*++

  Copyright (c) 1998  Microsoft Corporation
  
  Module Name:
  
      findE980.c
  
  Abstract:
  
      Looks in the registry to find the Intel Legacy Geyserville INT 15 /E980
      table.
      
  Author:
  
      Todd Carpenter
  
  Environment:
  
      user mode
  
  
  Revision History:
  
      08-08-00 : created, toddcar

--*/

#include "finde980.h"


    
VOID
_cdecl
main (
  INT    Argc,
  CHAR** Argv
  )
/*++

  Routine Description:

        
  Arguments:

   
  Return Value:

  
--*/
{
  LONG rc;
  PACPI_BIOS_MULTI_NODE     multiNode;
  PLEGACY_GEYSERVILLE_INT15 int15Info;

 
  rc = AcpiFindRsdt(&multiNode);

  if (rc != ERROR_SUCCESS) {
    printf("AcpiFindRsdt Failed!\n");
    return;
  }


  //
  // Geyserville BIOS information is appended to the E820 entries.
  //
  
  int15Info = (PLEGACY_GEYSERVILLE_INT15) &(multiNode->E820Entry[multiNode->Count]);
  
  if (int15Info->Signature == 'GS') {

    //
    // Dump E980 Structure
    //

    printf("\n");
    printf("Intel Geyserville INT 15 Interface:\n\n");
    printf("  Signature:               0x%x (GS)\n", int15Info->Signature);
    printf("  Smi Command Port:        0x%x\n", int15Info->CommandPortAddress);
    printf("  Smi Event Port:          0x%x\n", int15Info->EventPortAddress);
    printf("  Polling Interval:        0x%x\n", int15Info->PollInterval);
    printf("  Smi Command Data Value:  0x%x\n", int15Info->CommandDataValue);
    printf("  Event Port Bitmask:      0x%x\n", int15Info->EventPortBitmask);
    printf("  Max Perf Level on AC:    0x%x\n", int15Info->MaxLevelAc);
    printf("  Max Perf Level on DC:    0x%x\n", int15Info->MaxLevelDc);

  } else {

    printf("\n");
    printf("Geyserville Int 15 Interface is NOT supported.\n");

  }
  

}


LONG
AcpiFindRsdt (
  OUT PACPI_BIOS_MULTI_NODE   *AcpiMulti
  )
/*++

  Routine Description:
  
      This function looks into the registry to find the ACPI RSDT,
      which was stored there by ntdetect.com.
  
  Arguments:
  
      RsdtPtr - Pointer to a buffer that contains the ACPI
                Root System Description Pointer Structure.
                The caller is responsible for freeing this
                buffer.  Note:  This is returned in non-paged
                pool.
  
  Return Value:
  
      A NTSTATUS code to indicate the result of the initialization.

--*/
{
#define ACPI_CONFIGURATION_DATA   "Configuration Data"
#define ACPI_INDENTIFIER          "Identifier"
#define ACPI_BIOS_IDENTIFIER      "ACPI BIOS"

  CHAR  multiFunctionAdapter[] = "Hardware\\Description\\System\\MultifunctionAdapter\\0\0";
  ULONG adapterNumberOffset = sizeof(multiFunctionAdapter) - 3;
  CHAR  acpiBiosIdentifier[sizeof(ACPI_BIOS_IDENTIFIER)];
  ULONG acpiBiosIdentifierSize = sizeof(acpiBiosIdentifier);
  ULONG regDataSize;
  
  PCM_PARTIAL_RESOURCE_LIST prl;
  PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
  PACPI_BIOS_MULTI_NODE multiNode;
  ULONG multiNodeSize;
  PLEGACY_GEYSERVILLE_INT15 int15Info;

  HKEY   regKeyAcpi;
  LONG   rc;
  ULONG  valueType, i;
  PUCHAR configData = NULL;


  //
  // Look in the registry for the "ACPI BIOS bus" data
  //

  for (i = 0; TRUE; i++) { 
   
    regDataSize = acpiBiosIdentifierSize;
    
    //
    // Check every subkey of HKLM\Hardware\Description\System\MultifunctionAdapter
    // for Acpi bios data
    //

    _itoa(i, &multiFunctionAdapter[adapterNumberOffset], 10);

    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                      multiFunctionAdapter,
                      0,
                      KEY_READ,
                      &regKeyAcpi);


    if (rc != ERROR_SUCCESS) {
      printf("AcpiFindRsdt: RegOpenKeyEx(%s) Failed!\n", multiFunctionAdapter); 
      RegCloseKey(regKeyAcpi);
      return rc;
    }

    rc = RegQueryValueEx(regKeyAcpi,
                         ACPI_INDENTIFIER,
                         NULL,
                         &valueType,
                         acpiBiosIdentifier,
                         &regDataSize);


    //
    // All subkeys of "MultifunctionAdapter" should have an "Identifier" key
    //
    
    if ((rc != ERROR_SUCCESS) && (rc != ERROR_MORE_DATA)) {
      printf("AcpiFindRsdt: RegQueryValueEx() failed rc = 0x%x\n", rc);
      return rc;  
    }


    //
    // Compare value of "Identifier" key to "ACPI BIOS"
    //

    if (lstrcmp(acpiBiosIdentifier, ACPI_BIOS_IDENTIFIER)) {

      //
      // no match
      //
      
      continue;
    }
    

    //
    // We have a match, get "Configuration Data"
    //

    rc = GetRegistryValue(regKeyAcpi,
                          ACPI_CONFIGURATION_DATA,
                          &configData);


    //
    // All subkeys of "MultifunctionAdapter" should have an "Configuration Data" key
    //
    
    if (rc != ERROR_SUCCESS) {
      printf("AcpiFindRsdt: GetRegistryValue() failed, rc=0x%x\n", rc);
      return rc;  
    }

    
     

    prl = (PCM_PARTIAL_RESOURCE_LIST)(configData);
    prd = &prl->PartialDescriptors[0];
    multiNode = (PACPI_BIOS_MULTI_NODE)((PCHAR) prd + sizeof(CM_PARTIAL_RESOURCE_LIST));


    break;
  }

  multiNodeSize = sizeof(ACPI_BIOS_MULTI_NODE) +
                         ((ULONG)(multiNode->Count - 1) * sizeof(ACPI_E820_ENTRY)) +
                         sizeof(LEGACY_GEYSERVILLE_INT15);
  
  *AcpiMulti = HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
                         multiNodeSize);
                                                    
  if (*AcpiMulti == NULL) {  
    return ERROR_OUTOFMEMORY;
  }

  memcpy(*AcpiMulti, multiNode, multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15));
  
  //
  // Geyserville BIOS information is appended to the E820 entries.  Unfortunately,
  // there is no way to know if it is there.  So wrap the code in a try/except.
  //
  
  try {
      
    int15Info = (PLEGACY_GEYSERVILLE_INT15)&(multiNode->E820Entry[multiNode->Count]);
    
    if (int15Info->Signature == 'GS') {
  
      //
      // This BIOS supports Geyserville.
      //
  
      memcpy(((PUCHAR)*AcpiMulti + multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15)),
              int15Info, 
              sizeof(LEGACY_GEYSERVILLE_INT15));
  
    }
    
  } except (EXCEPTION_EXECUTE_HANDLER) {
      
      *((PUSHORT)((PUCHAR)*AcpiMulti + multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15))) = 0;
  }
  

  return ERROR_SUCCESS;
}


LONG
GetRegistryValue(
    IN HKEY    KeyHandle,
    IN LPTSTR  ValueName,
    OUT PUCHAR *Information
    )
/*++

  Routine Description:
   
  
  Arguments:

  
  Return Value:


  Note:

   It is the responsibility of the caller to free the buffer.

--*/

{
  LONG   rc;
  ULONG  keyValueLength;
  PUCHAR infoBuffer;
  ULONG  type;
   
  //
  // Figure out how big the data value is so that a buffer of the
  // appropriate size can be allocated.
  //
  
  rc = RegQueryValueEx(KeyHandle,
                       ValueName,
                       NULL,
                       &type,
                       NULL,
                       &keyValueLength);
                           
  if (rc != ERROR_SUCCESS) {
    printf("RegQueryValueKey() Failed, rc=0x%x\n",rc);
    return rc;
  }
  
  //
  // Allocate a buffer large enough to contain the entire key data value.
  //
  
  infoBuffer = HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
                         keyValueLength);
                         
  if (!infoBuffer) {
    return ERROR_OUTOFMEMORY;
  }
  
  //
  // Query the data for the key value.
  //
  
  rc = RegQueryValueEx(KeyHandle,
                       ValueName,
                       NULL,
                       &type,
                       infoBuffer,
                       &keyValueLength);
                        
                           
  if (rc != ERROR_SUCCESS) {
    HeapFree(GetProcessHeap, 0, infoBuffer);
    return rc;
  }
  
  //
  // Everything worked, so simply return the address of the allocated
  // buffer to the caller, who is now responsible for freeing it.
  //
  
  *Information = infoBuffer;
  return ERROR_SUCCESS;
    
}

