/root/src/xen/xen/arch/x86/domain_page.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * domain_page.h |
3 | | * |
4 | | * Allow temporary mapping of domain pages. |
5 | | * |
6 | | * Copyright (c) 2003-2006, Keir Fraser <keir@xensource.com> |
7 | | */ |
8 | | |
9 | | #include <xen/domain_page.h> |
10 | | #include <xen/efi.h> |
11 | | #include <xen/mm.h> |
12 | | #include <xen/perfc.h> |
13 | | #include <xen/pfn.h> |
14 | | #include <xen/sched.h> |
15 | | #include <xen/vmap.h> |
16 | | #include <asm/current.h> |
17 | | #include <asm/flushtlb.h> |
18 | | #include <asm/hardirq.h> |
19 | | #include <asm/setup.h> |
20 | | |
21 | | static DEFINE_PER_CPU(struct vcpu *, override); |
22 | | |
23 | | static inline struct vcpu *mapcache_current_vcpu(void) |
24 | 43.7M | { |
25 | 43.7M | /* In the common case we use the mapcache of the running VCPU. */ |
26 | 43.7M | struct vcpu *v = this_cpu(override) ?: current; |
27 | 43.7M | |
28 | 43.7M | /* |
29 | 43.7M | * When current isn't properly set up yet, this is equivalent to |
30 | 43.7M | * running in an idle vCPU (callers must check for NULL). |
31 | 43.7M | */ |
32 | 43.7M | if ( v == INVALID_VCPU ) |
33 | 263 | return NULL; |
34 | 43.7M | |
35 | 43.7M | /* |
36 | 43.7M | * When using efi runtime page tables, we have the equivalent of the idle |
37 | 43.7M | * domain's page tables but current may point at another domain's VCPU. |
38 | 43.7M | * Return NULL as though current is not properly set up yet. |
39 | 43.7M | */ |
40 | 43.7M | if ( efi_rs_using_pgtables() ) |
41 | 0 | return NULL; |
42 | 43.7M | |
43 | 43.7M | /* |
44 | 43.7M | * If guest_table is NULL, and we are running a paravirtualised guest, |
45 | 43.7M | * then it means we are running on the idle domain's page table and must |
46 | 43.7M | * therefore use its mapcache. |
47 | 43.7M | */ |
48 | 43.7M | if ( unlikely(pagetable_is_null(v->arch.guest_table)) && is_pv_vcpu(v) ) |
49 | 27.1M | { |
50 | 27.1M | /* If we really are idling, perform lazy context switch now. */ |
51 | 27.1M | if ( (v = idle_vcpu[smp_processor_id()]) == current ) |
52 | 27.1M | sync_local_execstate(); |
53 | 27.1M | /* We must now be running on the idle page table. */ |
54 | 27.1M | ASSERT(read_cr3() == __pa(idle_pg_table)); |
55 | 27.1M | } |
56 | 43.7M | |
57 | 43.7M | return v; |
58 | 43.7M | } |
59 | | |
60 | | void __init mapcache_override_current(struct vcpu *v) |
61 | 0 | { |
62 | 0 | this_cpu(override) = v; |
63 | 0 | } |
64 | | |
65 | | #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) |
66 | | #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) |
67 | | #define MAPCACHE_L1ENT(idx) \ |
68 | | __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] |
69 | | |
70 | | void *map_domain_page(mfn_t mfn) |
71 | 43.7M | { |
72 | 43.7M | unsigned long flags; |
73 | 43.7M | unsigned int idx, i; |
74 | 43.7M | struct vcpu *v; |
75 | 43.7M | struct mapcache_domain *dcache; |
76 | 43.7M | struct mapcache_vcpu *vcache; |
77 | 43.7M | struct vcpu_maphash_entry *hashent; |
78 | 43.7M | |
79 | 43.7M | #ifdef NDEBUG |
80 | | if ( mfn_x(mfn) <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) ) |
81 | | return mfn_to_virt(mfn_x(mfn)); |
82 | | #endif |
83 | 43.7M | |
84 | 43.7M | v = mapcache_current_vcpu(); |
85 | 43.7M | if ( !v || !is_pv_vcpu(v) ) |
86 | 16.6M | return mfn_to_virt(mfn_x(mfn)); |
87 | 43.7M | |
88 | 27.1M | dcache = &v->domain->arch.pv_domain.mapcache; |
89 | 27.1M | vcache = &v->arch.pv_vcpu.mapcache; |
90 | 27.1M | if ( !dcache->inuse ) |
91 | 27.1M | return mfn_to_virt(mfn_x(mfn)); |
92 | 27.1M | |
93 | 18.4E | perfc_incr(map_domain_page_count); |
94 | 18.4E | |
95 | 18.4E | local_irq_save(flags); |
96 | 18.4E | |
97 | 18.4E | hashent = &vcache->hash[MAPHASH_HASHFN(mfn_x(mfn))]; |
98 | 18.4E | if ( hashent->mfn == mfn_x(mfn) ) |
99 | 0 | { |
100 | 0 | idx = hashent->idx; |
101 | 0 | ASSERT(idx < dcache->entries); |
102 | 0 | hashent->refcnt++; |
103 | 0 | ASSERT(hashent->refcnt); |
104 | 0 | ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(idx)) == mfn_x(mfn)); |
105 | 0 | goto out; |
106 | 0 | } |
107 | 18.4E | |
108 | 18.4E | spin_lock(&dcache->lock); |
109 | 18.4E | |
110 | 18.4E | /* Has some other CPU caused a wrap? We must flush if so. */ |
111 | 18.4E | if ( unlikely(dcache->epoch != vcache->shadow_epoch) ) |
112 | 0 | { |
113 | 0 | vcache->shadow_epoch = dcache->epoch; |
114 | 0 | if ( NEED_FLUSH(this_cpu(tlbflush_time), dcache->tlbflush_timestamp) ) |
115 | 0 | { |
116 | 0 | perfc_incr(domain_page_tlb_flush); |
117 | 0 | flush_tlb_local(); |
118 | 0 | } |
119 | 0 | } |
120 | 18.4E | |
121 | 18.4E | idx = find_next_zero_bit(dcache->inuse, dcache->entries, dcache->cursor); |
122 | 18.4E | if ( unlikely(idx >= dcache->entries) ) |
123 | 0 | { |
124 | 0 | unsigned long accum = 0, prev = 0; |
125 | 0 |
|
126 | 0 | /* /First/, clean the garbage map and update the inuse list. */ |
127 | 0 | for ( i = 0; i < BITS_TO_LONGS(dcache->entries); i++ ) |
128 | 0 | { |
129 | 0 | accum |= prev; |
130 | 0 | dcache->inuse[i] &= ~xchg(&dcache->garbage[i], 0); |
131 | 0 | prev = ~dcache->inuse[i]; |
132 | 0 | } |
133 | 0 |
|
134 | 0 | if ( accum | (prev & BITMAP_LAST_WORD_MASK(dcache->entries)) ) |
135 | 0 | idx = find_first_zero_bit(dcache->inuse, dcache->entries); |
136 | 0 | else |
137 | 0 | { |
138 | 0 | /* Replace a hash entry instead. */ |
139 | 0 | i = MAPHASH_HASHFN(mfn_x(mfn)); |
140 | 0 | do { |
141 | 0 | hashent = &vcache->hash[i]; |
142 | 0 | if ( hashent->idx != MAPHASHENT_NOTINUSE && !hashent->refcnt ) |
143 | 0 | { |
144 | 0 | idx = hashent->idx; |
145 | 0 | ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(idx)) == hashent->mfn); |
146 | 0 | l1e_write(&MAPCACHE_L1ENT(idx), l1e_empty()); |
147 | 0 | hashent->idx = MAPHASHENT_NOTINUSE; |
148 | 0 | hashent->mfn = ~0UL; |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | if ( ++i == MAPHASH_ENTRIES ) |
152 | 0 | i = 0; |
153 | 0 | } while ( i != MAPHASH_HASHFN(mfn_x(mfn)) ); |
154 | 0 | } |
155 | 0 | BUG_ON(idx >= dcache->entries); |
156 | 0 |
|
157 | 0 | /* /Second/, flush TLBs. */ |
158 | 0 | perfc_incr(domain_page_tlb_flush); |
159 | 0 | flush_tlb_local(); |
160 | 0 | vcache->shadow_epoch = ++dcache->epoch; |
161 | 0 | dcache->tlbflush_timestamp = tlbflush_current_time(); |
162 | 0 | } |
163 | 18.4E | |
164 | 18.4E | set_bit(idx, dcache->inuse); |
165 | 18.4E | dcache->cursor = idx + 1; |
166 | 18.4E | |
167 | 18.4E | spin_unlock(&dcache->lock); |
168 | 18.4E | |
169 | 18.4E | l1e_write(&MAPCACHE_L1ENT(idx), l1e_from_mfn(mfn, __PAGE_HYPERVISOR_RW)); |
170 | 18.4E | |
171 | 0 | out: |
172 | 0 | local_irq_restore(flags); |
173 | 0 | return (void *)MAPCACHE_VIRT_START + pfn_to_paddr(idx); |
174 | 18.4E | } |
175 | | |
176 | | void unmap_domain_page(const void *ptr) |
177 | 43.7M | { |
178 | 43.7M | unsigned int idx; |
179 | 43.7M | struct vcpu *v; |
180 | 43.7M | struct mapcache_domain *dcache; |
181 | 43.7M | unsigned long va = (unsigned long)ptr, mfn, flags; |
182 | 43.7M | struct vcpu_maphash_entry *hashent; |
183 | 43.7M | |
184 | 43.7M | if ( va >= DIRECTMAP_VIRT_START ) |
185 | 43.7M | return; |
186 | 43.7M | |
187 | 18.4E | ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); |
188 | 18.4E | |
189 | 18.4E | v = mapcache_current_vcpu(); |
190 | 18.4E | ASSERT(v && is_pv_vcpu(v)); |
191 | 18.4E | |
192 | 18.4E | dcache = &v->domain->arch.pv_domain.mapcache; |
193 | 18.4E | ASSERT(dcache->inuse); |
194 | 18.4E | |
195 | 18.4E | idx = PFN_DOWN(va - MAPCACHE_VIRT_START); |
196 | 18.4E | mfn = l1e_get_pfn(MAPCACHE_L1ENT(idx)); |
197 | 18.4E | hashent = &v->arch.pv_vcpu.mapcache.hash[MAPHASH_HASHFN(mfn)]; |
198 | 18.4E | |
199 | 18.4E | local_irq_save(flags); |
200 | 18.4E | |
201 | 18.4E | if ( hashent->idx == idx ) |
202 | 0 | { |
203 | 0 | ASSERT(hashent->mfn == mfn); |
204 | 0 | ASSERT(hashent->refcnt); |
205 | 0 | hashent->refcnt--; |
206 | 0 | } |
207 | 18.4E | else if ( !hashent->refcnt ) |
208 | 0 | { |
209 | 0 | if ( hashent->idx != MAPHASHENT_NOTINUSE ) |
210 | 0 | { |
211 | 0 | /* /First/, zap the PTE. */ |
212 | 0 | ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(hashent->idx)) == |
213 | 0 | hashent->mfn); |
214 | 0 | l1e_write(&MAPCACHE_L1ENT(hashent->idx), l1e_empty()); |
215 | 0 | /* /Second/, mark as garbage. */ |
216 | 0 | set_bit(hashent->idx, dcache->garbage); |
217 | 0 | } |
218 | 0 |
|
219 | 0 | /* Add newly-freed mapping to the maphash. */ |
220 | 0 | hashent->mfn = mfn; |
221 | 0 | hashent->idx = idx; |
222 | 0 | } |
223 | 18.4E | else |
224 | 18.4E | { |
225 | 18.4E | /* /First/, zap the PTE. */ |
226 | 18.4E | l1e_write(&MAPCACHE_L1ENT(idx), l1e_empty()); |
227 | 18.4E | /* /Second/, mark as garbage. */ |
228 | 0 | set_bit(idx, dcache->garbage); |
229 | 0 | } |
230 | 18.4E | |
231 | 0 | local_irq_restore(flags); |
232 | 0 | } |
233 | | |
234 | | int mapcache_domain_init(struct domain *d) |
235 | 2 | { |
236 | 2 | struct mapcache_domain *dcache = &d->arch.pv_domain.mapcache; |
237 | 2 | unsigned int bitmap_pages; |
238 | 2 | |
239 | 2 | if ( !is_pv_domain(d) || is_idle_domain(d) ) |
240 | 2 | return 0; |
241 | 2 | |
242 | 2 | #ifdef NDEBUG |
243 | | if ( !mem_hotplug && max_page <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) ) |
244 | | return 0; |
245 | | #endif |
246 | 2 | |
247 | 0 | BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + |
248 | 0 | 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > |
249 | 0 | MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); |
250 | 0 | bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); |
251 | 0 | dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; |
252 | 0 | dcache->garbage = dcache->inuse + |
253 | 0 | (bitmap_pages + 1) * PAGE_SIZE / sizeof(long); |
254 | 0 |
|
255 | 0 | spin_lock_init(&dcache->lock); |
256 | 0 |
|
257 | 0 | return create_perdomain_mapping(d, (unsigned long)dcache->inuse, |
258 | 0 | 2 * bitmap_pages + 1, |
259 | 0 | NIL(l1_pgentry_t *), NULL); |
260 | 2 | } |
261 | | |
262 | | int mapcache_vcpu_init(struct vcpu *v) |
263 | 24 | { |
264 | 24 | struct domain *d = v->domain; |
265 | 24 | struct mapcache_domain *dcache = &d->arch.pv_domain.mapcache; |
266 | 24 | unsigned long i; |
267 | 24 | unsigned int ents = d->max_vcpus * MAPCACHE_VCPU_ENTRIES; |
268 | 24 | unsigned int nr = PFN_UP(BITS_TO_LONGS(ents) * sizeof(long)); |
269 | 24 | |
270 | 24 | if ( !is_pv_vcpu(v) || !dcache->inuse ) |
271 | 24 | return 0; |
272 | 24 | |
273 | 0 | if ( ents > dcache->entries ) |
274 | 0 | { |
275 | 0 | /* Populate page tables. */ |
276 | 0 | int rc = create_perdomain_mapping(d, MAPCACHE_VIRT_START, ents, |
277 | 0 | NIL(l1_pgentry_t *), NULL); |
278 | 0 |
|
279 | 0 | /* Populate bit maps. */ |
280 | 0 | if ( !rc ) |
281 | 0 | rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, |
282 | 0 | nr, NULL, NIL(struct page_info *)); |
283 | 0 | if ( !rc ) |
284 | 0 | rc = create_perdomain_mapping(d, (unsigned long)dcache->garbage, |
285 | 0 | nr, NULL, NIL(struct page_info *)); |
286 | 0 |
|
287 | 0 | if ( rc ) |
288 | 0 | return rc; |
289 | 0 |
|
290 | 0 | dcache->entries = ents; |
291 | 0 | } |
292 | 0 |
|
293 | 0 | /* Mark all maphash entries as not in use. */ |
294 | 0 | BUILD_BUG_ON(MAPHASHENT_NOTINUSE < MAPCACHE_ENTRIES); |
295 | 0 | for ( i = 0; i < MAPHASH_ENTRIES; i++ ) |
296 | 0 | { |
297 | 0 | struct vcpu_maphash_entry *hashent = &v->arch.pv_vcpu.mapcache.hash[i]; |
298 | 0 |
|
299 | 0 | hashent->mfn = ~0UL; /* never valid to map */ |
300 | 0 | hashent->idx = MAPHASHENT_NOTINUSE; |
301 | 0 | } |
302 | 0 |
|
303 | 0 | return 0; |
304 | 0 | } |
305 | | |
306 | | void *map_domain_page_global(mfn_t mfn) |
307 | 24 | { |
308 | 24 | ASSERT(!in_irq() && |
309 | 24 | ((system_state >= SYS_STATE_boot && |
310 | 24 | system_state < SYS_STATE_active) || |
311 | 24 | local_irq_is_enabled())); |
312 | 24 | |
313 | 24 | #ifdef NDEBUG |
314 | | if ( mfn_x(mfn) <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) ) |
315 | | return mfn_to_virt(mfn_x(mfn)); |
316 | | #endif |
317 | 24 | |
318 | 24 | return vmap(&mfn, 1); |
319 | 24 | } |
320 | | |
321 | | void unmap_domain_page_global(const void *ptr) |
322 | 0 | { |
323 | 0 | unsigned long va = (unsigned long)ptr; |
324 | 0 |
|
325 | 0 | if ( va >= DIRECTMAP_VIRT_START ) |
326 | 0 | return; |
327 | 0 |
|
328 | 0 | ASSERT(va >= VMAP_VIRT_START && va < VMAP_VIRT_END); |
329 | 0 |
|
330 | 0 | vunmap(ptr); |
331 | 0 | } |
332 | | |
333 | | /* Translate a map-domain-page'd address to the underlying MFN */ |
334 | | unsigned long domain_page_map_to_mfn(const void *ptr) |
335 | 0 | { |
336 | 0 | unsigned long va = (unsigned long)ptr; |
337 | 0 | const l1_pgentry_t *pl1e; |
338 | 0 |
|
339 | 0 | if ( va >= DIRECTMAP_VIRT_START ) |
340 | 0 | return virt_to_mfn(ptr); |
341 | 0 |
|
342 | 0 | if ( va >= VMAP_VIRT_START && va < VMAP_VIRT_END ) |
343 | 0 | { |
344 | 0 | pl1e = virt_to_xen_l1e(va); |
345 | 0 | BUG_ON(!pl1e); |
346 | 0 | } |
347 | 0 | else |
348 | 0 | { |
349 | 0 | ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); |
350 | 0 | pl1e = &__linear_l1_table[l1_linear_offset(va)]; |
351 | 0 | } |
352 | 0 |
|
353 | 0 | return l1e_get_pfn(*pl1e); |
354 | 0 | } |