1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <config.h>
8 #include <util.h>
9 #include <assert.h>
10 #include <machine/io.h>
11 #include <linker.h>
12 #include <plat/machine.h>
13 #include <plat/machine/acpi.h>
14 #include <plat/machine/devices.h>
15
16 enum acpi_type {
17 ACPI_RSDP,
18 ACPI_RSDT
19 };
20
21 /* DMA Remapping Reporting Table */
22 typedef struct acpi_dmar {
23 acpi_header_t header;
24 uint8_t host_addr_width;
25 uint8_t flags;
26 uint8_t reserved[10];
27 } acpi_dmar_t;
28 compile_assert(acpi_dmar_packed,
29 sizeof(acpi_dmar_t) == sizeof(acpi_header_t) + 12)
30
31 /* DMA Remapping Structure Header */
32 typedef struct acpi_dmar_header {
33 uint16_t type;
34 uint16_t length;
35 } acpi_dmar_header_t;
36 compile_assert(acpi_dmar_header_packed, sizeof(acpi_dmar_header_t) == 4)
37
38 /* DMA Remapping Structure Types */
39 enum acpi_table_dmar_struct_type {
40 DMAR_DRHD = 0,
41 DMAR_RMRR = 1,
42 DMAR_ATSR = 2,
43 };
44
45 /* DMA Remapping Hardware unit Definition */
46 typedef struct acpi_dmar_drhd {
47 acpi_dmar_header_t header;
48 uint8_t flags;
49 uint8_t reserved;
50 uint16_t segment;
51 uint32_t reg_base[2];
52 } acpi_dmar_drhd_t;
53 compile_assert(acpi_dmar_drhd_packed,
54 sizeof(acpi_dmar_drhd_t) == sizeof(acpi_dmar_header_t) + 12)
55
56 /* Reserved Memory Region Reporting structure Definition */
57 typedef struct acpi_dmar_devscope {
58 uint8_t type;
59 uint8_t length;
60 uint16_t reserved;
61 uint8_t enum_id;
62 uint8_t start_bus;
63 struct {
64 uint8_t dev;
65 uint8_t fun;
66 } path_0;
67 } acpi_dmar_devscope_t;
68 compile_assert(acpi_dmar_devscope_packed, sizeof(acpi_dmar_devscope_t) == 8)
69
70 /* Reserved Memory Region Reporting structure Definition */
71 typedef struct acpi_dmar_rmrr {
72 acpi_dmar_header_t header;
73 uint16_t reserved;
74 uint16_t segment;
75 uint32_t reg_base[2];
76 uint32_t reg_limit[2];
77 acpi_dmar_devscope_t devscope_0;
78 } acpi_dmar_rmrr_t;
79 compile_assert(acpi_dmar_rmrr_packed, sizeof(acpi_dmar_rmrr_t) ==
80 sizeof(acpi_dmar_header_t) + 20 + sizeof(acpi_dmar_devscope_t))
81
82 /* Fixed ACPI Description Table (FADT), partial as we only need flags */
83 typedef struct acpi_fadt {
84 acpi_header_t header;
85 uint8_t reserved[76];
86 uint32_t flags;
87 } acpi_fadt_t;
88 compile_assert(acpi_fadt_packed,
89 sizeof(acpi_fadt_t) == sizeof(acpi_header_t) + 80)
90
91 /* Multiple APIC Description Table (MADT) */
92 typedef struct acpi_madt {
93 acpi_header_t header;
94 uint32_t apic_addr;
95 uint32_t flags;
96 } acpi_madt_t;
97 compile_assert(acpi_madt_packed,
98 sizeof(acpi_madt_t) == sizeof(acpi_header_t) + 8)
99
100 typedef struct acpi_madt_header {
101 uint8_t type;
102 uint8_t length;
103 } acpi_madt_header_t;
104 compile_assert(acpi_madt_header_packed, sizeof(acpi_madt_header_t) == 2)
105
106 enum acpi_table_madt_struct_type {
107 MADT_APIC = 0,
108 MADT_IOAPIC = 1,
109 MADT_ISO = 2,
110 MADT_x2APIC = 9
111 };
112
113 typedef struct acpi_madt_apic {
114 acpi_madt_header_t header;
115 uint8_t cpu_id;
116 uint8_t apic_id;
117 uint32_t flags;
118 } acpi_madt_apic_t;
119 compile_assert(acpi_madt_apic_packed,
120 sizeof(acpi_madt_apic_t) == sizeof(acpi_madt_header_t) + 6)
121
122 typedef struct acpi_madt_x2apic {
123 acpi_madt_header_t header;
124 uint16_t reserved;
125 uint32_t x2apic_id;
126 uint32_t flags;
127 uint32_t acpi_processor_uid;
128 } acpi_madt_x2apic_t;
129 compile_assert(acpi_madt_x2apic_packed,
130 sizeof(acpi_madt_x2apic_t) == sizeof(acpi_madt_header_t) + 14)
131
132 typedef struct acpi_madt_ioapic {
133 acpi_madt_header_t header;
134 uint8_t ioapic_id;
135 uint8_t reserved[1];
136 uint32_t ioapic_addr;
137 uint32_t gsib;
138 } acpi_madt_ioapic_t;
139 compile_assert(acpi_madt_ioapic_packed,
140 sizeof(acpi_madt_ioapic_t) == sizeof(acpi_madt_header_t) + 10)
141
142 typedef struct acpi_madt_iso {
143 acpi_madt_header_t header;
144 uint8_t bus; /* always 0 (ISA) */
145 uint8_t source;
146 uint32_t gsi;
147 uint16_t flags;
148 } acpi_madt_iso_t;
149 /* We can't assert on the sizeof acpi_madt_iso because it contains trailing
150 * padding.
151 */
152 unverified_compile_assert(acpi_madt_iso_packed,
153 OFFSETOF(acpi_madt_iso_t, flags) == sizeof(acpi_madt_header_t) + 6)
154
155 /* workaround because string literals are not supported by C parser */
156 const char acpi_str_rsd[] = {'R', 'S', 'D', ' ', 'P', 'T', 'R', ' ', 0};
157 const char acpi_str_fadt[] = {'F', 'A', 'C', 'P', 0};
158 const char acpi_str_apic[] = {'A', 'P', 'I', 'C', 0};
159 const char acpi_str_dmar[] = {'D', 'M', 'A', 'R', 0};
160
acpi_calc_checksum(char * start,uint32_t length)161 BOOT_CODE static uint8_t acpi_calc_checksum(char *start, uint32_t length)
162 {
163 uint8_t checksum = 0;
164
165 while (length > 0) {
166 checksum += *start;
167 start++;
168 length--;
169 }
170 return checksum;
171 }
172
acpi_get_rsdp(void)173 BOOT_CODE static acpi_rsdp_t *acpi_get_rsdp(void)
174 {
175 char *addr;
176
177 for (addr = (char *)BIOS_PADDR_START; addr < (char *)BIOS_PADDR_END; addr += 16) {
178 if (strncmp(addr, acpi_str_rsd, 8) == 0) {
179 if (acpi_calc_checksum(addr, ACPI_V1_SIZE) == 0) {
180 return (acpi_rsdp_t *)addr;
181 }
182 }
183 }
184 return NULL;
185 }
186
acpi_table_init(void * entry,enum acpi_type table_type)187 BOOT_CODE static void *acpi_table_init(void *entry, enum acpi_type table_type)
188 {
189 void *acpi_table;
190 unsigned int pages_for_table;
191 unsigned int pages_for_header = 1;
192
193 /* if we need to map another page to read header */
194 unsigned long offset_in_page = (unsigned long)entry & MASK(LARGE_PAGE_BITS);
195 if (MASK(LARGE_PAGE_BITS) - offset_in_page < sizeof(acpi_rsdp_t)) {
196 pages_for_header++;
197 }
198
199 /* map in table's header */
200 acpi_table = map_temp_boot_page(entry, pages_for_header);
201
202 switch (table_type) {
203 case ACPI_RSDP: {
204 acpi_rsdp_t *rsdp_entry = (acpi_rsdp_t *)entry;
205 pages_for_table = (rsdp_entry->length + offset_in_page) / MASK(LARGE_PAGE_BITS) + 1;
206 break;
207 }
208 case ACPI_RSDT: { // RSDT, MADT, DMAR etc.
209 acpi_rsdt_t *rsdt_entry = (acpi_rsdt_t *)entry;
210 pages_for_table = (rsdt_entry->header.length + offset_in_page) / MASK(LARGE_PAGE_BITS) + 1;
211 break;
212 }
213 default:
214 printf("Error: Mapping unknown ACPI table type\n");
215 assert(false);
216 return NULL;
217 }
218
219 /* map in full table */
220 acpi_table = map_temp_boot_page(entry, pages_for_table);
221
222 return acpi_table;
223 }
224
acpi_init(acpi_rsdp_t * rsdp_data)225 BOOT_CODE bool_t acpi_init(acpi_rsdp_t *rsdp_data)
226 {
227 acpi_rsdp_t *acpi_rsdp = acpi_get_rsdp();
228
229 if (acpi_rsdp == NULL) {
230 printf("BIOS: No ACPI support detected\n");
231 return false;
232 }
233 printf("ACPI: RSDP paddr=%p\n", acpi_rsdp);
234 acpi_rsdp = acpi_table_init(acpi_rsdp, ACPI_RSDP);
235 printf("ACPI: RSDP vaddr=%p\n", acpi_rsdp);
236
237 /* create a copy of the rsdp data */
238 *rsdp_data = *acpi_rsdp;
239
240 /* perform final validation */
241 return acpi_validate_rsdp(rsdp_data);
242 }
243
acpi_validate_rsdp(acpi_rsdp_t * acpi_rsdp)244 BOOT_CODE bool_t acpi_validate_rsdp(acpi_rsdp_t *acpi_rsdp)
245 {
246 acpi_rsdt_t *acpi_rsdt;
247 acpi_rsdt_t *acpi_rsdt_mapped;
248
249 if (acpi_calc_checksum((char *)acpi_rsdp, ACPI_V1_SIZE) != 0) {
250 printf("BIOS: ACPIv1 information corrupt\n");
251 return false;
252 }
253
254 if (acpi_rsdp->revision > 0 && acpi_calc_checksum((char *)acpi_rsdp, sizeof(*acpi_rsdp)) != 0) {
255 printf("BIOS: ACPIv2 information corrupt\n");
256 return false;
257 }
258
259 /* verify the rsdt, even though we do not actually make use of the mapping right now */
260 acpi_rsdt = (acpi_rsdt_t *)(word_t)acpi_rsdp->rsdt_address;
261 printf("ACPI: RSDT paddr=%p\n", acpi_rsdt);
262 acpi_rsdt_mapped = (acpi_rsdt_t *)acpi_table_init(acpi_rsdt, ACPI_RSDT);
263 printf("ACPI: RSDT vaddr=%p\n", acpi_rsdt_mapped);
264
265 assert(acpi_rsdt_mapped->header.length > 0);
266 if (acpi_calc_checksum((char *)acpi_rsdt_mapped, acpi_rsdt_mapped->header.length) != 0) {
267 printf("ACPI: RSDT checksum failure\n");
268 return false;
269 }
270
271 return true;
272 }
273
acpi_madt_scan(acpi_rsdp_t * acpi_rsdp,cpu_id_t * cpu_list,uint32_t * num_ioapic,paddr_t * ioapic_paddrs)274 BOOT_CODE uint32_t acpi_madt_scan(
275 acpi_rsdp_t *acpi_rsdp,
276 cpu_id_t *cpu_list,
277 uint32_t *num_ioapic,
278 paddr_t *ioapic_paddrs
279 )
280 {
281 unsigned int entries;
282 uint32_t num_cpu;
283 uint32_t count;
284 acpi_madt_t *acpi_madt;
285 acpi_madt_header_t *acpi_madt_header;
286
287 acpi_rsdt_t *acpi_rsdt_mapped;
288 acpi_madt_t *acpi_madt_mapped;
289 acpi_rsdt_mapped = (acpi_rsdt_t *)acpi_table_init((acpi_rsdt_t *)(word_t)acpi_rsdp->rsdt_address, ACPI_RSDT);
290
291 num_cpu = 0;
292 *num_ioapic = 0;
293
294 assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
295 /* Divide by uint32_t explicitly as this is the size as mandated by the ACPI standard */
296 entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
297 for (count = 0; count < entries; count++) {
298 acpi_madt = (acpi_madt_t *)(word_t)acpi_rsdt_mapped->entry[count];
299 acpi_madt_mapped = (acpi_madt_t *)acpi_table_init(acpi_madt, ACPI_RSDT);
300
301 if (strncmp(acpi_str_apic, acpi_madt_mapped->header.signature, 4) == 0) {
302 printf("ACPI: MADT paddr=%p\n", acpi_madt);
303 printf("ACPI: MADT vaddr=%p\n", acpi_madt_mapped);
304 printf("ACPI: MADT apic_addr=0x%x\n", acpi_madt_mapped->apic_addr);
305 printf("ACPI: MADT flags=0x%x\n", acpi_madt_mapped->flags);
306
307 acpi_madt_header = (acpi_madt_header_t *)(acpi_madt_mapped + 1);
308
309 while ((char *)acpi_madt_header < (char *)acpi_madt_mapped + acpi_madt_mapped->header.length) {
310 switch (acpi_madt_header->type) {
311 /* ACPI specifies the following rules when listing APIC IDs:
312 * - Boot processor is listed first
313 * - For multi-threaded processors, BIOS should list the first logical
314 * processor of each of the individual multi-threaded processors in MADT
315 * before listing any of the second logical processors.
316 * - APIC IDs < 0xFF should be listed in APIC subtable, APIC IDs >= 0xFF
317 * should be listed in X2APIC subtable */
318 case MADT_APIC: {
319 /* what Intel calls apic_id is what is called cpu_id in seL4! */
320 uint8_t cpu_id = ((acpi_madt_apic_t *)acpi_madt_header)->apic_id;
321 uint32_t flags = ((acpi_madt_apic_t *)acpi_madt_header)->flags;
322 if (flags == 1) {
323 printf("ACPI: MADT_APIC apic_id=0x%x\n", cpu_id);
324 if (num_cpu == CONFIG_MAX_NUM_NODES) {
325 printf("ACPI: Not recording this APIC, only support %d\n", CONFIG_MAX_NUM_NODES);
326 } else {
327 cpu_list[num_cpu] = cpu_id;
328 num_cpu++;
329 }
330 }
331 break;
332 }
333 case MADT_x2APIC: {
334 uint32_t cpu_id = ((acpi_madt_x2apic_t *)acpi_madt_header)->x2apic_id;
335 uint32_t flags = ((acpi_madt_x2apic_t *)acpi_madt_header)->flags;
336 if (flags == 1) {
337 printf("ACPI: MADT_x2APIC apic_id=0x%x\n", cpu_id);
338 if (num_cpu == CONFIG_MAX_NUM_NODES) {
339 printf("ACPI: Not recording this APIC, only support %d\n", CONFIG_MAX_NUM_NODES);
340 } else {
341 cpu_list[num_cpu] = cpu_id;
342 num_cpu++;
343 }
344 }
345 break;
346 }
347 case MADT_IOAPIC:
348 printf(
349 "ACPI: MADT_IOAPIC ioapic_id=%d ioapic_addr=0x%x gsib=%d\n",
350 ((acpi_madt_ioapic_t *)acpi_madt_header)->ioapic_id,
351 ((acpi_madt_ioapic_t *)acpi_madt_header)->ioapic_addr,
352 ((acpi_madt_ioapic_t *)acpi_madt_header)->gsib
353 );
354 if (*num_ioapic == CONFIG_MAX_NUM_IOAPIC) {
355 printf("ACPI: Not recording this IOAPIC, only support %d\n", CONFIG_MAX_NUM_IOAPIC);
356 } else {
357 ioapic_paddrs[*num_ioapic] = ((acpi_madt_ioapic_t *)acpi_madt_header)->ioapic_addr;
358 (*num_ioapic)++;
359 }
360 break;
361 case MADT_ISO:
362 printf("ACPI: MADT_ISO bus=%d source=%d gsi=%d flags=0x%x\n",
363 ((acpi_madt_iso_t *)acpi_madt_header)->bus,
364 ((acpi_madt_iso_t *)acpi_madt_header)->source,
365 ((acpi_madt_iso_t *)acpi_madt_header)->gsi,
366 ((acpi_madt_iso_t *)acpi_madt_header)->flags);
367 break;
368 default:
369 break;
370 }
371 acpi_madt_header = (acpi_madt_header_t *)((char *)acpi_madt_header + acpi_madt_header->length);
372 }
373 }
374 }
375
376 printf("ACPI: %d CPU(s) detected\n", num_cpu);
377
378 return num_cpu;
379 }
380
acpi_fadt_scan(acpi_rsdp_t * acpi_rsdp)381 BOOT_CODE bool_t acpi_fadt_scan(
382 acpi_rsdp_t *acpi_rsdp
383 )
384 {
385 unsigned int entries;
386 uint32_t count;
387 acpi_fadt_t *acpi_fadt;
388
389 acpi_rsdt_t *acpi_rsdt_mapped;
390 acpi_fadt_t *acpi_fadt_mapped;
391 acpi_rsdt_mapped = (acpi_rsdt_t *)acpi_table_init((acpi_rsdt_t *)(word_t)acpi_rsdp->rsdt_address, ACPI_RSDT);
392
393 assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
394 /* Divide by uint32_t explicitly as this is the size as mandated by the ACPI standard */
395 entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
396 for (count = 0; count < entries; count++) {
397 acpi_fadt = (acpi_fadt_t *)(word_t)acpi_rsdt_mapped->entry[count];
398 acpi_fadt_mapped = (acpi_fadt_t *)acpi_table_init(acpi_fadt, ACPI_RSDT);
399
400 if (strncmp(acpi_str_fadt, acpi_fadt_mapped->header.signature, 4) == 0) {
401 printf("ACPI: FADT paddr=%p\n", acpi_fadt);
402 printf("ACPI: FADT vaddr=%p\n", acpi_fadt_mapped);
403 printf("ACPI: FADT flags=0x%x\n", acpi_fadt_mapped->flags);
404
405 if (config_set(CONFIG_USE_LOGICAL_IDS) &&
406 acpi_fadt_mapped->flags & BIT(19)) {
407 printf("system requires apic physical mode\n");
408 return false;
409 }
410 }
411 }
412
413 return true;
414 }
415
acpi_dmar_scan(acpi_rsdp_t * acpi_rsdp,paddr_t * drhu_list,uint32_t * num_drhu,uint32_t max_drhu_list_len,acpi_rmrr_list_t * rmrr_list)416 BOOT_CODE void acpi_dmar_scan(
417 acpi_rsdp_t *acpi_rsdp,
418 paddr_t *drhu_list,
419 uint32_t *num_drhu,
420 uint32_t max_drhu_list_len,
421 acpi_rmrr_list_t *rmrr_list
422 )
423 {
424 word_t i;
425 unsigned int entries;
426 uint32_t count;
427 uint32_t reg_basel, reg_baseh;
428 int rmrr_count;
429 dev_id_t dev_id;
430
431 acpi_dmar_t *acpi_dmar;
432 acpi_dmar_header_t *acpi_dmar_header;
433 acpi_dmar_rmrr_t *acpi_dmar_rmrr;
434 acpi_dmar_devscope_t *acpi_dmar_devscope;
435
436 acpi_rsdt_t *acpi_rsdt_mapped;
437 acpi_dmar_t *acpi_dmar_mapped;
438
439 acpi_rsdt_mapped = (acpi_rsdt_t *)acpi_table_init((acpi_rsdt_t *)(word_t)acpi_rsdp->rsdt_address, ACPI_RSDT);
440
441 *num_drhu = 0;
442 rmrr_count = 0;
443
444 assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
445 entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
446 for (count = 0; count < entries; count++) {
447 acpi_dmar = (acpi_dmar_t *)(word_t)acpi_rsdt_mapped->entry[count];
448 acpi_dmar_mapped = (acpi_dmar_t *)acpi_table_init(acpi_dmar, ACPI_RSDT);
449
450 if (strncmp(acpi_str_dmar, acpi_dmar_mapped->header.signature, 4) == 0) {
451 printf("ACPI: DMAR paddr=%p\n", acpi_dmar);
452 printf("ACPI: DMAR vaddr=%p\n", acpi_dmar_mapped);
453 printf("ACPI: IOMMU host address width: %d\n", acpi_dmar_mapped->host_addr_width + 1);
454 acpi_dmar_header = (acpi_dmar_header_t *)(acpi_dmar_mapped + 1);
455
456 while ((char *)acpi_dmar_header < (char *)acpi_dmar_mapped + acpi_dmar_mapped->header.length) {
457 switch (acpi_dmar_header->type) {
458
459 case DMAR_DRHD:
460 if (*num_drhu == max_drhu_list_len) {
461 printf("ACPI: too many IOMMUs, disabling IOMMU support\n");
462 /* try to increase MAX_NUM_DRHU in config.h */
463 *num_drhu = 0; /* report zero IOMMUs */
464 return;
465 }
466 reg_basel = ((acpi_dmar_drhd_t *)acpi_dmar_header)->reg_base[0];
467 reg_baseh = ((acpi_dmar_drhd_t *)acpi_dmar_header)->reg_base[1];
468 /* check if value fits into uint32_t */
469 if (reg_baseh != 0) {
470 printf("ACPI: DMAR_DRHD reg_base exceeds 32 bit, disabling IOMMU support\n");
471 /* try to make BIOS map it below 4G */
472 *num_drhu = 0; /* report zero IOMMUs */
473 return;
474 }
475 drhu_list[*num_drhu] = (paddr_t)reg_basel;
476 (*num_drhu)++;
477 break;
478
479 case DMAR_RMRR:
480 /* loop through all device scopes of this RMRR */
481 acpi_dmar_rmrr = (acpi_dmar_rmrr_t *)acpi_dmar_header;
482 if (acpi_dmar_rmrr->reg_base[1] != 0 ||
483 acpi_dmar_rmrr->reg_limit[1] != 0) {
484 printf("ACPI: RMRR device above 4GiB, disabling IOMMU support\n");
485 *num_drhu = 0;
486 return ;
487 }
488
489 for (i = 0; i <= (acpi_dmar_header->length - sizeof(acpi_dmar_rmrr_t)) / sizeof(acpi_dmar_devscope_t); i++) {
490 acpi_dmar_devscope = &acpi_dmar_rmrr->devscope_0 + i;
491
492 if (acpi_dmar_devscope->type != 1) {
493 /* FIXME - bugzilla bug 170 */
494 printf("ACPI: RMRR device scope: non-PCI-Endpoint-Devices not supported yet, disabling IOMMU support\n");
495 *num_drhu = 0; /* report zero IOMMUs */
496 return;
497 }
498
499 if (acpi_dmar_devscope->length > sizeof(acpi_dmar_devscope_t)) {
500 /* FIXME - bugzilla bug 170 */
501 printf("ACPI: RMRR device scope: devices behind bridges not supported yet, disabling IOMMU support\n");
502 *num_drhu = 0; /* report zero IOMMUs */
503 return;
504 }
505
506 dev_id =
507 get_dev_id(
508 acpi_dmar_devscope->start_bus,
509 acpi_dmar_devscope->path_0.dev,
510 acpi_dmar_devscope->path_0.fun
511 );
512
513 if (rmrr_count == CONFIG_MAX_RMRR_ENTRIES) {
514 printf("ACPI: Too many RMRR entries, disabling IOMMU support\n");
515 *num_drhu = 0;
516 return;
517 }
518 printf("\tACPI: registering RMRR entry for region for device: bus=0x%x dev=0x%x fun=0x%x\n",
519 acpi_dmar_devscope->start_bus,
520 acpi_dmar_devscope->path_0.dev,
521 acpi_dmar_devscope->path_0.fun
522 );
523
524 rmrr_list->entries[rmrr_count].device = dev_id;
525 rmrr_list->entries[rmrr_count].base = acpi_dmar_rmrr->reg_base[0];
526 rmrr_list->entries[rmrr_count].limit = acpi_dmar_rmrr->reg_limit[0];
527 rmrr_count++;
528 }
529 break;
530
531 case DMAR_ATSR:
532 /* not implemented yet */
533 break;
534
535 default:
536 printf("ACPI: Unknown DMA remapping structure type: %x\n", acpi_dmar_header->type);
537 }
538 acpi_dmar_header = (acpi_dmar_header_t *)((char *)acpi_dmar_header + acpi_dmar_header->length);
539 }
540 }
541 }
542 rmrr_list->num = rmrr_count;
543 printf("ACPI: %d IOMMUs detected\n", *num_drhu);
544 }
545