xref: /openbmc/qemu/hw/xen/xen-mapcache.c (revision 37fbfda8f4145ba1700f63f0cb7be4c108d545de)
1e7218dd5SVikram Garhwal /*
2e7218dd5SVikram Garhwal  * Copyright (C) 2011       Citrix Ltd.
3e7218dd5SVikram Garhwal  *
4e7218dd5SVikram Garhwal  * This work is licensed under the terms of the GNU GPL, version 2.  See
5e7218dd5SVikram Garhwal  * the COPYING file in the top-level directory.
6e7218dd5SVikram Garhwal  *
7e7218dd5SVikram Garhwal  * Contributions after 2012-01-13 are licensed under the terms of the
8e7218dd5SVikram Garhwal  * GNU GPL, version 2 or (at your option) any later version.
9e7218dd5SVikram Garhwal  */
10e7218dd5SVikram Garhwal 
11e7218dd5SVikram Garhwal #include "qemu/osdep.h"
12e7218dd5SVikram Garhwal #include "qemu/units.h"
13e7218dd5SVikram Garhwal #include "qemu/error-report.h"
14e7218dd5SVikram Garhwal 
15e7218dd5SVikram Garhwal #include <sys/resource.h>
16e7218dd5SVikram Garhwal 
179ecdd4bfSEdgar E. Iglesias #include "hw/xen/xen-hvm-common.h"
18e7218dd5SVikram Garhwal #include "hw/xen/xen_native.h"
19e7218dd5SVikram Garhwal #include "qemu/bitmap.h"
20e7218dd5SVikram Garhwal 
21e7218dd5SVikram Garhwal #include "sysemu/runstate.h"
22e7218dd5SVikram Garhwal #include "sysemu/xen-mapcache.h"
23e7218dd5SVikram Garhwal #include "trace.h"
24e7218dd5SVikram Garhwal 
259ecdd4bfSEdgar E. Iglesias #include <xenevtchn.h>
269ecdd4bfSEdgar E. Iglesias #include <xengnttab.h>
27e7218dd5SVikram Garhwal 
28e7218dd5SVikram Garhwal #if HOST_LONG_BITS == 32
29e7218dd5SVikram Garhwal #  define MCACHE_MAX_SIZE     (1UL<<31) /* 2GB Cap */
30e7218dd5SVikram Garhwal #else
31e7218dd5SVikram Garhwal #  define MCACHE_MAX_SIZE     (1UL<<35) /* 32GB Cap */
32e7218dd5SVikram Garhwal #endif
33e7218dd5SVikram Garhwal 
34e7218dd5SVikram Garhwal /* This is the size of the virtual address space reserve to QEMU that will not
35e7218dd5SVikram Garhwal  * be use by MapCache.
36e7218dd5SVikram Garhwal  * From empirical tests I observed that qemu use 75MB more than the
37e7218dd5SVikram Garhwal  * max_mcache_size.
38e7218dd5SVikram Garhwal  */
39e7218dd5SVikram Garhwal #define NON_MCACHE_MEMORY_SIZE (80 * MiB)
40e7218dd5SVikram Garhwal 
41e7218dd5SVikram Garhwal typedef struct MapCacheEntry {
42e7218dd5SVikram Garhwal     hwaddr paddr_index;
43e7218dd5SVikram Garhwal     uint8_t *vaddr_base;
44e7218dd5SVikram Garhwal     unsigned long *valid_mapping;
45e7218dd5SVikram Garhwal     uint32_t lock;
46e7218dd5SVikram Garhwal #define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0)
479ecdd4bfSEdgar E. Iglesias #define XEN_MAPCACHE_ENTRY_GRANT (1 << 1)
48e7218dd5SVikram Garhwal     uint8_t flags;
49e7218dd5SVikram Garhwal     hwaddr size;
50e7218dd5SVikram Garhwal     struct MapCacheEntry *next;
51e7218dd5SVikram Garhwal } MapCacheEntry;
52e7218dd5SVikram Garhwal 
53e7218dd5SVikram Garhwal typedef struct MapCacheRev {
54e7218dd5SVikram Garhwal     uint8_t *vaddr_req;
55e7218dd5SVikram Garhwal     hwaddr paddr_index;
56e7218dd5SVikram Garhwal     hwaddr size;
57e7218dd5SVikram Garhwal     QTAILQ_ENTRY(MapCacheRev) next;
58e7218dd5SVikram Garhwal     bool dma;
59e7218dd5SVikram Garhwal } MapCacheRev;
60e7218dd5SVikram Garhwal 
61e7218dd5SVikram Garhwal typedef struct MapCache {
62e7218dd5SVikram Garhwal     MapCacheEntry *entry;
63e7218dd5SVikram Garhwal     unsigned long nr_buckets;
64e7218dd5SVikram Garhwal     QTAILQ_HEAD(, MapCacheRev) locked_entries;
65e7218dd5SVikram Garhwal 
66e7218dd5SVikram Garhwal     /* For most cases (>99.9%), the page address is the same. */
67e7218dd5SVikram Garhwal     MapCacheEntry *last_entry;
68e7218dd5SVikram Garhwal     unsigned long max_mcache_size;
69b771b026SEdgar E. Iglesias     unsigned int bucket_shift;
70b771b026SEdgar E. Iglesias     unsigned long bucket_size;
71e7218dd5SVikram Garhwal 
72e7218dd5SVikram Garhwal     phys_offset_to_gaddr_t phys_offset_to_gaddr;
73e7218dd5SVikram Garhwal     QemuMutex lock;
74e7218dd5SVikram Garhwal     void *opaque;
75e7218dd5SVikram Garhwal } MapCache;
76e7218dd5SVikram Garhwal 
77e7218dd5SVikram Garhwal static MapCache *mapcache;
789ecdd4bfSEdgar E. Iglesias static MapCache *mapcache_grants;
799ecdd4bfSEdgar E. Iglesias static xengnttab_handle *xen_region_gnttabdev;
80e7218dd5SVikram Garhwal 
mapcache_lock(MapCache * mc)81efb0c6caSEdgar E. Iglesias static inline void mapcache_lock(MapCache *mc)
82e7218dd5SVikram Garhwal {
83efb0c6caSEdgar E. Iglesias     qemu_mutex_lock(&mc->lock);
84e7218dd5SVikram Garhwal }
85e7218dd5SVikram Garhwal 
mapcache_unlock(MapCache * mc)86efb0c6caSEdgar E. Iglesias static inline void mapcache_unlock(MapCache *mc)
87e7218dd5SVikram Garhwal {
88efb0c6caSEdgar E. Iglesias     qemu_mutex_unlock(&mc->lock);
89e7218dd5SVikram Garhwal }
90e7218dd5SVikram Garhwal 
test_bits(int nr,int size,const unsigned long * addr)91e7218dd5SVikram Garhwal static inline int test_bits(int nr, int size, const unsigned long *addr)
92e7218dd5SVikram Garhwal {
93e7218dd5SVikram Garhwal     unsigned long res = find_next_zero_bit(addr, size + nr, nr);
94e7218dd5SVikram Garhwal     if (res >= nr + size)
95e7218dd5SVikram Garhwal         return 1;
96e7218dd5SVikram Garhwal     else
97e7218dd5SVikram Garhwal         return 0;
98e7218dd5SVikram Garhwal }
99e7218dd5SVikram Garhwal 
xen_map_cache_init_single(phys_offset_to_gaddr_t f,void * opaque,unsigned int bucket_shift,unsigned long max_size)100886e5adeSEdgar E. Iglesias static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f,
101886e5adeSEdgar E. Iglesias                                            void *opaque,
102b771b026SEdgar E. Iglesias                                            unsigned int bucket_shift,
103886e5adeSEdgar E. Iglesias                                            unsigned long max_size)
104e7218dd5SVikram Garhwal {
105e7218dd5SVikram Garhwal     unsigned long size;
106886e5adeSEdgar E. Iglesias     MapCache *mc;
107886e5adeSEdgar E. Iglesias 
108b771b026SEdgar E. Iglesias     assert(bucket_shift >= XC_PAGE_SHIFT);
109b771b026SEdgar E. Iglesias 
110886e5adeSEdgar E. Iglesias     mc = g_new0(MapCache, 1);
111886e5adeSEdgar E. Iglesias 
112886e5adeSEdgar E. Iglesias     mc->phys_offset_to_gaddr = f;
113886e5adeSEdgar E. Iglesias     mc->opaque = opaque;
114886e5adeSEdgar E. Iglesias     qemu_mutex_init(&mc->lock);
115886e5adeSEdgar E. Iglesias 
116886e5adeSEdgar E. Iglesias     QTAILQ_INIT(&mc->locked_entries);
117886e5adeSEdgar E. Iglesias 
118b771b026SEdgar E. Iglesias     mc->bucket_shift = bucket_shift;
119b771b026SEdgar E. Iglesias     mc->bucket_size = 1UL << bucket_shift;
120886e5adeSEdgar E. Iglesias     mc->max_mcache_size = max_size;
121886e5adeSEdgar E. Iglesias 
122886e5adeSEdgar E. Iglesias     mc->nr_buckets =
123886e5adeSEdgar E. Iglesias         (((mc->max_mcache_size >> XC_PAGE_SHIFT) +
124b771b026SEdgar E. Iglesias           (1UL << (bucket_shift - XC_PAGE_SHIFT)) - 1) >>
125b771b026SEdgar E. Iglesias          (bucket_shift - XC_PAGE_SHIFT));
126886e5adeSEdgar E. Iglesias 
127886e5adeSEdgar E. Iglesias     size = mc->nr_buckets * sizeof(MapCacheEntry);
128886e5adeSEdgar E. Iglesias     size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1);
129886e5adeSEdgar E. Iglesias     trace_xen_map_cache_init(mc->nr_buckets, size);
130886e5adeSEdgar E. Iglesias     mc->entry = g_malloc0(size);
131886e5adeSEdgar E. Iglesias     return mc;
132886e5adeSEdgar E. Iglesias }
133886e5adeSEdgar E. Iglesias 
xen_map_cache_init(phys_offset_to_gaddr_t f,void * opaque)134886e5adeSEdgar E. Iglesias void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
135886e5adeSEdgar E. Iglesias {
136e7218dd5SVikram Garhwal     struct rlimit rlimit_as;
137886e5adeSEdgar E. Iglesias     unsigned long max_mcache_size;
138b771b026SEdgar E. Iglesias     unsigned int bucket_shift;
139b771b026SEdgar E. Iglesias 
1409ecdd4bfSEdgar E. Iglesias     xen_region_gnttabdev = xengnttab_open(NULL, 0);
1419ecdd4bfSEdgar E. Iglesias     if (xen_region_gnttabdev == NULL) {
1429ecdd4bfSEdgar E. Iglesias         error_report("mapcache: Failed to open gnttab device");
1439ecdd4bfSEdgar E. Iglesias         exit(EXIT_FAILURE);
1449ecdd4bfSEdgar E. Iglesias     }
1459ecdd4bfSEdgar E. Iglesias 
146b771b026SEdgar E. Iglesias     if (HOST_LONG_BITS == 32) {
147b771b026SEdgar E. Iglesias         bucket_shift = 16;
148b771b026SEdgar E. Iglesias     } else {
149b771b026SEdgar E. Iglesias         bucket_shift = 20;
150b771b026SEdgar E. Iglesias     }
151e7218dd5SVikram Garhwal 
152e7218dd5SVikram Garhwal     if (geteuid() == 0) {
153e7218dd5SVikram Garhwal         rlimit_as.rlim_cur = RLIM_INFINITY;
154e7218dd5SVikram Garhwal         rlimit_as.rlim_max = RLIM_INFINITY;
155886e5adeSEdgar E. Iglesias         max_mcache_size = MCACHE_MAX_SIZE;
156e7218dd5SVikram Garhwal     } else {
157e7218dd5SVikram Garhwal         getrlimit(RLIMIT_AS, &rlimit_as);
158e7218dd5SVikram Garhwal         rlimit_as.rlim_cur = rlimit_as.rlim_max;
159e7218dd5SVikram Garhwal 
160e7218dd5SVikram Garhwal         if (rlimit_as.rlim_max != RLIM_INFINITY) {
161e7218dd5SVikram Garhwal             warn_report("QEMU's maximum size of virtual"
162e7218dd5SVikram Garhwal                         " memory is not infinity");
163e7218dd5SVikram Garhwal         }
164e7218dd5SVikram Garhwal         if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) {
165886e5adeSEdgar E. Iglesias             max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE;
166e7218dd5SVikram Garhwal         } else {
167886e5adeSEdgar E. Iglesias             max_mcache_size = MCACHE_MAX_SIZE;
168e7218dd5SVikram Garhwal         }
169e7218dd5SVikram Garhwal     }
170e7218dd5SVikram Garhwal 
171b771b026SEdgar E. Iglesias     mapcache = xen_map_cache_init_single(f, opaque,
172b771b026SEdgar E. Iglesias                                          bucket_shift,
173b771b026SEdgar E. Iglesias                                          max_mcache_size);
1749ecdd4bfSEdgar E. Iglesias 
1759ecdd4bfSEdgar E. Iglesias     /*
1769ecdd4bfSEdgar E. Iglesias      * Grant mappings must use XC_PAGE_SIZE granularity since we can't
1779ecdd4bfSEdgar E. Iglesias      * map anything beyond the number of pages granted to us.
1789ecdd4bfSEdgar E. Iglesias      */
1799ecdd4bfSEdgar E. Iglesias     mapcache_grants = xen_map_cache_init_single(f, opaque,
1809ecdd4bfSEdgar E. Iglesias                                                 XC_PAGE_SHIFT,
1819ecdd4bfSEdgar E. Iglesias                                                 max_mcache_size);
1829ecdd4bfSEdgar E. Iglesias 
183e7218dd5SVikram Garhwal     setrlimit(RLIMIT_AS, &rlimit_as);
184e7218dd5SVikram Garhwal }
185e7218dd5SVikram Garhwal 
xen_remap_bucket(MapCache * mc,MapCacheEntry * entry,void * vaddr,hwaddr size,hwaddr address_index,bool dummy,bool grant,bool is_write,ram_addr_t ram_offset)1869b1f33faSEdgar E. Iglesias static void xen_remap_bucket(MapCache *mc,
1879b1f33faSEdgar E. Iglesias                              MapCacheEntry *entry,
188e7218dd5SVikram Garhwal                              void *vaddr,
189e7218dd5SVikram Garhwal                              hwaddr size,
190e7218dd5SVikram Garhwal                              hwaddr address_index,
19149a72029SEdgar E. Iglesias                              bool dummy,
1929ecdd4bfSEdgar E. Iglesias                              bool grant,
1939ecdd4bfSEdgar E. Iglesias                              bool is_write,
19449a72029SEdgar E. Iglesias                              ram_addr_t ram_offset)
195e7218dd5SVikram Garhwal {
196e7218dd5SVikram Garhwal     uint8_t *vaddr_base;
1979ecdd4bfSEdgar E. Iglesias     g_autofree uint32_t *refs = NULL;
1989ecdd4bfSEdgar E. Iglesias     g_autofree xen_pfn_t *pfns = NULL;
1999ecdd4bfSEdgar E. Iglesias     g_autofree int *err;
200e7218dd5SVikram Garhwal     unsigned int i;
201e7218dd5SVikram Garhwal     hwaddr nb_pfn = size >> XC_PAGE_SHIFT;
202e7218dd5SVikram Garhwal 
203e7218dd5SVikram Garhwal     trace_xen_remap_bucket(address_index);
204e7218dd5SVikram Garhwal 
2059ecdd4bfSEdgar E. Iglesias     if (grant) {
2069ecdd4bfSEdgar E. Iglesias         refs = g_new0(uint32_t, nb_pfn);
2079ecdd4bfSEdgar E. Iglesias     } else {
208e7218dd5SVikram Garhwal         pfns = g_new0(xen_pfn_t, nb_pfn);
2099ecdd4bfSEdgar E. Iglesias     }
210e7218dd5SVikram Garhwal     err = g_new0(int, nb_pfn);
211e7218dd5SVikram Garhwal 
212e7218dd5SVikram Garhwal     if (entry->vaddr_base != NULL) {
213e7218dd5SVikram Garhwal         if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) {
214e7218dd5SVikram Garhwal             ram_block_notify_remove(entry->vaddr_base, entry->size,
215e7218dd5SVikram Garhwal                                     entry->size);
216e7218dd5SVikram Garhwal         }
217e7218dd5SVikram Garhwal 
218e7218dd5SVikram Garhwal         /*
219e7218dd5SVikram Garhwal          * If an entry is being replaced by another mapping and we're using
220e7218dd5SVikram Garhwal          * MAP_FIXED flag for it - there is possibility of a race for vaddr
221e7218dd5SVikram Garhwal          * address with another thread doing an mmap call itself
222e7218dd5SVikram Garhwal          * (see man 2 mmap). To avoid that we skip explicit unmapping here
223e7218dd5SVikram Garhwal          * and allow the kernel to destroy the previous mappings by replacing
224e7218dd5SVikram Garhwal          * them in mmap call later.
225e7218dd5SVikram Garhwal          *
226e7218dd5SVikram Garhwal          * Non-identical replacements are not allowed therefore.
227e7218dd5SVikram Garhwal          */
228e7218dd5SVikram Garhwal         assert(!vaddr || (entry->vaddr_base == vaddr && entry->size == size));
229e7218dd5SVikram Garhwal 
230e7218dd5SVikram Garhwal         if (!vaddr && munmap(entry->vaddr_base, entry->size) != 0) {
231e7218dd5SVikram Garhwal             perror("unmap fails");
232e7218dd5SVikram Garhwal             exit(-1);
233e7218dd5SVikram Garhwal         }
234e7218dd5SVikram Garhwal     }
235e7218dd5SVikram Garhwal     g_free(entry->valid_mapping);
236e7218dd5SVikram Garhwal     entry->valid_mapping = NULL;
237e7218dd5SVikram Garhwal 
2389ecdd4bfSEdgar E. Iglesias     if (grant) {
2399ecdd4bfSEdgar E. Iglesias         hwaddr grant_base = address_index - (ram_offset >> XC_PAGE_SHIFT);
2409ecdd4bfSEdgar E. Iglesias 
2419ecdd4bfSEdgar E. Iglesias         for (i = 0; i < nb_pfn; i++) {
2429ecdd4bfSEdgar E. Iglesias             refs[i] = grant_base + i;
2439ecdd4bfSEdgar E. Iglesias         }
2449ecdd4bfSEdgar E. Iglesias     } else {
245e7218dd5SVikram Garhwal         for (i = 0; i < nb_pfn; i++) {
246b771b026SEdgar E. Iglesias             pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i;
247e7218dd5SVikram Garhwal         }
2489ecdd4bfSEdgar E. Iglesias     }
249e7218dd5SVikram Garhwal 
2509ecdd4bfSEdgar E. Iglesias     entry->flags &= ~XEN_MAPCACHE_ENTRY_GRANT;
2519ecdd4bfSEdgar E. Iglesias 
2529ecdd4bfSEdgar E. Iglesias     if (!dummy) {
2539ecdd4bfSEdgar E. Iglesias         if (grant) {
2549ecdd4bfSEdgar E. Iglesias             int prot = PROT_READ;
2559ecdd4bfSEdgar E. Iglesias 
2569ecdd4bfSEdgar E. Iglesias             if (is_write) {
2579ecdd4bfSEdgar E. Iglesias                 prot |= PROT_WRITE;
2589ecdd4bfSEdgar E. Iglesias             }
2599ecdd4bfSEdgar E. Iglesias 
2609ecdd4bfSEdgar E. Iglesias             entry->flags |= XEN_MAPCACHE_ENTRY_GRANT;
2619ecdd4bfSEdgar E. Iglesias             assert(vaddr == NULL);
2629ecdd4bfSEdgar E. Iglesias             vaddr_base = xengnttab_map_domain_grant_refs(xen_region_gnttabdev,
2639ecdd4bfSEdgar E. Iglesias                                                          nb_pfn,
2649ecdd4bfSEdgar E. Iglesias                                                          xen_domid, refs,
2659ecdd4bfSEdgar E. Iglesias                                                          prot);
2669ecdd4bfSEdgar E. Iglesias         } else {
267e7218dd5SVikram Garhwal             /*
268e7218dd5SVikram Garhwal              * If the caller has requested the mapping at a specific address use
269e7218dd5SVikram Garhwal              * MAP_FIXED to make sure it's honored.
2709ecdd4bfSEdgar E. Iglesias              *
2719ecdd4bfSEdgar E. Iglesias              * We don't yet support upgrading mappings from RO to RW, to handle
2729ecdd4bfSEdgar E. Iglesias              * models using ordinary address_space_rw(), foreign mappings ignore
2739ecdd4bfSEdgar E. Iglesias              * is_write and are always mapped RW.
274e7218dd5SVikram Garhwal              */
275e7218dd5SVikram Garhwal             vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr,
276e7218dd5SVikram Garhwal                                                PROT_READ | PROT_WRITE,
277e7218dd5SVikram Garhwal                                                vaddr ? MAP_FIXED : 0,
278e7218dd5SVikram Garhwal                                                nb_pfn, pfns, err);
2799ecdd4bfSEdgar E. Iglesias         }
280e7218dd5SVikram Garhwal         if (vaddr_base == NULL) {
2819ecdd4bfSEdgar E. Iglesias             perror(grant ? "xengnttab_map_domain_grant_refs"
2829ecdd4bfSEdgar E. Iglesias                            : "xenforeignmemory_map2");
283e7218dd5SVikram Garhwal             exit(-1);
284e7218dd5SVikram Garhwal         }
285e7218dd5SVikram Garhwal     } else {
286e7218dd5SVikram Garhwal         /*
287e7218dd5SVikram Garhwal          * We create dummy mappings where we are unable to create a foreign
288e7218dd5SVikram Garhwal          * mapping immediately due to certain circumstances (i.e. on resume now)
289e7218dd5SVikram Garhwal          */
290e7218dd5SVikram Garhwal         vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE,
291e7218dd5SVikram Garhwal                           MAP_ANON | MAP_SHARED | (vaddr ? MAP_FIXED : 0),
292e7218dd5SVikram Garhwal                           -1, 0);
293e7218dd5SVikram Garhwal         if (vaddr_base == MAP_FAILED) {
294e7218dd5SVikram Garhwal             perror("mmap");
295e7218dd5SVikram Garhwal             exit(-1);
296e7218dd5SVikram Garhwal         }
297e7218dd5SVikram Garhwal     }
298e7218dd5SVikram Garhwal 
299e7218dd5SVikram Garhwal     if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) {
300e7218dd5SVikram Garhwal         ram_block_notify_add(vaddr_base, size, size);
301e7218dd5SVikram Garhwal     }
302e7218dd5SVikram Garhwal 
303e7218dd5SVikram Garhwal     entry->vaddr_base = vaddr_base;
304e7218dd5SVikram Garhwal     entry->paddr_index = address_index;
305e7218dd5SVikram Garhwal     entry->size = size;
306e7218dd5SVikram Garhwal     entry->valid_mapping = g_new0(unsigned long,
307e7218dd5SVikram Garhwal                                   BITS_TO_LONGS(size >> XC_PAGE_SHIFT));
308e7218dd5SVikram Garhwal 
309e7218dd5SVikram Garhwal     if (dummy) {
310e7218dd5SVikram Garhwal         entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY;
311e7218dd5SVikram Garhwal     } else {
312e7218dd5SVikram Garhwal         entry->flags &= ~(XEN_MAPCACHE_ENTRY_DUMMY);
313e7218dd5SVikram Garhwal     }
314e7218dd5SVikram Garhwal 
315e7218dd5SVikram Garhwal     bitmap_zero(entry->valid_mapping, nb_pfn);
316e7218dd5SVikram Garhwal     for (i = 0; i < nb_pfn; i++) {
317e7218dd5SVikram Garhwal         if (!err[i]) {
318e7218dd5SVikram Garhwal             bitmap_set(entry->valid_mapping, i, 1);
319e7218dd5SVikram Garhwal         }
320e7218dd5SVikram Garhwal     }
321e7218dd5SVikram Garhwal }
322e7218dd5SVikram Garhwal 
xen_map_cache_unlocked(MapCache * mc,hwaddr phys_addr,hwaddr size,ram_addr_t ram_offset,uint8_t lock,bool dma,bool grant,bool is_write)323eda3a8cdSEdgar E. Iglesias static uint8_t *xen_map_cache_unlocked(MapCache *mc,
324eda3a8cdSEdgar E. Iglesias                                        hwaddr phys_addr, hwaddr size,
32549a72029SEdgar E. Iglesias                                        ram_addr_t ram_offset,
3269ecdd4bfSEdgar E. Iglesias                                        uint8_t lock, bool dma,
3279ecdd4bfSEdgar E. Iglesias                                        bool grant, bool is_write)
328e7218dd5SVikram Garhwal {
329e7218dd5SVikram Garhwal     MapCacheEntry *entry, *pentry = NULL,
330e7218dd5SVikram Garhwal                   *free_entry = NULL, *free_pentry = NULL;
331e7218dd5SVikram Garhwal     hwaddr address_index;
332e7218dd5SVikram Garhwal     hwaddr address_offset;
333e7218dd5SVikram Garhwal     hwaddr cache_size = size;
334e7218dd5SVikram Garhwal     hwaddr test_bit_size;
335e7218dd5SVikram Garhwal     bool translated G_GNUC_UNUSED = false;
336e7218dd5SVikram Garhwal     bool dummy = false;
337e7218dd5SVikram Garhwal 
338e7218dd5SVikram Garhwal tryagain:
339b771b026SEdgar E. Iglesias     address_index  = phys_addr >> mc->bucket_shift;
340b771b026SEdgar E. Iglesias     address_offset = phys_addr & (mc->bucket_size - 1);
341e7218dd5SVikram Garhwal 
342e7218dd5SVikram Garhwal     trace_xen_map_cache(phys_addr);
343e7218dd5SVikram Garhwal 
344e7218dd5SVikram Garhwal     /* test_bit_size is always a multiple of XC_PAGE_SIZE */
345e7218dd5SVikram Garhwal     if (size) {
346e7218dd5SVikram Garhwal         test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1));
347e7218dd5SVikram Garhwal 
348e7218dd5SVikram Garhwal         if (test_bit_size % XC_PAGE_SIZE) {
349e7218dd5SVikram Garhwal             test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE);
350e7218dd5SVikram Garhwal         }
351e7218dd5SVikram Garhwal     } else {
352e7218dd5SVikram Garhwal         test_bit_size = XC_PAGE_SIZE;
353e7218dd5SVikram Garhwal     }
354e7218dd5SVikram Garhwal 
355eda3a8cdSEdgar E. Iglesias     if (mc->last_entry != NULL &&
356eda3a8cdSEdgar E. Iglesias         mc->last_entry->paddr_index == address_index &&
357e7218dd5SVikram Garhwal         !lock && !size &&
358e7218dd5SVikram Garhwal         test_bits(address_offset >> XC_PAGE_SHIFT,
359e7218dd5SVikram Garhwal                   test_bit_size >> XC_PAGE_SHIFT,
360eda3a8cdSEdgar E. Iglesias                   mc->last_entry->valid_mapping)) {
3610402b968SManos Pitsidianakis         trace_xen_map_cache_return(
362eda3a8cdSEdgar E. Iglesias             mc->last_entry->vaddr_base + address_offset
3630402b968SManos Pitsidianakis         );
364eda3a8cdSEdgar E. Iglesias         return mc->last_entry->vaddr_base + address_offset;
365e7218dd5SVikram Garhwal     }
366e7218dd5SVikram Garhwal 
367b771b026SEdgar E. Iglesias     /* size is always a multiple of mc->bucket_size */
368e7218dd5SVikram Garhwal     if (size) {
369e7218dd5SVikram Garhwal         cache_size = size + address_offset;
370b771b026SEdgar E. Iglesias         if (cache_size % mc->bucket_size) {
371b771b026SEdgar E. Iglesias             cache_size += mc->bucket_size - (cache_size % mc->bucket_size);
372e7218dd5SVikram Garhwal         }
373e7218dd5SVikram Garhwal     } else {
374b771b026SEdgar E. Iglesias         cache_size = mc->bucket_size;
375e7218dd5SVikram Garhwal     }
376e7218dd5SVikram Garhwal 
377eda3a8cdSEdgar E. Iglesias     entry = &mc->entry[address_index % mc->nr_buckets];
378e7218dd5SVikram Garhwal 
379e7218dd5SVikram Garhwal     while (entry && (lock || entry->lock) && entry->vaddr_base &&
380e7218dd5SVikram Garhwal             (entry->paddr_index != address_index || entry->size != cache_size ||
381e7218dd5SVikram Garhwal              !test_bits(address_offset >> XC_PAGE_SHIFT,
382e7218dd5SVikram Garhwal                  test_bit_size >> XC_PAGE_SHIFT,
383e7218dd5SVikram Garhwal                  entry->valid_mapping))) {
384e7218dd5SVikram Garhwal         if (!free_entry && !entry->lock) {
385e7218dd5SVikram Garhwal             free_entry = entry;
386e7218dd5SVikram Garhwal             free_pentry = pentry;
387e7218dd5SVikram Garhwal         }
388e7218dd5SVikram Garhwal         pentry = entry;
389e7218dd5SVikram Garhwal         entry = entry->next;
390e7218dd5SVikram Garhwal     }
391e7218dd5SVikram Garhwal     if (!entry && free_entry) {
392e7218dd5SVikram Garhwal         entry = free_entry;
393e7218dd5SVikram Garhwal         pentry = free_pentry;
394e7218dd5SVikram Garhwal     }
395e7218dd5SVikram Garhwal     if (!entry) {
396e7218dd5SVikram Garhwal         entry = g_new0(MapCacheEntry, 1);
397e7218dd5SVikram Garhwal         pentry->next = entry;
39849a72029SEdgar E. Iglesias         xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy,
3999ecdd4bfSEdgar E. Iglesias                          grant, is_write, ram_offset);
400e7218dd5SVikram Garhwal     } else if (!entry->lock) {
401e7218dd5SVikram Garhwal         if (!entry->vaddr_base || entry->paddr_index != address_index ||
402e7218dd5SVikram Garhwal                 entry->size != cache_size ||
403e7218dd5SVikram Garhwal                 !test_bits(address_offset >> XC_PAGE_SHIFT,
404e7218dd5SVikram Garhwal                     test_bit_size >> XC_PAGE_SHIFT,
405e7218dd5SVikram Garhwal                     entry->valid_mapping)) {
40649a72029SEdgar E. Iglesias             xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy,
4079ecdd4bfSEdgar E. Iglesias                              grant, is_write, ram_offset);
408e7218dd5SVikram Garhwal         }
409e7218dd5SVikram Garhwal     }
410e7218dd5SVikram Garhwal 
411e7218dd5SVikram Garhwal     if(!test_bits(address_offset >> XC_PAGE_SHIFT,
412e7218dd5SVikram Garhwal                 test_bit_size >> XC_PAGE_SHIFT,
413e7218dd5SVikram Garhwal                 entry->valid_mapping)) {
414eda3a8cdSEdgar E. Iglesias         mc->last_entry = NULL;
415e7218dd5SVikram Garhwal #ifdef XEN_COMPAT_PHYSMAP
416eda3a8cdSEdgar E. Iglesias         if (!translated && mc->phys_offset_to_gaddr) {
417eda3a8cdSEdgar E. Iglesias             phys_addr = mc->phys_offset_to_gaddr(phys_addr, size);
418e7218dd5SVikram Garhwal             translated = true;
419e7218dd5SVikram Garhwal             goto tryagain;
420e7218dd5SVikram Garhwal         }
421e7218dd5SVikram Garhwal #endif
422e7218dd5SVikram Garhwal         if (!dummy && runstate_check(RUN_STATE_INMIGRATE)) {
423e7218dd5SVikram Garhwal             dummy = true;
424e7218dd5SVikram Garhwal             goto tryagain;
425e7218dd5SVikram Garhwal         }
426e7218dd5SVikram Garhwal         trace_xen_map_cache_return(NULL);
427e7218dd5SVikram Garhwal         return NULL;
428e7218dd5SVikram Garhwal     }
429e7218dd5SVikram Garhwal 
430eda3a8cdSEdgar E. Iglesias     mc->last_entry = entry;
431e7218dd5SVikram Garhwal     if (lock) {
432e7218dd5SVikram Garhwal         MapCacheRev *reventry = g_new0(MapCacheRev, 1);
433e7218dd5SVikram Garhwal         entry->lock++;
434e7218dd5SVikram Garhwal         if (entry->lock == 0) {
435d0e16850SManos Pitsidianakis             error_report("mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p",
436e7218dd5SVikram Garhwal                          entry->paddr_index, entry->vaddr_base);
437e7218dd5SVikram Garhwal             abort();
438e7218dd5SVikram Garhwal         }
439e7218dd5SVikram Garhwal         reventry->dma = dma;
440eda3a8cdSEdgar E. Iglesias         reventry->vaddr_req = mc->last_entry->vaddr_base + address_offset;
441eda3a8cdSEdgar E. Iglesias         reventry->paddr_index = mc->last_entry->paddr_index;
442e7218dd5SVikram Garhwal         reventry->size = entry->size;
443eda3a8cdSEdgar E. Iglesias         QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next);
444e7218dd5SVikram Garhwal     }
445e7218dd5SVikram Garhwal 
4460402b968SManos Pitsidianakis     trace_xen_map_cache_return(
447eda3a8cdSEdgar E. Iglesias         mc->last_entry->vaddr_base + address_offset
4480402b968SManos Pitsidianakis     );
449eda3a8cdSEdgar E. Iglesias     return mc->last_entry->vaddr_base + address_offset;
450e7218dd5SVikram Garhwal }
451e7218dd5SVikram Garhwal 
xen_map_cache(MemoryRegion * mr,hwaddr phys_addr,hwaddr size,ram_addr_t ram_addr_offset,uint8_t lock,bool dma,bool is_write)4525a5585f4SEdgar E. Iglesias uint8_t *xen_map_cache(MemoryRegion *mr,
4535a5585f4SEdgar E. Iglesias                        hwaddr phys_addr, hwaddr size,
45449a72029SEdgar E. Iglesias                        ram_addr_t ram_addr_offset,
4555a5585f4SEdgar E. Iglesias                        uint8_t lock, bool dma,
4565a5585f4SEdgar E. Iglesias                        bool is_write)
457e7218dd5SVikram Garhwal {
4589ecdd4bfSEdgar E. Iglesias     bool grant = xen_mr_is_grants(mr);
4599ecdd4bfSEdgar E. Iglesias     MapCache *mc = grant ? mapcache_grants : mapcache;
460e7218dd5SVikram Garhwal     uint8_t *p;
461e7218dd5SVikram Garhwal 
4629ecdd4bfSEdgar E. Iglesias     if (grant && !lock) {
4639ecdd4bfSEdgar E. Iglesias         /*
4649ecdd4bfSEdgar E. Iglesias          * Grants are only supported via address_space_map(). Anything
4659ecdd4bfSEdgar E. Iglesias          * else is considered a user/guest error.
4669ecdd4bfSEdgar E. Iglesias          *
4679ecdd4bfSEdgar E. Iglesias          * QEMU generally doesn't expect these mappings to ever fail, so
4689ecdd4bfSEdgar E. Iglesias          * if this happens we report an error message and abort().
4699ecdd4bfSEdgar E. Iglesias          */
4709ecdd4bfSEdgar E. Iglesias         error_report("Tried to access a grant reference without mapping it.");
4719ecdd4bfSEdgar E. Iglesias         abort();
4729ecdd4bfSEdgar E. Iglesias     }
4739ecdd4bfSEdgar E. Iglesias 
4749ecdd4bfSEdgar E. Iglesias     mapcache_lock(mc);
4759ecdd4bfSEdgar E. Iglesias     p = xen_map_cache_unlocked(mc, phys_addr, size, ram_addr_offset,
4769ecdd4bfSEdgar E. Iglesias                                lock, dma, grant, is_write);
4779ecdd4bfSEdgar E. Iglesias     mapcache_unlock(mc);
478e7218dd5SVikram Garhwal     return p;
479e7218dd5SVikram Garhwal }
480e7218dd5SVikram Garhwal 
xen_ram_addr_from_mapcache_single(MapCache * mc,void * ptr)4819b005553SEdgar E. Iglesias static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr)
482e7218dd5SVikram Garhwal {
483e7218dd5SVikram Garhwal     MapCacheEntry *entry = NULL;
484e7218dd5SVikram Garhwal     MapCacheRev *reventry;
485e7218dd5SVikram Garhwal     hwaddr paddr_index;
486e7218dd5SVikram Garhwal     hwaddr size;
487e7218dd5SVikram Garhwal     ram_addr_t raddr;
488e7218dd5SVikram Garhwal     int found = 0;
489e7218dd5SVikram Garhwal 
4909b005553SEdgar E. Iglesias     mapcache_lock(mc);
4919b005553SEdgar E. Iglesias     QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
492e7218dd5SVikram Garhwal         if (reventry->vaddr_req == ptr) {
493e7218dd5SVikram Garhwal             paddr_index = reventry->paddr_index;
494e7218dd5SVikram Garhwal             size = reventry->size;
495e7218dd5SVikram Garhwal             found = 1;
496e7218dd5SVikram Garhwal             break;
497e7218dd5SVikram Garhwal         }
498e7218dd5SVikram Garhwal     }
499e7218dd5SVikram Garhwal     if (!found) {
5000402b968SManos Pitsidianakis         trace_xen_ram_addr_from_mapcache_not_found(ptr);
5019b005553SEdgar E. Iglesias         mapcache_unlock(mc);
502337265dbSJuergen Gross         return RAM_ADDR_INVALID;
503e7218dd5SVikram Garhwal     }
504e7218dd5SVikram Garhwal 
5059b005553SEdgar E. Iglesias     entry = &mc->entry[paddr_index % mc->nr_buckets];
506e7218dd5SVikram Garhwal     while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
507e7218dd5SVikram Garhwal         entry = entry->next;
508e7218dd5SVikram Garhwal     }
509e7218dd5SVikram Garhwal     if (!entry) {
5100402b968SManos Pitsidianakis         trace_xen_ram_addr_from_mapcache_not_in_cache(ptr);
511337265dbSJuergen Gross         raddr = RAM_ADDR_INVALID;
512e7218dd5SVikram Garhwal     } else {
513b771b026SEdgar E. Iglesias         raddr = (reventry->paddr_index << mc->bucket_shift) +
514e7218dd5SVikram Garhwal              ((unsigned long) ptr - (unsigned long) entry->vaddr_base);
515e7218dd5SVikram Garhwal     }
5169b005553SEdgar E. Iglesias     mapcache_unlock(mc);
517e7218dd5SVikram Garhwal     return raddr;
518e7218dd5SVikram Garhwal }
519e7218dd5SVikram Garhwal 
xen_ram_addr_from_mapcache(void * ptr)5209b005553SEdgar E. Iglesias ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
5219b005553SEdgar E. Iglesias {
5229ecdd4bfSEdgar E. Iglesias     ram_addr_t addr;
5239ecdd4bfSEdgar E. Iglesias 
5249ecdd4bfSEdgar E. Iglesias     addr = xen_ram_addr_from_mapcache_single(mapcache, ptr);
5259ecdd4bfSEdgar E. Iglesias     if (addr == RAM_ADDR_INVALID) {
5269ecdd4bfSEdgar E. Iglesias         addr = xen_ram_addr_from_mapcache_single(mapcache_grants, ptr);
5279ecdd4bfSEdgar E. Iglesias     }
5289ecdd4bfSEdgar E. Iglesias 
5299ecdd4bfSEdgar E. Iglesias     return addr;
5309b005553SEdgar E. Iglesias }
5319b005553SEdgar E. Iglesias 
xen_invalidate_map_cache_entry_unlocked(MapCache * mc,uint8_t * buffer)53287b5a05aSEdgar E. Iglesias static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc,
53387b5a05aSEdgar E. Iglesias                                                     uint8_t *buffer)
534e7218dd5SVikram Garhwal {
535e7218dd5SVikram Garhwal     MapCacheEntry *entry = NULL, *pentry = NULL;
536e7218dd5SVikram Garhwal     MapCacheRev *reventry;
537e7218dd5SVikram Garhwal     hwaddr paddr_index;
538e7218dd5SVikram Garhwal     hwaddr size;
539e7218dd5SVikram Garhwal     int found = 0;
5409ecdd4bfSEdgar E. Iglesias     int rc;
541e7218dd5SVikram Garhwal 
54287b5a05aSEdgar E. Iglesias     QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
543e7218dd5SVikram Garhwal         if (reventry->vaddr_req == buffer) {
544e7218dd5SVikram Garhwal             paddr_index = reventry->paddr_index;
545e7218dd5SVikram Garhwal             size = reventry->size;
546e7218dd5SVikram Garhwal             found = 1;
547e7218dd5SVikram Garhwal             break;
548e7218dd5SVikram Garhwal         }
549e7218dd5SVikram Garhwal     }
550e7218dd5SVikram Garhwal     if (!found) {
5510402b968SManos Pitsidianakis         trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer);
55287b5a05aSEdgar E. Iglesias         QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
5530402b968SManos Pitsidianakis             trace_xen_invalidate_map_cache_entry_unlocked_found(
5540402b968SManos Pitsidianakis                 reventry->paddr_index,
5550402b968SManos Pitsidianakis                 reventry->vaddr_req
5560402b968SManos Pitsidianakis             );
557e7218dd5SVikram Garhwal         }
558e7218dd5SVikram Garhwal         return;
559e7218dd5SVikram Garhwal     }
56087b5a05aSEdgar E. Iglesias     QTAILQ_REMOVE(&mc->locked_entries, reventry, next);
561e7218dd5SVikram Garhwal     g_free(reventry);
562e7218dd5SVikram Garhwal 
56387b5a05aSEdgar E. Iglesias     if (mc->last_entry != NULL &&
56487b5a05aSEdgar E. Iglesias         mc->last_entry->paddr_index == paddr_index) {
56587b5a05aSEdgar E. Iglesias         mc->last_entry = NULL;
566e7218dd5SVikram Garhwal     }
567e7218dd5SVikram Garhwal 
56887b5a05aSEdgar E. Iglesias     entry = &mc->entry[paddr_index % mc->nr_buckets];
569e7218dd5SVikram Garhwal     while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
570e7218dd5SVikram Garhwal         pentry = entry;
571e7218dd5SVikram Garhwal         entry = entry->next;
572e7218dd5SVikram Garhwal     }
573e7218dd5SVikram Garhwal     if (!entry) {
5740402b968SManos Pitsidianakis         trace_xen_invalidate_map_cache_entry_unlocked_miss(buffer);
575e7218dd5SVikram Garhwal         return;
576e7218dd5SVikram Garhwal     }
577e7218dd5SVikram Garhwal     entry->lock--;
578123acd81SEdgar E. Iglesias     if (entry->lock > 0) {
579e7218dd5SVikram Garhwal         return;
580e7218dd5SVikram Garhwal     }
581e7218dd5SVikram Garhwal 
582e7218dd5SVikram Garhwal     ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size);
5839ecdd4bfSEdgar E. Iglesias     if (entry->flags & XEN_MAPCACHE_ENTRY_GRANT) {
5849ecdd4bfSEdgar E. Iglesias         rc = xengnttab_unmap(xen_region_gnttabdev, entry->vaddr_base,
5859ecdd4bfSEdgar E. Iglesias                              entry->size >> mc->bucket_shift);
5869ecdd4bfSEdgar E. Iglesias     } else {
5879ecdd4bfSEdgar E. Iglesias         rc = munmap(entry->vaddr_base, entry->size);
5889ecdd4bfSEdgar E. Iglesias     }
5899ecdd4bfSEdgar E. Iglesias 
5909ecdd4bfSEdgar E. Iglesias     if (rc) {
591e7218dd5SVikram Garhwal         perror("unmap fails");
592e7218dd5SVikram Garhwal         exit(-1);
593e7218dd5SVikram Garhwal     }
594123acd81SEdgar E. Iglesias 
595e7218dd5SVikram Garhwal     g_free(entry->valid_mapping);
596123acd81SEdgar E. Iglesias     if (pentry) {
597123acd81SEdgar E. Iglesias         pentry->next = entry->next;
598e7218dd5SVikram Garhwal         g_free(entry);
599123acd81SEdgar E. Iglesias     } else {
600*872cb9ccSEdgar E. Iglesias         /*
601*872cb9ccSEdgar E. Iglesias          * Invalidate mapping but keep entry->next pointing to the rest
602*872cb9ccSEdgar E. Iglesias          * of the list.
603*872cb9ccSEdgar E. Iglesias          *
604*872cb9ccSEdgar E. Iglesias          * Note that lock is already zero here, otherwise we don't unmap.
605*872cb9ccSEdgar E. Iglesias          */
606*872cb9ccSEdgar E. Iglesias         entry->paddr_index = 0;
607*872cb9ccSEdgar E. Iglesias         entry->vaddr_base = NULL;
608*872cb9ccSEdgar E. Iglesias         entry->valid_mapping = NULL;
609*872cb9ccSEdgar E. Iglesias         entry->flags = 0;
610*872cb9ccSEdgar E. Iglesias         entry->size = 0;
611123acd81SEdgar E. Iglesias     }
612e7218dd5SVikram Garhwal }
613e7218dd5SVikram Garhwal 
6149253d830SPeng Fan typedef struct XenMapCacheData {
6159253d830SPeng Fan     Coroutine *co;
6169253d830SPeng Fan     uint8_t *buffer;
6179253d830SPeng Fan } XenMapCacheData;
6189253d830SPeng Fan 
xen_invalidate_map_cache_entry_single(MapCache * mc,uint8_t * buffer)6199ecdd4bfSEdgar E. Iglesias static void xen_invalidate_map_cache_entry_single(MapCache *mc, uint8_t *buffer)
6209ecdd4bfSEdgar E. Iglesias {
6219ecdd4bfSEdgar E. Iglesias     mapcache_lock(mc);
6229ecdd4bfSEdgar E. Iglesias     xen_invalidate_map_cache_entry_unlocked(mc, buffer);
6239ecdd4bfSEdgar E. Iglesias     mapcache_unlock(mc);
6249ecdd4bfSEdgar E. Iglesias }
6259ecdd4bfSEdgar E. Iglesias 
xen_invalidate_map_cache_entry_all(uint8_t * buffer)6269ecdd4bfSEdgar E. Iglesias static void xen_invalidate_map_cache_entry_all(uint8_t *buffer)
6279ecdd4bfSEdgar E. Iglesias {
6289ecdd4bfSEdgar E. Iglesias     xen_invalidate_map_cache_entry_single(mapcache, buffer);
6299ecdd4bfSEdgar E. Iglesias     xen_invalidate_map_cache_entry_single(mapcache_grants, buffer);
6309ecdd4bfSEdgar E. Iglesias }
6319ecdd4bfSEdgar E. Iglesias 
xen_invalidate_map_cache_entry_bh(void * opaque)6329253d830SPeng Fan static void xen_invalidate_map_cache_entry_bh(void *opaque)
633e7218dd5SVikram Garhwal {
6349253d830SPeng Fan     XenMapCacheData *data = opaque;
6359253d830SPeng Fan 
6369ecdd4bfSEdgar E. Iglesias     xen_invalidate_map_cache_entry_all(data->buffer);
6379253d830SPeng Fan     aio_co_wake(data->co);
6389253d830SPeng Fan }
6399253d830SPeng Fan 
xen_invalidate_map_cache_entry(uint8_t * buffer)6409253d830SPeng Fan void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer)
6419253d830SPeng Fan {
6429253d830SPeng Fan     if (qemu_in_coroutine()) {
6439253d830SPeng Fan         XenMapCacheData data = {
6449253d830SPeng Fan             .co = qemu_coroutine_self(),
6459253d830SPeng Fan             .buffer = buffer,
6469253d830SPeng Fan         };
6479253d830SPeng Fan         aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
6489253d830SPeng Fan                                 xen_invalidate_map_cache_entry_bh, &data);
6499253d830SPeng Fan         qemu_coroutine_yield();
6509253d830SPeng Fan     } else {
6519ecdd4bfSEdgar E. Iglesias         xen_invalidate_map_cache_entry_all(buffer);
652e7218dd5SVikram Garhwal     }
6539253d830SPeng Fan }
654e7218dd5SVikram Garhwal 
xen_invalidate_map_cache_single(MapCache * mc)655946b4c9bSEdgar E. Iglesias static void xen_invalidate_map_cache_single(MapCache *mc)
656e7218dd5SVikram Garhwal {
657e7218dd5SVikram Garhwal     unsigned long i;
658e7218dd5SVikram Garhwal     MapCacheRev *reventry;
659e7218dd5SVikram Garhwal 
660946b4c9bSEdgar E. Iglesias     mapcache_lock(mc);
661e7218dd5SVikram Garhwal 
662946b4c9bSEdgar E. Iglesias     QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
663e7218dd5SVikram Garhwal         if (!reventry->dma) {
664e7218dd5SVikram Garhwal             continue;
665e7218dd5SVikram Garhwal         }
6660402b968SManos Pitsidianakis         trace_xen_invalidate_map_cache(reventry->paddr_index,
6670402b968SManos Pitsidianakis                                        reventry->vaddr_req);
668e7218dd5SVikram Garhwal     }
669e7218dd5SVikram Garhwal 
670946b4c9bSEdgar E. Iglesias     for (i = 0; i < mc->nr_buckets; i++) {
671946b4c9bSEdgar E. Iglesias         MapCacheEntry *entry = &mc->entry[i];
672e7218dd5SVikram Garhwal 
673e7218dd5SVikram Garhwal         if (entry->vaddr_base == NULL) {
674e7218dd5SVikram Garhwal             continue;
675e7218dd5SVikram Garhwal         }
676e7218dd5SVikram Garhwal         if (entry->lock > 0) {
677e7218dd5SVikram Garhwal             continue;
678e7218dd5SVikram Garhwal         }
679e7218dd5SVikram Garhwal 
680e7218dd5SVikram Garhwal         if (munmap(entry->vaddr_base, entry->size) != 0) {
681e7218dd5SVikram Garhwal             perror("unmap fails");
682e7218dd5SVikram Garhwal             exit(-1);
683e7218dd5SVikram Garhwal         }
684e7218dd5SVikram Garhwal 
685e7218dd5SVikram Garhwal         entry->paddr_index = 0;
686e7218dd5SVikram Garhwal         entry->vaddr_base = NULL;
687e7218dd5SVikram Garhwal         entry->size = 0;
688e7218dd5SVikram Garhwal         g_free(entry->valid_mapping);
689e7218dd5SVikram Garhwal         entry->valid_mapping = NULL;
690e7218dd5SVikram Garhwal     }
691e7218dd5SVikram Garhwal 
692946b4c9bSEdgar E. Iglesias     mc->last_entry = NULL;
693e7218dd5SVikram Garhwal 
694946b4c9bSEdgar E. Iglesias     mapcache_unlock(mc);
695946b4c9bSEdgar E. Iglesias }
696946b4c9bSEdgar E. Iglesias 
xen_invalidate_map_cache(void)697946b4c9bSEdgar E. Iglesias void xen_invalidate_map_cache(void)
698946b4c9bSEdgar E. Iglesias {
699946b4c9bSEdgar E. Iglesias     /* Flush pending AIO before destroying the mapcache */
700946b4c9bSEdgar E. Iglesias     bdrv_drain_all();
701946b4c9bSEdgar E. Iglesias 
702946b4c9bSEdgar E. Iglesias     xen_invalidate_map_cache_single(mapcache);
7039ecdd4bfSEdgar E. Iglesias     xen_invalidate_map_cache_single(mapcache_grants);
704e7218dd5SVikram Garhwal }
705e7218dd5SVikram Garhwal 
xen_replace_cache_entry_unlocked(MapCache * mc,hwaddr old_phys_addr,hwaddr new_phys_addr,hwaddr size)7068be27f50SEdgar E. Iglesias static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc,
7078be27f50SEdgar E. Iglesias                                                  hwaddr old_phys_addr,
708e7218dd5SVikram Garhwal                                                  hwaddr new_phys_addr,
709e7218dd5SVikram Garhwal                                                  hwaddr size)
710e7218dd5SVikram Garhwal {
711e7218dd5SVikram Garhwal     MapCacheEntry *entry;
712e7218dd5SVikram Garhwal     hwaddr address_index, address_offset;
713e7218dd5SVikram Garhwal     hwaddr test_bit_size, cache_size = size;
714e7218dd5SVikram Garhwal 
715b771b026SEdgar E. Iglesias     address_index  = old_phys_addr >> mc->bucket_shift;
716b771b026SEdgar E. Iglesias     address_offset = old_phys_addr & (mc->bucket_size - 1);
717e7218dd5SVikram Garhwal 
718e7218dd5SVikram Garhwal     assert(size);
719e7218dd5SVikram Garhwal     /* test_bit_size is always a multiple of XC_PAGE_SIZE */
720e7218dd5SVikram Garhwal     test_bit_size = size + (old_phys_addr & (XC_PAGE_SIZE - 1));
721e7218dd5SVikram Garhwal     if (test_bit_size % XC_PAGE_SIZE) {
722e7218dd5SVikram Garhwal         test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE);
723e7218dd5SVikram Garhwal     }
724e7218dd5SVikram Garhwal     cache_size = size + address_offset;
725b771b026SEdgar E. Iglesias     if (cache_size % mc->bucket_size) {
726b771b026SEdgar E. Iglesias         cache_size += mc->bucket_size - (cache_size % mc->bucket_size);
727e7218dd5SVikram Garhwal     }
728e7218dd5SVikram Garhwal 
7298be27f50SEdgar E. Iglesias     entry = &mc->entry[address_index % mc->nr_buckets];
730e7218dd5SVikram Garhwal     while (entry && !(entry->paddr_index == address_index &&
731e7218dd5SVikram Garhwal                       entry->size == cache_size)) {
732e7218dd5SVikram Garhwal         entry = entry->next;
733e7218dd5SVikram Garhwal     }
734e7218dd5SVikram Garhwal     if (!entry) {
7350402b968SManos Pitsidianakis         trace_xen_replace_cache_entry_unlocked(old_phys_addr);
736e7218dd5SVikram Garhwal         return NULL;
737e7218dd5SVikram Garhwal     }
738e7218dd5SVikram Garhwal 
7399ecdd4bfSEdgar E. Iglesias     assert((entry->flags & XEN_MAPCACHE_ENTRY_GRANT) == 0);
7409ecdd4bfSEdgar E. Iglesias 
741b771b026SEdgar E. Iglesias     address_index  = new_phys_addr >> mc->bucket_shift;
742b771b026SEdgar E. Iglesias     address_offset = new_phys_addr & (mc->bucket_size - 1);
743e7218dd5SVikram Garhwal 
7440402b968SManos Pitsidianakis     trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr);
745e7218dd5SVikram Garhwal 
7468be27f50SEdgar E. Iglesias     xen_remap_bucket(mc, entry, entry->vaddr_base,
7479ecdd4bfSEdgar E. Iglesias                      cache_size, address_index, false,
7489ecdd4bfSEdgar E. Iglesias                      false, false, old_phys_addr);
749e7218dd5SVikram Garhwal     if (!test_bits(address_offset >> XC_PAGE_SHIFT,
750e7218dd5SVikram Garhwal                 test_bit_size >> XC_PAGE_SHIFT,
751e7218dd5SVikram Garhwal                 entry->valid_mapping)) {
7520402b968SManos Pitsidianakis         trace_xen_replace_cache_entry_unlocked_could_not_update_entry(
7530402b968SManos Pitsidianakis             old_phys_addr
7540402b968SManos Pitsidianakis         );
755e7218dd5SVikram Garhwal         return NULL;
756e7218dd5SVikram Garhwal     }
757e7218dd5SVikram Garhwal 
758e7218dd5SVikram Garhwal     return entry->vaddr_base + address_offset;
759e7218dd5SVikram Garhwal }
760e7218dd5SVikram Garhwal 
xen_replace_cache_entry(hwaddr old_phys_addr,hwaddr new_phys_addr,hwaddr size)761e7218dd5SVikram Garhwal uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr,
762e7218dd5SVikram Garhwal                                  hwaddr new_phys_addr,
763e7218dd5SVikram Garhwal                                  hwaddr size)
764e7218dd5SVikram Garhwal {
765e7218dd5SVikram Garhwal     uint8_t *p;
766e7218dd5SVikram Garhwal 
767efb0c6caSEdgar E. Iglesias     mapcache_lock(mapcache);
7688be27f50SEdgar E. Iglesias     p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr,
7698be27f50SEdgar E. Iglesias                                          new_phys_addr, size);
770efb0c6caSEdgar E. Iglesias     mapcache_unlock(mapcache);
771e7218dd5SVikram Garhwal     return p;
772e7218dd5SVikram Garhwal }
773