]> git.proxmox.com Git - qemu.git/blob - hw/virtio/dataplane/hostmem.c
tcg/aarch64: Implement tlb lookup fast path
[qemu.git] / hw / virtio / dataplane / hostmem.c
1 /*
2 * Thread-safe guest to host memory mapping
3 *
4 * Copyright 2012 Red Hat, Inc. and/or its affiliates
5 *
6 * Authors:
7 * Stefan Hajnoczi <stefanha@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 *
12 */
13
14 #include "exec/address-spaces.h"
15 #include "hw/virtio/dataplane/hostmem.h"
16
17 static int hostmem_lookup_cmp(const void *phys_, const void *region_)
18 {
19 hwaddr phys = *(const hwaddr *)phys_;
20 const HostMemRegion *region = region_;
21
22 if (phys < region->guest_addr) {
23 return -1;
24 } else if (phys >= region->guest_addr + region->size) {
25 return 1;
26 } else {
27 return 0;
28 }
29 }
30
31 /**
32 * Map guest physical address to host pointer
33 */
34 void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write)
35 {
36 HostMemRegion *region;
37 void *host_addr = NULL;
38 hwaddr offset_within_region;
39
40 qemu_mutex_lock(&hostmem->current_regions_lock);
41 region = bsearch(&phys, hostmem->current_regions,
42 hostmem->num_current_regions,
43 sizeof(hostmem->current_regions[0]),
44 hostmem_lookup_cmp);
45 if (!region) {
46 goto out;
47 }
48 if (is_write && region->readonly) {
49 goto out;
50 }
51 offset_within_region = phys - region->guest_addr;
52 if (len <= region->size - offset_within_region) {
53 host_addr = region->host_addr + offset_within_region;
54 }
55 out:
56 qemu_mutex_unlock(&hostmem->current_regions_lock);
57
58 return host_addr;
59 }
60
61 /**
62 * Install new regions list
63 */
64 static void hostmem_listener_commit(MemoryListener *listener)
65 {
66 HostMem *hostmem = container_of(listener, HostMem, listener);
67
68 qemu_mutex_lock(&hostmem->current_regions_lock);
69 g_free(hostmem->current_regions);
70 hostmem->current_regions = hostmem->new_regions;
71 hostmem->num_current_regions = hostmem->num_new_regions;
72 qemu_mutex_unlock(&hostmem->current_regions_lock);
73
74 /* Reset new regions list */
75 hostmem->new_regions = NULL;
76 hostmem->num_new_regions = 0;
77 }
78
79 /**
80 * Add a MemoryRegionSection to the new regions list
81 */
82 static void hostmem_append_new_region(HostMem *hostmem,
83 MemoryRegionSection *section)
84 {
85 void *ram_ptr = memory_region_get_ram_ptr(section->mr);
86 size_t num = hostmem->num_new_regions;
87 size_t new_size = (num + 1) * sizeof(hostmem->new_regions[0]);
88
89 hostmem->new_regions = g_realloc(hostmem->new_regions, new_size);
90 hostmem->new_regions[num] = (HostMemRegion){
91 .host_addr = ram_ptr + section->offset_within_region,
92 .guest_addr = section->offset_within_address_space,
93 .size = int128_get64(section->size),
94 .readonly = section->readonly,
95 };
96 hostmem->num_new_regions++;
97 }
98
99 static void hostmem_listener_append_region(MemoryListener *listener,
100 MemoryRegionSection *section)
101 {
102 HostMem *hostmem = container_of(listener, HostMem, listener);
103
104 /* Ignore non-RAM regions, we may not be able to map them */
105 if (!memory_region_is_ram(section->mr)) {
106 return;
107 }
108
109 /* Ignore regions with dirty logging, we cannot mark them dirty */
110 if (memory_region_is_logging(section->mr)) {
111 return;
112 }
113
114 hostmem_append_new_region(hostmem, section);
115 }
116
117 /* We don't implement most MemoryListener callbacks, use these nop stubs */
118 static void hostmem_listener_dummy(MemoryListener *listener)
119 {
120 }
121
122 static void hostmem_listener_section_dummy(MemoryListener *listener,
123 MemoryRegionSection *section)
124 {
125 }
126
127 static void hostmem_listener_eventfd_dummy(MemoryListener *listener,
128 MemoryRegionSection *section,
129 bool match_data, uint64_t data,
130 EventNotifier *e)
131 {
132 }
133
134 static void hostmem_listener_coalesced_mmio_dummy(MemoryListener *listener,
135 MemoryRegionSection *section,
136 hwaddr addr, hwaddr len)
137 {
138 }
139
140 void hostmem_init(HostMem *hostmem)
141 {
142 memset(hostmem, 0, sizeof(*hostmem));
143
144 qemu_mutex_init(&hostmem->current_regions_lock);
145
146 hostmem->listener = (MemoryListener){
147 .begin = hostmem_listener_dummy,
148 .commit = hostmem_listener_commit,
149 .region_add = hostmem_listener_append_region,
150 .region_del = hostmem_listener_section_dummy,
151 .region_nop = hostmem_listener_append_region,
152 .log_start = hostmem_listener_section_dummy,
153 .log_stop = hostmem_listener_section_dummy,
154 .log_sync = hostmem_listener_section_dummy,
155 .log_global_start = hostmem_listener_dummy,
156 .log_global_stop = hostmem_listener_dummy,
157 .eventfd_add = hostmem_listener_eventfd_dummy,
158 .eventfd_del = hostmem_listener_eventfd_dummy,
159 .coalesced_mmio_add = hostmem_listener_coalesced_mmio_dummy,
160 .coalesced_mmio_del = hostmem_listener_coalesced_mmio_dummy,
161 .priority = 10,
162 };
163
164 memory_listener_register(&hostmem->listener, &address_space_memory);
165 if (hostmem->num_new_regions > 0) {
166 hostmem_listener_commit(&hostmem->listener);
167 }
168 }
169
170 void hostmem_finalize(HostMem *hostmem)
171 {
172 memory_listener_unregister(&hostmem->listener);
173 g_free(hostmem->new_regions);
174 g_free(hostmem->current_regions);
175 qemu_mutex_destroy(&hostmem->current_regions_lock);
176 }