463 lines
18 KiB
C#
463 lines
18 KiB
C#
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 <manifestfile | (-d | -s) manifestdirectory | -r appdirectory>");
|
|
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());
|
|
}
|
|
}
|
|
}
|