/*++

Module Name:

    MCALOG.C

Abstract:

    Sample Application for logging errors for Machine Check Architecture

Author:

    Anil Aggarwal (10/12/98)
    Intel Corporation

Revision History:

--*/

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <winbase.h>
#include "imca.h"

//
// Variables for parsing command line arguments
//
extern int  opterr;
extern int  optind;
extern char *optarg;

//****** From NTDDK.H
//
// STATUS register for each MCA bank.
//

typedef union _MCI_STATS {
    struct {
        USHORT  McaCod;
        USHORT  MsCod;
        ULONG   OtherInfo : 25;
        ULONG   Damage : 1;
        ULONG   AddressValid : 1;
        ULONG   MiscValid : 1;
        ULONG   Enabled : 1;
        ULONG   UnCorrected : 1;
        ULONG   OverFlow : 1;
        ULONG   Valid : 1;
    } MciStats;

    ULONGLONG QuadPart;

} MCI_STATS, *PMCI_STATS;

//
// ADDR register for each MCA bank
//

typedef union _MCI_ADDR{
    struct {
        ULONG Address;
        ULONG Reserved;
    };

    ULONGLONG   QuadPart;
} MCI_ADDR, *PMCI_ADDR;


typedef enum {
    HAL_MCE_RECORD,
    HAL_MCA_RECORD
} MCA_EXCEPTION_TYPE;

//
// MCA exception log entry
// Defined as a union to contain MCA specific log or Pentium style MCE info.
//

typedef struct _MCA_EXCEPTION {

    ULONG               VersionNumber;      // Version number of this record type
    MCA_EXCEPTION_TYPE  ExceptionType;      // MCA or MCE
    LARGE_INTEGER       TimeStamp;          // exception recording timestamp
    ULONG               ProcessorNumber;

    union {
        struct {
            UCHAR           BankNumber;
            MCI_STATS       Status;
            MCI_ADDR        Address;
            ULONGLONG       Misc;
        } Mca;

        struct {
            ULONGLONG       Address;        // physical addr of cycle causing the error
            ULONGLONG       Type;           // cycle specification causing the error
        } Mce;
    } u;

} MCA_EXCEPTION, *PMCA_EXCEPTION;

//****** End of From NTDDK.H


//
// Print the usage information for MCA logging application
//

VOID
McaUsage(
    PCHAR Name
    )
{
    fprintf(stderr,"Usage\n\t%s: [-s] [-a]\n",Name);
    fprintf(stderr,"\n\t-s: Read Machine Check registers now\n");
    fprintf(stderr,"\n\t-a: Post asynchronous request for errors\n");

    ExitProcess(1);
}

//
// This routine prints the Machine Check registers
//

VOID
McaPrintLog(
    PMCA_EXCEPTION  McaException
    )
{
    if (McaException->ExceptionType != HAL_MCA_RECORD) {
        fprintf(stderr, "Bad exception record type\n");
        //ExitProcess(1);
    }

    printf("Processor Number = %d\n", McaException->ProcessorNumber);

    printf("Bank Number = %d\n", (__int64)McaException->u.Mca.BankNumber);
    printf("Mci_Status %I64X\n", (__int64)McaException->u.Mca.Status.QuadPart);
    printf("Mci_Address %I64X\n", (__int64)McaException->u.Mca.Address.QuadPart);
    printf("Mci_Misc %I64X\n", (__int64)McaException->u.Mca.Misc);
}

//
// This routine prints a user friendly error message based on GetLastError()
//

VOID
McaPrintError(
    VOID
    )
{
    LPVOID lpMsgBuf;
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL 
    );

    fprintf(stderr, "%s\n", lpMsgBuf);

    LocalFree( lpMsgBuf );
} 

//
// Main entry point
//

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    CHAR            Option;
    BOOLEAN         ReadBanks = FALSE;
    BOOLEAN         PostAsyncRequest = FALSE;
    HANDLE          McaDeviceHandle;
    HANDLE          LogEvent;
    OVERLAPPED      Overlap;
    BOOL            ReturnStatus;
    DWORD           ActualCount;
    DWORD           WaitStatus;
    DWORD           NumberOfBytes;
    MCA_EXCEPTION   McaException;
    LONG            i;

    //
    // Process the command line arguments
    //
    for (i=1; i < argc; i++) {
        if (!((argv[i][0] == '-') || (argv[i][2] != 0)) ) {
            McaUsage(argv[0]);
        }

        Option = argv[i][1];

        switch (Option) {
            case 's':
                ReadBanks = TRUE;
                break;

            case 'a':
                PostAsyncRequest = TRUE;
                break;

            default:
                McaUsage(argv[0]);
        }
    }

    if ((ReadBanks != TRUE) && (PostAsyncRequest != TRUE)) {
        fprintf(stderr, "One of -s and -a options must be specified\n");
        ExitProcess(1);
    }

    if ((ReadBanks == TRUE) && (PostAsyncRequest == TRUE)) {
        fprintf(stderr, "Only one of -s and -a options can be specified\n");
        ExitProcess(1);
    }

    //
    // Open MCA device with overlap flag set
    //

    McaDeviceHandle = CreateFile(
                            MCA_DEVICE_NAME_WIN32,
                            GENERIC_READ|GENERIC_WRITE,
                            0,
                            (LPSECURITY_ATTRIBUTES)NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 
                            (HANDLE)NULL
                            );

    if (McaDeviceHandle == INVALID_HANDLE_VALUE)  {
        fprintf(stderr, "%s: Error 0x%lx opening MCA device\n",
                                        argv[0], GetLastError());
        ExitProcess(1);
    }

    if (ReadBanks == TRUE) {
        
            //
            // Read the error logs on all banks on all procs.
            // IOCTL_READ_BANKS will read only one error at a time. So
            // we need to keep issuing this ioctl till all the errors are read
            //
    
        do {
            ReturnStatus = DeviceIoControl(
                                McaDeviceHandle,
                                (ULONG)IOCTL_READ_BANKS,
                                NULL,
                                0,
                                &McaException,
                                sizeof(MCA_EXCEPTION),
                                &ActualCount,
                                NULL
                                );

            if (ReturnStatus == 0)  {
                //
                // Some error has occurred. Either there are no more machine
                // check errors present or the processor does not have 
                // support for Intel Machine Check Architecture
                //

                if (GetLastError() == ERROR_NOT_FOUND) {
                    fprintf(stderr, "No Machine Check errors present\n");
                } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
                    fprintf(stderr, "Intel Machine Check support not available\n");
                    ExitProcess(1);
                } else {
                    fprintf(stderr, "%s: Error 0x%lx in DeviceIoControl\n",
                                        argv[0], GetLastError());
                    ExitProcess(1);
                }

            } else {
                //
                // Successfully read the error. Print it.
                //
                McaPrintLog(&McaException);
            }

        } while (ReturnStatus != 0);
    
        //
        // We are done
        //
        return 1;
    }

    //
    // If we are here, we are supposed to post asynchronous calls to MCA driver
    //
    
    //
    // Set up structures for asynchronous call for reading the log
    // Create the event object
    //

    LogEvent = CreateEvent(
                            NULL,   // No Security Attributes
                            FALSE,  // Auto Reset Event
                            FALSE,  // Initial State = non-signaled 
                            NULL    // Unnamed object
                            );
        
    if (LogEvent == NULL) {
        fprintf(stderr, "%s: Error 0x%lx creating event\n",
                                        argv[0], GetLastError());
        ExitProcess(1);
    }

    //
    // Initialize the overlap structure
    //

    Overlap.hEvent = LogEvent; // Specify event for overlapped object
    Overlap.Offset = 0;        // Offset is zero for devices
    Overlap.OffsetHigh = 0;    // OffsetHigh is zero for devices
    
    ReturnStatus = DeviceIoControl(
                        McaDeviceHandle,
                        (ULONG)IOCTL_READ_BANKS_ASYNC,
                        NULL,
                        0,
                        &McaException,
                        sizeof(MCA_EXCEPTION),    
                        &ActualCount,
                        &Overlap
                        );

    if ((ReturnStatus == 0) && (GetLastError() != ERROR_IO_PENDING))  {
        fprintf(stderr, "%s: Error 0x%lx in IOCTL_READ_BANKS_ASYNC\n",
                    argv[0], GetLastError());
        ExitProcess(1);
    } 

    //
    // Either Ioctl was successful or IO is currently pending
    // If successful then display the log else wait for specified interval
    //
    if (ReturnStatus == TRUE) {
        //
        // Read log async returned succesfully. Display it
        //

        McaPrintLog(&McaException);

    }
            
    //
    // Wait forever to get an error
    //

    WaitStatus = WaitForSingleObject(
                                    LogEvent,
                                    INFINITE
                                    );

    if (WaitStatus == WAIT_OBJECT_0) {
                 
        //
        // The state of the event object is signalled 
        // check if the I/O operation was successful
        //

        ReturnStatus = GetOverlappedResult(
                                        McaDeviceHandle,
                                        &Overlap,
                                        &NumberOfBytes,
                                        FALSE        // Return immediately
                                        );
                                                
        if (ReturnStatus == 0) {

                fprintf(stderr, "%s: Error 0x%lx in GetOverlappedResult\n",
                                        argv[0], GetLastError());
                ExitProcess(1);
        }

        if (NumberOfBytes) {

                //
                // Print the results
                //

                McaPrintLog(&McaException);

        } else {

                //
                // Error as the I/O operation was signalled complete before 
                // timeout but no data transferred
                //

                fprintf(stderr, "%s: No data from GetOverlappedResult\n",
                                argv[0]);
                ExitProcess(1);
        }

    } else {
    
        //
        // We should not get any other return value 
        //

        fprintf(stderr, "%s: Unexpected return value from WaitForSingleObject()\n");
        ExitProcess(1);
    }

    CloseHandle(McaDeviceHandle);

    return 1;
}

