You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			145 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			145 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
| #include "acpi.h"
 | |
| 
 | |
| #include "../debug/output.h"
 | |
| 
 | |
| #pragma GCC diagnostic push
 | |
| #pragma GCC diagnostic ignored "-Warray-bounds"
 | |
| 
 | |
| namespace ACPI {
 | |
| 
 | |
| static RSDP *rsdp = 0;
 | |
| static RSDT *rsdt = 0;
 | |
| static XSDT *xsdt = 0;
 | |
| 
 | |
| const char *RSDP_SIGNATURE = "RSD PTR ";
 | |
| 
 | |
| static int checksum(const void *pos, unsigned len) {
 | |
|   const uint8_t *mem = reinterpret_cast<const uint8_t *>(pos);
 | |
|   uint8_t sum = 0;
 | |
|   for (unsigned i = 0; i < len; i++) {
 | |
|     sum += mem[i];
 | |
|   }
 | |
| 
 | |
|   return sum;
 | |
| }
 | |
| 
 | |
| static const RSDP *findRSDP(const void *pos, unsigned len) {
 | |
|   /* since the RSDP is 16-Byte aligned, we only need to check
 | |
|      every second 64bit memory block */
 | |
|   for (unsigned block = 0; block < len / 8; block += 2) {
 | |
|     const uint64_t *mem = reinterpret_cast<const uint64_t *>(pos) + block;
 | |
|     if (*mem == *reinterpret_cast<const uint64_t *>(RSDP_SIGNATURE)) {
 | |
|       const RSDP *rsdp = reinterpret_cast<const RSDP *>(mem);
 | |
|       /* ACPI Specification Revision 4.0a: 5.2.5.3*/
 | |
|       if ((rsdp->revision == 0 && checksum(mem, 20) == 0) ||
 | |
|           (rsdp->length > 20 && checksum(mem, rsdp->length) == 0)) {
 | |
|         return rsdp;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| bool init() {
 | |
|   /* ACPI Specification Revision 4.0a:
 | |
|    * 5.2.5.1 Finding the RSDP on IA-PC Systems
 | |
|    * OSPM finds the Root System Description Pointer (RSDP) structure by
 | |
|    * searching physical memory ranges on 16-byte boundaries for a valid
 | |
|    * Root System Description Pointer structure signature and checksum
 | |
|    * match as follows:
 | |
|    * * The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or
 | |
|    *   MCA systems, the EBDA can be found in the two-byte location 40:0Eh
 | |
|    *   on the BIOS data area.
 | |
|    * * The BIOS read-only memory space between 0E0000h and 0FFFFFh.
 | |
|    */
 | |
|   const uintptr_t ebda =
 | |
|       static_cast<uintptr_t>(*reinterpret_cast<uint32_t *>(0x40e));
 | |
|   const RSDP *rsdp = findRSDP(reinterpret_cast<void *>(ebda), 1024);
 | |
|   if (rsdp == nullptr) {
 | |
|     rsdp = findRSDP(reinterpret_cast<void *>(0xe0000), 0xfffff - 0xe0000);
 | |
|   }
 | |
| 
 | |
|   if (rsdp == nullptr) {
 | |
|     DBG_VERBOSE << "No ACPI!" << endl;
 | |
|     return false;
 | |
|   }
 | |
|   rsdt = reinterpret_cast<RSDT *>(static_cast<uintptr_t>(rsdp->rsdtaddress));
 | |
| 
 | |
|   /* If the XSDT is present we must use it; see:
 | |
|    * ACPI Specification Revision 4.0a:
 | |
|    * "An ACPI-compatible OS must use the XSDT if present."
 | |
|    */
 | |
|   if (rsdp->revision != 0 && rsdp->length >= 36) {
 | |
|     xsdt = reinterpret_cast<XSDT *>(rsdp->xsdtaddress);
 | |
|   }
 | |
|   DBG_VERBOSE << "ACPI revision " << rsdp->revision << endl;
 | |
|   for (unsigned i = 0; i != count(); ++i) {
 | |
|     SDTH *sdt = get(i);
 | |
|     if (sdt != nullptr) {
 | |
|       char *c = reinterpret_cast<char *>(&sdt->signature);
 | |
|       DBG_VERBOSE << i << ". " << c[0] << c[1] << c[2] << c[3] << " @ "
 | |
|                   << reinterpret_cast<void *>(sdt) << endl;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| unsigned count() {
 | |
|   if (xsdt != nullptr) {
 | |
|     return (xsdt->length - 36) / 8;
 | |
|   } else if (rsdt != nullptr) {
 | |
|     return (rsdt->length - 36) / 4;
 | |
|   } else {
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| SDTH *get(unsigned num) {
 | |
|   if (xsdt != nullptr) {
 | |
|     SDTH *entry = reinterpret_cast<SDTH *>(xsdt->entries[num]);
 | |
|     if (checksum(entry, entry->length) == 0) {
 | |
|       return entry;
 | |
|     }
 | |
|   } else if (rsdt != nullptr) {
 | |
|     SDTH *entry =
 | |
|         reinterpret_cast<SDTH *>(static_cast<uintptr_t>(rsdt->entries[num]));
 | |
|     if (checksum(entry, entry->length) == 0) {
 | |
|       return entry;
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| SDTH *get(char a, char b, char c, char d) {
 | |
|   union {
 | |
|     char signature[4];
 | |
|     uint32_t value;
 | |
|   };
 | |
|   signature[0] = a;
 | |
|   signature[1] = b;
 | |
|   signature[2] = c;
 | |
|   signature[3] = d;
 | |
| 
 | |
|   if (xsdt != nullptr) {
 | |
|     for (unsigned i = 0; i < count(); i++) {
 | |
|       SDTH *entry = reinterpret_cast<SDTH *>(xsdt->entries[i]);
 | |
|       if (entry->signature == value && checksum(entry, entry->length) == 0) {
 | |
|         return entry;
 | |
|       }
 | |
|     }
 | |
|   } else if (rsdt != nullptr) {
 | |
|     for (unsigned i = 0; i < count(); i++) {
 | |
|       SDTH *entry =
 | |
|           reinterpret_cast<SDTH *>(static_cast<uintptr_t>(rsdt->entries[i]));
 | |
|       if (entry->signature == value && checksum(entry, entry->length) == 0) {
 | |
|         return entry;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int revision() { return rsdp != nullptr ? rsdp->revision : -1; }
 | |
| 
 | |
| }  // namespace ACPI
 |