1049 lines
33 KiB
C#
1049 lines
33 KiB
C#
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
|
|
// Interprets ACPI methods.
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
|
|
using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node;
|
|
using NodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.NodePath;
|
|
using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath;
|
|
|
|
using Microsoft.Singularity.Hal.Acpi;
|
|
using Microsoft.Singularity.Hal.Acpi.AcpiObject;
|
|
using Microsoft.Singularity.Hal.Acpi.AmlParserUnions;
|
|
using Microsoft.Singularity.Hal.Acpi.StackIR;
|
|
|
|
namespace Microsoft.Singularity.Hal.Acpi
|
|
{
|
|
public class InterpretException : Exception
|
|
{
|
|
public InterpretException() : base("Error interpreting AML") { }
|
|
|
|
public InterpretException(string s) : base("Error interpreting AML: " + s) { }
|
|
}
|
|
|
|
public class MethodNotFoundInterpretException : Exception
|
|
{
|
|
public MethodNotFoundInterpretException() : base("Error interpreting AML: Method not found") { }
|
|
|
|
public MethodNotFoundInterpretException(string s) : base("Error interpreting AML: Method not found: " + s) { }
|
|
}
|
|
|
|
public abstract class IoLocation
|
|
{
|
|
public abstract AcpiObject.AcpiObject Read();
|
|
public abstract void Write(AcpiObject.AcpiObject value);
|
|
}
|
|
|
|
public class ValueIoLocation : IoLocation
|
|
{
|
|
private AcpiObject.AcpiObject value;
|
|
|
|
public ValueIoLocation()
|
|
{
|
|
value = new UninitializedObject();
|
|
}
|
|
|
|
public ValueIoLocation(AcpiObject.AcpiObject acpiObject)
|
|
{
|
|
this.value = acpiObject;
|
|
}
|
|
|
|
public override AcpiObject.AcpiObject Read()
|
|
{
|
|
return value;
|
|
}
|
|
|
|
public override void Write(AcpiObject.AcpiObject value)
|
|
{
|
|
throw new InterpretException("Attempt to store to temporary object");
|
|
}
|
|
}
|
|
|
|
public class DebugIoLocation : IoLocation
|
|
{
|
|
public override AcpiObject.AcpiObject Read()
|
|
{
|
|
throw new InterpretException("Attempt to read from debug object");
|
|
}
|
|
|
|
public override void Write(AcpiObject.AcpiObject value)
|
|
{
|
|
#if SINGULARITY_KERNEL
|
|
DebugStub.WriteLine("ACPI DEBUG: " + value.ToString());
|
|
#else
|
|
Console.WriteLine("ACPI DEBUG: " + value.ToString());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public class VariableIoLocation : ValueIoLocation
|
|
{
|
|
private AcpiObject.AcpiObject value;
|
|
|
|
public VariableIoLocation()
|
|
{
|
|
value = new UninitializedObject();
|
|
}
|
|
|
|
public VariableIoLocation(AcpiObject.AcpiObject acpiObject)
|
|
{
|
|
value = acpiObject;
|
|
}
|
|
|
|
public override AcpiObject.AcpiObject Read()
|
|
{
|
|
return value;
|
|
}
|
|
|
|
public override void Write(AcpiObject.AcpiObject value)
|
|
{
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
public class NodePathIoLocation : ValueIoLocation
|
|
{
|
|
private AcpiNamespace.Node node;
|
|
|
|
public NodePathIoLocation(AcpiNamespace.Node node)
|
|
{
|
|
if (node == null) {
|
|
throw new ArgumentException("node should be non-null");
|
|
}
|
|
this.node = node;
|
|
}
|
|
|
|
public override AcpiObject.AcpiObject Read()
|
|
{
|
|
return node.Value;
|
|
}
|
|
|
|
public override void Write(AcpiObject.AcpiObject value)
|
|
{
|
|
node.Value = value;
|
|
}
|
|
}
|
|
|
|
public class AmlStackFrame : IDisposable
|
|
{
|
|
// Local variable and argument objects
|
|
public const int ReservedLocals = 2; // For use by the AmlStackIR compiler
|
|
public const int FirstReservedLocal = 8;
|
|
public const int NumLocals = FirstReservedLocal + ReservedLocals;
|
|
VariableIoLocation[] locals = new VariableIoLocation[NumLocals];
|
|
public const int NumArgs = 7;
|
|
VariableIoLocation[] args = new VariableIoLocation[NumArgs];
|
|
|
|
StackIRNode[] instructions;
|
|
int currentInstructionIndex;
|
|
|
|
private class AcpiNamespaceNodeSet : IEnumerable
|
|
{
|
|
private ArrayList nodeSet = new ArrayList();
|
|
|
|
public void Add(AcpiNamespace.Node node)
|
|
{
|
|
if (!nodeSet.Contains(node)) {
|
|
nodeSet.Add(node);
|
|
}
|
|
}
|
|
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return nodeSet.GetEnumerator();
|
|
}
|
|
}
|
|
|
|
AcpiNamespaceNodeSet methodTemporaryNamespaceObjectSet = new AcpiNamespaceNodeSet();
|
|
|
|
public AmlStackFrame(StackIRNode[] instructions, AcpiObject.AcpiObject[] args)
|
|
{
|
|
this.instructions = instructions;
|
|
currentInstructionIndex = 0;
|
|
|
|
int i;
|
|
for (i = 0; i < locals.Length; i++) {
|
|
locals[i] = new VariableIoLocation();
|
|
}
|
|
for (i = 0; i < args.Length; i++) {
|
|
this.args[i] = new VariableIoLocation(args[i]);
|
|
}
|
|
for (; i < this.args.Length; i++) {
|
|
this.args[i] = new VariableIoLocation();
|
|
}
|
|
}
|
|
|
|
public void NotifyCreateNodeAt(NodePath nodePath, Node node)
|
|
{
|
|
methodTemporaryNamespaceObjectSet.Add(node);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (Node node in methodTemporaryNamespaceObjectSet) {
|
|
node.Remove();
|
|
}
|
|
}
|
|
|
|
public VariableIoLocation GetArgument(int argNum)
|
|
{
|
|
return args[argNum];
|
|
}
|
|
|
|
public void SetArgument(int argNum, AcpiObject.AcpiObject value)
|
|
{
|
|
args[argNum].Write(value);
|
|
}
|
|
|
|
public VariableIoLocation GetLocal(int localNum)
|
|
{
|
|
return locals[localNum];
|
|
}
|
|
|
|
public void SetLocal(int localNum, AcpiObject.AcpiObject value)
|
|
{
|
|
locals[localNum].Write(value);
|
|
}
|
|
|
|
public StackIRNode GetNextInstruction()
|
|
{
|
|
return instructions[currentInstructionIndex];
|
|
}
|
|
|
|
public void AdvanceInstruction()
|
|
{
|
|
currentInstructionIndex++;
|
|
}
|
|
|
|
public void JumpTo(int index)
|
|
{
|
|
currentInstructionIndex = index;
|
|
}
|
|
}
|
|
|
|
public delegate void MethodResultCallback(AcpiObject.AcpiObject value);
|
|
|
|
public class AmlInterpreterThread
|
|
{
|
|
private class AmlStack
|
|
{
|
|
private Stack stack = new Stack();
|
|
|
|
public void Push(AmlStackFrame node)
|
|
{
|
|
stack.Push(node);
|
|
}
|
|
|
|
public AmlStackFrame Pop()
|
|
{
|
|
return (AmlStackFrame)stack.Pop();
|
|
}
|
|
|
|
public AmlStackFrame Peek()
|
|
{
|
|
return (AmlStackFrame)stack.Peek();
|
|
}
|
|
|
|
public bool IsEmpty()
|
|
{
|
|
return stack.Count == 0;
|
|
}
|
|
}
|
|
|
|
private class IoLocationStack
|
|
{
|
|
private Stack stack = new Stack();
|
|
|
|
public void Push(IoLocation node)
|
|
{
|
|
stack.Push(node);
|
|
}
|
|
|
|
public IoLocation Pop()
|
|
{
|
|
return (IoLocation)stack.Pop();
|
|
}
|
|
|
|
public IoLocation Peek()
|
|
{
|
|
return (IoLocation)stack.Peek();
|
|
}
|
|
}
|
|
|
|
AcpiNamespace acpiNamespace;
|
|
AbsoluteNodePath currentPath;
|
|
AmlStack frameStack = new AmlStack();
|
|
bool exited = false;
|
|
bool blocked = false;
|
|
AcpiObject.AcpiObject exitValue;
|
|
IoLocationStack stack = new IoLocationStack();
|
|
MethodResultCallback callback;
|
|
IOperationRegionAccessor operationRegionAccessor;
|
|
|
|
public AmlInterpreterThread(AcpiNamespace acpiNamespace, MethodResultCallback callback,
|
|
IOperationRegionAccessor operationRegionAccessor)
|
|
{
|
|
this.acpiNamespace = acpiNamespace;
|
|
this.currentPath = AbsoluteNodePath.CreateRoot();
|
|
this.callback = callback;
|
|
this.operationRegionAccessor = operationRegionAccessor;
|
|
}
|
|
|
|
public void InvokeMethod(Method method, AcpiObject.AcpiObject[] parameters)
|
|
{
|
|
method.Invoke(this, parameters, acpiNamespace);
|
|
}
|
|
|
|
public void InvokeMethod(AbsoluteNodePath nodePath, AcpiObject.AcpiObject[] parameters)
|
|
{
|
|
Node methodNode = acpiNamespace.LookupNode(nodePath);
|
|
if (methodNode == null) {
|
|
throw new MethodNotFoundInterpretException();
|
|
}
|
|
if (!(methodNode.Value is AcpiObject.BytecodeMethod)) {
|
|
throw new AmlTypeException();
|
|
}
|
|
this.currentPath = methodNode.Path;
|
|
((AcpiObject.Method)(methodNode.Value)).Invoke(this, parameters, acpiNamespace);
|
|
}
|
|
|
|
public void PushFrame(AmlStackFrame frame)
|
|
{
|
|
frameStack.Push(frame);
|
|
}
|
|
|
|
public void Return(AcpiObject.AcpiObject result)
|
|
{
|
|
frameStack.Pop().Dispose();
|
|
if (frameStack.IsEmpty()) {
|
|
Exit(result);
|
|
}
|
|
else {
|
|
Push(new ValueIoLocation(result));
|
|
}
|
|
}
|
|
|
|
public AmlStackFrame CurrentFrame
|
|
{
|
|
get
|
|
{
|
|
return frameStack.Peek();
|
|
}
|
|
}
|
|
|
|
public IOperationRegionAccessor OperationRegionAccessor
|
|
{
|
|
get
|
|
{
|
|
return operationRegionAccessor;
|
|
}
|
|
}
|
|
|
|
public void Step()
|
|
{
|
|
if (Exited || Blocked) {
|
|
throw new InterpretException("Tried to step exited or blocked thread");
|
|
}
|
|
new InterpretStepVisitor(this).Step();
|
|
}
|
|
|
|
public bool Exited
|
|
{
|
|
get
|
|
{
|
|
return exited;
|
|
}
|
|
}
|
|
|
|
public void Exit(AcpiObject.AcpiObject exitValue)
|
|
{
|
|
exited = true;
|
|
this.exitValue = exitValue;
|
|
if (callback != null) {
|
|
callback(ExitValue);
|
|
}
|
|
}
|
|
|
|
public AcpiObject.AcpiObject ExitValue
|
|
{
|
|
get
|
|
{
|
|
return exitValue;
|
|
}
|
|
}
|
|
|
|
public bool Blocked
|
|
{
|
|
get
|
|
{
|
|
return blocked;
|
|
}
|
|
}
|
|
|
|
public void Block()
|
|
{
|
|
blocked = true;
|
|
}
|
|
|
|
public void Notify()
|
|
{
|
|
blocked = false;
|
|
}
|
|
|
|
public Node LookupNode(NodePath nodePath)
|
|
{
|
|
return acpiNamespace.LookupNode(nodePath, currentPath);
|
|
}
|
|
|
|
public Node CreateNodeAt(NodePath nodePath)
|
|
{
|
|
Node node = acpiNamespace.CreateNodeAt(nodePath, currentPath);
|
|
frameStack.Peek().NotifyCreateNodeAt(nodePath, node);
|
|
return node;
|
|
}
|
|
|
|
public void Push(IoLocation location)
|
|
{
|
|
stack.Push(location);
|
|
}
|
|
|
|
public IoLocation Pop()
|
|
{
|
|
return stack.Pop();
|
|
}
|
|
}
|
|
|
|
public class InterpretStepVisitor : StackIRNodeVisitor
|
|
{
|
|
AmlInterpreterThread thread;
|
|
|
|
public InterpretStepVisitor(AmlInterpreterThread thread)
|
|
{
|
|
this.thread = thread;
|
|
}
|
|
|
|
private AmlStackFrame Frame
|
|
{
|
|
get
|
|
{
|
|
return thread.CurrentFrame;
|
|
}
|
|
}
|
|
|
|
public void Step()
|
|
{
|
|
AmlStackFrame frame = Frame;
|
|
frame.GetNextInstruction().Accept(this);
|
|
if (!thread.Blocked && !thread.Exited) {
|
|
frame.AdvanceInstruction();
|
|
}
|
|
}
|
|
|
|
public override void Visit(Jump node)
|
|
{
|
|
Frame.JumpTo(node.Target - 1); // Minus one because IP will still advance
|
|
}
|
|
|
|
public override void Visit(JumpIfNonZero node)
|
|
{
|
|
AcpiObject.AcpiObject predicate = thread.Pop().Read();
|
|
if (predicate.GetAsInt().Value != 0) {
|
|
Frame.JumpTo(node.ThenTarget - 1); // Minus one because IP will still advance
|
|
}
|
|
}
|
|
|
|
public override void Visit(JumpIfNodePathExists node)
|
|
{
|
|
if (thread.LookupNode(node.NodePath) != null) {
|
|
Frame.JumpTo(node.ThenTarget - 1); // Minus one because IP will still advance
|
|
}
|
|
}
|
|
|
|
public override void Visit(PushArgObj node)
|
|
{
|
|
thread.Push(Frame.GetArgument(node.ArgNum));
|
|
}
|
|
|
|
public override void Visit(PushLocalObj node)
|
|
{
|
|
thread.Push(Frame.GetLocal(node.LocalNum));
|
|
}
|
|
|
|
public override void Visit(PushDebugObj node)
|
|
{
|
|
thread.Push(new DebugIoLocation());
|
|
}
|
|
|
|
public override void Visit(PushNodePath node)
|
|
{
|
|
Node pathNode = thread.LookupNode(node.Value);
|
|
thread.Push(new NodePathIoLocation(pathNode));
|
|
if (pathNode.Value is Method && pathNode.Value.GetAsMethod().ArgCount == 0) {
|
|
Visit(new MethodCall());
|
|
}
|
|
}
|
|
|
|
public override void Visit(PushConst node)
|
|
{
|
|
thread.Push(new ValueIoLocation(node.Value));
|
|
}
|
|
|
|
public override void Visit(Discard node)
|
|
{
|
|
thread.Pop();
|
|
}
|
|
|
|
public override void Visit(Store node)
|
|
{
|
|
IoLocation value = thread.Pop();
|
|
IoLocation destination = thread.Pop();
|
|
destination.Write(value.Read());
|
|
thread.Push(destination);
|
|
}
|
|
|
|
public override void Visit(MethodCall node)
|
|
{
|
|
IoLocation methodLocation = thread.Pop();
|
|
Method method = methodLocation.Read().GetAsMethod();
|
|
|
|
AcpiObject.AcpiObject[] args = new AcpiObject.AcpiObject[method.ArgCount];
|
|
for (int i = 0; i < method.ArgCount; i++) {
|
|
args[i] = thread.Pop().Read();
|
|
}
|
|
|
|
thread.InvokeMethod(method, args);
|
|
}
|
|
|
|
public override void Visit(Index node)
|
|
{
|
|
AcpiObject.AcpiObject container = thread.Pop().Read();
|
|
ulong index = thread.Pop().Read().GetAsInt().Value;
|
|
thread.Push(new ValueIoLocation(new AcpiObject.BufferField(container, index * 8, 8)));
|
|
}
|
|
|
|
public override void Visit(ShiftLeft node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left << (int)right);
|
|
}
|
|
|
|
public override void Visit(ShiftRight node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left >> (int)right);
|
|
}
|
|
|
|
public override void Visit(Concatenate node)
|
|
{
|
|
AcpiObject.AcpiObject leftObj = thread.Pop().Read();
|
|
AcpiObject.AcpiObject rightObj = thread.Pop().Read();
|
|
|
|
if (leftObj is AcpiObject.String) {
|
|
string leftStr = leftObj.GetAsString().Value;
|
|
string rightStr = rightObj.GetAsString().Value;
|
|
thread.Push(new ValueIoLocation(new AcpiObject.String(leftStr + rightStr)));
|
|
}
|
|
else {
|
|
throw new InterpretException("Concatenate only implemented for strings");
|
|
}
|
|
}
|
|
|
|
public override void Visit(Add node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left + right);
|
|
}
|
|
|
|
public override void Visit(Subtract node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left - right);
|
|
}
|
|
|
|
public override void Visit(Multiply node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left * right);
|
|
}
|
|
|
|
public override void Visit(Divide node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left / right);
|
|
}
|
|
|
|
public override void Visit(Remainder node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = thread.Pop().Read().GetAsInt().Value;
|
|
PushInteger(left % right);
|
|
}
|
|
|
|
public override void Visit(LogicalOp node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = 0;
|
|
|
|
if (node.Operator != LogicalOp.Op.Not) {
|
|
right = thread.Pop().Read().GetAsInt().Value;
|
|
}
|
|
|
|
switch (node.Operator) {
|
|
case LogicalOp.Op.Less:
|
|
PushInteger(left < right ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.LessEq:
|
|
PushInteger(left <= right ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.Equal:
|
|
PushInteger(left == right ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.Greater:
|
|
PushInteger(left > right ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.GreaterEq:
|
|
PushInteger(left >= right ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.And:
|
|
PushInteger(left != 0 && right != 0 ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.Or:
|
|
PushInteger(left != 0 || right != 0 ? 1u : 0u);
|
|
break;
|
|
case LogicalOp.Op.Not:
|
|
PushInteger(left == 0 ? 1u : 0u);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void Visit(BitOp node)
|
|
{
|
|
ulong left = thread.Pop().Read().GetAsInt().Value;
|
|
ulong right = 0;
|
|
|
|
if (node.Operator != BitOp.Op.Not) {
|
|
right = thread.Pop().Read().GetAsInt().Value;
|
|
}
|
|
|
|
switch (node.Operator) {
|
|
case BitOp.Op.And:
|
|
PushInteger(left & right);
|
|
break;
|
|
case BitOp.Op.Or:
|
|
PushInteger(left | right);
|
|
break;
|
|
case BitOp.Op.NAnd:
|
|
PushInteger(~(left & right));
|
|
break;
|
|
case BitOp.Op.NOr:
|
|
PushInteger(~(left | right));
|
|
break;
|
|
case BitOp.Op.Not:
|
|
PushInteger(~left);
|
|
break;
|
|
case BitOp.Op.XOr:
|
|
PushInteger(left ^ right);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void Visit(Return node)
|
|
{
|
|
thread.Return(thread.Pop().Read());
|
|
}
|
|
|
|
public override void Visit(CreateField node)
|
|
{
|
|
AcpiObject.Buffer sourceBuff = thread.Pop().Read().GetAsBuffer();
|
|
ulong startBitIndex = thread.Pop().Read().GetAsInt().Value;
|
|
ulong numBits = thread.Pop().Read().GetAsInt().Value;
|
|
|
|
Node destinationNode = thread.CreateNodeAt(node.NodePath);
|
|
destinationNode.Value = new BufferField(sourceBuff, startBitIndex, numBits);
|
|
}
|
|
|
|
public override void Visit(DefBuffer node)
|
|
{
|
|
ulong size = thread.Pop().Read().GetAsInt().Value;
|
|
byte[] contents = new byte[size];
|
|
byte[] initializer = node.Initializer;
|
|
Array.Copy(initializer, contents, initializer.Length);
|
|
thread.Push(new ValueIoLocation(new AcpiObject.Buffer(contents)));
|
|
}
|
|
|
|
public override void Visit(FindSetLeftBit node)
|
|
{
|
|
// Result: Zero indicates no bit set; k indicates the kth bit from the right is set
|
|
ulong value = thread.Pop().Read().GetAsInt().Value;
|
|
for (int i = 63; i >= 0; i--) {
|
|
if (((value >> i) & 1) != 0) {
|
|
PushInteger((ulong)(i + 1));
|
|
return;
|
|
}
|
|
}
|
|
PushInteger(0);
|
|
}
|
|
|
|
public override void Visit(FindSetRightBit node)
|
|
{
|
|
// Result: Zero indicates no bit set; k indicates the kth bit from the right is set
|
|
ulong value = thread.Pop().Read().GetAsInt().Value;
|
|
for (ulong i = 1; i <= 64; i++) {
|
|
if ((value & 1) != 0) {
|
|
PushInteger(i);
|
|
return;
|
|
}
|
|
value >>= 1;
|
|
}
|
|
PushInteger(0);
|
|
}
|
|
|
|
public override void Visit(SizeOf node)
|
|
{
|
|
PushInteger(thread.Pop().Read().Size);
|
|
}
|
|
|
|
public override void Visit(DefName node)
|
|
{
|
|
Node namespaceNode = thread.CreateNodeAt(node.NodePath);
|
|
namespaceNode.Value = thread.Pop().Read();
|
|
}
|
|
|
|
public override void Visit(Load node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(Stall node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(Match node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(DerefOf node)
|
|
{
|
|
thread.Push(new ValueIoLocation(thread.Pop().Read().Dereference()));
|
|
}
|
|
|
|
public override void Visit(StackIR.Package node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
private void PushInteger(ulong i)
|
|
{
|
|
thread.Push(new ValueIoLocation(new AcpiObject.Integer(i)));
|
|
}
|
|
|
|
public override void Visit(Notify node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(Sleep node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(RefOf node)
|
|
{
|
|
// TODO: This implementation is not strictly correct, as it
|
|
// will not continue to refer to the location if its value is
|
|
// replaced (as can happen if it's currently uninitialized).
|
|
// But currently the result of RefOf isn't used on any of
|
|
// the tested machines, so postponing this.
|
|
thread.Push(new ValueIoLocation(new AcpiObject.ObjectReference(new AcpiObjectCell(thread.Pop().Read()))));
|
|
}
|
|
|
|
public override void Visit(StackIR.OperationRegion node)
|
|
{
|
|
ulong startIndex = thread.Pop().Read().GetAsInt().Value;
|
|
ulong length = thread.Pop().Read().GetAsInt().Value;
|
|
|
|
Node namespaceNode = thread.CreateNodeAt(node.NodePath);
|
|
namespaceNode.Value = new AcpiObject.OperationRegion(thread.OperationRegionAccessor,
|
|
node.OperationSpace,
|
|
startIndex, length);
|
|
}
|
|
|
|
public override void Visit(Field node)
|
|
{
|
|
Node operationRegionNode = thread.LookupNode(node.NodePath);
|
|
SortedList fields = FieldUnit.CreateFromFieldList((AcpiObject.OperationRegion)(operationRegionNode.Value.GetTarget()),
|
|
node.FieldElements,
|
|
node.FieldFlags.accessType, AccessAttrib.SMBNone,
|
|
node.FieldFlags.lockRule, node.FieldFlags.updateRule);
|
|
foreach (DictionaryEntry entry in fields) {
|
|
Node namespaceNode = thread.CreateNodeAt(
|
|
new NodePath(false/*isAbsolute*/, 0, new string[] { (string)entry.Key }));
|
|
namespaceNode.Value = (FieldUnit)entry.Value;
|
|
}
|
|
}
|
|
|
|
public override void Visit(Acquire node)
|
|
{
|
|
Mutex mutex = thread.Pop().Read().GetAsMutex();
|
|
mutex.Acquire(thread);
|
|
}
|
|
|
|
public override void Visit(Release node)
|
|
{
|
|
Mutex mutex = thread.Pop().Read().GetAsMutex();
|
|
mutex.Release(thread);
|
|
}
|
|
|
|
public override void Visit(ToBuffer node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(ToInteger node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
|
|
public override void Visit(ToString node)
|
|
{
|
|
throw new InterpretException("TODO");
|
|
}
|
|
}
|
|
|
|
public class AmlInterpreter
|
|
{
|
|
private class AmlInterpreterThreadList : IEnumerable
|
|
{
|
|
Queue queue = new Queue();
|
|
|
|
public void Enqueue(AmlInterpreterThread thread)
|
|
{
|
|
queue.Enqueue(thread);
|
|
}
|
|
|
|
public AmlInterpreterThread Dequeue()
|
|
{
|
|
return (AmlInterpreterThread)queue.Dequeue();
|
|
}
|
|
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return queue.GetEnumerator();
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return queue.Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
AmlInterpreterThreadList threadsReadyToRun = new AmlInterpreterThreadList();
|
|
AmlInterpreterThreadList threadsBlocked = new AmlInterpreterThreadList();
|
|
AcpiNamespace acpiNamespace;
|
|
IOperationRegionAccessor operationRegionAccessor;
|
|
|
|
public AmlInterpreter(AcpiNamespace acpiNamespace,
|
|
IOperationRegionAccessor operationRegionAccessor)
|
|
{
|
|
this.acpiNamespace = acpiNamespace;
|
|
this.operationRegionAccessor = operationRegionAccessor;
|
|
}
|
|
|
|
public AmlParser.ParseSuccess ParseMethodBodies()
|
|
{
|
|
foreach (Node node in acpiNamespace.GetAllNodes()) {
|
|
if (!(node.Value is AcpiObject.BytecodeMethod)) {
|
|
continue;
|
|
}
|
|
|
|
AcpiObject.BytecodeMethod method = (AcpiObject.BytecodeMethod)node.Value;
|
|
|
|
#if SINGULARITY_KERNEL
|
|
DebugStub.WriteLine("Parsing method " + node.Path.ToString());
|
|
#else
|
|
Console.WriteLine("Parsing method " + node.Path.ToString());
|
|
#endif
|
|
|
|
if (method.Parse(acpiNamespace, node.Path) == AmlParser.ParseSuccess.Failure) {
|
|
return AmlParser.ParseSuccess.Failure;
|
|
}
|
|
}
|
|
return AmlParser.ParseSuccess.Success;
|
|
}
|
|
|
|
public AmlInterpreterThread InvokeMethodOnNewThread(MethodResultCallback callback,
|
|
AbsoluteNodePath nodePath, AcpiObject.AcpiObject[] parameters)
|
|
{
|
|
AmlInterpreterThread thread = new AmlInterpreterThread(acpiNamespace, callback, operationRegionAccessor);
|
|
thread.InvokeMethod(nodePath, parameters);
|
|
threadsReadyToRun.Enqueue(thread);
|
|
return thread;
|
|
}
|
|
|
|
public void Run()
|
|
{
|
|
while (threadsReadyToRun.Count > 0) {
|
|
while (threadsReadyToRun.Count > 0) {
|
|
AmlInterpreterThread currentThread = threadsReadyToRun.Dequeue();
|
|
while (!currentThread.Blocked && !currentThread.Exited) {
|
|
currentThread.Step();
|
|
}
|
|
if (currentThread.Blocked) {
|
|
threadsBlocked.Enqueue(currentThread);
|
|
}
|
|
}
|
|
foreach (AmlInterpreterThread thread in threadsBlocked) {
|
|
if (!thread.Blocked) {
|
|
threadsReadyToRun.Enqueue(thread);
|
|
}
|
|
}
|
|
}
|
|
if (threadsBlocked.Count > 0) {
|
|
throw new InterpretException("Deadlock between ACPI AML threads detected");
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class ByteBufferAmlStreamAdapter : IAmlStream
|
|
{
|
|
byte[] buffer;
|
|
|
|
public ByteBufferAmlStreamAdapter(byte[] buffer)
|
|
{
|
|
this.buffer = buffer;
|
|
}
|
|
|
|
public byte ReadByteData(ref int offset)
|
|
{
|
|
try {
|
|
byte result = buffer[offset];
|
|
offset++;
|
|
return result;
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
|
|
public bool TryReadByteData(ref int offset, out byte result)
|
|
{
|
|
if (offset >= buffer.Length) {
|
|
result = 0;
|
|
return false;
|
|
}
|
|
else {
|
|
result = buffer[offset];
|
|
offset++;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public char ReadChar(ref int offset)
|
|
{
|
|
try {
|
|
char result = (char)buffer[offset];
|
|
offset++;
|
|
return result;
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
|
|
public byte[] ReadByteDataArray(ref int offset, int length)
|
|
{
|
|
try {
|
|
byte[] result = new byte[length];
|
|
System.Array.Copy(buffer, offset, result, 0, length);
|
|
offset += length;
|
|
return result;
|
|
}
|
|
catch (System.ArgumentException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
|
|
public bool TryReadByteDataArray(ref int offset, int length, out byte[] result)
|
|
{
|
|
if (offset + length > buffer.Length) {
|
|
result = null;
|
|
return false;
|
|
}
|
|
else {
|
|
result = new byte[length];
|
|
System.Array.Copy(buffer, offset, result, 0, length);
|
|
offset += length;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public UInt16 ReadWordData(ref int offset)
|
|
{
|
|
try {
|
|
UInt16 result = (UInt16)(buffer[offset] +
|
|
(((UInt16)buffer[offset + 1]) << 8));;
|
|
offset += 2;
|
|
return result;
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
|
|
public UInt32 ReadDWordData(ref int offset)
|
|
{
|
|
try {
|
|
UInt32 result = (UInt32)(buffer[offset] +
|
|
(((UInt32)buffer[offset + 1]) << 8) +
|
|
(((UInt32)buffer[offset + 2]) << 16) +
|
|
(((UInt32)buffer[offset + 3]) << 24));
|
|
offset += 4;
|
|
return result;
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
|
|
public UInt64 ReadQWordData(ref int offset)
|
|
{
|
|
try {
|
|
UInt64 result = (UInt64)(buffer[offset] +
|
|
(((UInt64)buffer[offset + 1]) << 8) +
|
|
(((UInt64)buffer[offset + 2]) << 16) +
|
|
(((UInt64)buffer[offset + 3]) << 24) +
|
|
(((UInt64)buffer[offset + 4]) << 32) +
|
|
(((UInt64)buffer[offset + 5]) << 40) +
|
|
(((UInt64)buffer[offset + 6]) << 48) +
|
|
(((UInt64)buffer[offset + 7]) << 56));
|
|
offset += 8;
|
|
return result;
|
|
}
|
|
catch (System.IndexOutOfRangeException) {
|
|
throw new EndOfAmlStreamException();
|
|
}
|
|
}
|
|
}
|
|
} |