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