Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
acpi.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "precompiled.h"
24 #include "lib/sysdep/acpi.h"
25 
26 #include "lib/byte_order.h"
27 #include "lib/sysdep/cpu.h"
28 #include "lib/module_init.h"
29 
30 #define ENABLE_MAHAF 0
31 #if ENABLE_MAHAF
32 # include "lib/sysdep/os/win/mahaf.h"
33 #else
35 #endif
36 
37 #pragma pack(1)
38 
39 typedef const volatile u8* PCV_u8;
40 typedef const volatile AcpiTable* PCV_AcpiTable;
41 
42 
43 //-----------------------------------------------------------------------------
44 // table
45 
46 static AcpiTable* AllocateTable(size_t size)
47 {
48  ENSURE(size >= sizeof(AcpiTable));
49  return (AcpiTable*)malloc(size);
50 }
51 
52 
53 template<typename T>
54 static void DeallocateTable(const T* table)
55 {
56  free((void*)table);
57 }
58 
59 
60 // return 8-bit checksum of a buffer (should be 0)
61 static u8 ComputeChecksum(PCV_u8 buf, size_t numBytes)
62 {
63  // (can't use std::accumulate - we need 8-bit wraparound)
64  u8 sum = 0;
65  for(PCV_u8 p = buf; p < buf+numBytes; p++)
66  sum = u8((sum + *p) & 0xFF);
67  return sum;
68 }
69 
70 
71 static bool ValidateTable(const AcpiTable* table, const char* signature = 0)
72 {
73  if(!table)
74  return false;
75 
76  // caller knowns the signature; make sure it matches
77  if(signature)
78  {
79  if(memcmp(table->signature, signature, 4) != 0)
80  return false;
81  }
82  // no specific signature is called for, just validate the characters.
83  else
84  {
85  for(size_t i = 0; i < 4; i++)
86  {
87  const char c = table->signature[i];
88  // "ASF!" and "____" have been encountered
89  if(!isalpha(c) && c != '_' && c != '!')
90  return false;
91  }
92  }
93 
94  // must be at least as large as the common header
95  if(table->size < sizeof(AcpiTable))
96  return false;
97 
98  // checksum of table must be 0
99  // .. AMIBIOS OEMB table has an incorrect checksum (off-by-one),
100  // so don't complain about any OEM tables (ignored anyway).
101  const bool isOemTable = (memcmp(table->signature, "OEM", 3) == 0);
102  if(!isOemTable)
103  {
104  if(ComputeChecksum((PCV_u8)table, table->size) != 0)
105  return false;
106  }
107 
108  return true;
109 }
110 
111 
112 #if ENABLE_MAHAF
113 
114 //-----------------------------------------------------------------------------
115 // exception-safe transactional map/use/unmap
116 
117 // note: if the OS happens to unmap our physical memory, the Unsafe*
118 // functions may crash. we catch this via SEH; on Unix, we'd need handlers
119 // for SIGBUS and/or SIGSEGV. the code is safe in that it releases the
120 // mapped memory and returns an error code.
121 
122 static void* SUCCEEDED = (void*)(intptr_t)1;
123 static void* FAILED = (void*)(intptr_t)-1;
124 
125 typedef void* (*UnsafeFunction)(PCV_u8 mem, size_t numBytes, void* arg);
126 
127 static void* CallWithSafetyBlanket(UnsafeFunction func, PCV_u8 mem, size_t numBytes, void* arg)
128 {
129 #if MSC_VERSION
130  __try
131  {
132  return func(mem, numBytes, arg);
133  }
134  __except(1)
135  {
136  return FAILED;
137  }
138 #else
139  return func(mem, numBytes, arg);
140 #endif
141 }
142 
143 static void* TransactPhysicalMemory(uintptr_t physicalAddress, size_t numBytes, UnsafeFunction func, void* arg = 0)
144 {
145  PCV_u8 mem = (PCV_u8)mahaf_MapPhysicalMemory(physicalAddress, numBytes);
146  if(!mem)
147  return FAILED;
148  void* ret = CallWithSafetyBlanket(func, mem, numBytes, arg);
149  mahaf_UnmapPhysicalMemory((volatile void*)mem);
150  return ret;
151 }
152 
153 
154 //-----------------------------------------------------------------------------
155 // Root System Descriptor Pointer
156 
157 struct BiosDataArea
158 {
159  u16 serialBase[4];
160  u16 parallelBase[3];
161  u16 ebdaSegment;
162 };
163 
164 typedef const volatile BiosDataArea* PCV_BiosDataArea;
165 
166 static void* UnsafeReadEbdaPhysicalAddress(PCV_u8 mem, size_t numBytes, void* UNUSED(arg))
167 {
168  ENSURE(numBytes >= sizeof(BiosDataArea));
169 
170  PCV_BiosDataArea bda = (PCV_BiosDataArea)mem;
171  const uintptr_t ebdaPhysicalAddress = ((uintptr_t)bda->ebdaSegment) * 16;
172  return (void*)ebdaPhysicalAddress;
173 }
174 
175 
176 struct RSDP
177 {
178  char signature[8]; // "RSD PTR "
179  u8 checksum; // sum of this struct = 0
180  char oemId[6];
181  u8 revision; // 0 for 1.0, 2 for 2.0
182  u32 rsdtPhysicalAddress;
183 };
184 
185 typedef const volatile RSDP* PCV_RSDP;
186 
187 static const size_t RSDP_ALIGNMENT = 16;
188 
189 static void* UnsafeLocateAndRetrieveRsdp(PCV_u8 buf, size_t numBytes, void* arg)
190 {
191  ENSURE(numBytes >= sizeof(RSDP));
192 
193  for(PCV_u8 p = buf; p < buf+numBytes; p += RSDP_ALIGNMENT)
194  {
195  RSDP* prsdp = (RSDP*)p;
196  if(memcmp(prsdp->signature, "RSD PTR ", 8) != 0)
197  continue;
198  if(ComputeChecksum(p, sizeof(RSDP)) != 0)
199  continue;
200 
201  memcpy(arg, prsdp, sizeof(RSDP));
202  return SUCCEEDED;
203  }
204 
205  return FAILED;
206 }
207 
208 static bool RetrieveRsdp(RSDP& rsdp)
209 {
210  // See ACPIspec30b, section 5.2.5.1:
211  // RSDP is either in the first KIB of the extended BIOS data area,
212  void* ret = TransactPhysicalMemory(0x400, 0x100, UnsafeReadEbdaPhysicalAddress);
213  if(ret != FAILED)
214  {
215  const uintptr_t ebdaPhysicalAddress = (uintptr_t)ret;
216  ret = TransactPhysicalMemory(ebdaPhysicalAddress, 0x400, UnsafeLocateAndRetrieveRsdp, &rsdp);
217  if(ret == SUCCEEDED)
218  return true;
219  }
220 
221  // or in read-only BIOS memory.
222  ret = TransactPhysicalMemory(0xE0000, 0x20000, UnsafeLocateAndRetrieveRsdp, &rsdp);
223  if(ret == SUCCEEDED)
224  return true;
225 
226  return false; // not found
227 }
228 
229 
230 //-----------------------------------------------------------------------------
231 // copy tables from physical memory
232 
233 static void* UnsafeAllocateAndCopyTable(PCV_u8 mem, size_t numBytes, void* arg)
234 {
235  ENSURE(numBytes >= sizeof(AcpiTable));
236 
237  PCV_AcpiTable table = (PCV_AcpiTable)mem;
238  const size_t tableSize = table->size;
239 
240  // physical memory window is smaller than the table
241  // (caller will map a larger window and call us again)
242  if(numBytes < tableSize)
243  {
244  memcpy(arg, &tableSize, sizeof(size_t));
245  return 0;
246  }
247 
248  PCV_u8 copy = (PCV_u8)AllocateTable(tableSize);
249  if(!copy)
250  return FAILED;
251 
252  memcpy((void*)copy, (const void*)mem, tableSize);
253  return (void*)copy;
254 }
255 
256 
257 static const AcpiTable* AllocateAndCopyTable(uintptr_t physicalAddress)
258 {
259  // ACPI table sizes are not known until they've been mapped. since that
260  // is slow, we don't always want to do it twice. the solution is to map
261  // enough for a typical table; if that is too small, realloc and map again.
262  static const size_t initialSize = 4*KiB;
263  size_t actualSize = 0;
264  void* ret = TransactPhysicalMemory(physicalAddress, initialSize, UnsafeAllocateAndCopyTable, &actualSize);
265  // initialSize was too small; actualSize has been set
266  if(ret == 0)
267  ret = TransactPhysicalMemory(physicalAddress, actualSize, UnsafeAllocateAndCopyTable);
268  // *either* of the above calls failed to allocate memory
269  if(ret == FAILED)
270  return 0;
271  return (const AcpiTable*)ret;
272 }
273 
274 #endif // ENABLE_MAHAF
275 
276 
277 static void AllocateAndCopyTables(const AcpiTable**& tables, size_t& numTables)
278 {
279 #if ENABLE_MAHAF
281  return;
282  if(mahaf_Init() != INFO::OK)
283  return;
284 
285  RSDP rsdp;
286  if(!RetrieveRsdp(rsdp))
287  return;
288 
289  // Root System Descriptor Table
290  struct RSDT
291  {
292  AcpiTable header;
293  u32 tableAddresses[1];
294  };
295  const RSDT* rsdt = (const RSDT*)AllocateAndCopyTable(rsdp.rsdtPhysicalAddress);
296  if(!ValidateTable(&rsdt->header, "RSDT"))
297  {
298  DeallocateTable(rsdt);
299  return;
300  }
301 
302  numTables = (rsdt->header.size - sizeof(AcpiTable)) / sizeof(rsdt->tableAddresses[0]);
303  ENSURE(numTables != 0);
304 
305  tables = new const AcpiTable*[numTables];
306  for(size_t i = 0; i < numTables; i++)
307  tables[i] = AllocateAndCopyTable(rsdt->tableAddresses[i]);
308 
309  DeallocateTable(rsdt);
310 #else
311  const wfirmware::Provider provider = FOURCC_BE('A','C','P','I');
312  const wfirmware::TableIds tableIDs = wfirmware::GetTableIDs(provider);
313 
314  numTables = tableIDs.size();
315  tables = new const AcpiTable*[numTables];
316 
317  for(size_t i = 0; i < numTables; i++)
318  {
319  wfirmware::Table table = wfirmware::GetTable(provider, tableIDs[i]);
320  ENSURE(!table.empty());
321  tables[i] = AllocateTable(table.size());
322  memcpy((void*)tables[i], &table[0], table.size());
323  }
324 #endif
325 
326  // to prevent callers from choking on invalid tables, we
327  // zero out the corresponding tables[] entries.
328  for(size_t i = 0; i < numTables; i++)
329  {
330  if(!ValidateTable(tables[i]))
331  {
332  DeallocateTable(tables[i]);
333  tables[i] = 0;
334  }
335  }
336 }
337 
338 
339 //-----------------------------------------------------------------------------
340 
341 // note: avoid global std::map etc. because we may be called before _cinit
342 static const AcpiTable** tables; // tables == 0 <=> not initialized
343 static const AcpiTable* invalidTables; // tables == &invalidTables => init failed
344 static size_t numTables;
345 
347 {
348  if(tables)
349  {
350  for(size_t i = 0; i < numTables; i++)
351  DeallocateTable(tables[i]);
352  SAFE_ARRAY_DELETE(tables);
353  numTables = 0;
354  }
355 
356 #if ENABLE_MAHAF
357  mahaf_Shutdown();
358 #endif
359 }
360 
361 
362 const AcpiTable* acpi_GetTable(const char* signature)
363 {
364  if(cpu_CAS(&tables, (const AcpiTable**)0, &invalidTables))
365  AllocateAndCopyTables(tables, numTables);
366 
367  // (typically only a few tables, linear search is OK)
368  for(size_t i = 0; i < numTables; i++)
369  {
370  const AcpiTable* table = tables[i];
371  if(!table)
372  continue; // skip invalid tables, e.g. OEM (see above)
373  if(strncmp(table->signature, signature, 4) == 0)
374  return table;
375  }
376 
377  return 0; // no matching AND valid table found
378 }
#define u8
Definition: types.h:39
Status mahaf_Init()
Definition: mahaf.cpp:390
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
void mahaf_Shutdown()
Definition: mahaf.cpp:395
const Status OK
Definition: status.h:386
static void AllocateAndCopyTables(const AcpiTable **&tables, size_t &numTables)
Definition: acpi.cpp:277
static void DeallocateTable(const T *table)
Definition: acpi.cpp:54
static u8 ComputeChecksum(PCV_u8 buf, size_t numBytes)
Definition: acpi.cpp:61
static bool ValidateTable(const AcpiTable *table, const char *signature=0)
Definition: acpi.cpp:71
const AcpiTable * acpi_GetTable(const char *signature)
Definition: acpi.cpp:362
char signature[4]
Definition: acpi.h:35
static const AcpiTable * invalidTables
Definition: acpi.cpp:343
Table GetTable(Provider provider, TableId tableId)
Definition: wfirmware.cpp:27
u32 size
Definition: acpi.h:36
const volatile AcpiTable * PCV_AcpiTable
Definition: acpi.cpp:40
u32 Provider
Definition: wfirmware.h:6
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static size_t numTables
Definition: acpi.cpp:344
std::vector< u8 > Table
Definition: wfirmware.h:13
void mahaf_UnmapPhysicalMemory(volatile void *virtualAddress)
Definition: mahaf.cpp:142
static const size_t KiB
Definition: alignment.h:71
void acpi_Shutdown()
invalidates all pointers returned by acpi_GetTable.
Definition: acpi.cpp:346
Definition: acpi.h:33
std::vector< TableId > TableIds
Definition: wfirmware.h:9
#define T(string_literal)
Definition: secure_crt.cpp:70
volatile void * mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
Definition: mahaf.cpp:122
static const AcpiTable ** tables
Definition: acpi.cpp:342
const volatile u8 * PCV_u8
Definition: acpi.cpp:39
#define u16
Definition: types.h:40
#define FOURCC_BE(a, b, c, d)
big-endian version of FOURCC
Definition: byte_order.h:61
#define u32
Definition: types.h:41
bool mahaf_IsPhysicalMappingDangerous()
Definition: mahaf.cpp:111
bool cpu_CAS(volatile intptr_t *location, intptr_t expected, intptr_t newValue)
atomic &quot;compare and swap&quot;.
Definition: arm.cpp:36
static AcpiTable * AllocateTable(size_t size)
Definition: acpi.cpp:46
#define SAFE_ARRAY_DELETE(p)
delete memory ensuing from new[] and set the pointer to zero (thus making double-frees safe / a no-op...
TableIds GetTableIDs(Provider provider)
Definition: wfirmware.cpp:8