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#pragma pack(push,1)
14
15/* Generic entry header */
16typedef struct acpi_dmar_remap_hdr {
17    uint16_t type;
18    uint16_t length;
19} acpi_dmar_remap_hdr_t;
20
21/* DMA remapping table "DMAR" */
22typedef struct acpi_dmar_hdr {
23    acpi_header_t      header;
24    uint8_t            host_address_width;
25    uint8_t            flags;
26    uint8_t            res[10];
27    /* first remapping structure in "linked list" */
28//    acpi_dmar_remap_hdr_t sheader;
29} acpi_dmar_hdr_t;
30
31/*******************
32 *** Entry types ***
33 *******************/
34
35#define ACPI_DMAR_DRHD_TYPE 0
36#define ACPI_DMAR_RMRR_TYPE 1
37#define ACPI_DMAR_ATSR_TYPE 2
38#define ACPI_DMAR_RHSA_TYPE 3
39#define ACPI_DMAR_TYPE_IS_VALID(x) ((x) < 4)
40
41// device path structure
42typedef struct acpi_device_path {
43    uint8_t device;
44    uint8_t function;
45} acpi_device_path_t;
46
47// device scope structure
48typedef struct acpi_dmar_dscope {
49    uint8_t            type;
50    uint8_t            length;
51    uint8_t            res[2];
52    uint8_t            enum_id;
53    uint8_t            start_bus_number;
54    /*
55     * We could access this field as an array, but for
56     * consistancy, we will treat it as a linked list
57     */
58//    acpi_device_path_t path[];
59} acpi_dmar_dscope_t;
60
61/********************
62 *** DScope types ***
63 ********************/
64#define ACPI_DSCOPE_PCI_ENDPOINT 0x01
65#define ACPI_DSCOPE_PCI_BRIDGE   0x02
66#define ACPI_DSCOPE_IOAPIC       0x03
67#define ACPI_DSCOPE_HPET         0x04
68#define ACPI_DSCOPE_VALID(x)     \
69         ((uint8_t)( (x) - ACPI_DSCOPE_PCI_ENDPOINT ) < 4)
70
71/******************************
72 **** Sub tables of DMAR ******
73 ******************************/
74
75// Reserved Memory Region Report
76typedef struct acpi_dmar_rmrr {
77    acpi_dmar_remap_hdr_t header;
78    uint8_t               res[2];
79    uint16_t              segment_number;
80    uint64_t               base_address;
81    uint64_t               limit_address;
82    /* first device scope in "linked list" */
83//    acpi_dmar_dscope_t device_scope;
84} acpi_dmar_rmrr_t;
85
86// DMA Remapping Hardware unit Definition
87typedef struct acpi_dmar_drhd {
88    acpi_dmar_remap_hdr_t header;
89    uint8_t               flags;
90    uint8_t               res[1];
91    uint16_t              segment_number;
92    uint64_t               register_address;
93    /* first device scope in "linked list" */
94//    acpi_dmar_dscope_t device_scope;
95} acpi_dmar_drhd_t;
96
97// Root Port ATS Capability Reporting
98typedef struct acpi_dmar_atsr {
99    acpi_dmar_remap_hdr_t header;
100    uint8_t               flags;
101    uint8_t               res[1];
102    uint16_t              segment_number;
103    /* first device scope in "linked list" */
104//    acpi_dmar_dscope_t device_scope;
105} acpi_dmar_atsr_t;
106
107// Remaping Hardware Static Affinity structure
108typedef struct acpi_dmar_rhsa {
109    acpi_dmar_remap_hdr_t header;
110    uint8_t            res[4];
111    uint64_t            base_address;
112    uint32_t           proximity_domain;
113} acpi_dmar_rhsa_t;
114
115#pragma pack(pop)
116
117/********************************
118 **** DMAR sub table helpers ****
119 ********************************/
120
121/* Retrieve the header of the first entry */
122static inline acpi_dmar_remap_hdr_t*
123acpi_dmar_first_remap(acpi_dmar_hdr_t* tbl)
124{
125    return (acpi_dmar_remap_hdr_t*)(tbl + 1);
126}
127
128/* Retrieve the next DMAR sub header */
129static inline acpi_dmar_remap_hdr_t*
130acpi_dmar_next_remap(acpi_dmar_hdr_t* tbl,
131                     acpi_dmar_remap_hdr_t* hdr)
132{
133    void* next = (uint8_t*)hdr + hdr->length;
134    void* end  = (uint8_t*)tbl + tbl->header.length;
135    if (next < end) {
136        return (acpi_dmar_remap_hdr_t*)next;
137    } else {
138        return NULL;
139    }
140}
141
142/* Retrieve the DMAR sub header located at the specified index */
143static inline acpi_dmar_remap_hdr_t*
144acpi_dmar_remap_at(acpi_dmar_hdr_t* tbl, int index)
145{
146    acpi_dmar_remap_hdr_t* next = acpi_dmar_first_remap(tbl);
147    while (next != NULL && index-- > 0) {
148        next = acpi_dmar_next_remap(tbl, next);
149    }
150    return next;
151}
152
153/* Retrieve the next DMAR sub header of the specified type */
154static inline acpi_dmar_remap_hdr_t*
155acpi_dmar_next_remap_type(acpi_dmar_hdr_t* tbl,
156                          acpi_dmar_remap_hdr_t* hdr, int type)
157{
158    do {
159        hdr = acpi_dmar_next_remap(tbl, hdr);
160        if (hdr == NULL) {
161            return NULL;
162        }
163        if (hdr->type == type) {
164            return hdr;
165        }
166    } while (1);
167}
168
169/* Retrieve the first DMAR sub header of the specified type */
170static inline acpi_dmar_remap_hdr_t*
171acpi_dmar_first_remap_type(acpi_dmar_hdr_t* tbl, int type)
172{
173    acpi_dmar_remap_hdr_t* hdr = acpi_dmar_first_remap(tbl);
174    if (hdr == NULL) {
175        return NULL;
176    }
177
178    if (hdr->type != type) {
179        return acpi_dmar_next_remap_type(tbl, hdr, type);
180    } else {
181        return hdr;
182    }
183}
184
185/***********************************
186 **** DMAR device scope helpers ****
187 ***********************************/
188
189/* Retrieve the first device scope for a DRHD table */
190static inline acpi_dmar_dscope_t*
191acpi_dmar_drhd_first_dscope(acpi_dmar_drhd_t* h)
192{
193    return (acpi_dmar_dscope_t*)(h + 1);
194}
195
196/* Retrieve the first device scope for an RMRR table */
197static inline acpi_dmar_dscope_t*
198acpi_dmar_rmrr_first_dscope(acpi_dmar_rmrr_t* h)
199{
200    return (acpi_dmar_dscope_t*)(h + 1);
201}
202
203/* Retrieve the first device scope for an ATSR table */
204static inline acpi_dmar_dscope_t*
205acpi_dmar_atsr_first_dscope(acpi_dmar_atsr_t* h)
206{
207    return (acpi_dmar_dscope_t*)(h + 1);
208}
209
210/* Retrieve the first device scope (table independant) */
211static inline acpi_dmar_dscope_t*
212acpi_dmar_first_dscope(acpi_dmar_remap_hdr_t* h)
213{
214    switch (h->type) {
215    case ACPI_DMAR_DRHD_TYPE:
216        return acpi_dmar_drhd_first_dscope((acpi_dmar_drhd_t*)h);
217    case ACPI_DMAR_RMRR_TYPE:
218        return acpi_dmar_rmrr_first_dscope((acpi_dmar_rmrr_t*)h);
219    case ACPI_DMAR_ATSR_TYPE:
220        return acpi_dmar_atsr_first_dscope((acpi_dmar_atsr_t*)h);
221    case ACPI_DMAR_RHSA_TYPE:
222        return NULL; /* RHSA has no device scope */
223    default:
224        return NULL;
225    }
226}
227
228/* Retrieve the next device scope */
229static inline acpi_dmar_dscope_t*
230acpi_dmar_next_dscope(acpi_dmar_remap_hdr_t* sh, acpi_dmar_dscope_t* scope)
231{
232    void* next = (uint8_t*)scope + scope->length;
233    void* end  = (uint8_t*)sh + sh->length;
234    if (next < end) {
235        return (acpi_dmar_dscope_t*)next;
236    } else {
237        return NULL;
238    }
239}
240
241/* Retrieve the device scope at the given index */
242static inline acpi_dmar_dscope_t*
243acpi_dmar_dscope_at(acpi_dmar_remap_hdr_t* sh, int index)
244{
245    acpi_dmar_dscope_t* next = acpi_dmar_first_dscope(sh);
246    while (next != NULL && index-- > 0) {
247        next = acpi_dmar_next_dscope(sh, next);
248    }
249    return next;
250}
251
252static inline acpi_device_path_t*
253acpi_dmar_path_first(acpi_dmar_dscope_t* dscope)
254{
255    return (acpi_device_path_t*)(dscope + 1);
256}
257
258static inline int
259acpi_dmar_dscope_path_length(acpi_dmar_dscope_t* dscope)
260{
261    int path_bytes = dscope->length - sizeof(*dscope);
262    return path_bytes / sizeof(acpi_device_path_t);
263}
264