1 /* 2 * Copyright 2011, Rene Gollent, rene@gollent.com. 3 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. 4 * Copyright 2007, Michael Lotz, mmlr@mlotz.ch 5 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. 6 * Distributed under the terms of the MIT License. 7 * 8 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 9 * Distributed under the terms of the NewOS License. 10 */ 11 12 13 #include "acpi.h" 14 #include "mmu.h" 15 16 #include <string.h> 17 18 #include <KernelExport.h> 19 20 21 //#define TRACE_ACPI 22 #ifdef TRACE_ACPI 23 # define TRACE(x) dprintf x 24 #else 25 # define TRACE(x) ; 26 #endif 27 #define ERROR(x...) dprintf(x) 28 29 static struct scan_spots_struct acpi_scan_spots[] = { 30 { 0x0, 0x400, 0x400 - 0x0 }, 31 { 0xe0000, 0x100000, 0x100000 - 0xe0000 }, 32 { 0, 0, 0 } 33 }; 34 35 static acpi_descriptor_header* sAcpiRsdt; // System Description Table 36 static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table 37 static int32 sNumEntries = -1; 38 39 40 static status_t 41 acpi_validate_rsdp(acpi_rsdp* rsdp) 42 { 43 const char* data = (const char*)rsdp; 44 unsigned char checksum = 0; 45 for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++) 46 checksum += data[i]; 47 48 if ((checksum & 0xff) != 0) { 49 TRACE(("acpi: rsdp failed basic checksum\n")); 50 return B_BAD_DATA; 51 } 52 53 // for ACPI 2.0+ we need to also validate the extended checksum 54 if (rsdp->revision > 0) { 55 for (uint32 i = sizeof(acpi_rsdp_legacy); 56 i < sizeof(acpi_rsdp_extended); i++) { 57 checksum += data[i]; 58 } 59 60 if ((checksum & 0xff) != 0) { 61 TRACE(("acpi: rsdp failed extended checksum\n")); 62 return B_BAD_DATA; 63 } 64 } 65 66 return B_OK; 67 } 68 69 70 static status_t 71 acpi_validate_rsdt(acpi_descriptor_header* rsdt) 72 { 73 const char* data = (const char*)rsdt; 74 unsigned char checksum = 0; 75 for (uint32 i = 0; i < rsdt->length; i++) 76 checksum += data[i]; 77 78 return checksum == 0 ? B_OK : B_BAD_DATA; 79 } 80 81 82 static status_t 83 acpi_check_rsdt(acpi_rsdp* rsdp) 84 { 85 if (acpi_validate_rsdp(rsdp) != B_OK) 86 return B_BAD_DATA; 87 88 bool usingXsdt = false; 89 90 TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n", 91 rsdp, rsdp->oem_id, rsdp->revision)); 92 TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address)); 93 94 uint32 length = 0; 95 acpi_descriptor_header* rsdt = NULL; 96 if (rsdp->revision > 0) { 97 length = rsdp->xsdt_length; 98 rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( 99 (uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags); 100 if (rsdt != NULL 101 && strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) { 102 mmu_free(rsdt, rsdp->xsdt_length); 103 rsdt = NULL; 104 TRACE(("acpi: invalid extended system description table\n")); 105 } else 106 usingXsdt = true; 107 } 108 109 // if we're ACPI v1 or we fail to map the XSDT for some reason, 110 // attempt to use the RSDT instead. 111 if (rsdt == NULL) { 112 // map and validate the root system description table 113 rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( 114 rsdp->rsdt_address, sizeof(acpi_descriptor_header), 115 kDefaultPageFlags); 116 if (rsdt == NULL) { 117 TRACE(("acpi: couldn't map rsdt header\n")); 118 return B_ERROR; 119 } 120 if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) { 121 mmu_free(rsdt, sizeof(acpi_descriptor_header)); 122 rsdt = NULL; 123 TRACE(("acpi: invalid root system description table\n")); 124 return B_ERROR; 125 } 126 127 length = rsdt->length; 128 // Map the whole table, not just the header 129 TRACE(("acpi: rsdt length: %lu\n", length)); 130 mmu_free(rsdt, sizeof(acpi_descriptor_header)); 131 rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( 132 rsdp->rsdt_address, length, kDefaultPageFlags); 133 } 134 135 if (rsdt != NULL) { 136 if (acpi_validate_rsdt(rsdt) != B_OK) { 137 ERROR("acpi: %.4s failed checksum validation\n", rsdt->signature); 138 } 139 if (usingXsdt) 140 sAcpiXsdt = rsdt; 141 else 142 sAcpiRsdt = rsdt; 143 TRACE(("acpi: found %.4s at %p\n", rsdt->signature, rsdt)); 144 } else 145 return B_ERROR; 146 147 return B_OK; 148 } 149 150 151 template<typename PointerType> 152 acpi_descriptor_header* 153 acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt) 154 { 155 if (acpiSdt == NULL) 156 return NULL; 157 158 if (sNumEntries == -1) { 159 // if using the xsdt, our entries are 64 bits wide. 160 sNumEntries = (acpiSdt->length 161 - sizeof(acpi_descriptor_header)) 162 / sizeof(PointerType); 163 } 164 165 if (sNumEntries <= 0) { 166 TRACE(("acpi: root system description table is empty\n")); 167 return NULL; 168 } 169 170 TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries, 171 signature)); 172 173 PointerType* pointer = (PointerType*)((uint8*)acpiSdt 174 + sizeof(acpi_descriptor_header)); 175 176 acpi_descriptor_header* header = NULL; 177 for (int32 j = 0; j < sNumEntries; j++, pointer++) { 178 header = (acpi_descriptor_header*) 179 mmu_map_physical_memory((uint32)*pointer, 180 sizeof(acpi_descriptor_header), kDefaultPageFlags); 181 182 if (header == NULL 183 || strncmp(header->signature, signature, 4) != 0) { 184 // not interesting for us 185 TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n", 186 signature, header != NULL ? header->signature : "null")); 187 188 if (header != NULL) { 189 mmu_free(header, sizeof(acpi_descriptor_header)); 190 header = NULL; 191 } 192 193 continue; 194 } 195 196 TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer)); 197 break; 198 } 199 200 201 if (header == NULL) 202 return NULL; 203 204 // Map the whole table, not just the header 205 uint32 length = header->length; 206 mmu_free(header, sizeof(acpi_descriptor_header)); 207 208 return (acpi_descriptor_header*)mmu_map_physical_memory( 209 (uint32)*pointer, length, kDefaultPageFlags); 210 } 211 212 213 acpi_descriptor_header* 214 acpi_find_table(const char* signature) 215 { 216 if (sAcpiRsdt != NULL) 217 return acpi_find_table_generic<uint32>(signature, sAcpiRsdt); 218 else if (sAcpiXsdt != NULL) 219 return acpi_find_table_generic<uint64>(signature, sAcpiXsdt); 220 221 return NULL; 222 } 223 224 225 void 226 acpi_init() 227 { 228 // Try to find the ACPI RSDP. 229 for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) { 230 acpi_rsdp* rsdp = NULL; 231 232 TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n", 233 acpi_scan_spots[i].start, acpi_scan_spots[i].stop)); 234 235 for (char* pointer = (char*)acpi_scan_spots[i].start; 236 (uint32)pointer < acpi_scan_spots[i].stop; pointer += 16) { 237 if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) { 238 TRACE(("acpi_init: found ACPI RSDP signature at %p\n", 239 pointer)); 240 rsdp = (acpi_rsdp*)pointer; 241 } 242 } 243 244 if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) 245 break; 246 } 247 } 248