using System; using System.Collections; using System.Diagnostics; using System.IO; using Microsoft.Singularity.Hal.Acpi; using AcpiObject = Microsoft.Singularity.Hal.Acpi.AcpiObject; namespace AppReader { public class FileStreamAmlStreamAdapter : IAmlStream { private FileStream stream; public FileStreamAmlStreamAdapter(FileStream stream) { this.stream = stream; } public byte ReadByteData(ref int offset) { stream.Seek(offset, SeekOrigin.Begin); offset++; return (byte)stream.ReadByte(); } public bool TryReadByteData(ref int offset, out byte result) { if (offset > stream.Length) { result = 0; return false; } else { stream.Seek(offset, SeekOrigin.Begin); offset++; result = (byte)stream.ReadByte(); return true; } } public char ReadChar(ref int offset) { stream.Seek(offset, SeekOrigin.Begin); offset++; return (char)stream.ReadByte(); } public byte[] ReadByteDataArray(ref int offset, int length) { stream.Seek(offset, SeekOrigin.Begin); offset += length; byte[] result = new byte[length]; stream.Read(result, 0, length); return result; } public bool TryReadByteDataArray(ref int offset, int length, out byte[] result) { if (offset + length > stream.Length) { result = null; return false; } else { stream.Seek(offset, SeekOrigin.Begin); offset += length; result = new byte[length]; stream.Read(result, 0, length); return true; } } public ushort ReadWordData(ref int offset) { stream.Seek(offset, SeekOrigin.Begin); offset += 2; byte[] result = new byte[2]; stream.Read(result, 0, result.Length); return (ushort)(result[0] + (((ushort)result[1]) << 8)); } public uint ReadDWordData(ref int offset) { stream.Seek(offset, SeekOrigin.Begin); offset += 4; byte[] result = new byte[4]; stream.Read(result, 0, result.Length); return (uint)(result[0] + (((uint)result[1]) << 8) + (((uint)result[2]) << 16) + (((uint)result[3]) << 24)); } public ulong ReadQWordData(ref int offset) { stream.Seek(offset, SeekOrigin.Begin); offset += 8; byte[] result = new byte[8]; stream.Read(result, 0, result.Length); return (ulong)(result[0] + (((ulong)result[1]) << 8) + (((ulong)result[2]) << 16) + (((ulong)result[3]) << 24) + (((ulong)result[4]) << 32) + (((ulong)result[5]) << 40) + (((ulong)result[6]) << 48) + (((ulong)result[7]) << 56)); } } public class MismatchedTraceException : Exception { public MismatchedTraceException() : base("Read/write requests do not match supplied trace") { } } // Uses a trace from a run on a real machine to ensure identical results offline public class TraceOperationRegionAccessor : AcpiObject.IOperationRegionAccessor { StreamReader trace; bool isDone; bool isRead; bool isWrite; AcpiObject.RegionSpace regionSpace; ulong offset; ulong length; ulong value; byte[] valueSequence; public TraceOperationRegionAccessor(Stream traceStream) { trace = new StreamReader(traceStream); ReadNext(); } private void ReadNext() { string line = trace.ReadLine(); isDone = (line == null || line.Trim() == ""); if (!isDone) { string[] fields = line.Trim().Split(new char[] {' '}); isRead = fields[0].StartsWith("r"); isWrite = !isRead; // Just for readability regionSpace = (AcpiObject.RegionSpace)int.Parse(fields[1]); offset = ulong.Parse(fields[2]); length = ulong.Parse(fields[3]); if (fields.Length > 4) { value = ulong.Parse(fields[4]); } if (fields.Length != 5) { valueSequence = new byte[fields.Length - 4]; for (int i = 0; i < valueSequence.Length; i++) { valueSequence[i] = byte.Parse(fields[4 + i]); } } } } public byte Read8(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read8 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isRead && length == 1 && regionSpace == this.regionSpace && offset == this.offset)) { throw new MismatchedTraceException(); } byte result = (byte)value; ReadNext(); return result; } public void Write8(AcpiObject.RegionSpace regionSpace, ulong offset, byte value) { Console.WriteLine("Write8 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isWrite && length == 1 && regionSpace == this.regionSpace && offset == this.offset && value == this.value)) { throw new MismatchedTraceException(); } ReadNext(); } public ushort Read16(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read16 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isRead && length == 2 && regionSpace == this.regionSpace && offset == this.offset)) { throw new MismatchedTraceException(); } ushort result = (ushort)value; ReadNext(); return result; } public void Write16(AcpiObject.RegionSpace regionSpace, ulong offset, ushort value) { Console.WriteLine("Write16 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isWrite && length == 2 && regionSpace == this.regionSpace && offset == this.offset && value == this.value)) { throw new MismatchedTraceException(); } ReadNext(); } public uint Read32(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read32 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isRead && length == 4 && regionSpace == this.regionSpace && offset == this.offset)) { throw new MismatchedTraceException(); } uint result = (uint)value; ReadNext(); return result; } public void Write32(AcpiObject.RegionSpace regionSpace, ulong offset, uint value) { Console.WriteLine("Write32 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isWrite && length == 4 && regionSpace == this.regionSpace && offset == this.offset && value == this.value)) { throw new MismatchedTraceException(); } ReadNext(); } public byte[] ReadBytes(AcpiObject.RegionSpace regionSpace, ulong offset, ulong length) { Console.WriteLine("Read " + length + " bytes at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); if (!(isRead && length == (ulong)valueSequence.Length && regionSpace == this.regionSpace && offset == this.offset)) { throw new MismatchedTraceException(); } byte[] result = valueSequence; ReadNext(); return valueSequence; } } public class TestOperationRegionAccessor : AcpiObject.IOperationRegionAccessor { public byte Read8(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read8 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); return 10; } public void Write8(AcpiObject.RegionSpace regionSpace, ulong offset, byte value) { Console.WriteLine("Write8 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); } public ushort Read16(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read16 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); return 1024; } public void Write16(AcpiObject.RegionSpace regionSpace, ulong offset, ushort value) { Console.WriteLine("Write16 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); } public uint Read32(AcpiObject.RegionSpace regionSpace, ulong offset) { Console.WriteLine("Read32 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); return 1024; } public void Write32(AcpiObject.RegionSpace regionSpace, ulong offset, uint value) { Console.WriteLine("Write32 at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); } public byte[] ReadBytes(AcpiObject.RegionSpace regionSpace, ulong offset, ulong length) { Console.WriteLine("Read " + length + " bytes at " + regionSpace.ToString() + ":0x" + offset.ToString("X")); byte[] result = new byte[length]; for (ulong i = 0; i < length; i++) { result[i] = 10; } return result; } } public class ReaderMain { static void Usage() { Console.WriteLine("AppReader.exe "); return; } [STAThread] public static void Main(string[] args) { if (args.Length < 1) { Usage(); Environment.Exit(1); } try { AcpiNamespace acpiNamespace = new AcpiNamespace(); ReservedObjects reservedObjects = new ReservedObjects(acpiNamespace); reservedObjects.CreateReservedObjects(); AcpiObject.IOperationRegionAccessor operationRegionAccessor = new TestOperationRegionAccessor(); AmlInterpreter interpreter = new AmlInterpreter(acpiNamespace, operationRegionAccessor); foreach (string flag in args) { if (flag.StartsWith("/tracefile=")) { operationRegionAccessor = new TraceOperationRegionAccessor(new FileStream(flag.Substring("/tracefile=".Length), FileMode.Open, FileAccess.Read)); } else if (flag.StartsWith("/")) { Console.WriteLine("Unrecognized flag '" + flag + "'"); Environment.Exit(1); } } AmlParser.ParseSuccess parseSuccess = AmlParser.ParseSuccess.Success; foreach (string dumpFilename in args) { if (dumpFilename.StartsWith("/")) { continue; } using (FileStream reader = new FileStream(dumpFilename, FileMode.Open, FileAccess.Read)) { AmlParser.AMLCode result; int offset = 0x24; // Skip header AmlParser parser = new AmlParser(new FileStreamAmlStreamAdapter(reader), null, null); parseSuccess = parser.ParseAMLCode(out result, ref offset, (int)(new FileInfo(dumpFilename).Length)); if (parseSuccess == AmlParser.ParseSuccess.Success) { AmlLoader loader = new AmlLoader(acpiNamespace, operationRegionAccessor); loader.Load(result); } else { break; } } } if (parseSuccess == AmlParser.ParseSuccess.Success) { parseSuccess = interpreter.ParseMethodBodies(); } if (parseSuccess == AmlParser.ParseSuccess.Success) { LoadDeviceInfo(acpiNamespace, operationRegionAccessor); } if (parseSuccess == AmlParser.ParseSuccess.Success) { Console.WriteLine("Parsed successfully"); Environment.Exit(0); } else { Console.WriteLine("Encountered error during parse"); Environment.Exit(1); } } catch (Exception e) { Console.WriteLine("Encountered exception: " + e.Message); Console.WriteLine(e.StackTrace); Environment.Exit(1); } } private static void LoadDeviceInfo(AcpiNamespace acpiNamespace, AcpiObject.IOperationRegionAccessor operationRegionAccessor) { AmlInterpreter interpreter = new AmlInterpreter(acpiNamespace, operationRegionAccessor); foreach (AcpiNamespace.Node crsNode in acpiNamespace.GetAllNodes()) { if (crsNode.Name != "_CRS") { continue; } Console.Write("Loading resource descriptors for ACPI device "); Console.WriteLine(crsNode.Path.RemoveSegment().ToString()); 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); Console.WriteLine("Loaded resource descriptor for device " + deviceId); } else { Console.WriteLine("No resource descriptor for device " + deviceId); } } } private static string HidObjectToDeviceId(AcpiObject.AcpiObject obj) { AcpiObject.AcpiObjectType type = (AcpiObject.AcpiObjectType)((AcpiObject.Integer)(obj.ObjectType())).Value; string hid; if (obj is AcpiObject.Integer) { // Swap byte order so that all fields are contiguous ulong eisaId = ByteOrderSwap((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 (obj is AcpiObject.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; } } private static uint ByteOrderSwap(uint i) { return (((i >> 24) & 0xFF) << 0) | (((i >> 16) & 0xFF) << 8) | (((i >> 8) & 0xFF) << 16) | (((i >> 0) & 0xFF) << 24); } private static void PrintResult(AcpiObject.AcpiObject result) { Console.WriteLine(result.ToString()); } } }