-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathACPI.cs
305 lines (277 loc) · 10.6 KB
/
ACPI.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
using System;
using System.Runtime.InteropServices;
namespace ZenStates.Core
{
public class ACPI
{
public static class TableSignature
{
public const string RSDP = "RSD PTR ";
public const string RSDT = "RSDT";
public const string XSDT = "XSDT";
public const string SSDT = "SSDT";
// Table OemId signatures
public const string AOD_ = "AOD ";
public const string AAOD = "AMD AOD";
public const string LENOVO_AOD = "CB-01 ";
// Region signatures
public const string AODE = "AODE";
public const string AODT = "AODT";
}
internal const ushort EBDA_START_SEGMENT_PTR = 0x40e;
internal const uint EBDA_EARLIEST_START = 0x80000;
internal const uint EBDA_END = 0x9ffff;
internal const uint RSDP_REGION_BASE_ADDRESS = 0x0e0000;
internal const int RSDP_REGION_LENGTH = 0x01ffff;
// 5.2.5.3 RSDP Structure
// https://uefi.org/sites/default/files/resources/ACPI_5_1_Errata_B.PDF p.110
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 36)]
public struct RSDP
{
// [FieldOffset(0)]
public ulong Signature; // "RSD PTR " (note the space at the end)
// [FieldOffset(8)]
public byte Checksum; // Includes only the first 20 bytes of this table, bytes 0 to 19, including the checksum field. These bytes must sum to zero.
// [FieldOffset(9)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] OEMID;
// [FieldOffset(15)]
public byte Revision;
// [FieldOffset(16)]
public uint RsdtAddress; // 32 bit physical address of the RSDT table
// [FieldOffset(20)]
public uint Length; // The length of the whole table, in bytes, including the header, starting from offset 0.
// [FieldOffset(24)]
public ulong XsdtAddress; // 64 bit physical address of the XSDT table
// [FieldOffset(32)]
public byte ExtendedChecksum; // This is a checksum of the entire table, including both checksum fields
// [FieldOffset(33)]
public byte Reserved1;
// [FieldOffset(34)]
public byte Reserved2;
// [FieldOffset(35)]
public byte Reserved3;
};
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 36)]
public struct SDTHeader
{
// [FieldOffset(0)]
public uint Signature;
// [FieldOffset(4)]
public uint Length;
// [FieldOffset(8)]
public byte Revision;
// [FieldOffset(9)]
public byte Checksum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
// [FieldOffset(10)]
public byte[] OEMID;
// [FieldOffset(16)]
public ulong OEMTableID;
// [FieldOffset(24)]
public uint OEMRevision;
// [FieldOffset(28)]
public uint CreatorID;
// [FieldOffset(32)]
public uint CreatorRevision;
};
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct ParsedSDTHeader
{
public string Signature;
public uint Length;
public byte Revision;
public byte Checksum;
public string OEMID;
public string OEMTableID;
public uint OEMRevision;
public string CreatorID;
public uint CreatorRevision;
};
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct ACPITable
{
public SDTHeader RawHeader;
public ParsedSDTHeader Header;
[MarshalAs(UnmanagedType.ByValArray)]
public byte[] Data;
};
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct RSDT
{
public SDTHeader Header;
[MarshalAs(UnmanagedType.ByValArray)]
public uint[] Data;
};
// 5.2.9 Fixed ACPI Description Table (FADT)
// https://uefi.org/sites/default/files/resources/ACPI_5_1_Errata_B.PDF p.116
[Serializable]
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct FADT
{
[FieldOffset(0)]
public SDTHeader Header;
[FieldOffset(36)]
public uint FIRMWARE_CTRL; // Physical memory address of the FACS table
[FieldOffset(40)]
public uint DSDT; // Physical memory address of the DSDT table
[FieldOffset(132)]
public ulong X_FIRMWARE_CTRL;
[FieldOffset(140)]
public ulong X_DSDT;
}
// https://github.com/rust-osdev/acpi/blob/main/acpi/src/address.rs
public enum AddressSpace : byte
{
SystemMemory,
SystemIo,
PciConfigSpace,
EmbeddedController,
SMBus,
SystemCmos,
PciBarTarget,
Ipmi,
GeneralIo,
GenericSerialBus,
PlatformCommunicationsChannel,
FunctionalFixedHardware,
OemDefined,
}
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)]
public struct OperationRegion
{
public uint RegionName;
public AddressSpace RegionSpace;
public byte _unknown1;
public uint Offset;
public byte _unknown2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] Length;
public byte _unknown3;
public byte _unknown4;
public byte _unknown5;
};
private readonly IOModule io;
public ACPI(IOModule io)
{
this.io = io ?? throw new ArgumentNullException(nameof(io));
}
public static ParsedSDTHeader ParseRawHeader(SDTHeader rawHeader)
{
return new ParsedSDTHeader()
{
Signature = Utils.GetStringFromBytes(rawHeader.Signature),
Length = rawHeader.Length,
Revision = rawHeader.Revision,
Checksum = rawHeader.Checksum,
OEMID = Utils.GetStringFromBytes(rawHeader.OEMID),
OEMTableID = Utils.GetStringFromBytes(rawHeader.OEMTableID),
OEMRevision = rawHeader.OEMRevision,
CreatorID = Utils.GetStringFromBytes(rawHeader.CreatorID),
CreatorRevision = rawHeader.CreatorRevision,
};
}
// ASCII string to Little-Endian uint, used for table signatures and OEM ID
public static uint Signature(string ascii)
{
uint val = 0x0;
int length = Math.Min(ascii.Length, 4);
for (int i = 0; i < length; i++)
{
val |= (uint)ascii[i] << i * 8;
}
return val;
}
public static ulong SignatureUL(string ascii)
{
ulong val = 0x0;
int length = Math.Min(ascii.Length, 8);
for (int i = 0; i < length; i++)
{
val |= (ulong)ascii[i] << i * 8;
}
return val;
}
public static byte[] ByteSignature(string ascii) => BitConverter.GetBytes(Signature(ascii));
public static byte[] ByteSignatureUL(string ascii) => BitConverter.GetBytes(SignatureUL(ascii));
public T GetHeader<T>(uint address, int length = 36) where T : new()
{
byte[] bytes = io.ReadMemory(new IntPtr(address), length);
return Utils.ByteArrayToStructure<T>(bytes);
}
public T GetHeader<T>(ulong address, int length = 36) where T : new()
{
byte[] bytes = io.ReadMemory(new IntPtr((long)address), length);
return Utils.ByteArrayToStructure<T>(bytes);
}
public RSDP GetRsdp()
{
byte[] bytes = io.ReadMemory(new IntPtr(RSDP_REGION_BASE_ADDRESS), RSDP_REGION_LENGTH);
int rsdpOffset = Utils.FindSequence(bytes, 0, ByteSignatureUL(TableSignature.RSDP));
if (rsdpOffset < 0)
throw new SystemException("ACPI: Could not find RSDP signature");
return Utils.ByteArrayToStructure<RSDP>(io.ReadMemory(new IntPtr(RSDP_REGION_BASE_ADDRESS + rsdpOffset), 36));
}
public RSDT GetRsdt()
{
RSDT rsdtTable;
RSDP rsdp = GetRsdp();
SDTHeader rsdtHeader = GetHeader<SDTHeader>(rsdp.RsdtAddress > 0 ? rsdp.RsdtAddress : rsdp.XsdtAddress);
byte[] rawTable = io.ReadMemory(new IntPtr(rsdp.RsdtAddress > 0 ? rsdp.RsdtAddress : (long)rsdp.XsdtAddress), (int)rsdtHeader.Length);
if (rawTable == null)
return new RSDT();
GCHandle handle = GCHandle.Alloc(rawTable, GCHandleType.Pinned);
try
{
int headerSize = Marshal.SizeOf(rsdtHeader);
int dataSize = (int)rsdtHeader.Length - headerSize;
rsdtTable = new RSDT()
{
Header = rsdtHeader,
Data = new uint[dataSize],
};
Buffer.BlockCopy(rawTable, headerSize, rsdtTable.Data, 0, dataSize);
}
finally
{
handle.Free();
}
return rsdtTable;
}
public static ACPITable ParseSdtTable(byte[] rawTable)
{
ACPITable acpiTable;
GCHandle handle = GCHandle.Alloc(rawTable, GCHandleType.Pinned);
try
{
SDTHeader rawHeader = (SDTHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SDTHeader));
int headerSize = Marshal.SizeOf(rawHeader);
int dataSize = (int)rawHeader.Length - headerSize;
acpiTable = new ACPITable()
{
RawHeader = rawHeader,
Header = ParseRawHeader(rawHeader),
Data = new byte[dataSize],
};
Buffer.BlockCopy(rawTable, headerSize, acpiTable.Data, 0, dataSize);
}
finally
{
handle.Free();
}
return acpiTable;
}
private static T ReadMemory<T>(IntPtr address)
{
T result = default;
Marshal.PtrToStructure(address, result);
return result;
}
}
}