acpi: Abort VT-d initialization when no HW units found.
[barrelfish] / usr / acpi / intel_vtd.c
1 /*
2  * Copyright (c) 2014, University of Washington.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich. 
8  * Attn: Systems Group.
9  */
10
11 #include <barrelfish/barrelfish.h>
12 #include <barrelfish/invocations_arch.h>
13 #include <barrelfish/sys_debug.h>
14 #include <acpi.h>
15 #include <mm/mm.h>
16 #include <skb/skb.h>
17 #include <pci/confspace/pci_confspace.h>
18
19 #include "intel_vtd.h"
20 #include "vtd_debug.h"
21
22 extern struct mm pci_mm_physaddr;
23
24 // cache coherency for frame mappings
25 static bool cache_coherence = false;
26 static vregion_flags_t vtd_map_attr;
27
28 static struct vtd_unit *vtd_units = NULL;
29 static struct vtd_domain_list *domains = NULL;
30
31 static struct vtd_domain *identity_domain = NULL;
32 static lvaddr_t *id_pagetable_vbase = NULL;
33
34 #ifdef VTD_DEBUG_
35 static void vtd_dump_registers(struct vtd_unit *unit)
36 {
37     size_t size = 1 << 13;
38     char *buffer = malloc(size);
39     assert(buffer != NULL);
40     vtd_iotlb_pr(buffer, size, unit->iotlb_regs);
41     vtd_pr(buffer, size, unit->regset);
42     puts(buffer);
43     free(buffer);
44 }
45 #endif
46
47 #ifdef VTD_DEBUG_
48 static void dump_domains(struct vtd_domain_list *lst)
49 {
50     struct vtd_domain *d;
51     VTD_DEBUG("Printing the list of domains:\n");
52   
53     if (domain_list_empty(lst)) {
54         printf("No domains\n");
55         return;
56     }
57     VTD_FOR_EACH(d, lst->head) {
58         if (d == lst->head) printf("H");
59         printf("->[%d]", d->did);
60         if (d == lst->tail) printf("<-T");
61     }
62     printf("\n");
63 }
64 #endif
65
66 // Creates the root-table and context-tables for unit. 
67 static void vtd_create_tables(struct vtd_unit *unit)
68 {
69     assert(unit != NULL);
70     errval_t err;
71     err = frame_alloc(&(unit->rt_frame), BASE_PAGE_SIZE, NULL);
72     assert(err_is_ok(err));
73
74     err = vspace_map_one_frame_attr((void **)&(unit->root_table), BASE_PAGE_BITS, 
75                                     unit->rt_frame, vtd_map_attr, NULL, NULL);
76     assert(err_is_ok(err));
77
78     for (int i = 0; i < NUM_ROOT_ENTRIES; i++) {
79         err = frame_alloc(&(unit->ct_frame_caps[i]), BASE_PAGE_SIZE, NULL);
80         assert(err_is_ok(err));
81
82         err = vspace_map_one_frame_attr((void **)&(unit->context_tables[i]), BASE_PAGE_BITS,
83                                         unit->ct_frame_caps[i], vtd_map_attr, NULL, NULL);
84         assert(err_is_ok(err));
85     }
86 }
87
88 // Maps the remapping hardware memory-mapped registers of a unit to vspace.
89 static void *vtd_map_registers(genpaddr_t regset_base)
90 {
91     errval_t err;
92     void *regset_vbase;
93     struct capref regset_frame, regset_devframe;
94     struct frame_identity regset_frame_id;
95   
96     err = mm_alloc_range(&pci_mm_physaddr, BASE_PAGE_BITS, regset_base, 
97                          regset_base + BASE_PAGE_SIZE, &regset_frame, NULL);     
98     assert(err_is_ok(err));
99     
100     err = invoke_frame_identify(regset_frame, &regset_frame_id);
101     assert(err_is_ok(err));
102
103     err = devframe_type(&regset_devframe, regset_frame, BASE_PAGE_BITS);
104     assert(err_is_ok(err));
105     assert(regset_base == regset_frame_id.base);
106
107     err = vspace_map_one_frame_attr(&regset_vbase, BASE_PAGE_SIZE, regset_devframe, 
108                                     vtd_map_attr, NULL, NULL);
109     assert(err_is_ok(err));
110     return regset_vbase;
111 }
112
113 static void vtd_set_root_table(struct vtd_unit *unit) 
114 {
115     assert(unit != NULL);
116     errval_t err;
117     genpaddr_t rt_base;
118     struct frame_identity rt_frame_id;
119     err = invoke_frame_identify(unit->rt_frame, &rt_frame_id);
120     assert(err_is_ok(err));
121     rt_base = rt_frame_id.base;
122     assert((rt_base & BASE_PAGE_MASK) == 0);
123     // Set the root-table type
124     vtd_RTADDR_rtt_wrf(unit->regset, ROOT_TABLE_TYPE);
125     // Set the physical address of the root-table
126     vtd_RTADDR_rta_wrf(unit->regset, (rt_base >> BASE_PAGE_BITS));
127     // Update the root-table pointer
128     GSTS_srtp_wait(unit);
129     // Globally invalidate the context-cache and then globally invalidate
130     // the IOTLB (only in this order). 
131     vtd_context_cache_glob_inval(unit);
132     vtd_iotlb_glob_inval(unit);
133 }
134
135 // Creates and initializes a structure for a remapping hardware unit
136 // with its register set at regset_vbase.
137 // 
138 // We will have to change this as it is possible for multiple hardware
139 // units may reside on the same segment.
140 static struct vtd_unit *vtd_create_unit(void *regset_vbase, uint16_t segment)
141 {
142     int iro;
143     struct vtd_unit *new_unit;
144     void *iotlb_regs_vbase;
145
146     new_unit = (struct vtd_unit *)malloc(sizeof(struct vtd_unit));
147     assert(new_unit != NULL);
148
149     new_unit->regset = malloc(sizeof(*(new_unit->regset)));
150     assert(new_unit->regset != NULL);
151     vtd_initialize(new_unit->regset, regset_vbase);
152
153     iro = vtd_ECAP_iro_rdf(new_unit->regset); 
154     iotlb_regs_vbase = (void *)((uint8_t *)regset_vbase + iro);
155
156     new_unit->iotlb_regs = malloc(sizeof(*(new_unit->iotlb_regs)));
157     assert(new_unit->iotlb_regs != NULL);
158     vtd_iotlb_initialize(new_unit->iotlb_regs, iotlb_regs_vbase);
159
160     new_unit->pci_seg = segment;
161     new_unit->next = NULL;
162     
163     // Create root table and context tables for the new remapping unit
164     vtd_create_tables(new_unit); 
165     return new_unit;
166 }
167
168 static void vtd_insert_context_tables(struct vtd_unit *unit)
169 {
170     assert(unit != NULL);
171     assert(vtd_CAP_cm_rdf(unit->regset) == 0);
172  
173     errval_t err;
174     struct frame_identity ct_id;
175     for (int i = 0; i < NUM_ROOT_ENTRIES; i++) {
176         err = invoke_frame_identify(unit->ct_frame_caps[i], &ct_id);
177         assert(err_is_ok(err));
178         assert((ct_id.base & BASE_PAGE_MASK) == 0);
179         vtd_root_entry_ctp_insert(unit->root_table[i], (ct_id.base >> BASE_PAGE_BITS));
180         vtd_root_entry_p_insert(unit->root_table[i], 1);
181     }
182 }
183
184 // If the cap is a capability for an x86-64 PML4 VNode we return
185 // the base physical address of it. If not, we return 0.
186 static inline genpaddr_t pml4_base(struct capref cap)
187 {
188     errval_t err;
189     struct vnode_identity pml4_id;
190     err = invoke_vnode_identify(cap, &pml4_id);
191     assert(err_is_ok(err));
192     genpaddr_t pt = pml4_id.base;
193     if (pml4_id.type != ObjType_VNode_x86_64_pml4) {
194         return 0; 
195     }
196   
197     // If the cap is for the identity domain, we return the physical
198     // address of the identity pagetable, which has a PML4 table created 
199     // from a frame capability, instead of the base address of the empty
200     // PML4 VNode.
201     pml4_id.base = 0;
202     pml4_id.type = 0;
203     err = invoke_vnode_identify(identity_domain->pml4, &pml4_id);
204     assert(err_is_ok(err));
205     if (pml4_id.base == pt) {
206         pt = identity_domain->pt_gp;
207     }
208     return pt;
209 }
210
211 // Removes a device from the domain specified by pml4.
212 errval_t vtd_domain_remove_device(int seg, int bus, int dev, int func, struct capref pml4)
213 {
214     if (vtd_no_units(vtd_units)) return VTD_ERR_NO_UNITS;
215     if (!valid_device(bus, dev, func)) return VTD_ERR_DEV_NOT_FOUND;
216   
217     genpaddr_t pt = pml4_base(pml4);
218     if (pt == 0) return VTD_ERR_INVALID_CAP;
219   
220     // Find the domain in the list of domains
221     struct vtd_domain *dom = NULL;
222     VTD_FOR_EACH(dom, domains->head) {
223         if (dom->pt_gp == pt) break;
224     }
225
226     if (dom == NULL) return VTD_ERR_DOM_NOT_FOUND;
227   
228     // Find the unit containing the device under its scope
229     struct vtd_unit *u = NULL;
230     VTD_FOR_EACH(u, dom->units) {
231         if (u->pci_seg == seg) break;
232     }
233     if (u == NULL) return VTD_ERR_DEV_NOT_FOUND;
234
235     vtd_context_entry_array_t *context_table = u->context_tables[bus];
236     uint8_t id = (dev << 3) | func;
237
238     // The device doesn't belong to this domain
239     if (!vtd_context_entry_p_extract(context_table[id])) {
240         return VTD_ERR_DEV_NOT_FOUND;
241     }
242
243     vtd_context_entry_p_insert(context_table[id], 0);
244     vtd_context_entry_t_insert(context_table[id], 0);
245     vtd_context_entry_slptptr_insert(context_table[id], 0);
246     vtd_context_entry_did_insert(context_table[id], 0);
247     vtd_context_entry_aw_insert(context_table[id], 0);
248
249     // After removing the devices, we perform a context-cache device-selective 
250     // invalidation followed by an IOTLB domain-selective invalidation.
251     int sid = (bus << 16) | id;
252     vtd_context_cache_dev_inval(dom, sid, vtd_nomask);
253     vtd_iotlb_dom_inval(dom);
254     return SYS_ERR_OK;
255 }
256
257 // Adds a device to the domain specified by pml4.
258 errval_t vtd_domain_add_device(int seg, int bus, int dev, int func, struct capref pml4)
259 {
260     errval_t err;
261     if (vtd_no_units(vtd_units)) return VTD_ERR_NO_UNITS;
262     if (!valid_device(bus, dev, func)) return VTD_ERR_DEV_NOT_FOUND;
263     
264     genpaddr_t pt = pml4_base(pml4);
265     if (pt == 0) return VTD_ERR_INVALID_CAP;
266
267     // Find the domain with the provided pml4 capability
268     struct vtd_domain *dom = NULL;
269     VTD_FOR_EACH(dom, domains->head) {
270         if (dom->pt_gp == pt) break;
271     }
272
273     if (dom == NULL) return VTD_ERR_DOM_NOT_FOUND;
274
275     // Find the unit containing the device under its scope
276     struct vtd_unit *u = NULL;
277     VTD_FOR_EACH(u, dom->units) {
278         if (u->pci_seg == seg) break;
279     }
280     if (u == NULL) return VTD_ERR_DEV_NOT_FOUND;
281
282     vtd_context_entry_array_t *context_table = u->context_tables[bus];
283     uint8_t id = (dev << 3) | func;
284
285     // When a request is made for a device, if it belongs to the identity domain,
286     // we remove it before adding it to the domain specified by pml4
287     if (vtd_context_entry_p_extract(context_table[id])) {
288         int did = vtd_context_entry_did_extract(context_table[id]);
289         if (did == identity_domain->did && (pt != identity_domain->pt_gp)) {
290             err = vtd_domain_remove_device(seg, bus, dev, func, identity_domain->pml4);
291             assert(err_is_ok(err));
292         } else {
293             return VTD_ERR_DEV_USED;
294         }
295     }
296     
297     // If device-TLBs are supported, allow translated and translation requests
298     if (vtd_ECAP_dt_rdf(u->regset)) {
299         vtd_context_entry_t_insert(context_table[id], vtd_hme);
300     }    
301     vtd_context_entry_aw_insert(context_table[id], vtd_agaw48);
302     vtd_context_entry_did_insert(context_table[id], dom->did);
303    
304     sys_debug_flush_cache();
305     
306     vtd_context_entry_slptptr_insert(context_table[id], (pt >> 12));
307     vtd_context_entry_p_insert(context_table[id], 1);
308     return SYS_ERR_OK;
309 }
310
311 // Determines the minimum and maximum domain-ids among all
312 // hardware units.
313 static void vtd_create_did_bounds(struct vtd_unit *head, struct vtd_domain_list *doms)
314 {
315     assert(head != NULL);
316     struct vtd_unit *u = head;
317     doms->min_did = vtd_CAP_cm_rdf(u->regset);
318     doms->max_did = vtd_number_domains_supported(u)-1;
319     VTD_FOR_EACH(u, head) {
320         doms->min_did = MAX(doms->min_did, vtd_CAP_cm_rdf(u->regset));
321         doms->max_did = MIN(doms->max_did, vtd_number_domains_supported(u)-1);
322     }
323 }
324
325 // Creates a new domain for an application using a capability to its root PML4.
326 errval_t vtd_create_domain(struct capref pml4)
327 {
328     if (vtd_no_units(vtd_units)) return VTD_ERR_NO_UNITS;
329  
330     // Check that pml4 is a capability for a x86-64 PML4 VNode
331     errval_t err;
332     struct vnode_identity pml4_id;
333     err = invoke_vnode_identify(pml4, &pml4_id);
334     assert(err_is_ok(err));
335     genpaddr_t pt = pml4_id.base;
336     if (pml4_id.type != ObjType_VNode_x86_64_pml4) return VTD_ERR_INVALID_CAP;
337     int did = domains->min_did;
338
339     // Find a domain-id for the new domain
340     struct vtd_domain *d = NULL;
341     VTD_FOR_EACH(d, domains->head) {
342         if (did < d->did) break;
343         did++;
344     }
345
346     // All domain-ids have been exausted. Return an error.
347     if (did > domains->max_did) return VTD_ERR_FULL;
348
349     VTD_DEBUG("Creating domain with pt = %"PRIu64", did = %d\n", pt, did);
350 #ifdef VTD_DEBUG_
351     dump_domains(domains);
352 #endif
353
354     struct vtd_domain *new_domain = vtd_new_domain(did, pt, pml4, vtd_units);
355     vtd_insert_domain(new_domain, d, domains);
356   
357     return SYS_ERR_OK;
358 }
359
360 // Removes a domain for an application with the specified root PML4.
361 errval_t vtd_remove_domain(struct capref pml4)
362 {
363     if (vtd_no_units(vtd_units)) return VTD_ERR_NO_UNITS;
364
365     // Check that pml4 is a capability for a x86-64 PML4 VNode
366     errval_t err;
367     struct vnode_identity pml4_id;
368     err = invoke_vnode_identify(pml4, &pml4_id);
369     assert(err_is_ok(err));
370     genpaddr_t pt = pml4_id.base;
371     if (pml4_id.type != ObjType_VNode_x86_64_pml4) return VTD_ERR_INVALID_CAP;
372
373     // empty
374     if (domain_list_empty(domains)) return VTD_ERR_DOM_NOT_FOUND;
375
376     struct vtd_domain *d = NULL;
377     VTD_FOR_EACH(d, domains->head) {
378         if (d->pt_gp == pt) {
379             vtd_delete_domain(d, domains);
380             return SYS_ERR_OK;
381         }
382     }
383     return VTD_ERR_DOM_NOT_FOUND;
384 }
385
386 // Establishes a mapping from va to pa in pt, a second-level 
387 // pagetable structure.
388 static inline uint64_t vtd_map(uint64_t va, uint64_t pa, lvaddr_t *pt, int levels)
389 {
390     struct capref pe_frame;
391     struct frame_identity pe_id;
392     lvaddr_t *vtp = pt;
393
394     int e = 0;
395     for (int current_level = 1; current_level <= levels; current_level++) {
396         switch (current_level) {
397         case 1: 
398             e = X86_64_PML4_BASE(va);
399             break;
400         case 2: 
401             e = X86_64_PDPT_BASE(va);
402             break; 
403         case 3: 
404             e = X86_64_PDIR_BASE(va);
405             break;
406         case 4:
407             e = X86_64_PTABLE_BASE(va);
408             break;
409         }
410         if (current_level == levels) break;
411         if (vtp[e + PTABLE_SIZE] == 0) {
412             int bytes = 2 * BASE_PAGE_SIZE;
413             bytes = (current_level == (levels-1)) ?  bytes : 2 * bytes;
414  
415             errval_t err = frame_alloc(&pe_frame, bytes, NULL);
416             assert(err_is_ok(err));
417     
418             void *vbase;
419             err = vspace_map_one_frame_attr(&vbase, bytes, pe_frame, 
420                                             vtd_map_attr, NULL, NULL);
421             assert(err_is_ok(err));
422             assert(((lvaddr_t)vbase & BASE_PAGE_MASK) == 0);
423     
424             err = invoke_frame_identify(pe_frame, &pe_id);
425             assert(err_is_ok(err));
426             assert((pe_id.base & BASE_PAGE_MASK) == 0);
427
428             union sl_pdir_entry *entry = (union sl_pdir_entry *)vtp + e;
429             sl_map_table(entry, pe_id.base);
430             vtp[e + PTABLE_SIZE] = (lvaddr_t)vbase;
431         }
432         vtp = (lvaddr_t *)vtp[e + PTABLE_SIZE];
433     }
434
435     union sl_ptable_entry *entry = (union sl_ptable_entry *)vtp + e;
436     paging_sl_flags_t flags = SL_PTABLE_READ | SL_PTABLE_WRITE;
437    
438     switch (levels) {
439     case 2:
440         sl_map_large30(entry, pa, flags);
441         break;
442     case 3:
443         sl_map_large21(entry, pa, flags);
444         break;
445     case 4:
446         sl_map(entry, pa, flags);
447         break;
448     }
449
450     return (1UL << vtd_levels_to_page_bits(levels));
451 }
452
453 // Returns the minimum number of supported page bits among all hardware 
454 // units.
455 static int vtd_page_bits(struct vtd_unit *head) 
456 {
457     assert(head != NULL);
458     int num_page_bits = 30;
459
460     struct vtd_unit *u = head;
461     VTD_FOR_EACH(u, head) {
462       int unit_page_bits;
463       if (vtd_CAP_sllps30_rdf(u->regset)) {
464           unit_page_bits = 30;
465       } else if (vtd_CAP_sllps21_rdf(u->regset)) {
466           unit_page_bits = 21;
467       } else {
468           unit_page_bits = 12;
469       }
470       num_page_bits = MIN(num_page_bits, unit_page_bits);
471     }
472     return num_page_bits;
473 }
474
475 // Create the identity domain along with its identity pagetable if there
476 // is at least one remapping unit present.
477 static void vtd_create_identity_domain(void)
478 {   
479     int levels = 4;
480     int page_bits = vtd_page_bits(vtd_units);
481     levels -= (page_bits - BASE_PAGE_BITS) / SL_PTABLE_MASK_BITS;
482
483     // Map only the first 1024 GB of physical memory. Attempting to map
484     // the entire address space with this current implementation is 
485     // infeasible.
486     uint64_t max_addr = 1ULL << 40;
487     errval_t err;
488     struct frame_identity pe_frame_id;
489     struct capref pe_frame;
490     void *pe_vaddr;
491     err = frame_alloc(&pe_frame, 2 * X86_64_BASE_PAGE_SIZE, NULL);
492     assert(err_is_ok(err));
493     err = invoke_frame_identify(pe_frame, &pe_frame_id);
494     assert(err_is_ok(err));
495     err = vspace_map_one_frame_attr(&pe_vaddr, 1 << pe_frame_id.bits, pe_frame,
496                                     vtd_map_attr, NULL, NULL);
497     assert(err_is_ok(err));
498     assert((pe_frame_id.base & X86_64_BASE_PAGE_MASK) == 0 &&
499            ((lvaddr_t)pe_vaddr & X86_64_BASE_PAGE_MASK) == 0);
500     
501     struct capref empty_pml4;
502     err = slot_alloc(&empty_pml4);
503     assert(err_is_ok(err));
504     err = vnode_create(empty_pml4, ObjType_VNode_x86_64_pml4);
505     assert(err_is_ok(err));
506     
507     id_pagetable_vbase = (lvaddr_t *)pe_vaddr;
508     
509     identity_domain = vtd_new_domain(domains->min_did, pe_frame_id.base, empty_pml4, vtd_units);
510     assert(domains != NULL);
511     assert(domain_list_empty(domains));
512     domains->head = identity_domain;
513     domains->tail = identity_domain;
514
515     uint64_t mapped, remaining, vaddr, paddr;
516     vaddr = 0, paddr = 0;
517     remaining = max_addr;
518     while (remaining > 0) {
519         mapped = vtd_map(vaddr, paddr, id_pagetable_vbase, levels);
520         vaddr += mapped;
521         paddr += mapped;
522         remaining -= mapped;
523     }
524 }
525
526 // Called to add the devices specified in the translation structures to 
527 // the identity domain.
528 static void vtd_add_dmar_devices(void)
529 {
530     errval_t err;
531     err = skb_client_connect();
532     assert(err_is_ok(err));
533   
534     err = skb_execute_query("dmar_devices(L),""length(L,Len),writeln(L)");
535     assert(err_is_ok(err));
536
537     struct list_parser_status status;
538     skb_read_list_init(&status);
539     
540     int seg, bus, dev, func;
541     while(skb_read_list(&status, "address(%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32")", 
542                         &seg, &bus, &dev, &func)) {
543         err = vtd_domain_add_device(seg, bus, dev, func, identity_domain->pml4);
544         assert(err == VTD_ERR_DEV_USED || err == SYS_ERR_OK);
545     }
546 }
547
548 // Given the address of a PCIe bridge, return the bus downstream of it.
549 static int vtd_find_secondary_bus(int bus, int dev, int func)
550 {
551     errval_t err;
552     err = skb_execute_query("bridge(PCIE,addr(%d,%d,%d),_,_,_,_,_,secondary(BUS)),"
553                             "write(secondary_bus(BUS)).", bus, dev, func);
554     assert(err_is_ok(err));
555     
556     int next_bus;
557     err = skb_read_output("secondary_bus(%d)", &next_bus);
558     assert(err_is_ok(err));
559
560     return next_bus;
561 }
562
563 // Parses a Path structure, comprised of (Device number, Function number) pairs, 
564 // representing the hierarchical path of a device. The address of the device
565 // is returned in bus, dev, and func.
566 static void vtd_parse_dev_path(int begin_bus, int *bus, int *dev, int *func, char *begin, char *end)
567 {
568     assert((bus != NULL) && (dev != NULL) && (func != NULL));
569     int curr_bus, curr_dev, curr_func;
570     ACPI_DMAR_PCI_PATH *path_entry;
571
572     path_entry = (ACPI_DMAR_PCI_PATH *)begin;
573   
574     curr_bus  = begin_bus;  
575     curr_dev  = path_entry->Device;
576     curr_func = path_entry->Function;
577     
578     path_entry = (ACPI_DMAR_PCI_PATH *)((char *)path_entry + sizeof(ACPI_DMAR_PCI_PATH));
579     while ((char *)path_entry != end) {
580         curr_bus  = vtd_find_secondary_bus(curr_bus, curr_dev, curr_func);
581         curr_dev  = path_entry->Device;
582         curr_func = path_entry->Function;
583     
584         path_entry = (ACPI_DMAR_PCI_PATH *)((char *)path_entry + sizeof(ACPI_DMAR_PCI_PATH));  
585     }
586     *bus  = curr_bus;
587     *dev  = curr_dev;
588     *func = curr_func;
589 }
590
591 // Parses a Device Scope Structure belonging to a remapping structure.
592 static void vtd_parse_dev_scope_structure(int segment, char *begin, char *end, enum AcpiDmarType type) 
593 {
594     errval_t err;
595     int path_length;
596     ACPI_DMAR_DEVICE_SCOPE *entry;
597     ACPI_DMAR_PCI_PATH *path_begin, *path_end;
598
599     entry = (ACPI_DMAR_DEVICE_SCOPE *)begin;
600     while ((char *)entry != end) {
601         path_length = entry->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE);
602         assert(path_length == 2);
603
604         path_begin = (ACPI_DMAR_PCI_PATH *)((char *)entry + sizeof(ACPI_DMAR_DEVICE_SCOPE));
605         path_end   = (ACPI_DMAR_PCI_PATH *)((char *)path_begin + path_length);
606  
607         int bus, dev, func;
608         vtd_parse_dev_path(entry->Bus, &bus, &dev, &func, (char *)path_begin, (char *)path_end);
609
610         err = skb_execute_query("dmar_device(%"PRIu8",%"PRIu8","
611                                 "addr(%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"),%"PRIu8").",
612                                 type, entry->EntryType, segment, bus, dev, func, entry->EnumerationId);
613
614         // A device may have already been reported to the SKB for an earlier
615         // translation structure.
616         if (err_is_fail(err)) {
617             skb_add_fact("dmar_device(%"PRIu8",%"PRIu8","
618                          "addr(%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"),%"PRIu8").",
619                          type, entry->EntryType, segment, bus, dev, func, entry->EnumerationId);
620             VTD_DEBUG("Adding device %d:%d:%d:%d\n", segment, bus, dev, func);
621         }
622
623         entry = (ACPI_DMAR_DEVICE_SCOPE *)((char *)entry + entry->Length);
624     }
625 }
626
627 // Parses a DMA Remapping Hardware Unit (DRHD) structure. There is at least one
628 // such structure for each PCI segment.
629 static void vtd_parse_drhd_structure(char *begin, char *end)
630 {
631     ACPI_DMAR_HARDWARE_UNIT *drhd;
632     struct vtd_unit *new_unit;
633   
634     drhd = (ACPI_DMAR_HARDWARE_UNIT *)begin;
635     
636     skb_add_fact("dmar_hardware_unit(%"PRIu8", %"PRIu16", %"PRIu64").",
637                  drhd->Flags, drhd->Segment, drhd->Address);
638   
639     new_unit = vtd_create_unit(vtd_map_registers(drhd->Address), drhd->Segment);
640     vtd_insert_context_tables(new_unit);
641     VTD_ADD_UNIT(new_unit, vtd_units); 
642
643     vtd_parse_dev_scope_structure(drhd->Segment, begin + sizeof(ACPI_DMAR_HARDWARE_UNIT), 
644                                   end, ACPI_DMAR_TYPE_HARDWARE_UNIT);
645  
646 #ifdef VTD_DEBUG_
647     vtd_dump_registers(new_unit);
648 #endif
649 }
650  
651 // Parses a Reserved Memory Region Reporting (RMRR) remapping structure.
652 // Reserved Memory Region Reporting structures report reserved memory regions for 
653 // devices that are each under the scope of some remapping hardware unit.
654 static void vtd_parse_rmrr_structure(char *begin, char *end) 
655 {
656     ACPI_DMAR_RESERVED_MEMORY *rmrr;
657     rmrr = (ACPI_DMAR_RESERVED_MEMORY *)begin;
658     skb_add_fact("dmar_reserved_memory(%"PRIu16", %"PRIu64", %"PRIu64").",
659                  rmrr->Segment, rmrr->BaseAddress, rmrr->EndAddress);
660     vtd_parse_dev_scope_structure(rmrr->Segment, begin + sizeof(ACPI_DMAR_RESERVED_MEMORY), 
661                                   end, ACPI_DMAR_TYPE_RESERVED_MEMORY);
662
663
664 // Parses a Root Port ATS Capability Reporting (ATSR) structure.
665 // An ATSR structure is provided for each PCI segment supporting Device-TLBs. Currently, 
666 // we only report the PCI segments supporting Device-TLBs and the associated PCIe 
667 // Root-Ports to the SKB.
668 static void vtd_parse_atsr_structure(char *begin, char *end) 
669 {
670     ACPI_DMAR_ATSR *atsr;
671     atsr = (ACPI_DMAR_ATSR *)begin;
672     skb_add_fact("dmar_atsr(%"PRIu8", %"PRIu16").", atsr->Flags, atsr->Segment);
673     if (atsr->Flags == ACPI_DMAR_ALL_PORTS) {
674         return;
675     }
676     vtd_parse_dev_scope_structure(atsr->Segment, begin + sizeof(ACPI_DMAR_ATSR),
677                                   end, ACPI_DMAR_TYPE_ATSR);
678 }
679
680 // Parses a Remapping Hardware Static Affinity (RHSA) structure.
681 // RHSA structures are optional and are for platforms supporting non-uniform memory. 
682 // Currently, we only report the proximity domain each hardware unit belongs to(identified 
683 // by the base address of its register set) to the SKB. 
684 static void vtd_parse_rhsa_structure(char *begin, char *end) 
685 {
686     ACPI_DMAR_RHSA *rhsa;
687     rhsa = (ACPI_DMAR_RHSA *)begin;
688     skb_add_fact("dmar_rhsa(%"PRIu64", %"PRIu32").", rhsa->BaseAddress, rhsa->ProximityDomain);
689 }
690
691 // Parses an ACPI Name-space Device Declaration structure (ANDD).
692 // Currently, we only add the information about each ACPI name-space enumerated device 
693 // to the SKB.
694 static void vtd_parse_andd_structure(char *begin, char *end) 
695 {
696     ACPI_DMAR_ANDD *andd;
697     andd = (ACPI_DMAR_ANDD *)begin;
698     skb_add_fact("dmar_andd(%"PRIu8", %s).", andd->DeviceNumber, andd->ObjectName);
699
700
701 // Parses the DMA Remapping Reporting (DMAR) ACPI table.
702 static ACPI_STATUS vtd_parse_dmar_table(void)
703
704     ACPI_STATUS status;
705     ACPI_TABLE_DMAR *dmar;
706     ACPI_DMAR_HEADER *header;
707     char *structure, *structure_end;
708
709     status = AcpiGetTable(ACPI_SIG_DMAR, 0, (ACPI_TABLE_HEADER **)&dmar);
710     if (ACPI_FAILURE(status)) {
711         VTD_DEBUG("Failure in retrieving DMAR table.\n");
712         return status;
713     }
714     structure = (char *)dmar + sizeof(ACPI_TABLE_DMAR);
715     while (structure != ((char *)dmar + dmar->Header.Length)) {
716         header = (ACPI_DMAR_HEADER *)structure;
717         structure_end = structure + header->Length;
718    
719         switch (header->Type) {
720         case ACPI_DMAR_TYPE_HARDWARE_UNIT:
721             vtd_parse_drhd_structure(structure, structure_end);
722             break;
723         case ACPI_DMAR_TYPE_RESERVED_MEMORY: 
724             vtd_parse_rmrr_structure(structure, structure_end);
725             break;
726         case ACPI_DMAR_TYPE_ATSR:
727             vtd_parse_atsr_structure(structure, structure_end);
728             break;
729         case ACPI_DMAR_HARDWARE_AFFINITY:
730             vtd_parse_rhsa_structure(structure, structure_end);
731             break;
732         case ACPI_DMAR_TYPE_ANDD:
733             vtd_parse_andd_structure(structure, structure_end);
734             break;
735         default: assert(!"Reserved for future use!\n");
736         }
737
738         structure = structure_end;
739     }
740     return AE_OK;
741 }
742
743 // Add devices on this platform to the identity domain
744 void vtd_identity_domain_add_devices(void)
745 {
746     if (vtd_no_units(vtd_units)) return;
747
748     errval_t err;
749     err = skb_client_connect();
750     assert(err_is_ok(err));
751
752     // Add PCIe-to-PCIe bridges to the identity domain.  
753     err = skb_execute_query("pcie_bridges(L),""length(L,Len),writeln(L)");
754     assert(err_is_ok(err));
755
756     struct list_parser_status status;
757     skb_read_list_init(&status);
758
759     int bus, dev, func;
760     while(skb_read_list(&status, "address(%"PRIu32",%"PRIu32",%"PRIu32")", &bus, &dev, &func)) {
761         VTD_DEBUG("adding device (bridge) %d:%d:%d to the identity domain.\n", bus, dev, func); 
762         err = vtd_domain_add_device(0, bus, dev, func, identity_domain->pml4);
763         assert(err == VTD_ERR_DEV_USED || err == SYS_ERR_OK);
764     }
765  
766     err = skb_execute_query("find_devices(L),""length(L,Len),writeln(L)");
767     assert(err_is_ok(err));
768
769     skb_read_list_init(&status);
770
771     // Add all PCIe devices present on the platform to the identity domain. Since PCI 
772     // devices behind PCIe-to-PCI/PCI-X bridges and conventional PCI bridges have the 
773     // same source-id on their transactions, only add endpoint PCI devices on the root 
774     // bus.  
775     char s_type[5];
776     while(skb_read_list(&status, "address(%[a-z],%"PRIu32",%"PRIu32",%"PRIu32")", s_type, &bus, &dev, &func)) {
777         VTD_DEBUG("adding %s device %d:%d:%d to the identity domain.\n", s_type, bus, dev, func);
778         if (!strncmp(s_type, "pcie", strlen("pcie"))) {
779             err = vtd_domain_add_device(0, bus, dev, func, identity_domain->pml4);
780             assert(err == VTD_ERR_DEV_USED || err == SYS_ERR_OK);
781         } else if (!strncmp(s_type, "pci", strlen("pci"))) {
782             if (bus == 0) {
783                 err = vtd_domain_add_device(0, bus, dev, func, identity_domain->pml4);
784                 assert(err == VTD_ERR_DEV_USED || err == SYS_ERR_OK);
785             }
786         }
787     }
788 }
789
790 int vtd_init(void)
791 {
792     ACPI_STATUS as;
793     vtd_map_attr = (cache_coherence ? VREGION_FLAGS_READ_WRITE :
794                                       VREGION_FLAGS_READ_WRITE_NOCACHE);
795     as = vtd_parse_dmar_table();
796     if (ACPI_FAILURE(as)) {
797         return 1;
798     }
799
800     if (vtd_units == NULL) {
801         VTD_DEBUG("DMA remapping: no HW units, not enabling\n");
802         return 0;
803     }
804
805     // When we have finished parsing the DMAR table, we create the identity 
806     // domain and determine the domain-id bounds that can be used on all 
807     // hardware units.
808     domains = vtd_new_domain_list();
809     vtd_create_did_bounds(vtd_units, domains);
810     vtd_create_identity_domain();
811     vtd_add_dmar_devices();
812
813     struct vtd_unit *u = NULL;
814     VTD_FOR_EACH(u, vtd_units) {
815         vtd_set_root_table(u);
816         vtd_trnsl_enable(u);
817         skb_add_fact("vtd_enabled(%"PRIu16",%"PRIu8").", u->pci_seg, vtd_coherency(u));
818     }
819
820     VTD_DEBUG("Enabling DMA remapping succeeded\n");
821
822     return 0;
823 }