///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   AcpiTables.cs
//
//  Note:
//    Based on ACPI 3.0 Spec.

// Define to dump all ACPI tables in uuencoded binary form to the debugger
// at boot time.
//#define DUMP_ACPI_TABLES_UUENCODED

// Define to dump a trace of all raw read/writes performed by the ACPI
// interpreter to the debugger. This can be processed by a tool
// %SINGULARITY_ROOT%\Windows\ACPI\TestFiles\parse_read_write_trace.pl
// to produce a read/write trace for use by the test harness.
//#define DUMP_RAW_READ_WRITES

// Define to dump all names in the initial ACPI namespace tree.
//#define DUMP_ACPI_NAMESPACE

namespace Microsoft.Singularity.Hal.Acpi
{
    using System;
    using System.Collections;
    using System.Text;

    using Microsoft.Singularity.Io;
    using Microsoft.Singularity.Hal;

    internal sealed class VerboseOut
    {
        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void WriteLine(string format, __arglist)
        {
            DebugStub.WriteLine(format, new ArgIterator(__arglist));
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void WriteLine(string message)
        {
            DebugStub.WriteLine(message);
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void WriteLine()
        {
            DebugStub.WriteLine();
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void Write(string format, __arglist)
        {
            DebugStub.Write(format, new ArgIterator(__arglist));
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void Write(string message)
        {
            DebugStub.Write(message);
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void Print(string format, __arglist)
        {
            DebugStub.Print(format, new ArgIterator(__arglist));
        }

        [System.Diagnostics.Conditional("VERBOSE")]
        internal static void Print(string message)
        {
            DebugStub.Print(message);
        }
    }

    [ CLSCompliant(false) ]
    public class AcpiTables
    {
        private static Fadt fadt;
        private static Madt madt;

        private static Dsdt dsdt;
        private static Ssdt ssdt;

        private static Srat srat;

        private static SystemTableHeader rsdtHeader;
        private static Rsdt rsdt;

        private static PMTimer pmTimer;

        private static AcpiNamespace acpiNamespace;
        private static ReservedObjects reservedObjects;

        private static UIntPtr GetRsdpBase()
        {
            unsafe
            {
                return Platform.ThePlatform.AcpiRoot32;
            }
        }

        public static Fadt GetFadt()
        {
            return fadt;
        }

        public static Madt GetMadt()
        {
            return madt;
        }

        public static Srat GetSrat()
        {
            return srat;
        }

        public static PMTimer GetPMTimer()
        {
            return pmTimer;
        }

        public static AcpiNamespace AcpiNamespace
        {
            get
            {
                return acpiNamespace;
            }
        }

        private class StringSet
        {
            private SortedList map = new SortedList();

            public void Add(string s)
            {
                map.Add(s, null);
            }

            public bool Contains(string s)
            {
                return map.ContainsKey(s);
            }
        }

        public static void Parse()
        {
            UIntPtr rsdpBase = GetRsdpBase();

            if (rsdpBase == UIntPtr.Zero) {
                VerboseOut.Print("ACPI RSDP not found\n");
            }

#if DUMP_ACPI_TABLES_UUENCODED
            UuencodeDumpRegion("RSDP.dmp",
                IoMemory.MapPhysicalMemory(
                    rsdpBase, 36u,
                    true, false));
#endif
            Rsdp rsdp = Rsdp.Parse(rsdpBase, 36u);

            VerboseOut.Print("ACPI RSDP OemId is {0:x8}\n",
                            __arglist(rsdp.OemId));
            VerboseOut.Print("ACPI RSDP revision is {0:x8}\n",
                            __arglist(rsdp.Revision));

            if (rsdp.Revision == 2) {
                rsdtHeader = SystemTableHeader.Create(rsdp.XsdtAddress);
#if DUMP_ACPI_TABLES_UUENCODED
                UuencodeDumpRegion("XSDT.dmp",
                    IoMemory.MapPhysicalMemory(
                        rsdtHeader.Address, rsdtHeader.FullTableLength,
                        true, false));
#endif
                rsdt = Xsdt.Create(rsdtHeader);
            }
            else {
                rsdtHeader = SystemTableHeader.Create(rsdp.RsdtAddress);
#if DUMP_ACPI_TABLES_UUENCODED
                UuencodeDumpRegion("RSDT.dmp",
                    IoMemory.MapPhysicalMemory(
                        rsdtHeader.Address, rsdtHeader.FullTableLength,
                        true, false));
#endif
                rsdt = Rsdt.Create(rsdtHeader);
            }

            VerboseOut.Print("ACPI RSDT/XSDT OemTableId is {0}\n",
                            __arglist(rsdtHeader.OemTableId));
            VerboseOut.Print("ACPI RSDT/XSDT Revision is {0:x8}\n",
                            __arglist(rsdtHeader.Revision));
            VerboseOut.Print("ACPI RSDT/XSDT CreatorId is {0:x8}\n",
                            __arglist(rsdtHeader.CreatorId));
            VerboseOut.Print("ACPI RSDT/XSDT CreatorRevision is {0:x8}\n",
                            __arglist(rsdtHeader.CreatorRevision));

            VerboseOut.Print("RSDT contains:\n");
            for (int i = 0; i < rsdt.EntryCount; i++) {
                SystemTableHeader header = rsdt.GetTableHeader(i);
                VerboseOut.Print("    {0:x8}\n", __arglist(header.Signature));
                if (header.Signature == Fadt.Signature) {
                    fadt = Fadt.Create(header);
                }
                else if (header.Signature == Madt.Signature) {
                    madt = Madt.Create(header);
                }
                else if (header.Signature == Ssdt.Signature) {
                    ssdt = Ssdt.Create(header);
                }
                // Srat, Slit
                else if (header.Signature == Srat.Signature) {
                    srat = Srat.Create(header);
                    srat.ParseSratStructure();

                    // srat.DumpSratOffsets();
                    // srat.DumpSratImportantFields();
                    // srat.DumpSratStructure();

                }
            }

            SystemTableHeader dsdtHeader = null;

            if (fadt != null) {
                pmTimer = PMTimer.Create(fadt);
                VerboseOut.Print("PMTimer Value={0} Width={1}\n",
                                __arglist(pmTimer.Value, pmTimer.Width));
                uint t0 = pmTimer.Value;
                uint t1 = pmTimer.Value;
                uint t2 = pmTimer.Value;
                uint delta = (t2 >= t1) ? t2 - t1 : ((t1 | 0xff000000) - t2);
                VerboseOut.Print("Read cost {0} ticks\n", __arglist(delta));

                if (fadt.DSDT != 0) {
                    dsdtHeader = SystemTableHeader.Create(fadt.DSDT);
                    dsdt = Dsdt.Create(dsdtHeader);
                }
            }

            VerboseOut.Print("Parsing and loading AML\n");

#if DUMP_ACPI_TABLES_UUENCODED
            if (dsdtHeader != null) {
                UuencodeDumpRegion("ACPI.DSDT.dmp",
                    IoMemory.MapPhysicalMemory(
                                dsdtHeader.Address, dsdtHeader.FullTableLength,
                                true, false));
            }

            for (int i = 0; i < rsdt.EntryCount; i++) {
                SystemTableHeader header = rsdt.GetTableHeader(i);
                UuencodeDumpRegion("ACPI." + header.Signature + "." + header.OemTableId + ".dmp",
                    IoMemory.MapPhysicalMemory(
                        header.Address, header.FullTableLength,
                        true, false));
            }
#endif // DUMP_ACPI_TABLES_UUENCODED
        }

        public static AcpiDevice[] LoadDevices()
        {
            OperationRegionAccessor operationRegionAccessor = new OperationRegionAccessor();
            acpiNamespace = new AcpiNamespace();
            reservedObjects = new ReservedObjects(acpiNamespace);
            reservedObjects.CreateReservedObjects();

            if (dsdt != null) {
                if (ParseAndLoadRegion(dsdt.Region, operationRegionAccessor) == AmlParser.ParseSuccess.Failure) {
                    throw new Exception("AML parser failure while parsing DSDT");
                }
            }

            // From the spec: "SSDTs are a continuation of the DSDT. Multiple SSDTs
            // can be used as part of a platform description. After the DSDT is loaded
            // into the ACPI Namespace, each secondary description table listed in the
            // RSDT/XSDT with a unique OEM Table ID is loaded." - section 2.1, General
            // ACPI Terminology
            StringSet visitedOemTableIds = new StringSet();
            for (int i = 0; i < rsdt.EntryCount; i++) {
                SystemTableHeader header = rsdt.GetTableHeader(i);
                VerboseOut.Print("    {0:x8}\n", __arglist(header.Signature));

                string oemTableId = header.OemTableId;
                if (!visitedOemTableIds.Contains(oemTableId) && header.Signature == Ssdt.Signature) {
                    visitedOemTableIds.Add(oemTableId);
                    ssdt = Ssdt.Create(header);
                    if (ParseAndLoadRegion(ssdt.Region, operationRegionAccessor) == AmlParser.ParseSuccess.Failure) {
                        throw new Exception("AML parser failure while parsing SSDT " + oemTableId);
                    }
                }
            }

#if DUMP_ACPI_NAMESPACE
            DebugStub.WriteLine("Dumping ACPI namespace tree...");
            acpiNamespace.DumpTree();
#endif

            return GetDeviceInfo(operationRegionAccessor);
        }

#if DUMP_ACPI_TABLES_UUENCODED
        private static void UuencodeDumpRegion(string filename, IoMemory region)
        {
            DebugStub.Print("\nbegin 777 {0}\n", __arglist(filename));

            StringBuilder line = new StringBuilder();
            int inputBytesOnLine = 0;
            for (int i = 0; i < region.Length; i += 3) {
                byte b1 = (byte)0, b2 = (byte)0, b3 = (byte)0;

                b1 = region.Read8(i);
                inputBytesOnLine++;

                if (i + 1 < region.Length) {
                    b2 = region.Read8(i + 1);
                    inputBytesOnLine++;
                }

                if (i + 2 < region.Length) {
                    b3 = region.Read8(i + 2);
                    inputBytesOnLine++;
                }

                line.Append((char)(32 + (b1 >> 2)));
                line.Append((char)(32 + (((b1 << 4) | (b2 >> 4)) & 0x3F)));
                line.Append((char)(32 + (((b2 << 2) | (b3 >> 6)) & 0x3F)));
                line.Append((char)(32 + (b3 & 0x3F)));

                if (line.Length >= 60 || i + 3 >= region.Length) {
                    DebugStub.Print("{0}{1}\n", __arglist((char)(inputBytesOnLine + 32), line.ToString()));
                    line.Remove(0, line.Length);
                    inputBytesOnLine = 0;
                }
            }

            DebugStub.Print("end\n\n");
        }
#endif // #if DUMP_ACPI_TABLES_UUENCODED

        private static AmlParser.ParseSuccess ParseAndLoadRegion(IoMemory region, AcpiObject.IOperationRegionAccessor operationRegionAccessor)
        {
            AmlParser.AMLCode result;
            int offset = 0;
            AmlParser parser = new AmlParser(new IoMemoryAmlStreamAdapter(region), null, null);
            AmlParser.ParseSuccess parseSuccess =
                parser.ParseAMLCode(out result, ref offset, region.Length);

            if (parseSuccess == AmlParser.ParseSuccess.Success) {
                AmlLoader loader = new AmlLoader(acpiNamespace, operationRegionAccessor);
                loader.Load(result);
            }
            return parseSuccess;
        }

        private static AcpiDevice[] GetDeviceInfo(AcpiObject.IOperationRegionAccessor operationRegionAccessor)
        {
            ArrayList deviceInfo = new ArrayList();

            AmlInterpreter interpreter = new AmlInterpreter(acpiNamespace, operationRegionAccessor);

            foreach (AcpiNamespace.Node crsNode in acpiNamespace.GetAllNodes()) {
                if (crsNode.Name != "_CRS") {
                    continue;
                }

                VerboseOut.Write("Loading resource descriptors for ACPI device ");
                foreach (string segment in crsNode.Path.RemoveSegment().NameSegments) {
                    VerboseOut.Write(segment + "\\");
                }
                VerboseOut.WriteLine();

                AcpiNamespace.Node hidNode =
                    acpiNamespace.LookupNode(crsNode.Path.RemoveSegmentAbsolute().AddSegmentAbsolute("_HID"));
                if (hidNode == null) {
                    throw new Exception("Found device with _CRS property but no matching _HID property");
                }

                AcpiObject.AcpiObject hidObject = hidNode.Value;
                if (hidObject is AcpiObject.BytecodeMethod) {
                    AmlInterpreterThread thread =
                        interpreter.InvokeMethodOnNewThread(null, hidNode.Path, new AcpiObject.AcpiObject[] { });
                    interpreter.Run();
                    hidObject = thread.ExitValue;
                }
                string deviceId = HidObjectToDeviceId(hidObject);

                AcpiObject.AcpiObject crsObject = crsNode.Value;
                if (crsObject is AcpiObject.BytecodeMethod) {
                    AmlInterpreterThread thread =
                        interpreter.InvokeMethodOnNewThread(null, crsNode.Path, new AcpiObject.AcpiObject[] { });
                    interpreter.Run();
                    crsObject = thread.ExitValue;
                }

                if (crsObject is AcpiObject.Buffer) {
                    byte[] crsBuffer = crsObject.GetAsBuffer().Contents;
                    ResourceDescriptor[] resourceDescriptors = ResourceDescriptorParser.Parse(crsBuffer);

                    VerboseOut.WriteLine("Loaded resource descriptor for device " + deviceId);

                    deviceInfo.Add(new AcpiDevice(deviceId, resourceDescriptors));
                }
                else {
                    VerboseOut.WriteLine("No resource descriptor for device " + deviceId);
                }
            }

            return (AcpiDevice[])deviceInfo.ToArray(typeof(AcpiDevice));
        }

        private static string HidObjectToDeviceId(AcpiObject.AcpiObject obj)
        {
            AcpiObject.AcpiObjectType type =
                (AcpiObject.AcpiObjectType)((AcpiObject.Integer)(obj.ObjectType())).Value;
            string hid;

            if (type == AcpiObject.AcpiObjectType.Integer)
            {
                // Swap byte order so that all fields are contiguous
                ulong eisaId = ByteOrder.Swap((uint)(((AcpiObject.Integer)obj).Value));
                hid = String.Format("{0}{1}{2}{3:X}{4:X}{5:X}{6:X}",
                                    (char)(((eisaId >> 26) & 0x1F) + '@'),
                                    (char)(((eisaId >> 21) & 0x1F) + '@'),
                                    (char)(((eisaId >> 16) & 0x1F) + '@'),
                                    (eisaId >> 12) & 0xF,
                                    (eisaId >> 8) & 0xF,
                                    (eisaId >> 4) & 0xF,
                                    (eisaId >> 0) & 0xF);
            }
            else if (type == AcpiObject.AcpiObjectType.String) {
                hid = ((AcpiObject.String)obj).Value;
            }
            else {
                throw new ArgumentException("_HID object was not an integer or string as expected");
            }

            if (hid.StartsWith("PNP")) {
                return "/pnp/" + hid;
            }
            else {
                return "/acpi/" + hid;
            }
        }

        public class IoMemoryAmlStreamAdapter : IAmlStream
        {
            private IoMemory region;

            public IoMemoryAmlStreamAdapter(IoMemory region)
            {
                this.region = region;
            }

            public byte ReadByteData(ref int offset)
            {
                if (offset + 1 > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                byte result = region.Read8(offset);
                offset++;
                return result;
            }

            public bool TryReadByteData(ref int offset, out byte result)
            {
                if (offset + 1 > region.Length) {
                    result = 0;
                    return false;
                }
                result = region.Read8(offset);
                offset++;
                return true;
            }

            public char ReadChar(ref int offset)
            {
                if (offset + 1 > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                char result = (char)region.Read8(offset);
                offset++;
                return result;
            }

            public byte[] ReadByteDataArray(ref int offset, int length)
            {
                if (offset + length > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                byte[] result = new byte[length];
                if (length != 0) {
                    region.Read8(offset, result, 0, length);
                }
                offset += length;
                return result;
            }

            public bool TryReadByteDataArray(ref int offset, int length, out byte[] result)
            {
                if (offset + length > region.Length) {
                    result = null;
                    return false;
                }
                result = new byte[length];
                if (length != 0) {
                    region.Read8(offset, result, 0, length);
                }
                offset += length;
                return true;
            }

            public ushort ReadWordData(ref int offset)
            {
                if (offset + 2 > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                ushort result = ByteOrder.LittleEndianToHost(region.Read16(offset));
                offset += 2;
                return result;
            }

            public uint ReadDWordData(ref int offset)
            {
                if (offset + 4 > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                uint result = ByteOrder.LittleEndianToHost(region.Read32(offset));
                offset += 4;
                return result;
            }

            public ulong ReadQWordData(ref int offset)
            {
                if (offset + 8 > region.Length) {
                    throw new EndOfAmlStreamException();
                }
                ulong result = ByteOrder.LittleEndianToHost(region.Read64(offset));
                offset += 8;
                return result;
            }
        }

        public class OperationRegionAccessor : AcpiObject.IOperationRegionAccessor
        {
            private const ushort PciAddressPort = 0xcf8;
            private const uint PciConfigEnableMask = 1u << 31;
            private IoPort pciConfigAddressPort;
            private IoPort pciConfigDataPort;

            public OperationRegionAccessor()
            {
                IoPortRange pciConfigPorts = new IoPortRange(PciAddressPort, 8, Access.ReadWrite);
                pciConfigAddressPort = pciConfigPorts.PortAtOffset(0, 4, Access.Write);
                pciConfigDataPort = pciConfigPorts.PortAtOffset(4, 4, Access.ReadWrite);
            }

            public byte Read8(AcpiObject.RegionSpace regionSpace, ulong offset)
            {
                byte result;
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        // TODO: This is a first stab - ideally the AcpiObject.OperationRegion
                        // ought to be holding onto an IoMemoryRange and passing it in repeatedly.
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 1, true/*readable*/, false/*writable*/);
                        result = region.Read8(0);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 1, Access.Read);
                        result = port.Read8();
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        result = pciConfigDataPort.Read8();
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }
#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 1 + ", result: " + result.ToString("X"));
#endif
                return result;
            }

            public void Write8(AcpiObject.RegionSpace regionSpace, ulong offset, byte value)
            {
#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 1 + ", value: " + value.ToString("X"));
#endif
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 1, true/*readable*/, true/*writable*/);
                        region.Write8(0, value);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 1, Access.ReadWrite);
                        port.Write8(value);
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        pciConfigDataPort.Write8(value);
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }
            }

            public ushort Read16(AcpiObject.RegionSpace regionSpace, ulong offset)
            {
                ushort result;
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        // TODO: This is a first stab - ideally the AcpiObject.OperationRegion
                        // ought to be holding onto an IoMemoryRange and passing it in.
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 2, true/*readable*/, false/*writable*/);
                        result = region.Read16(0);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 2, Access.Read);
                        result = port.Read16();
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        result = pciConfigDataPort.Read16();
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }

#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 2 + ", result: " + result.ToString("X"));
#endif
                return result;
            }

            public void Write16(AcpiObject.RegionSpace regionSpace, ulong offset, ushort value)
            {
#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 2 + ", value: " + value.ToString("X"));
#endif
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 2, true/*readable*/, true/*writable*/);
                        region.Write16(0, value);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 2, Access.ReadWrite);
                        port.Write16(value);
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        pciConfigDataPort.Write16(value);
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }
            }

            public uint Read32(AcpiObject.RegionSpace regionSpace, ulong offset)
            {
                uint result;
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 4, true/*readable*/, false/*writable*/);
                        result = region.Read32(0);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 4, Access.Read);
                        result = port.Read32();
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        result = pciConfigDataPort.Read32();
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }
#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 4 + ", result: " + result.ToString("X"));
#endif
                return result;
            }

            public void Write32(AcpiObject.RegionSpace regionSpace, ulong offset, uint value)
            {
#if DUMP_RAW_READ_WRITES
                DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 4 + ", value: " + value.ToString("X"));
#endif
                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, 4, true/*readable*/, true/*writable*/);
                        region.Write32(0, value);
                        break;

                    case AcpiObject.RegionSpace.SystemIO:
                        IoPort port = new IoPort((ushort)offset, 4, Access.ReadWrite);
                        port.Write32(value);
                        break;

                    case AcpiObject.RegionSpace.PCI_Config:
                        pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset);
                        pciConfigDataPort.Write32(value);
                        break;

                    default:
                        throw new Exception("Unimplemented operation region type" + regionSpace);
                }
            }

            public byte[] ReadBytes(AcpiObject.RegionSpace regionSpace, ulong offset, ulong length)
            {
                byte[] result = new byte[length];

                switch (regionSpace) {
                    case AcpiObject.RegionSpace.SystemMemory:
                        IoMemory region = IoMemory.MapPhysicalMemory(offset, length, true/*readable*/, false/*writable*/);
                        for (ulong i = 0; i < length; i++) {
                            result[i] = region.Read8((int)i);
                        }
                        break;

                    default:
                        throw new Exception("ReadBytes() only supported for SystemMemory regions");
                }

#if DUMP_RAW_READ_WRITES
                DebugStub.Write("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + length + ", result: {");
                for (int i = 0; i < result.Length; i++) {
                    DebugStub.Write(result[i].ToString("X"));
                    if (i < result.Length - 1) {
                        DebugStub.Write(",");
                    }
                }
                DebugStub.WriteLine("}");
#endif

                return result;
            }
        }
    }

    public class AcpiDevice
    {
        string deviceId;
        ResourceDescriptor[] resourceDescriptors;

        public AcpiDevice(string deviceId, ResourceDescriptor[] resourceDescriptors)
        {
            this.deviceId = deviceId;
            this.resourceDescriptors = resourceDescriptors;
        }

        public string DeviceId
        {
            get
            {
                return deviceId;
            }
        }

        public ResourceDescriptor[] ResourceDescriptors {
            get
            {
                return resourceDescriptors;
            }
        }
    }
}
