///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Note:
//    System Resource Affinity Table (SRAT)
//    Page 123 of ACPI 3.0 Spec.
//    Also see: "Static Resource Affinity Table" at www.microsoft.com

namespace Microsoft.Singularity.Hal.Acpi
{

    using System;
    using Microsoft.Singularity.Io;

    public class Srat
    {
        public const string Signature = "SRAT";
        private const int ProcessorStructureId = 0;
        private const int MemoryStructureId = 1;

        private int [] processorOffsets;
        private int [] memoryOffsets;

        private IoMemory          region;
        private SystemTableHeader header;


        public int GetNumberOfProcessors()
        {
            return processorOffsets.Length;
        }

        public int GetNumberOfMemories()
        {
            return memoryOffsets.Length;
        }

        // Processor fields offsets (important fields marked with XX)
        // -----------------------------------------------------------
        //     Entry                offsets      byte length
        // -----------------------------------------------------------
        //     Type               :   0
        //     Length             :   1
        //     Proximity Domain L :   2    XX
        //     APIC ID            :   3    XX
        //     Flags              :   4    XX
        //     Local SAPIC EID    :   8
        //     Proximity Domain H :   9
        //     Reserved           :  12
        public uint GetProcessorDomain(int processorIndex)
        {
            int offset = processorOffsets[processorIndex];
            uint domain = 0;
            domain = domain + region.Read8(offset+9) << 24;
            domain = domain + region.Read8(offset+10) << 16;
            domain = domain + region.Read8(offset+11) << 8;
            domain = domain + region.Read8(offset+2);
            return domain;
        }

        public uint GetProcessorApicId(int processorIndex)
        {
            return region.Read8(processorOffsets[processorIndex]+3);
        }

        public uint GetProcessorFlagIgnore(int processorIndex)
        {
            uint flags = region.Read32(processorOffsets[processorIndex]+4);
            return (flags & 0x1);
        }

        // Memory fields offsets (important fields marked with XX)
        // -----------------------------------------------------------
        //     Entry                offsets      byte length
        // -----------------------------------------------------------
        //     Type               :   0
        //     Length             :   1
        //     Proximity Domain   :   2    XX
        //     Reserved           :   6
        //     Base Address Low   :   8    XX
        //     Base Address High  :  12    XX
        //     Length Low         :  16    XX
        //     Length High        :  20    XX
        //     Reserved           :  24
        //     Flags              :  28    XX
        //     Reserved           :  32
        public uint GetMemoryDomain(int memoryIndex)
        {
            return (region.Read32(memoryOffsets[memoryIndex]+2));
        }

        public ulong GetMemoryBaseAddress(int memoryIndex)
        {
            int offset = memoryOffsets[memoryIndex];
            uint baseHigh = region.Read32(offset+12);
            uint baseLow  = region.Read32(offset+8);
            ulong baseAddr = (((ulong)baseHigh) << 32) + baseLow;
            return baseAddr;
        }

        public ulong GetMemoryEndAddress(int memoryIndex)
        {
            ulong baseAddr = GetMemoryBaseAddress(memoryIndex);
            ulong memSize = GetMemorySize(memoryIndex);
            return baseAddr+memSize;
        }

        public ulong GetMemorySize(int memoryIndex)
        {
            int offset = memoryOffsets[memoryIndex];
            uint lenHigh = region.Read32(offset+20);
            uint lenLow  = region.Read32(offset+16);
            ulong memSize = (((ulong)lenHigh) << 32) + lenLow;
            return memSize;
        }

        public uint GetMemoryFlagIgnore(int memoryIndex)
        {
            uint flags = region.Read32(memoryOffsets[memoryIndex]+28);
            return (flags & 0x1);
        }

        public uint GetMemoryFlagHotPluggable(int memoryIndex)
        {
            uint flags = region.Read32(memoryOffsets[memoryIndex]+28);
            return (flags & 0x2);
        }

        public uint GetMemoryFlagNonVolatile(int memoryIndex)
        {
            uint flags = region.Read32(memoryOffsets[memoryIndex]+28);
            return (flags & 0x4);
        }

        public Srat(IoMemory region, SystemTableHeader header)
        {
            this.region = region;
            this.header = header;
        }

        // Create() Srat
        public static Srat Create(SystemTableHeader header)
        {
            return new Srat(
                IoMemory.MapPhysicalMemory(
                    header.PostHeaderAddress, header.PostHeaderLength,
                    true, false
                    ),
                header
                );
        }

        // Parse Srat structure
        public void ParseSratStructure()
        {
            int offset = 12; // there are 12 bytes offset
            int processorCount = 0;
            int memoryCount = 0;
            int type,length;

            // 1st stage, count the number of processors
            // reset counter
            while (offset < region.Length) {

                type = region.Read8(offset);

                // this is a processor structure
                if (type == ProcessorStructureId) {
                    length = region.Read8(offset+1);
                    processorCount++;
                    offset += length;
                }
                // this is a memory structure
                else if (type == MemoryStructureId) {
                    length = region.Read8(offset+1);
                    memoryCount++;
                    offset += length;
                }
                else {
                    DebugStub.Print("  ERROR: Impossible Srat Id\n");
                    DebugStub.Break();
                    break;
                }
            }

            // 2nd stage, create arrays
            processorOffsets = new int [processorCount];
            memoryOffsets = new int [memoryCount];

            // 3rd stage: fill the offsets
            // clear everything first
            offset = 12;
            processorCount = 0;
            memoryCount = 0;

            while (offset < region.Length) {

                type = region.Read8(offset);

                // this is a processor structure
                if (type == ProcessorStructureId) {
                    length = region.Read8(offset+1);
                    processorOffsets[processorCount] = offset;
                    processorCount++;
                    offset += length;
                }
                // this is a memory structure
                else if (type == MemoryStructureId) {
                    length = region.Read8(offset+1);
                    memoryOffsets[memoryCount] = offset;
                    memoryCount++;
                    offset += length;
                }
                else {
                    DebugStub.Print("  ERROR: Impossible Srat Id\n");
                    DebugStub.Break();
                    break;
                }
            }
        }

        // Dump Srat structure
        public void DumpSratOffsets()
        {
            DebugStub.Print("\n\n\n  SRAT Offset Details: \n");
            DebugStub.Print("------------------------------------\n");
            DebugStub.Print("  {0} processors, {1} memories \n",
                            __arglist(processorOffsets.Length,
                                      memoryOffsets.Length));

            DebugStub.Print("  P Offset --  ");
            for (int i = 0; i < processorOffsets.Length; i++) {
                DebugStub.Print("{0}  ", __arglist(processorOffsets[i]));
            }
            DebugStub.Print("\n");
            DebugStub.Print("  M Offset --  ");
            for (int i = 0; i < memoryOffsets.Length; i++) {
                DebugStub.Print("{0}  ", __arglist(memoryOffsets[i]));
            }
            DebugStub.Print("\n\n\n");
        }

        // Dump Srat Important fields
        public void DumpSratImportantFields()
        {
            DebugStub.Print("\n\n\n  SRAT Important Fields: \n");
            DebugStub.Print("------------------------------------\n");


            for (int i = 0; i < processorOffsets.Length; i++) {
                DebugStub.Print("     PROCESSOR {0}\n",
                                __arglist(i));
                DebugStub.Print("         Domain  : {0}\n",
                                __arglist(GetProcessorDomain(i)));
                DebugStub.Print("         APIC ID : {0}\n",
                                __arglist(GetProcessorApicId(i)));
                DebugStub.Print("         Ignore  : {0}\n",
                                __arglist(GetProcessorFlagIgnore(i)));
            }

            for (int i = 0; i < memoryOffsets.Length; i++) {
                DebugStub.Print("     MEMORY {0}\n",
                                __arglist(i));
                DebugStub.Print("         Domain  : {0}\n",
                                __arglist(GetMemoryDomain(i)));
                DebugStub.Print("         Base    : {0} KB\n",
                                __arglist(GetMemoryBaseAddress(i)/1024));
                DebugStub.Print("         End     : {0} KB\n",
                                __arglist(GetMemoryEndAddress(i)/1024));
                DebugStub.Print("         MemSize : {0} KB\n",
                                __arglist(GetMemorySize(i)/1024));
                DebugStub.Print("         Ignore  : {0}\n",
                                __arglist(GetMemoryFlagIgnore(i)));
                DebugStub.Print("         HotPlug : {0}\n",
                                __arglist(GetMemoryFlagHotPluggable(i)));
                DebugStub.Print("         NonVol  : {0}\n",
                                __arglist(GetMemoryFlagNonVolatile(i)));
            }
            DebugStub.Print("\n\n\n");
        }

        // Dump Srat structure
        public void DumpSratStructure()
        {
            int offset = 12; // there are 12 bytes offset
            int processorCount = 0;
            int memoryCount = 0;
            int type,length;

            // fields
            uint ptype, plen, pdom, pid, pflag, peid, pdom2, pres;
            uint mtype, mlen, mdom, mres, mlow, mhigh, mlenl, mlenh;
            uint mres2, mflag, mres3;

            DebugStub.Print("  Region length {0}\n",
                            __arglist(region.Length));
            DebugStub.Print("------------------------------------\n");

            // reset counter
            while (offset < region.Length) {

                // this is a processor structure
                type = region.Read8(offset);
                if (type == ProcessorStructureId) {

                    length = region.Read8(offset+1);

                    DebugStub.Print("\n  proc:{0}  \n",
                                    __arglist(processorCount, length));
                    DebugStub.Print("------------------------------------\n");

                    ptype = region.Read8 (offset);
                    plen  = region.Read8 (offset+1);
                    pdom  = region.Read8 (offset+2);
                    pid   = region.Read8 (offset+3);
                    pflag = region.Read32(offset+4);
                    peid  = region.Read8 (offset+8);
                    pdom2 = region.Read32(offset+9);
                    pres  = region.Read32(offset+12);

                    DebugStub.Print("   ptype: {0}\n", __arglist(ptype));
                    DebugStub.Print("   plen : {0}\n", __arglist(plen ));
                    DebugStub.Print("   pdom : {0}\n", __arglist(pdom ));
                    DebugStub.Print("   pid  : {0}\n", __arglist(pid  ));
                    DebugStub.Print("   pflag: {0}\n", __arglist(pflag));
                    DebugStub.Print("   peid : {0}\n", __arglist(peid ));
                    DebugStub.Print("   pdom2: {0}\n", __arglist(pdom2));
                    DebugStub.Print("   pres : {0}\n", __arglist(pres ));


                    processorCount++;
                    offset += length;
                }

                // this is a memory structure
                else if (type == MemoryStructureId) {

                    length = region.Read8(offset+1);
                    DebugStub.Print("\n  memr:{0} \n",
                                    __arglist(memoryCount, length));
                    DebugStub.Print("------------------------------------\n");

                    mtype = region.Read8 (offset);
                    mlen  = region.Read8 (offset+1);
                    mdom  = region.Read32(offset+2);
                    mres  = region.Read8 (offset+6);
                    mlow  = region.Read32(offset+8);
                    mhigh = region.Read32(offset+12);
                    mlenl = region.Read32(offset+16);
                    mlenh = region.Read32(offset+20);
                    mres2 = region.Read32(offset+24);
                    mflag = region.Read32(offset+28);
                    mres3 = region.Read32(offset+32);

                    DebugStub.Print("   mtype: {0}\n", __arglist(mtype));
                    DebugStub.Print("   mlen : {0}\n", __arglist(mlen ));
                    DebugStub.Print("   mdom : {0}\n", __arglist(mdom ));
                    DebugStub.Print("   mres : {0}\n", __arglist(mres ));
                    DebugStub.Print("   mlow : {0}\n", __arglist(mlow ));
                    DebugStub.Print("   mhigh: {0}\n", __arglist(mhigh));
                    DebugStub.Print("   mlenl: {0}\n", __arglist(mlenl));
                    DebugStub.Print("   mlenh: {0}\n", __arglist(mlenh));
                    DebugStub.Print("   mres2: {0}\n", __arglist(mres2));
                    DebugStub.Print("   mflag: {0}\n", __arglist(mflag));
                    DebugStub.Print("   mres3: {0}\n", __arglist(mres3));

                    memoryCount++;
                    offset += length;
                }
                else {
                    DebugStub.Print("  Error \n");
                    DebugStub.Print("Impossible Affinity Structure Id\n");
                    DebugStub.Break();
                    break;
                }
            }

            DebugStub.Print("------------------------------------\n");
            DebugStub.Print("  Done Parsing Srat Structure ... \n");
            DebugStub.Print("  Found {0} processors and {1} memory\n",
                            __arglist(processorCount,memoryCount));
        }
    }
}
