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