VT-d: basic support for multiple units per segment. closes T157.
[barrelfish] / usr / acpi / intel_vtd.h
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 #ifndef INTEL_VTD_H
12 #define INTEL_VTD_H
13
14 #include <pci/confspace/pci_confspace.h>
15
16 #include <dev/vtd_dev.h>
17 #include <dev/vtd_iotlb_dev.h>
18
19 #include "vtd_domains.h"
20 #include "vtd_sl_paging.h"
21
22 #define ROOT_TABLE_TYPE         vtd_rt
23 #define NUM_ROOT_ENTRIES        512
24
25 #define VTD_FOR_EACH(var, head) for (var = (head); var; var = var->next)
26 #define VTD_ADD_UNIT(x, head) do {(x)->next = (head); (head) = (x);} while(0)
27
28 #define MIN(a,b) ((a) < (b) ? (a) : (b))
29 #define MAX(a,b) ((a) > (b) ? (a) : (b))
30
31 // A structure representing a remapping hardware unit. We currently assume
32 // that there is only one such unit per segment.
33 struct vtd_unit {
34     vtd_t *                     regset;     // Remapping register set
35     vtd_iotlb_t *               iotlb_regs; // IOTLB registers
36
37     uint16_t                    pci_seg;
38     // capability for the root table frame
39     struct capref               rt_frame;
40     vtd_root_entry_array_t *    root_table;
41   
42     // array of capabilities of context table frames
43     struct capref               ct_frame_caps[NUM_ROOT_ENTRIES]; 
44     vtd_context_entry_array_t * context_tables[NUM_ROOT_ENTRIES];
45
46     struct vtd_unit *  next;
47 };
48
49 errval_t vtd_create_domain(struct capref pml4);
50 errval_t vtd_remove_domain(struct capref pml4);
51
52 errval_t vtd_domain_remove_device(int seg, int bus, int dev, int func, struct capref pml4);
53 errval_t vtd_domain_add_device(int seg, int bus, int dev, int func, struct capref pml4);
54
55 void vtd_identity_domain_add_devices(void);
56
57 int vtd_init(void);
58
59 // This will need to be changed when (and if) Arrakis supports PCI
60 // segments.
61 static inline int valid_device(int bus, int dev, int func)
62 {
63     return ((bus >= 0 && bus < PCI_NBUSES)   && 
64             (dev >= 0 && dev < PCI_NDEVICES) &&
65             (func >= 0 && func < PCI_NFUNCTIONS));
66 }
67
68 // Returns true if units, a list of remapping hardware units, is empty.
69 static inline int vtd_no_units(struct vtd_unit *units) {
70     return (units == NULL);
71 }
72
73 // Returns 1 if the remapping unit snoops the processor caches for both
74 // paging-entries and pages, else 0.
75 static inline int vtd_coherency(struct vtd_unit *unit)
76 {
77     int pwc = vtd_ECAP_pwc_rdf(unit->regset);
78     int sc  = vtd_ECAP_sc_rdf(unit->regset);
79     return (pwc & sc);
80 }
81
82 // Perform command to set the root table and then wait until hardware
83 // sets the bit to indicate completion.
84 static inline void GSTS_srtp_wait(struct vtd_unit *unit)
85 {
86     vtd_GCMD_srtp_wrf(unit->regset, 1);
87     while(vtd_GSTS_rtps_rdf(unit->regset) == 0);
88 }
89
90 // Perform command to flush the write buffer and then wait until hardware
91 // clears the bit to indicate completion.
92 static inline void GSTS_wbf_wait(struct vtd_unit *unit)
93 {
94     vtd_GCMD_wbf_wrf(unit->regset, 1);
95     while(vtd_GSTS_wbfs_rdf(unit->regset));
96 }
97
98 // Perform command to enable/diable DMA-remapping and then wait until hardware 
99 // sets/clears the bit to indicate completion.
100 static inline void GSTS_te_wait(struct vtd_unit *unit, int val)
101 {
102     assert(val == 1 || val == 0);
103     vtd_GCMD_te_wrf(unit->regset, val);
104     while(vtd_GSTS_tes_rdf(unit->regset) == !val);
105 }
106
107 // Perform command to invalidate the context-cache and then wait until 
108 // the hardware clears the bit to indicate completion.
109 static inline void CCMD_icc_wait(struct vtd_unit *unit)
110 {
111     vtd_CCMD_icc_wrf(unit->regset, 1);
112     while(vtd_CCMD_icc_rdf(unit->regset));
113 }
114
115 // Perform command to invalidate the IOTLB and then wait until hardware 
116 // clears the bit to indicate completion.
117 static inline void IOTLB_ivt_wait(struct vtd_unit *unit)
118
119     vtd_iotlb_iotlb_reg_ivt_wrf(unit->iotlb_regs, 1);
120     while (vtd_iotlb_iotlb_reg_ivt_rdf(unit->iotlb_regs));
121 }
122
123 // Flush the internal write buffers.
124 static inline void vtd_flush_write_buffer(struct vtd_unit *unit)
125 {
126     if (vtd_CAP_rwbf_rdf(unit->regset)) {
127         GSTS_wbf_wait(unit);
128     }
129 }
130
131 // Perform ax global invalidation of context-cache for unit.
132 // vtd_iotlb_glob_inval must be called afterwards.
133 static inline void vtd_context_cache_glob_inval(struct vtd_unit *unit)
134 {
135     // Flush the Root-Complex internal write buffers
136     vtd_flush_write_buffer(unit);
137     // We want a global invalidation
138     vtd_CCMD_cirg_wrf(unit->regset, vtd_gir);
139     // Perform invalidation
140     CCMD_icc_wait(unit);
141 }
142
143 // Perform a domain-selective invalidation of context-cache for dom.
144 // vtd_iotlb_dom_inval must be called afterwards
145 static inline void vtd_context_cache_dom_inval(struct vtd_domain *dom)
146 {
147     struct vtd_unit *u = NULL;
148     VTD_FOR_EACH(u, dom->units) {
149         vtd_flush_write_buffer(u);
150         vtd_CCMD_cirg_wrf(u->regset, vtd_domir);
151         vtd_CCMD_did_wrf(u->regset, dom->did);
152         CCMD_icc_wait(u);
153     }
154 }
155
156 // Perform a device-selective invalidation of context-cache for 
157 // the device with the source-id sid contained in the domain dom.
158 // vtd_iotlb_dom_inval must be called afterwards
159 static inline void vtd_context_cache_dev_inval(struct vtd_domain *dom, int sid, int func_mask)
160 {
161     struct vtd_unit *u = NULL;
162     VTD_FOR_EACH(u, dom->units) {
163         vtd_flush_write_buffer(u);
164         vtd_CCMD_cirg_wrf(u->regset, vtd_devir);
165         vtd_CCMD_did_wrf(u->regset, dom->did);
166         vtd_CCMD_sid_wrf(u->regset, sid);
167         vtd_CCMD_fm_wrf(u->regset, func_mask);
168         CCMD_icc_wait(u);
169     }
170 }
171
172 // Perform a global IOTLB invalidation for unit.
173 static inline void vtd_iotlb_glob_inval(struct vtd_unit *unit)
174 {
175     // Flush the Root-Complex internal write buffers
176     vtd_flush_write_buffer(unit);
177     // We want a global invalidation
178     vtd_iotlb_iotlb_reg_iirg_wrf(unit->iotlb_regs, vtd_iotlb_gir);
179     // Drain writes and reads (if not required, will be ignored by hardware)
180     vtd_iotlb_iotlb_reg_dw_wrf(unit->iotlb_regs, 1);
181     vtd_iotlb_iotlb_reg_dr_wrf(unit->iotlb_regs, 1);
182     // Invalidate IOTLB
183     IOTLB_ivt_wait(unit);
184 }
185
186 // Perform a domain-selective IOTLB invalidation for dom.
187 static inline void vtd_iotlb_dom_inval(struct vtd_domain *dom)
188 {
189     struct vtd_unit *u = NULL;
190     VTD_FOR_EACH(u, dom->units) {
191         vtd_flush_write_buffer(u);
192         vtd_iotlb_iotlb_reg_iirg_wrf(u->iotlb_regs, vtd_iotlb_domir);
193         vtd_iotlb_iotlb_reg_did_wrf(u->iotlb_regs, dom->did);
194         vtd_iotlb_iotlb_reg_dw_wrf(u->iotlb_regs, 1);
195         vtd_iotlb_iotlb_reg_dr_wrf(u->iotlb_regs, 1);
196         IOTLB_ivt_wait(u);
197     }
198 }
199
200 // Perform a page-selective-within-domain IOTLB invalidation for dom.
201 //
202 // addr - the sl-input-address that needs to be invalidated
203 //
204 // addr_mask - specifies the number of low bits of addr to be masked. 
205 // Used for the invalidation of multiple contiguous pages which may 
206 // comprise a large page. 
207 //
208 // inval_hint - a value of 0 indicates that both leaf and non-leaf sl 
209 // entries may have been modified. 1 indicates that no sl non-leaf entry
210 // has been modified.
211 static inline void vtd_iotlb_page_inval(struct vtd_domain *dom, int ival_hint, int addr, int addr_mask)
212
213     struct vtd_unit *u = NULL;
214     VTD_FOR_EACH(u, dom->units) {
215         vtd_flush_write_buffer(u);
216         vtd_iotlb_iotlb_reg_iirg_wrf(u->iotlb_regs, vtd_iotlb_pir);
217         vtd_iotlb_iotlb_reg_did_wrf(u->iotlb_regs, dom->did);
218         vtd_iotlb_iotlb_reg_dw_wrf(u->iotlb_regs, 1);
219         vtd_iotlb_iotlb_reg_dr_wrf(u->iotlb_regs, 1);
220         vtd_iotlb_iva_reg_ih_wrf(u->iotlb_regs, ival_hint);
221         vtd_iotlb_iva_reg_addr_wrf(u->iotlb_regs, (addr >> 12));
222         vtd_iotlb_iva_reg_am_wrf(u->iotlb_regs, addr_mask);
223         IOTLB_ivt_wait(u);
224     }
225 }
226
227 // Enable DMA-remapping for unit.
228 static inline void vtd_trnsl_enable(struct vtd_unit *unit)
229 {
230     GSTS_te_wait(unit, 1);
231 }
232
233 // Disable DMA-remapping for unit.
234 static inline void vtd_trnsl_disable(struct vtd_unit *unit)
235 {
236     GSTS_te_wait(unit, 0);
237 }
238
239 // Returns the number of domains supported. A simple linear relationship
240 // exists between the value of the nd field in the Capability register 
241 // and the number of domains.
242 static inline int vtd_number_domains_supported(struct vtd_unit *unit)
243 {
244     int nd_bits;
245     assert(unit != NULL);
246     nd_bits = (2 * vtd_CAP_nd_rdf(unit->regset)) + 4;
247     return (1 << nd_bits);
248 }
249
250 // Returns the number of bits per page for a pagetable with the specified
251 // number of levels.
252 static inline int vtd_levels_to_page_bits(int levels) {
253     assert(levels >= 2 && levels <= 4);
254     return (48 - SL_PTABLE_MASK_BITS * levels);
255 }
256
257 #endif //INTEL_VTD_H