]>
Commit | Line | Data |
---|---|---|
a77dabc3 | 1 | /* |
45e077d7 CF |
2 | * QEMU TCG vCPU common functionality |
3 | * | |
4 | * Functionality common to all TCG vCPU variants: mttcg, rr and icount. | |
a77dabc3 CF |
5 | * |
6 | * Copyright (c) 2003-2008 Fabrice Bellard | |
7 | * Copyright (c) 2014 Red Hat Inc. | |
8 | * | |
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
10 | * of this software and associated documentation files (the "Software"), to deal | |
11 | * in the Software without restriction, including without limitation the rights | |
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
13 | * copies of the Software, and to permit persons to whom the Software is | |
14 | * furnished to do so, subject to the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice shall be included in | |
17 | * all copies or substantial portions of the Software. | |
18 | * | |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
22 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
25 | * THE SOFTWARE. | |
26 | */ | |
27 | ||
28 | #include "qemu/osdep.h" | |
a77dabc3 CF |
29 | #include "sysemu/tcg.h" |
30 | #include "sysemu/replay.h" | |
03ff4f8d | 31 | #include "sysemu/cpu-timers.h" |
a77dabc3 CF |
32 | #include "qemu/main-loop.h" |
33 | #include "qemu/guest-random.h" | |
533206f0 | 34 | #include "qemu/timer.h" |
a77dabc3 | 35 | #include "exec/exec-all.h" |
ae7467b1 AB |
36 | #include "exec/hwaddr.h" |
37 | #include "exec/gdbstub.h" | |
a77dabc3 | 38 | |
b86f59c7 CF |
39 | #include "tcg-accel-ops.h" |
40 | #include "tcg-accel-ops-mttcg.h" | |
41 | #include "tcg-accel-ops-rr.h" | |
42 | #include "tcg-accel-ops-icount.h" | |
a77dabc3 | 43 | |
45e077d7 | 44 | /* common functionality among all TCG variants */ |
a77dabc3 | 45 | |
6cc9d67c RH |
46 | void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) |
47 | { | |
a371975e PMD |
48 | uint32_t cflags; |
49 | ||
50 | /* | |
51 | * Include the cluster number in the hash we use to look up TBs. | |
52 | * This is important because a TB that is valid for one cluster at | |
53 | * a given physical address and set of CPU flags is not necessarily | |
54 | * valid for another: | |
55 | * the two clusters may have different views of physical memory, or | |
56 | * may have different CPU features (eg FPU present or absent). | |
57 | */ | |
58 | cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; | |
59 | ||
6cc9d67c RH |
60 | cflags |= parallel ? CF_PARALLEL : 0; |
61 | cflags |= icount_enabled() ? CF_USE_ICOUNT : 0; | |
62 | cpu->tcg_cflags = cflags; | |
63 | } | |
64 | ||
9e2658d6 | 65 | void tcg_cpus_destroy(CPUState *cpu) |
a77dabc3 | 66 | { |
45e077d7 | 67 | cpu_thread_signal_destroyed(cpu); |
a77dabc3 CF |
68 | } |
69 | ||
9e2658d6 | 70 | int tcg_cpus_exec(CPUState *cpu) |
a77dabc3 | 71 | { |
45e077d7 CF |
72 | int ret; |
73 | #ifdef CONFIG_PROFILER | |
74 | int64_t ti; | |
75 | #endif | |
76 | assert(tcg_enabled()); | |
77 | #ifdef CONFIG_PROFILER | |
78 | ti = profile_getclock(); | |
79 | #endif | |
80 | cpu_exec_start(cpu); | |
81 | ret = cpu_exec(cpu); | |
82 | cpu_exec_end(cpu); | |
83 | #ifdef CONFIG_PROFILER | |
84 | qatomic_set(&tcg_ctx->prof.cpu_exec_time, | |
85 | tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); | |
86 | #endif | |
87 | return ret; | |
a77dabc3 CF |
88 | } |
89 | ||
bb4776be | 90 | /* mask must never be zero, except for A20 change call */ |
b86f59c7 | 91 | void tcg_handle_interrupt(CPUState *cpu, int mask) |
bb4776be | 92 | { |
bb4776be CF |
93 | g_assert(qemu_mutex_iothread_locked()); |
94 | ||
bb4776be CF |
95 | cpu->interrupt_request |= mask; |
96 | ||
97 | /* | |
98 | * If called from iothread context, wake the target cpu in | |
99 | * case its halted. | |
100 | */ | |
101 | if (!qemu_cpu_is_self(cpu)) { | |
102 | qemu_cpu_kick(cpu); | |
103 | } else { | |
104 | qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); | |
bb4776be CF |
105 | } |
106 | } | |
b86f59c7 | 107 | |
a48e7d9e AB |
108 | static bool tcg_supports_guest_debug(void) |
109 | { | |
110 | return true; | |
111 | } | |
112 | ||
ae7467b1 AB |
113 | /* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */ |
114 | static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) | |
115 | { | |
116 | static const int xlat[] = { | |
117 | [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, | |
118 | [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ, | |
119 | [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, | |
120 | }; | |
121 | ||
122 | CPUClass *cc = CPU_GET_CLASS(cpu); | |
123 | int cputype = xlat[gdbtype]; | |
124 | ||
125 | if (cc->gdb_stop_before_watchpoint) { | |
126 | cputype |= BP_STOP_BEFORE_ACCESS; | |
127 | } | |
128 | return cputype; | |
129 | } | |
130 | ||
55b5b8e9 | 131 | static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) |
ae7467b1 AB |
132 | { |
133 | CPUState *cpu; | |
134 | int err = 0; | |
135 | ||
136 | switch (type) { | |
137 | case GDB_BREAKPOINT_SW: | |
138 | case GDB_BREAKPOINT_HW: | |
139 | CPU_FOREACH(cpu) { | |
140 | err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); | |
141 | if (err) { | |
142 | break; | |
143 | } | |
144 | } | |
145 | return err; | |
146 | case GDB_WATCHPOINT_WRITE: | |
147 | case GDB_WATCHPOINT_READ: | |
148 | case GDB_WATCHPOINT_ACCESS: | |
149 | CPU_FOREACH(cpu) { | |
150 | err = cpu_watchpoint_insert(cpu, addr, len, | |
151 | xlat_gdb_type(cpu, type), NULL); | |
152 | if (err) { | |
153 | break; | |
154 | } | |
155 | } | |
156 | return err; | |
157 | default: | |
158 | return -ENOSYS; | |
159 | } | |
160 | } | |
161 | ||
55b5b8e9 | 162 | static int tcg_remove_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) |
ae7467b1 AB |
163 | { |
164 | CPUState *cpu; | |
165 | int err = 0; | |
166 | ||
167 | switch (type) { | |
168 | case GDB_BREAKPOINT_SW: | |
169 | case GDB_BREAKPOINT_HW: | |
170 | CPU_FOREACH(cpu) { | |
171 | err = cpu_breakpoint_remove(cpu, addr, BP_GDB); | |
172 | if (err) { | |
173 | break; | |
174 | } | |
175 | } | |
176 | return err; | |
177 | case GDB_WATCHPOINT_WRITE: | |
178 | case GDB_WATCHPOINT_READ: | |
179 | case GDB_WATCHPOINT_ACCESS: | |
180 | CPU_FOREACH(cpu) { | |
181 | err = cpu_watchpoint_remove(cpu, addr, len, | |
182 | xlat_gdb_type(cpu, type)); | |
183 | if (err) { | |
184 | break; | |
185 | } | |
186 | } | |
187 | return err; | |
188 | default: | |
189 | return -ENOSYS; | |
190 | } | |
191 | } | |
192 | ||
193 | static inline void tcg_remove_all_breakpoints(CPUState *cpu) | |
194 | { | |
195 | cpu_breakpoint_remove_all(cpu, BP_GDB); | |
196 | cpu_watchpoint_remove_all(cpu, BP_GDB); | |
197 | } | |
198 | ||
b86f59c7 CF |
199 | static void tcg_accel_ops_init(AccelOpsClass *ops) |
200 | { | |
201 | if (qemu_tcg_mttcg_enabled()) { | |
202 | ops->create_vcpu_thread = mttcg_start_vcpu_thread; | |
203 | ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; | |
204 | ops->handle_interrupt = tcg_handle_interrupt; | |
b86f59c7 CF |
205 | } else { |
206 | ops->create_vcpu_thread = rr_start_vcpu_thread; | |
207 | ops->kick_vcpu_thread = rr_kick_vcpu_thread; | |
18b8c47f PMD |
208 | |
209 | if (icount_enabled()) { | |
210 | ops->handle_interrupt = icount_handle_interrupt; | |
211 | ops->get_virtual_clock = icount_get; | |
212 | ops->get_elapsed_ticks = icount_get; | |
213 | } else { | |
214 | ops->handle_interrupt = tcg_handle_interrupt; | |
215 | } | |
b86f59c7 | 216 | } |
ae7467b1 | 217 | |
a48e7d9e | 218 | ops->supports_guest_debug = tcg_supports_guest_debug; |
ae7467b1 AB |
219 | ops->insert_breakpoint = tcg_insert_breakpoint; |
220 | ops->remove_breakpoint = tcg_remove_breakpoint; | |
221 | ops->remove_all_breakpoints = tcg_remove_all_breakpoints; | |
b86f59c7 CF |
222 | } |
223 | ||
224 | static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) | |
225 | { | |
226 | AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); | |
227 | ||
228 | ops->ops_init = tcg_accel_ops_init; | |
229 | } | |
230 | ||
231 | static const TypeInfo tcg_accel_ops_type = { | |
232 | .name = ACCEL_OPS_NAME("tcg"), | |
233 | ||
234 | .parent = TYPE_ACCEL_OPS, | |
235 | .class_init = tcg_accel_ops_class_init, | |
236 | .abstract = true, | |
237 | }; | |
9e5d3b69 | 238 | module_obj(ACCEL_OPS_NAME("tcg")); |
b86f59c7 CF |
239 | |
240 | static void tcg_accel_ops_register_types(void) | |
241 | { | |
242 | type_register_static(&tcg_accel_ops_type); | |
243 | } | |
244 | type_init(tcg_accel_ops_register_types); |