1366 lines
42 KiB
C#
1366 lines
42 KiB
C#
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
//
|
||
// Microsoft Research Singularity
|
||
//
|
||
|
||
using System;
|
||
using System.Collections;
|
||
using System.Diagnostics;
|
||
using System.Text;
|
||
|
||
using TermObj = Microsoft.Singularity.Hal.Acpi.AmlParserUnions.TermObj;
|
||
using Microsoft.Singularity.Hal.Acpi.StackIR;
|
||
using Microsoft.Singularity.Hal.Acpi.AmlParserUnions;
|
||
|
||
namespace Microsoft.Singularity.Hal.Acpi.AcpiObject
|
||
{
|
||
//
|
||
// Based roughly on Table 17-6 from the ACPI Specification 3.0b, the
|
||
// following class hierarchy describes ACPI objects, which constitute
|
||
// the values assigned to each named node in the ACPI namespace as well
|
||
// as all values assigned to temporary objects, local objects, and so on.
|
||
// They are created and used at both load time and run time.
|
||
//
|
||
|
||
/// <summary>
|
||
/// Values returned by ObjectType operator as described in section
|
||
/// 17.5.86 of the ACPI specification 3.0b.
|
||
/// </summary>
|
||
public enum AcpiObjectType
|
||
{
|
||
Uninitialized = 0,
|
||
Integer = 1,
|
||
String = 2,
|
||
Buffer = 3,
|
||
Package = 4,
|
||
FieldUnit = 5,
|
||
Device = 6,
|
||
Event = 7,
|
||
Method = 8,
|
||
Mutex = 9,
|
||
OperationRegion = 10,
|
||
PowerResource = 11,
|
||
Processor = 12,
|
||
ThermalZone = 13,
|
||
BufferField = 14,
|
||
DdbHandle = 15,
|
||
DebugObject = 16
|
||
}
|
||
|
||
public abstract class AcpiObject
|
||
{
|
||
public abstract Integer ObjectType();
|
||
|
||
public abstract void Write(AcpiObject value);
|
||
|
||
/// <summary>
|
||
/// Get the value referred to by this value, for creating indirection.
|
||
/// Returns self for concrete types.
|
||
/// </summary>
|
||
public virtual AcpiObject GetTarget()
|
||
{
|
||
return this;
|
||
}
|
||
|
||
public virtual Integer GetAsInt()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual String GetAsString()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual Buffer GetAsBuffer()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual Method GetAsMethod()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual Mutex GetAsMutex()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual Device GetAsDevice()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual ulong Size
|
||
{
|
||
get
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
}
|
||
|
||
public virtual AcpiObject Dereference()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual AcpiObject Index(ulong index)
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual AcpiObject[] GetObjects()
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
|
||
public virtual void SetIndex(ulong index, AcpiObject value)
|
||
{
|
||
throw new AmlTypeException();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Contains a reference to an AcpiObject - this added level of indirection
|
||
/// is necessary to permit AcpiObject objects to be modified through
|
||
/// ObjectReference objects.
|
||
/// </summary>
|
||
public class AcpiObjectCell
|
||
{
|
||
AcpiObject containedValue;
|
||
|
||
public AcpiObjectCell(AcpiObject containedValue)
|
||
{
|
||
this.containedValue = containedValue;
|
||
}
|
||
|
||
public AcpiObject Value
|
||
{
|
||
get
|
||
{
|
||
return containedValue;
|
||
}
|
||
set
|
||
{
|
||
if (containedValue is UninitializedObject) {
|
||
containedValue = value;
|
||
}
|
||
else {
|
||
containedValue.Write(value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An uninitialized ACPI object.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: No assigned type or value. This is the type
|
||
/// of all control method LocalX variables and unused ArgX variables at
|
||
/// the beginning of method execution, as well as all uninitialized
|
||
/// Package elements. Uninitialized objects must be initialized (via
|
||
/// Store or CopyObject) before they may be used as source operands in
|
||
/// ASL expressions.</remarks>
|
||
public class UninitializedObject : AcpiObject
|
||
{
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Uninitialized);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to uninitialized object");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI buffer object representing an array of bytes.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: An array of bytes. Uninitialized elements
|
||
/// are zero by default.</remarks>
|
||
public class Buffer : AcpiObject
|
||
{
|
||
byte[] contents;
|
||
|
||
public Buffer(byte[] contents)
|
||
{
|
||
this.contents = contents;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Buffer);
|
||
}
|
||
|
||
public override AcpiObject Index(ulong index)
|
||
{
|
||
return new Integer(contents[index]);
|
||
}
|
||
|
||
public override void SetIndex(ulong index, AcpiObject value)
|
||
{
|
||
contents[index] = (byte)value.GetAsInt().Value;
|
||
}
|
||
|
||
public byte[] Contents
|
||
{
|
||
get
|
||
{
|
||
return (byte[])contents.Clone();
|
||
}
|
||
}
|
||
|
||
public override Buffer GetAsBuffer()
|
||
{
|
||
return this;
|
||
}
|
||
|
||
public override string ToString()
|
||
{
|
||
StringBuilder result = new StringBuilder();
|
||
result.Append("{");
|
||
bool first = true;
|
||
foreach (byte b in contents) {
|
||
if (first) {
|
||
first = false;
|
||
}
|
||
else {
|
||
result.Append(", ");
|
||
}
|
||
result.Append(b.ToString("x"));
|
||
}
|
||
result.Append("}");
|
||
return result.ToString();
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("TODO");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI buffer field object representing a portion of a buffer.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Portion of a buffer created using
|
||
/// CreateBitField, CreateByteField, CreateWordField, CreateQWordField,
|
||
/// CreateField, or returned by the Index operator.</remarks>
|
||
public class BufferField : AcpiObject
|
||
{
|
||
AcpiObject sourceBuffer = null;
|
||
ulong startBitIndex;
|
||
ulong numBits;
|
||
|
||
public BufferField(AcpiObject sourceBuffer, ulong startBitIndex, ulong numBits)
|
||
{
|
||
if (numBits > sizeof(ulong) * 8) {
|
||
throw new AmlTypeException("Tried to create field with more than maximum number of bits");
|
||
}
|
||
|
||
this.sourceBuffer = sourceBuffer;
|
||
this.startBitIndex = startBitIndex;
|
||
this.numBits = numBits;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.BufferField);
|
||
}
|
||
|
||
public override Integer GetAsInt()
|
||
{
|
||
return (Integer)Dereference();
|
||
}
|
||
|
||
public override AcpiObject Dereference()
|
||
{
|
||
if ((startBitIndex % 8) != 0 || (numBits % 8) != 0) {
|
||
throw new AmlTypeException("TODO: Non-byte-aligned buffer fields");
|
||
}
|
||
|
||
ulong result = 0;
|
||
// Loop from first byte to last
|
||
for(ulong idx = startBitIndex/8u; idx < startBitIndex/8u + numBits/8u; idx++) {
|
||
result = (result << 8) | sourceBuffer.Index(idx).GetAsInt().Value;
|
||
}
|
||
return new Integer(result);
|
||
}
|
||
|
||
public override void Write(AcpiObject valueObj)
|
||
{
|
||
if ((startBitIndex % 8) != 0 || (numBits % 8) != 0) {
|
||
throw new AmlTypeException("TODO: Non-byte-aligned buffer fields");
|
||
}
|
||
|
||
ulong value = valueObj.GetAsInt().Value;
|
||
|
||
// We ignore high bits above the number of bits fitting in the field.
|
||
// Used to check this, but some code actually depends on the behavior of
|
||
// truncating high bits, like this from VPC:
|
||
|
||
// Method (_CRS, 0, NotSerialized)
|
||
// {
|
||
// CreateDWordField (CRS, \_SB.SYSM._Y10._BAS, BAS4)
|
||
// CreateDWordField (CRS, \_SB.SYSM._Y10._LEN, LEN4)
|
||
// Subtract (0x00, BAS4, LEN4)
|
||
// }
|
||
|
||
// NB write in little endian byte-order
|
||
ulong end = startBitIndex/8u + numBits/8u;
|
||
for (ulong idx = startBitIndex / 8u; idx < end; idx++) {
|
||
sourceBuffer.SetIndex(idx, new Integer(value & 0xFF));
|
||
value >>= 8;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI DDB handle object referring to a Differentiated Definition Block.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Definition block handle returned
|
||
/// by the Load operator [and passed to the Unload operator].</remarks>
|
||
public class DdbHandle : AcpiObject
|
||
{
|
||
// TODO: The representation of this will come from the table loading code
|
||
// and depend on the implementation of Load and Unload.
|
||
|
||
private DdbHandle() { } // Prevent construction until completed
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.DdbHandle);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("TODO");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI debug object used for formatting and printing debug output.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Debug output object. Formats an object
|
||
/// and prints it to the system debug port. Has no effect if debugging
|
||
/// is not active. Section 17.5.23: The debug data object is a virtual
|
||
/// data object. Writes to this object provide debugging information.
|
||
/// On at least debug versions of the interpreter, any writes into this
|
||
/// object are appropriately displayed on the system<65>s native kernel
|
||
/// debugger. All writes to the debug object are otherwise benign. If
|
||
/// the system is in use without a kernel debugger, then writes to the
|
||
/// debug object are ignored.</remarks>
|
||
public class DebugObject : AcpiObject
|
||
{
|
||
private DebugObject() { }
|
||
|
||
public readonly DebugObject Instance = new DebugObject();
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.DebugObject);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
#if SINGULARITY_KERNEL
|
||
DebugStub.Write(value.ToString());
|
||
#else
|
||
Console.Write(value.ToString());
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI device object representing a device or bus.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Device or bus object</remarks>
|
||
public class Device : AcpiObject
|
||
{
|
||
AcpiNamespace.AbsoluteNodePath path;
|
||
|
||
public Device(AcpiNamespace.AbsoluteNodePath path)
|
||
{
|
||
this.path = path;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Device);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("TODO");
|
||
}
|
||
|
||
public AcpiNamespace.AbsoluteNodePath Path
|
||
{
|
||
get
|
||
{
|
||
return path;
|
||
}
|
||
}
|
||
|
||
public override Device GetAsDevice()
|
||
{
|
||
return this;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI event synchronization object
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Event synchronization object</remarks>
|
||
public class Event : AcpiObject
|
||
{
|
||
public Event()
|
||
{
|
||
// No data - an event is described entirely by its name
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Event);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("TODO");
|
||
}
|
||
}
|
||
|
||
//
|
||
// Enums based on FieldFlags rule in AML grammar,
|
||
// ACPI specification 3.0b section 18.2.5.2
|
||
//
|
||
|
||
public enum AccessType
|
||
{
|
||
AnyAcc = 0,
|
||
ByteAcc = 1,
|
||
WordAcc = 2,
|
||
DWordAcc = 3,
|
||
QWordAcc = 4,
|
||
BufferAcc = 5,
|
||
Reserved1 = 6,
|
||
Reserved2 = 7,
|
||
Reserved3 = 8,
|
||
Reserved4 = 9,
|
||
Reserved5 = 10,
|
||
Reserved6 = 11,
|
||
Reserved7 = 12,
|
||
Reserved8 = 13,
|
||
Reserved9 = 14,
|
||
Reserved10 = 15
|
||
}
|
||
|
||
public enum LockRule
|
||
{
|
||
NoLock = 0,
|
||
Lock = 1
|
||
}
|
||
|
||
public enum UpdateRule
|
||
{
|
||
Preserved = 0,
|
||
WriteAsOnes = 1,
|
||
WriteAsZeros = 2,
|
||
Invalid = 3
|
||
}
|
||
|
||
public enum AccessAttrib
|
||
{
|
||
SMBNone = 0x00,
|
||
SMBQuick = 0x02,
|
||
SMBSendReceive = 0x04,
|
||
SMBByte = 0x06,
|
||
SMBWord = 0x08,
|
||
SMBBlock = 0x0A,
|
||
SMBProcessCall = 0x0C,
|
||
SMBBlockProcessCall = 0x0D
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI field unit object referring to a portion of an address space.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: (within an Operation Region) Portion of an
|
||
/// address space, bit-aligned and of one-bit granularity. Created using
|
||
/// Field, BankField, or IndexField.</remarks>
|
||
public class FieldUnit : AcpiObject
|
||
{
|
||
OperationRegion operationRegion;
|
||
int startBitIndex;
|
||
int numBits;
|
||
AccessType accessType;
|
||
AccessAttrib accessAttrib;
|
||
LockRule lockRule;
|
||
UpdateRule updateRule;
|
||
|
||
public FieldUnit(OperationRegion operationRegion, int startBitIndex, int numBits,
|
||
AccessType accessType, AccessAttrib accessAttrib,
|
||
LockRule lockRule, UpdateRule updateRule)
|
||
{
|
||
if (startBitIndex < 0 || (startBitIndex + numBits + 7)/8 > (int)operationRegion.Length) {
|
||
throw new ArgumentException("Field unit not in bounds of operation region");
|
||
}
|
||
|
||
this.operationRegion = operationRegion;
|
||
this.startBitIndex = startBitIndex;
|
||
this.numBits = numBits;
|
||
this.accessType = accessType;
|
||
this.accessAttrib = accessAttrib;
|
||
this.lockRule = lockRule;
|
||
this.updateRule = updateRule;
|
||
}
|
||
|
||
public override Integer GetAsInt()
|
||
{
|
||
return new Integer(Read());
|
||
}
|
||
|
||
public override Buffer GetAsBuffer()
|
||
{
|
||
if ((startBitIndex % 8) == 0) {
|
||
return new Buffer(operationRegion.ReadBytes((ulong)(startBitIndex / 8),
|
||
(ulong)(numBits + 7)/8));
|
||
}
|
||
else {
|
||
throw new Exception("Unimplemented conversion of unaligned field to buffer");
|
||
}
|
||
}
|
||
|
||
public ulong Read()
|
||
{
|
||
if (numBits == 8 && (startBitIndex % 8) == 0) {
|
||
return operationRegion.Read8At((ulong)(startBitIndex / 8));
|
||
}
|
||
else if (numBits == 16 && (startBitIndex % 8) == 0) {
|
||
return operationRegion.Read16At((ulong)(startBitIndex / 8));
|
||
}
|
||
else if (numBits == 32 && (startBitIndex % 8) == 0) {
|
||
return operationRegion.Read32At((ulong)(startBitIndex / 8));
|
||
}
|
||
else {
|
||
// Small field or unaligned, do general case
|
||
int bitIndex = startBitIndex;
|
||
int remainingBits = numBits;
|
||
|
||
uint result = 0;
|
||
while (remainingBits > 0) {
|
||
byte b = operationRegion.Read8At((ulong)(bitIndex / 8));
|
||
for (int bitInWord = bitIndex % 8;
|
||
bitInWord < 8 && remainingBits > 0;
|
||
bitInWord++, bitIndex++, remainingBits--) {
|
||
result = (result << 1) | (uint)((b >> (7 - bitInWord)) & 1);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// This is currently just used by IndexField, will probably become accessible
|
||
/// from AML when implementing stores.
|
||
/// </summary>
|
||
public override void Write(AcpiObject valueObj)
|
||
{
|
||
ulong value = valueObj.GetAsInt().Value;
|
||
|
||
Debug.Assert(AcpiObjectUtils.GetNumBits(value) <= (ulong)numBits, "Writing value too large for field");
|
||
|
||
if (numBits == 8 && (startBitIndex % 8) == 0) {
|
||
operationRegion.Write8At((ulong)(startBitIndex / 8), (byte)value);
|
||
}
|
||
else if (numBits == 16 && (startBitIndex % 8) == 0) {
|
||
operationRegion.Write16At((ulong)(startBitIndex / 8), (byte)value);
|
||
}
|
||
else if (numBits == 32 && (startBitIndex % 8) == 0) {
|
||
operationRegion.Write32At((ulong)(startBitIndex / 8), (byte)value);
|
||
}
|
||
else {
|
||
throw new Exception("Unimplemented operation region field size");
|
||
}
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.FieldUnit);
|
||
}
|
||
|
||
// SortedList will be Dictionary<string, FieldUnit> when generics are available
|
||
public static SortedList CreateFromFieldList(OperationRegion operationRegionNode,
|
||
FieldElement[] fieldList,
|
||
AccessType initialAccessType,
|
||
AccessAttrib initialAccessAttrib,
|
||
LockRule lockRule,
|
||
UpdateRule updateRule
|
||
)
|
||
{
|
||
SortedList result = new SortedList(); // = new Dictionary<string, FieldUnit>();
|
||
AccessType accessType = initialAccessType;
|
||
AccessAttrib accessAttrib = initialAccessAttrib;
|
||
int bitIndex = 0;
|
||
|
||
foreach (FieldElement fieldElement in fieldList) {
|
||
switch (fieldElement.Tag) {
|
||
case FieldElement.TagValue.NamedField:
|
||
AmlParser.NamedField namedField = fieldElement.GetAsNamedField();
|
||
result.Add(namedField.nameSeg.data,
|
||
new FieldUnit(operationRegionNode,
|
||
bitIndex, namedField.bitWidth,
|
||
accessType, accessAttrib, lockRule, updateRule));
|
||
bitIndex += namedField.bitWidth;
|
||
break;
|
||
case FieldElement.TagValue.ReservedField:
|
||
AmlParser.ReservedField reservedField = fieldElement.GetAsReservedField();
|
||
bitIndex += reservedField.bitWidth;
|
||
break;
|
||
case FieldElement.TagValue.AccessField:
|
||
AmlParser.AccessField accessField = fieldElement.GetAsAccessField();
|
||
accessType = accessField.accessType;
|
||
accessAttrib = accessField.accessAttrib;
|
||
break;
|
||
default:
|
||
throw new LoadException("Unhandled alternative in switch over 'FieldElement'");
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// This operation region accessor accesses internal device registers
|
||
/// through an index/data pair (typically a pair of I/O ports inside an
|
||
/// I/O operation region). It is used by IndexField definitions, which
|
||
/// create FieldUnits inside these internal operation regions. The
|
||
/// RegionSpace type is ignored.
|
||
/// </summary>
|
||
public class IndexFieldOperationRegionAccessor : IOperationRegionAccessor
|
||
{
|
||
FieldUnit index;
|
||
FieldUnit data;
|
||
|
||
public IndexFieldOperationRegionAccessor(FieldUnit index, FieldUnit data)
|
||
{
|
||
this.index = index;
|
||
this.data = data;
|
||
}
|
||
|
||
public byte Read8(RegionSpace regionSpace, ulong offset)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
return (byte)data.Read();
|
||
}
|
||
|
||
public void Write8(RegionSpace regionSpace, ulong offset, byte value)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
data.Write(new Integer(value));
|
||
}
|
||
|
||
public ushort Read16(RegionSpace regionSpace, ulong offset)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
return (ushort)data.Read();
|
||
}
|
||
|
||
public void Write16(RegionSpace regionSpace, ulong offset, ushort value)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
data.Write(new Integer(value));
|
||
}
|
||
|
||
public uint Read32(RegionSpace regionSpace, ulong offset)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
return (uint)data.Read();
|
||
}
|
||
|
||
public void Write32(RegionSpace regionSpace, ulong offset, uint value)
|
||
{
|
||
index.Write(new Integer(offset));
|
||
data.Write(new Integer(value));
|
||
}
|
||
|
||
public byte[] ReadBytes(RegionSpace regionSpace, ulong offset, ulong length)
|
||
{
|
||
throw new Exception("Unimplemented ReadBytes() from index field");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI integer object
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: An n-bit little-endian unsigned integer.
|
||
/// In ACPI 1.0 this was at least 32 bits. In ACPI 2.0 and later, this
|
||
/// is 64 bits.</remarks>
|
||
public class Integer : AcpiObject
|
||
{
|
||
ulong value;
|
||
|
||
public Integer(ulong value)
|
||
{
|
||
this.value = value;
|
||
}
|
||
|
||
public ulong Value
|
||
{
|
||
get
|
||
{
|
||
return value;
|
||
}
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Integer);
|
||
}
|
||
|
||
public override Integer GetAsInt()
|
||
{
|
||
if (this == IntegerConstant.Zero ||
|
||
this == IntegerConstant.One ||
|
||
this == IntegerConstant.Ones) {
|
||
// Reading an IntegerConstant does not get the singleton object
|
||
// but just the value in it.
|
||
return new Integer(value);
|
||
}
|
||
return this;
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
if (this == IntegerConstant.Zero ||
|
||
this == IntegerConstant.One ||
|
||
this == IntegerConstant.Ones) {
|
||
throw new AmlTypeException("Cannot write to reserved integer constant objects");
|
||
}
|
||
this.value = value.GetAsInt().Value;
|
||
}
|
||
|
||
public override string ToString()
|
||
{
|
||
return value.ToString();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Wrapper class for some static methods referring to ACPI integer
|
||
/// constant built-in integer constants.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Created by the ASL terms "Zero", "One",
|
||
/// "Ones", and "Revision". We did not create a subclass for these
|
||
/// because ObjectType has no "IntegerConstant" type, only Integer.</remarks>
|
||
public class IntegerConstant
|
||
{
|
||
private IntegerConstant() { } // Don't allow construction
|
||
|
||
public readonly static Integer Zero = new Integer(0UL);
|
||
public readonly static Integer One = new Integer(1UL);
|
||
public readonly static Integer Ones = new Integer(~(0UL));
|
||
public readonly static Integer Revision = new Integer(0); // TODO: What's the right value for this?
|
||
}
|
||
|
||
//
|
||
// Enum based on MethodFlags rule in AML grammar,
|
||
// ACPI specification 3.0b section 18.2.5.2
|
||
//
|
||
|
||
public enum SerializeFlag
|
||
{
|
||
NotSerialized = 0,
|
||
Serialized = 1
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI method object referring to an invokable method.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Control Method (Executable AML function)</remarks>
|
||
public abstract class Method : AcpiObject
|
||
{
|
||
byte argCount;
|
||
SerializeFlag serializeFlag;
|
||
byte syncLevel;
|
||
|
||
public Method(byte argCount, SerializeFlag serializeFlag, byte syncLevel)
|
||
{
|
||
this.argCount = argCount;
|
||
this.serializeFlag = serializeFlag;
|
||
this.syncLevel = syncLevel;
|
||
}
|
||
|
||
public byte ArgCount
|
||
{
|
||
get
|
||
{
|
||
return argCount;
|
||
}
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Method);
|
||
}
|
||
|
||
public override Method GetAsMethod()
|
||
{
|
||
return this;
|
||
}
|
||
|
||
public abstract void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace);
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to method");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// A method whose body is represented using AML bytecode which can be parsed
|
||
/// and interpreted.
|
||
/// </summary>
|
||
public class BytecodeMethod : Method
|
||
{
|
||
byte[] unparsedTermList;
|
||
StackIRNode[] body;
|
||
|
||
public BytecodeMethod(byte argCount, SerializeFlag serializeFlag, byte syncLevel, byte[] unparsedTermList)
|
||
: base(argCount, serializeFlag, syncLevel)
|
||
{
|
||
this.unparsedTermList = unparsedTermList;
|
||
this.body = null;
|
||
}
|
||
|
||
public AmlParser.ParseSuccess Parse(AcpiNamespace acpiNamespace, AcpiNamespace.AbsoluteNodePath initialNodePath)
|
||
{
|
||
int offset = 0;
|
||
TermObj[] termList;
|
||
if (new AmlParser(new ByteBufferAmlStreamAdapter(unparsedTermList), acpiNamespace, initialNodePath).
|
||
ParseTermList(out termList, ref offset, unparsedTermList.Length) == AmlParser.ParseSuccess.Failure) {
|
||
return AmlParser.ParseSuccess.Failure;
|
||
}
|
||
Debug.Assert(offset == unparsedTermList.Length, "offset == unparsedTermList.Length");
|
||
|
||
AmlToStackIRVisitor amlToStackIRVisitor = new AmlToStackIRVisitor();
|
||
amlToStackIRVisitor.VisitSequence(termList);
|
||
body = amlToStackIRVisitor.Result;
|
||
|
||
return AmlParser.ParseSuccess.Success;
|
||
}
|
||
|
||
public override void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace)
|
||
{
|
||
if (body == null) {
|
||
AcpiNamespace.Node node = acpiNamespace.FindValue(this);
|
||
if (Parse(acpiNamespace, node.Path) == AmlParser.ParseSuccess.Failure) {
|
||
throw new InterpretException("AML parser failure while just-in-time parsing AML method body");
|
||
}
|
||
}
|
||
thread.PushFrame(new AmlStackFrame(body, parameters));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// A reserved method implemented directly by the operating system.
|
||
/// </summary>
|
||
public class ReservedMethod : Method
|
||
{
|
||
public delegate AcpiObject AcpiMethodImpl(AcpiObject[] arguments);
|
||
|
||
private AcpiMethodImpl impl;
|
||
|
||
public ReservedMethod(byte argCount, SerializeFlag serializeFlag, byte syncLevel, AcpiMethodImpl impl)
|
||
: base(argCount, serializeFlag, syncLevel)
|
||
{
|
||
this.impl = impl;
|
||
}
|
||
|
||
public override void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace)
|
||
{
|
||
thread.Push(new ValueIoLocation(impl(parameters)));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI mutex synchronization object
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Mutex synchronization object</remarks>
|
||
public class Mutex : AcpiObject
|
||
{
|
||
private class AmlInterpreterThreadSet : IEnumerable
|
||
{
|
||
ArrayList set = new ArrayList();
|
||
|
||
public void Add(AmlInterpreterThread thread) {
|
||
set.Add(thread);
|
||
}
|
||
|
||
public IEnumerator GetEnumerator() {
|
||
return set.GetEnumerator();
|
||
}
|
||
|
||
public void Clear() {
|
||
set.Clear();
|
||
}
|
||
}
|
||
|
||
byte syncLevel;
|
||
AmlInterpreterThread owner = null;
|
||
AmlInterpreterThreadSet waitingThreads = new AmlInterpreterThreadSet();
|
||
|
||
public Mutex(byte syncLevel)
|
||
{
|
||
this.syncLevel = syncLevel;
|
||
}
|
||
|
||
public void Acquire(AmlInterpreterThread thread) {
|
||
if (owner == null) {
|
||
owner = thread;
|
||
}
|
||
else {
|
||
waitingThreads.Add(thread);
|
||
thread.Block();
|
||
}
|
||
}
|
||
|
||
public void Release(AmlInterpreterThread thread) {
|
||
if (owner == null) {
|
||
throw new InterpretException("Attempt to release unlocked mutex");
|
||
}
|
||
owner = null;
|
||
|
||
foreach (AmlInterpreterThread waitingThread in waitingThreads) {
|
||
waitingThread.Notify();
|
||
}
|
||
waitingThreads.Clear();
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Mutex);
|
||
}
|
||
|
||
public override Mutex GetAsMutex()
|
||
{
|
||
return this;
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to mutex");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// See AmlLoader.LoadTimeEvaluate(AmlParser.UserTermObj userTermObj)
|
||
/// for an explanation of the existence of this object type, which is
|
||
/// not defined in the specification.
|
||
/// </summary>
|
||
public class NodePathReference : AcpiObject
|
||
{
|
||
private AcpiNamespace acpiNamespace;
|
||
private AcpiNamespace.AbsoluteNodePath nodePath;
|
||
|
||
public NodePathReference(AcpiNamespace acpiNamespace,
|
||
AcpiNamespace.AbsoluteNodePath nodePath)
|
||
{
|
||
this.acpiNamespace = acpiNamespace;
|
||
this.nodePath = nodePath;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return GetTarget().ObjectType();
|
||
}
|
||
|
||
public override AcpiObject GetTarget()
|
||
{
|
||
return acpiNamespace.LookupNode(nodePath).Value;
|
||
}
|
||
|
||
public override Integer GetAsInt()
|
||
{
|
||
return GetTarget().GetAsInt();
|
||
}
|
||
|
||
public override Device GetAsDevice()
|
||
{
|
||
return GetTarget().GetAsDevice();
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
GetTarget().Write(value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI object reference object referring to some other AcpiObject
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Reference to an object created using the
|
||
/// RefOf, Index, or CondRefOf operators</remarks>
|
||
public class ObjectReference : AcpiObject
|
||
{
|
||
AcpiObjectCell target;
|
||
|
||
public ObjectReference(AcpiObjectCell target)
|
||
{
|
||
this.target = target;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
// From 17.5.86: if this operation is performed on an object
|
||
// reference [...] the object type of the base object is returned.
|
||
return GetTarget().ObjectType();
|
||
}
|
||
|
||
public override AcpiObject GetTarget()
|
||
{
|
||
return target.Value;
|
||
}
|
||
|
||
public override Integer GetAsInt()
|
||
{
|
||
return GetTarget().GetAsInt();
|
||
}
|
||
|
||
public override Device GetAsDevice()
|
||
{
|
||
return GetTarget().GetAsDevice();
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
GetTarget().Write(value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// From 17.5.89, describes the built-in region spaces in which an
|
||
/// operation region can be created.
|
||
/// </summary>
|
||
public enum RegionSpace
|
||
{
|
||
SystemMemory = 0,
|
||
SystemIO = 1,
|
||
PCI_Config = 2,
|
||
EmbeddedControl = 3,
|
||
SMBus = 4,
|
||
CMOS = 5,
|
||
PCIBARTarget = 6
|
||
}
|
||
|
||
/// <summary>
|
||
/// Abstracts away reading of operation regions such as arbitrary
|
||
/// memory read/writes, I/O, and PCI configuration space access.
|
||
/// </summary>
|
||
public interface IOperationRegionAccessor
|
||
{
|
||
byte[] ReadBytes(RegionSpace regionSpace, ulong offset, ulong length);
|
||
byte Read8(RegionSpace regionSpace, ulong offset);
|
||
void Write8(RegionSpace regionSpace, ulong offset, byte value);
|
||
ushort Read16(RegionSpace regionSpace, ulong offset);
|
||
void Write16(RegionSpace regionSpace, ulong offset, ushort value);
|
||
uint Read32(RegionSpace regionSpace, ulong offset);
|
||
void Write32(RegionSpace regionSpace, ulong offset, uint value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI operation region object, representing a region within an
|
||
/// address space.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Reference to an object created using the
|
||
/// RefOf, Index, or CondRefOf operators</remarks>
|
||
public class OperationRegion : AcpiObject
|
||
{
|
||
IOperationRegionAccessor accessor;
|
||
RegionSpace regionSpace;
|
||
ulong startByteIndex;
|
||
ulong numBytes;
|
||
|
||
public OperationRegion(IOperationRegionAccessor accessor,
|
||
RegionSpace regionSpace, ulong startByteIndex, ulong numBytes)
|
||
{
|
||
this.accessor = accessor;
|
||
this.regionSpace = regionSpace;
|
||
this.startByteIndex = startByteIndex;
|
||
this.numBytes = numBytes;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.OperationRegion);
|
||
}
|
||
|
||
public ulong Length
|
||
{
|
||
get
|
||
{
|
||
return numBytes;
|
||
}
|
||
}
|
||
|
||
//
|
||
// These should only be used by FieldUnit.
|
||
//
|
||
|
||
public byte[] ReadBytes(ulong offset, ulong length)
|
||
{
|
||
if (offset < 0 || offset + length > numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
return accessor.ReadBytes(regionSpace, startByteIndex + offset, length);
|
||
}
|
||
|
||
public byte Read8At(ulong offset)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
return accessor.Read8(regionSpace, startByteIndex + offset);
|
||
}
|
||
|
||
public uint Read16At(ulong offset)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
return accessor.Read16(regionSpace, startByteIndex + offset);
|
||
}
|
||
|
||
public uint Read32At(ulong offset)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
return accessor.Read32(regionSpace, startByteIndex + offset);
|
||
}
|
||
|
||
public void Write8At(ulong offset, byte value)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
accessor.Write8(regionSpace, startByteIndex + offset, value);
|
||
}
|
||
|
||
public void Write16At(ulong offset, ushort value)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
accessor.Write16(regionSpace, startByteIndex + offset, value);
|
||
}
|
||
|
||
public void Write32At(ulong offset, uint value)
|
||
{
|
||
if (offset < 0 || offset >= numBytes) {
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
accessor.Write32(regionSpace, startByteIndex + offset, value);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
// Must first create fields inside operation region and then write to those
|
||
throw new AmlTypeException("Cannot write directly to operation region");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI package object, a fixed-length list of other AcpiObject objects
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Collection of ASL objects with a fixed
|
||
/// number of elements (up to 255).</remarks>
|
||
public class Package : AcpiObject
|
||
{
|
||
AcpiObjectCell[] objectList;
|
||
|
||
public Package(AcpiObjectCell[] objectList)
|
||
{
|
||
this.objectList = objectList;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Package);
|
||
}
|
||
|
||
public override AcpiObject Index(ulong index)
|
||
{
|
||
return objectList[index].Value;
|
||
}
|
||
|
||
public override AcpiObject[] GetObjects()
|
||
{
|
||
AcpiObject[] result = new AcpiObject[objectList.Length];
|
||
for (ulong index = 0; index < (ulong) objectList.Length; index++) {
|
||
result[index] = this.Index(index);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to package object");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI power resource description object.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Power Resource description object</remarks>
|
||
public class PowerResource : AcpiObject
|
||
{
|
||
public byte systemLevel;
|
||
public int resourceOrder;
|
||
|
||
public PowerResource(byte systemLevel, int resourceOrder)
|
||
{
|
||
this.systemLevel = systemLevel;
|
||
this.resourceOrder = resourceOrder;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.PowerResource);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to power resource object");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI processor description object.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Processor description object. See
|
||
/// section 17.5.93.</remarks>
|
||
public class Processor : AcpiObject
|
||
{
|
||
byte processorId;
|
||
uint pblkAddress;
|
||
byte pblkLength;
|
||
|
||
public Processor(byte processorId, uint pblkAddress, byte pblkLength)
|
||
{
|
||
this.processorId = processorId;
|
||
this.pblkAddress = pblkAddress;
|
||
this.pblkLength = pblkLength;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.Processor);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to processor object");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI string object.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Null-terminated ASCII string.</remarks>
|
||
public class String : AcpiObject
|
||
{
|
||
string contents;
|
||
|
||
public String(string contents)
|
||
{
|
||
for (int i = 0; i < contents.Length; i++) {
|
||
if (contents[i] == '\0') {
|
||
Debug.Assert(false, "ACPI string contains embedded null");
|
||
}
|
||
}
|
||
this.contents = contents;
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.String);
|
||
}
|
||
|
||
public override ulong Size
|
||
{
|
||
get
|
||
{
|
||
return (ulong)contents.Length;
|
||
}
|
||
}
|
||
|
||
public override AcpiObject Index(ulong index)
|
||
{
|
||
return new Integer(contents[(int)index]);
|
||
}
|
||
|
||
public string Value
|
||
{
|
||
get
|
||
{
|
||
return contents;
|
||
}
|
||
}
|
||
|
||
public override String GetAsString()
|
||
{
|
||
return this;
|
||
}
|
||
|
||
public override string ToString()
|
||
{
|
||
return contents;
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
this.contents = value.GetAsString().Value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// An ACPI thermal zone description object.
|
||
/// </summary>
|
||
/// <remarks>From Table 17-6: Thermal Zone description object</remarks>
|
||
public class ThermalZone : AcpiObject
|
||
{
|
||
public ThermalZone()
|
||
{
|
||
// No data - a thermal zone is described entirely by its name and children
|
||
}
|
||
|
||
public override Integer ObjectType()
|
||
{
|
||
return new Integer((ulong)AcpiObjectType.ThermalZone);
|
||
}
|
||
|
||
public override void Write(AcpiObject value)
|
||
{
|
||
throw new AmlTypeException("Cannot write to thermal zone object");
|
||
}
|
||
}
|
||
|
||
internal class AcpiObjectUtils
|
||
{
|
||
public static ulong GetNumBits(ulong value)
|
||
{
|
||
ulong numBits = 0;
|
||
while (value != 0) {
|
||
value >>= 1;
|
||
numBits++;
|
||
}
|
||
return numBits;
|
||
}
|
||
}
|
||
}
|