1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <platsupport/plat/acpi/acpi.h> 14#include "acpi.h" 15 16#include "walker.h" 17 18#include <assert.h> 19 20#include <stdlib.h> 21#include <stdio.h> 22#include <string.h> 23#include <inttypes.h> 24#include <stdbool.h> 25#define _GNU_SOURCE /* for getpagesize() */ 26#include <unistd.h> 27 28#define SIG_SEARCH_STEP (16) /* value from ACPIAC RSDP search */ 29 30static void* 31_sig_search(const char* sig, int sig_len, const char* loc, const char* end) 32{ 33 for (; loc < end; loc += SIG_SEARCH_STEP) { 34 if (strncmp(sig, loc, sig_len) == 0) { 35 return (void*)loc; 36 } 37 } 38 return NULL; 39} 40 41static void * 42acpi_map_table(acpi_t *acpi, void *table_paddr) 43{ 44 45 /* map the first part of the page in to read the size */ 46 acpi_header_t *header = (acpi_header_t *) ps_io_map(&acpi->io_mapper, 47 (uintptr_t)table_paddr, sizeof(acpi_header_t), 1, PS_MEM_NORMAL); 48 49 if (header == NULL) { 50 ZF_LOGD("Failed to map paddr %p, size %zu\n", table_paddr, sizeof(acpi_header_t)); 51 assert(header != NULL); 52 return NULL; 53 } 54 55 size_t length = acpi_table_length(header); 56 if (length == 0xffffffff) { 57 ZF_LOGD("Skipping table %s, unknown\n", header->signature); 58 ps_io_unmap(&acpi->io_mapper, (void *) header, sizeof(acpi_header_t)); 59 return NULL; 60 } 61 62 /* if the size is bigger than a page, unmap and remap a contiguous region */ 63 if (!SAME_PAGE_4K(header, ((void *) header) + length)) { 64 ps_io_unmap(&acpi->io_mapper, (void *) header, sizeof(acpi_header_t)); 65 header = ps_io_map(&acpi->io_mapper, (uintptr_t)table_paddr, length, 1, PS_MEM_NORMAL); 66 67 if (header == NULL) { 68 ZF_LOGD("Failed tomap paddr %p, size %"PRIu32"\n", table_paddr, header->length); 69 assert(header != NULL); 70 return NULL; 71 } 72 } 73 74 return header; 75} 76 77static void 78acpi_unmap_table(acpi_t *acpi, acpi_header_t *header) 79{ 80 ps_io_unmap(&acpi->io_mapper, (void *) header, acpi_table_length(header)); 81} 82 83void* 84acpi_sig_search(acpi_t *acpi, const char* sig, int sig_len, void* start, void* end) 85{ 86 void *found = NULL; 87 void *vaddr; 88 89 /* work a page at a time, searching for the target string */ 90 while (start < end && !found) { 91 vaddr = ps_io_map(&acpi->io_mapper, (uintptr_t) start, getpagesize(), 1, PS_MEM_NORMAL); 92 if (vaddr == NULL) { 93 ZF_LOGD("Failed to map physical page %p\n", start); 94 return NULL; 95 } 96 97 found = _sig_search(sig, sig_len, vaddr, vaddr + getpagesize()); 98 99 if (!found) { 100 start += getpagesize(); 101 } 102 103 ps_io_unmap(&acpi->io_mapper, vaddr, getpagesize()); 104 } 105 106 if (!found) { 107 ZF_LOGD("Faied to find sig %s in range %p <-> %p\n", sig, start, end); 108 return NULL; 109 } 110 111 /* return the physical address of sig */ 112 return (void*)((uintptr_t) start + ((uintptr_t)found % getpagesize())); 113} 114 115acpi_header_t* 116acpi_parse_table(acpi_t *acpi, void *table_paddr) 117{ 118 119 /* map the table into virtual memory */ 120 acpi_header_t* header_vaddr = acpi_map_table(acpi, table_paddr); 121 if (header_vaddr == NULL) { 122 return NULL; 123 } 124 125 /* now create a copy of the table for us to keep */ 126 size_t length = acpi_table_length(header_vaddr); 127 acpi_header_t *copy = (acpi_header_t *) malloc(length); 128 if (copy == NULL) { 129 ZF_LOGD("Failed to malloc object size %zu\n", length); 130 assert(copy != NULL); 131 return NULL; 132 } 133 134 memcpy(copy, header_vaddr, length); 135 136 /* finally, unmap the original table */ 137 acpi_unmap_table(acpi, header_vaddr); 138 139 /* return the copy. 140 * 141 * The reason we do this in this round-about way is: 142 * 143 * Acpi tables can be scattered all over the entire memory range. 144 * We can't guarantee that we can map in all the tables at their addresses 145 * as that address may not be available. 146 * 147 * But we can be confident that the acpi tables don't take up all of memory. 148 * By copying them to dynamic memory, we can keep them all in one place. 149 */ 150 return copy; 151} 152 153static void 154_acpi_parse_tables(acpi_t *acpi, void* table_addr, RegionList_t* regions, 155 int parent) 156{ 157 158 int this_rec; 159 region_type_t type; 160 acpi_header_t* header; 161 162 if (table_addr == NULL) { 163 return; 164 } 165 166 // check whether we need to parse table_addr 167 header = acpi_parse_table(acpi, table_addr); 168 if (header == NULL) { 169 /* skip table */ 170 return; 171 } 172 173 void *table_vaddr = (void *) header; 174 type = acpi_sig_id(header->signature); 175 176 // optimistic: remove later if the table is bad 177 this_rec = add_region_size(regions, type, table_vaddr, 178 header->length, parent); 179 if (this_rec < 0) { 180 return; /* List full */ 181 } 182 183 switch (type) { 184 /******************************************* 185 * These tables are completely implemented * 186 *******************************************/ 187 case ACPI_RSDT: { 188 acpi_rsdt_t* rsdt = (acpi_rsdt_t*) table_vaddr; 189 uint32_t* subtbl = acpi_rsdt_first(rsdt); 190 while (subtbl != NULL) { 191 _acpi_parse_tables(acpi, (void*)(uintptr_t)*subtbl, 192 regions, this_rec); 193 subtbl = acpi_rsdt_next(rsdt, subtbl); 194 } 195 break; 196 } 197 198 /* 199 * XSDT is the same as RSDT but with 64bit addresses 200 * Don't parse this table to avoid duplicate entries 201 * 202 * TODO this could actually contain unique entries, 203 * need to parse and sort out dups. 204 */ 205 case ACPI_XSDT: { 206 ZF_LOGW("Warning: skipping table ACPI XSDT\n"); 207// acpi_xsdt_t* xsdt = (acpi_xsdt_t*)table; 208 break; 209 } 210 211 case ACPI_FADT: { 212 acpi_fadt_t* fadt = (acpi_fadt_t*)table_vaddr; 213 _acpi_parse_tables(acpi, (void*)(uintptr_t)fadt->facs_address, 214 regions, this_rec); 215 _acpi_parse_tables(acpi, (void*)(uintptr_t)fadt->dsdt_address, 216 regions, this_rec); 217 break; 218 } 219 220 /****************************************** 221 * These tables use a standard header and * 222 * have no sub-tables * 223 ******************************************/ 224 case ACPI_HPET: 225 case ACPI_BOOT: 226 case ACPI_SPCR: 227 case ACPI_MCFG: 228 case ACPI_SPMI: 229 case ACPI_SSDT: 230 case ACPI_DSDT: 231 case ACPI_FACS: 232 case ACPI_MADT: 233 case ACPI_ERST: { 234 break; 235 } 236 237 /********************************************* 238 * These tables use a standard header and * 239 * have no sub-tables but depend on device * 240 * caps that may not be available. It may be * 241 * best to withhold these tables from linux * 242 *********************************************/ 243 case ACPI_ASF : 244 case ACPI_DMAR: { 245 break; 246 } 247 248 /****************************************** 249 * These tables are partially implemented * 250 ******************************************/ 251 case ACPI_BERT: { 252// acpi_bert_t* bert = (acpi_bert_t*)table; 253 /* not complemetely implemented so exclude */ 254 ZF_LOGW("Warning: skipping table ACPI_BERT (unimplemented)"); 255 remove_region(regions, this_rec); 256 break; 257 } 258 case ACPI_EINJ: { 259// acpi_einj_t* einj = (acpi_einj_t*)table; 260 /* not complemetely implemented so exclude */ 261 ZF_LOGW("Warning: skipping table ACPI_EINJ (unimplemented)"); 262 remove_region(regions, this_rec); 263 break; 264 } 265 case ACPI_HEST: { 266// acpi_hest_t* hest = (acpi_hest_t*)table; 267 /* not complemetely implemented so exclude */ 268 ZF_LOGW("Warning: skipping table ACPI_HEST (unimplemented)"); 269 remove_region(regions, this_rec); 270 break; 271 } 272 273 /******************************************* 274 * These tables are not implemented at all * 275 *******************************************/ 276 case ACPI_ASPT:/* present on Dogfood machine: unknown table */ 277 case ACPI_MSCT: 278 case ACPI_CPEP: 279 case ACPI_ECDT: 280 case ACPI_SBST: 281 case ACPI_SLIT: 282 case ACPI_SRAT: 283 /* Not implemented */ 284 ZF_LOGE("Warning: skipping table %s (unimplemented)", header->signature); 285 remove_region(regions, this_rec); 286 break; 287 288 default: 289 ZF_LOGE("Warning: skipping table %s (unimplemented)", header->signature); 290 remove_region(regions, this_rec); 291 } 292 return; 293} 294 295int 296acpi_parse_tables(acpi_t *acpi) 297{ 298 RegionList_t *regions = (RegionList_t *) acpi->regions; 299 regions->region_count = 0; 300 regions->offset = 0; 301 acpi_rsdp_t *acpi_rsdp; 302 303 acpi_rsdp = (acpi_rsdp_t *) malloc(sizeof(acpi_rsdp_t)); 304 if(acpi_rsdp == NULL) { 305 ZF_LOGE("Failed to allocate rsdp"); 306 return -1; 307 } 308 memcpy(acpi_rsdp, &(acpi->rsdp), sizeof(acpi_rsdp_t)); 309 310 int rec = add_region_size(regions, ACPI_RSDP, (void *)acpi_rsdp, 311 acpi_rsdp->length, -1); 312 if (rec < 0) { 313 free(acpi_rsdp); 314 return -1; /* List is full? */ 315 } 316 317 _acpi_parse_tables(acpi, (void*)(uintptr_t)acpi_rsdp->rsdt_address, 318 regions, rec); 319 _acpi_parse_tables(acpi, (void*)(uintptr_t)acpi_rsdp->xsdt_address, 320 regions, rec); 321 322 return 0; 323} 324