1
The following changes since commit 6587b0c1331d427b0939c37e763842550ed581db:
1
Changes from v1:
2
* Patch 10 is new, avoiding an overflow in probe_guest_base,
3
visible with aarch64 host, --static --disable-pie, exposed
4
by the placement of the host binary in the address space.
2
5
3
Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2021-10-15' into staging (2021-10-15 14:16:28 -0700)
6
r~
4
7
5
are available in the Git repository at:
8
Emilio Cota (2):
9
util: import GTree as QTree
10
tcg: use QTree instead of GTree
6
11
7
https://gitlab.com/rth7680/qemu.git tags/pull-tcg-20211016
12
Richard Henderson (10):
13
linux-user: Diagnose misaligned -R size
14
accel/tcg: Pass last not end to page_set_flags
15
accel/tcg: Pass last not end to page_reset_target_data
16
accel/tcg: Pass last not end to PAGE_FOR_EACH_TB
17
accel/tcg: Pass last not end to page_collection_lock
18
accel/tcg: Pass last not end to tb_invalidate_phys_page_range__locked
19
accel/tcg: Pass last not end to tb_invalidate_phys_range
20
linux-user: Pass last not end to probe_guest_base
21
include/exec: Change reserved_va semantics to last byte
22
linux-user/arm: Take more care allocating commpage
8
23
9
for you to fetch changes up to 995b87dedc78b0467f5f18bbc3546072ba97516a:
24
configure | 15 +
25
meson.build | 4 +
26
include/exec/cpu-all.h | 15 +-
27
include/exec/exec-all.h | 2 +-
28
include/qemu/qtree.h | 201 +++++
29
linux-user/arm/target_cpu.h | 2 +-
30
accel/tcg/tb-maint.c | 112 +--
31
accel/tcg/translate-all.c | 2 +-
32
accel/tcg/user-exec.c | 25 +-
33
bsd-user/main.c | 10 +-
34
bsd-user/mmap.c | 10 +-
35
linux-user/elfload.c | 72 +-
36
linux-user/flatload.c | 2 +-
37
linux-user/main.c | 31 +-
38
linux-user/mmap.c | 22 +-
39
linux-user/syscall.c | 4 +-
40
softmmu/physmem.c | 2 +-
41
tcg/region.c | 19 +-
42
tests/bench/qtree-bench.c | 286 +++++++
43
tests/unit/test-qtree.c | 333 +++++++++
44
util/qtree.c | 1390 +++++++++++++++++++++++++++++++++++
45
tests/bench/meson.build | 4 +
46
tests/unit/meson.build | 1 +
47
util/meson.build | 1 +
48
24 files changed, 2415 insertions(+), 150 deletions(-)
49
create mode 100644 include/qemu/qtree.h
50
create mode 100644 tests/bench/qtree-bench.c
51
create mode 100644 tests/unit/test-qtree.c
52
create mode 100644 util/qtree.c
10
53
11
Revert "cpu: Move cpu_common_props to hw/core/cpu.c" (2021-10-15 16:39:15 -0700)
54
--
12
55
2.34.1
13
----------------------------------------------------------------
14
Move gdb singlestep to generic code
15
Fix cpu_common_props
16
17
----------------------------------------------------------------
18
Richard Henderson (24):
19
accel/tcg: Handle gdb singlestep in cpu_tb_exec
20
target/alpha: Drop checks for singlestep_enabled
21
target/avr: Drop checks for singlestep_enabled
22
target/cris: Drop checks for singlestep_enabled
23
target/hexagon: Drop checks for singlestep_enabled
24
target/arm: Drop checks for singlestep_enabled
25
target/hppa: Drop checks for singlestep_enabled
26
target/i386: Check CF_NO_GOTO_TB for dc->jmp_opt
27
target/i386: Drop check for singlestep_enabled
28
target/m68k: Drop checks for singlestep_enabled
29
target/microblaze: Check CF_NO_GOTO_TB for DISAS_JUMP
30
target/microblaze: Drop checks for singlestep_enabled
31
target/mips: Fix single stepping
32
target/mips: Drop exit checks for singlestep_enabled
33
target/openrisc: Drop checks for singlestep_enabled
34
target/ppc: Drop exit checks for singlestep_enabled
35
target/riscv: Remove dead code after exception
36
target/riscv: Remove exit_tb and lookup_and_goto_ptr
37
target/rx: Drop checks for singlestep_enabled
38
target/s390x: Drop check for singlestep_enabled
39
target/sh4: Drop check for singlestep_enabled
40
target/tricore: Drop check for singlestep_enabled
41
target/xtensa: Drop check for singlestep_enabled
42
Revert "cpu: Move cpu_common_props to hw/core/cpu.c"
43
44
include/hw/core/cpu.h | 1 +
45
target/i386/helper.h | 1 -
46
target/rx/helper.h | 1 -
47
target/sh4/helper.h | 1 -
48
target/tricore/helper.h | 1 -
49
accel/tcg/cpu-exec.c | 11 ++++
50
cpu.c | 21 ++++++++
51
hw/core/cpu-common.c | 17 +-----
52
target/alpha/translate.c | 13 ++---
53
target/arm/translate-a64.c | 10 +---
54
target/arm/translate.c | 36 +++----------
55
target/avr/translate.c | 19 ++-----
56
target/cris/translate.c | 16 ------
57
target/hexagon/translate.c | 12 +----
58
target/hppa/translate.c | 17 ++----
59
target/i386/tcg/misc_helper.c | 8 ---
60
target/i386/tcg/translate.c | 9 ++--
61
target/m68k/translate.c | 44 ++++-----------
62
target/microblaze/translate.c | 18 ++-----
63
target/mips/tcg/translate.c | 75 ++++++++++++--------------
64
target/openrisc/translate.c | 18 ++-----
65
target/ppc/translate.c | 38 +++----------
66
target/riscv/translate.c | 27 +---------
67
target/rx/op_helper.c | 8 ---
68
target/rx/translate.c | 12 +----
69
target/s390x/tcg/translate.c | 8 +--
70
target/sh4/op_helper.c | 5 --
71
target/sh4/translate.c | 14 ++---
72
target/tricore/op_helper.c | 7 ---
73
target/tricore/translate.c | 14 +----
74
target/xtensa/translate.c | 25 +++------
75
target/riscv/insn_trans/trans_privileged.c.inc | 10 ++--
76
target/riscv/insn_trans/trans_rvi.c.inc | 8 ++-
77
target/riscv/insn_trans/trans_rvv.c.inc | 2 +-
78
34 files changed, 141 insertions(+), 386 deletions(-)
79
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
From: Emilio Cota <cota@braap.org>
2
2
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
3
The only reason to add this implementation is to control the memory allocator
4
used. Some users (e.g. TCG) cannot work reliably in multi-threaded
5
environments (e.g. forking in user-mode) with GTree's allocator, GSlice.
6
See https://gitlab.com/qemu-project/qemu/-/issues/285 for details.
7
8
Importing GTree is a temporary workaround until GTree migrates away
9
from GSlice.
10
11
This implementation is identical to that in glib v2.75.0, except that
12
we don't import recent additions to the API nor deprecated API calls,
13
none of which are used in QEMU.
14
15
I've imported tests from glib and added a benchmark just to
16
make sure that performance is similar. Note: it cannot be identical
17
because (1) we are not using GSlice, (2) we use different compilation flags
18
(e.g. -fPIC) and (3) we're linking statically.
19
20
$ cat /proc/cpuinfo| grep 'model name' | head -1
21
model name : AMD Ryzen 7 PRO 5850U with Radeon Graphics
22
$ echo '0' | sudo tee /sys/devices/system/cpu/cpufreq/boost
23
$ tests/bench/qtree-bench
24
25
Tree Op 32 1024 4096 131072 1048576
26
------------------------------------------------------------------------------------------------
27
GTree Lookup 83.23 43.08 25.31 19.40 16.22
28
QTree Lookup 113.42 (1.36x) 53.83 (1.25x) 28.38 (1.12x) 17.64 (0.91x) 13.04 (0.80x)
29
GTree Insert 44.23 29.37 25.83 19.49 17.03
30
QTree Insert 46.87 (1.06x) 25.62 (0.87x) 24.29 (0.94x) 16.83 (0.86x) 12.97 (0.76x)
31
GTree Remove 53.27 35.15 31.43 24.64 16.70
32
QTree Remove 57.32 (1.08x) 41.76 (1.19x) 38.37 (1.22x) 29.30 (1.19x) 15.07 (0.90x)
33
GTree RemoveAll 135.44 127.52 126.72 120.11 64.34
34
QTree RemoveAll 127.15 (0.94x) 110.37 (0.87x) 107.97 (0.85x) 97.13 (0.81x) 55.10 (0.86x)
35
GTree Traverse 277.71 276.09 272.78 246.72 98.47
36
QTree Traverse 370.33 (1.33x) 411.97 (1.49x) 400.23 (1.47x) 262.82 (1.07x) 78.52 (0.80x)
37
------------------------------------------------------------------------------------------------
38
39
As a sanity check, the same benchmark when Glib's version
40
is >= $glib_dropped_gslice_version (i.e. QTree == GTree):
41
42
Tree Op 32 1024 4096 131072 1048576
43
------------------------------------------------------------------------------------------------
44
GTree Lookup 82.72 43.09 24.18 19.73 16.09
45
QTree Lookup 81.82 (0.99x) 43.10 (1.00x) 24.20 (1.00x) 19.76 (1.00x) 16.26 (1.01x)
46
GTree Insert 45.07 29.62 26.34 19.90 17.18
47
QTree Insert 45.72 (1.01x) 29.60 (1.00x) 26.38 (1.00x) 19.71 (0.99x) 17.20 (1.00x)
48
GTree Remove 54.48 35.36 31.77 24.97 16.95
49
QTree Remove 54.46 (1.00x) 35.32 (1.00x) 31.77 (1.00x) 24.91 (1.00x) 17.15 (1.01x)
50
GTree RemoveAll 140.68 127.36 125.43 121.45 68.20
51
QTree RemoveAll 140.65 (1.00x) 127.64 (1.00x) 125.01 (1.00x) 121.73 (1.00x) 67.06 (0.98x)
52
GTree Traverse 278.68 276.05 266.75 251.65 104.93
53
QTree Traverse 278.31 (1.00x) 275.78 (1.00x) 266.42 (1.00x) 247.89 (0.99x) 104.58 (1.00x)
54
------------------------------------------------------------------------------------------------
55
56
Signed-off-by: Emilio Cota <cota@braap.org>
57
Message-Id: <20230205163758.416992-2-cota@braap.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
58
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
59
---
6
target/mips/tcg/translate.c | 50 +++++++++++++------------------------
60
configure | 15 +
7
1 file changed, 18 insertions(+), 32 deletions(-)
61
meson.build | 4 +
62
include/qemu/qtree.h | 201 ++++++
63
tests/bench/qtree-bench.c | 286 ++++++++
64
tests/unit/test-qtree.c | 333 +++++++++
65
util/qtree.c | 1390 +++++++++++++++++++++++++++++++++++++
66
tests/bench/meson.build | 4 +
67
tests/unit/meson.build | 1 +
68
util/meson.build | 1 +
69
9 files changed, 2235 insertions(+)
70
create mode 100644 include/qemu/qtree.h
71
create mode 100644 tests/bench/qtree-bench.c
72
create mode 100644 tests/unit/test-qtree.c
73
create mode 100644 util/qtree.c
8
74
9
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
75
diff --git a/configure b/configure
76
index XXXXXXX..XXXXXXX 100755
77
--- a/configure
78
+++ b/configure
79
@@ -XXX,XX +XXX,XX @@ safe_stack=""
80
use_containers="yes"
81
gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
82
gdb_arches=""
83
+glib_has_gslice="no"
84
85
if test -e "$source_path/.git"
86
then
87
@@ -XXX,XX +XXX,XX @@ for i in $glib_modules; do
88
fi
89
done
90
91
+# Check whether glib has gslice, which we have to avoid for correctness.
92
+# TODO: remove this check and the corresponding workaround (qtree) when
93
+# the minimum supported glib is >= $glib_dropped_gslice_version.
94
+glib_dropped_gslice_version=2.75.3
95
+for i in $glib_modules; do
96
+ if ! $pkg_config --atleast-version=$glib_dropped_gslice_version $i; then
97
+ glib_has_gslice="yes"
98
+    break
99
+ fi
100
+done
101
+
102
glib_bindir="$($pkg_config --variable=bindir glib-2.0)"
103
if test -z "$glib_bindir" ; then
104
    glib_bindir="$($pkg_config --variable=prefix glib-2.0)"/bin
105
@@ -XXX,XX +XXX,XX @@ echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
106
echo "GLIB_LIBS=$glib_libs" >> $config_host_mak
107
echo "GLIB_BINDIR=$glib_bindir" >> $config_host_mak
108
echo "GLIB_VERSION=$($pkg_config --modversion glib-2.0)" >> $config_host_mak
109
+if test "$glib_has_gslice" = "yes" ; then
110
+ echo "HAVE_GLIB_WITH_SLICE_ALLOCATOR=y" >> $config_host_mak
111
+fi
112
echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak
113
echo "EXESUF=$EXESUF" >> $config_host_mak
114
115
diff --git a/meson.build b/meson.build
10
index XXXXXXX..XXXXXXX 100644
116
index XXXXXXX..XXXXXXX 100644
11
--- a/target/mips/tcg/translate.c
117
--- a/meson.build
12
+++ b/target/mips/tcg/translate.c
118
+++ b/meson.build
13
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
119
@@ -XXX,XX +XXX,XX @@ glib = declare_dependency(compile_args: config_host['GLIB_CFLAGS'].split(),
14
tcg_gen_exit_tb(ctx->base.tb, n);
120
})
15
} else {
121
# override glib dep with the configure results (for subprojects)
16
gen_save_pc(dest);
122
meson.override_dependency('glib-2.0', glib)
17
- if (ctx->base.singlestep_enabled) {
123
+# pass down whether Glib has the slice allocator
18
- save_cpu_state(ctx, 0);
124
+if config_host.has_key('HAVE_GLIB_WITH_SLICE_ALLOCATOR')
19
- gen_helper_raise_exception_debug(cpu_env);
125
+ config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', true)
20
- } else {
126
+endif
21
- tcg_gen_lookup_and_goto_ptr();
127
22
- }
128
gio = not_found
23
+ tcg_gen_lookup_and_goto_ptr();
129
gdbus_codegen = not_found
24
}
130
diff --git a/include/qemu/qtree.h b/include/qemu/qtree.h
25
}
131
new file mode 100644
26
132
index XXXXXXX..XXXXXXX
27
@@ -XXX,XX +XXX,XX @@ static void gen_branch(DisasContext *ctx, int insn_bytes)
133
--- /dev/null
28
} else {
134
+++ b/include/qemu/qtree.h
29
tcg_gen_mov_tl(cpu_PC, btarget);
135
@@ -XXX,XX +XXX,XX @@
30
}
136
+/*
31
- if (ctx->base.singlestep_enabled) {
137
+ * GLIB - Library of useful routines for C programming
32
- save_cpu_state(ctx, 0);
138
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
33
- gen_helper_raise_exception_debug(cpu_env);
139
+ *
34
- }
140
+ * SPDX-License-Identifier: LGPL-2.1-or-later
35
tcg_gen_lookup_and_goto_ptr();
141
+ *
36
break;
142
+ * This library is free software; you can redistribute it and/or
37
default:
143
+ * modify it under the terms of the GNU Lesser General Public
38
@@ -XXX,XX +XXX,XX @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
144
+ * License as published by the Free Software Foundation; either
39
{
145
+ * version 2.1 of the License, or (at your option) any later version.
40
DisasContext *ctx = container_of(dcbase, DisasContext, base);
146
+ *
41
147
+ * This library is distributed in the hope that it will be useful,
42
- if (ctx->base.singlestep_enabled && ctx->base.is_jmp != DISAS_NORETURN) {
148
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
43
- save_cpu_state(ctx, ctx->base.is_jmp != DISAS_EXIT);
149
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44
- gen_helper_raise_exception_debug(cpu_env);
150
+ * Lesser General Public License for more details.
45
- } else {
151
+ *
46
- switch (ctx->base.is_jmp) {
152
+ * You should have received a copy of the GNU Lesser General Public
47
- case DISAS_STOP:
153
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
48
- gen_save_pc(ctx->base.pc_next);
154
+ */
49
- tcg_gen_lookup_and_goto_ptr();
155
+
50
- break;
156
+/*
51
- case DISAS_NEXT:
157
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
52
- case DISAS_TOO_MANY:
158
+ * file for a list of people on the GLib Team. See the ChangeLog
53
- save_cpu_state(ctx, 0);
159
+ * files for a list of changes. These files are distributed with
54
- gen_goto_tb(ctx, 0, ctx->base.pc_next);
160
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
55
- break;
161
+ */
56
- case DISAS_EXIT:
162
+
57
- tcg_gen_exit_tb(NULL, 0);
163
+/*
58
- break;
164
+ * QTree is a partial import of Glib's GTree. The parts excluded correspond
59
- case DISAS_NORETURN:
165
+ * to API calls either deprecated (e.g. g_tree_traverse) or recently added
60
- break;
166
+ * (e.g. g_tree_search_node, added in 2.68); neither have callers in QEMU.
61
- default:
167
+ *
62
- g_assert_not_reached();
168
+ * The reason for this import is to allow us to control the memory allocator
63
- }
169
+ * used by the tree implementation. Until Glib 2.75.3, GTree uses Glib's
64
+ switch (ctx->base.is_jmp) {
170
+ * slice allocator, which causes problems when forking in user-mode;
65
+ case DISAS_STOP:
171
+ * see https://gitlab.com/qemu-project/qemu/-/issues/285 and glib's
66
+ gen_save_pc(ctx->base.pc_next);
172
+ * "45b5a6c1e gslice: Remove slice allocator and use malloc() instead".
67
+ tcg_gen_lookup_and_goto_ptr();
173
+ *
174
+ * TODO: remove QTree when QEMU's minimum Glib version is >= 2.75.3.
175
+ */
176
+
177
+#ifndef QEMU_QTREE_H
178
+#define QEMU_QTREE_H
179
+
180
+#include "qemu/osdep.h"
181
+
182
+#ifdef HAVE_GLIB_WITH_SLICE_ALLOCATOR
183
+
184
+typedef struct _QTree QTree;
185
+
186
+typedef struct _QTreeNode QTreeNode;
187
+
188
+typedef gboolean (*QTraverseNodeFunc)(QTreeNode *node,
189
+ gpointer user_data);
190
+
191
+/*
192
+ * Balanced binary trees
193
+ */
194
+QTree *q_tree_new(GCompareFunc key_compare_func);
195
+QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func,
196
+ gpointer key_compare_data);
197
+QTree *q_tree_new_full(GCompareDataFunc key_compare_func,
198
+ gpointer key_compare_data,
199
+ GDestroyNotify key_destroy_func,
200
+ GDestroyNotify value_destroy_func);
201
+QTree *q_tree_ref(QTree *tree);
202
+void q_tree_unref(QTree *tree);
203
+void q_tree_destroy(QTree *tree);
204
+void q_tree_insert(QTree *tree,
205
+ gpointer key,
206
+ gpointer value);
207
+void q_tree_replace(QTree *tree,
208
+ gpointer key,
209
+ gpointer value);
210
+gboolean q_tree_remove(QTree *tree,
211
+ gconstpointer key);
212
+gboolean q_tree_steal(QTree *tree,
213
+ gconstpointer key);
214
+gpointer q_tree_lookup(QTree *tree,
215
+ gconstpointer key);
216
+gboolean q_tree_lookup_extended(QTree *tree,
217
+ gconstpointer lookup_key,
218
+ gpointer *orig_key,
219
+ gpointer *value);
220
+void q_tree_foreach(QTree *tree,
221
+ GTraverseFunc func,
222
+ gpointer user_data);
223
+gpointer q_tree_search(QTree *tree,
224
+ GCompareFunc search_func,
225
+ gconstpointer user_data);
226
+gint q_tree_height(QTree *tree);
227
+gint q_tree_nnodes(QTree *tree);
228
+
229
+#else /* !HAVE_GLIB_WITH_SLICE_ALLOCATOR */
230
+
231
+typedef GTree QTree;
232
+typedef GTreeNode QTreeNode;
233
+typedef GTraverseNodeFunc QTraverseNodeFunc;
234
+
235
+static inline QTree *q_tree_new(GCompareFunc key_compare_func)
236
+{
237
+ return g_tree_new(key_compare_func);
238
+}
239
+
240
+static inline QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func,
241
+ gpointer key_compare_data)
242
+{
243
+ return g_tree_new_with_data(key_compare_func, key_compare_data);
244
+}
245
+
246
+static inline QTree *q_tree_new_full(GCompareDataFunc key_compare_func,
247
+ gpointer key_compare_data,
248
+ GDestroyNotify key_destroy_func,
249
+ GDestroyNotify value_destroy_func)
250
+{
251
+ return g_tree_new_full(key_compare_func, key_compare_data,
252
+ key_destroy_func, value_destroy_func);
253
+}
254
+
255
+static inline QTree *q_tree_ref(QTree *tree)
256
+{
257
+ return g_tree_ref(tree);
258
+}
259
+
260
+static inline void q_tree_unref(QTree *tree)
261
+{
262
+ g_tree_unref(tree);
263
+}
264
+
265
+static inline void q_tree_destroy(QTree *tree)
266
+{
267
+ g_tree_destroy(tree);
268
+}
269
+
270
+static inline void q_tree_insert(QTree *tree,
271
+ gpointer key,
272
+ gpointer value)
273
+{
274
+ g_tree_insert(tree, key, value);
275
+}
276
+
277
+static inline void q_tree_replace(QTree *tree,
278
+ gpointer key,
279
+ gpointer value)
280
+{
281
+ g_tree_replace(tree, key, value);
282
+}
283
+
284
+static inline gboolean q_tree_remove(QTree *tree,
285
+ gconstpointer key)
286
+{
287
+ return g_tree_remove(tree, key);
288
+}
289
+
290
+static inline gboolean q_tree_steal(QTree *tree,
291
+ gconstpointer key)
292
+{
293
+ return g_tree_steal(tree, key);
294
+}
295
+
296
+static inline gpointer q_tree_lookup(QTree *tree,
297
+ gconstpointer key)
298
+{
299
+ return g_tree_lookup(tree, key);
300
+}
301
+
302
+static inline gboolean q_tree_lookup_extended(QTree *tree,
303
+ gconstpointer lookup_key,
304
+ gpointer *orig_key,
305
+ gpointer *value)
306
+{
307
+ return g_tree_lookup_extended(tree, lookup_key, orig_key, value);
308
+}
309
+
310
+static inline void q_tree_foreach(QTree *tree,
311
+ GTraverseFunc func,
312
+ gpointer user_data)
313
+{
314
+ return g_tree_foreach(tree, func, user_data);
315
+}
316
+
317
+static inline gpointer q_tree_search(QTree *tree,
318
+ GCompareFunc search_func,
319
+ gconstpointer user_data)
320
+{
321
+ return g_tree_search(tree, search_func, user_data);
322
+}
323
+
324
+static inline gint q_tree_height(QTree *tree)
325
+{
326
+ return g_tree_height(tree);
327
+}
328
+
329
+static inline gint q_tree_nnodes(QTree *tree)
330
+{
331
+ return g_tree_nnodes(tree);
332
+}
333
+
334
+#endif /* HAVE_GLIB_WITH_SLICE_ALLOCATOR */
335
+
336
+#endif /* QEMU_QTREE_H */
337
diff --git a/tests/bench/qtree-bench.c b/tests/bench/qtree-bench.c
338
new file mode 100644
339
index XXXXXXX..XXXXXXX
340
--- /dev/null
341
+++ b/tests/bench/qtree-bench.c
342
@@ -XXX,XX +XXX,XX @@
343
+/* SPDX-License-Identifier: GPL-2.0-or-later */
344
+#include "qemu/osdep.h"
345
+#include "qemu/qtree.h"
346
+#include "qemu/timer.h"
347
+
348
+enum tree_op {
349
+ OP_LOOKUP,
350
+ OP_INSERT,
351
+ OP_REMOVE,
352
+ OP_REMOVE_ALL,
353
+ OP_TRAVERSE,
354
+};
355
+
356
+struct benchmark {
357
+ const char * const name;
358
+ enum tree_op op;
359
+ bool fill_on_init;
360
+};
361
+
362
+enum impl_type {
363
+ IMPL_GTREE,
364
+ IMPL_QTREE,
365
+};
366
+
367
+struct tree_implementation {
368
+ const char * const name;
369
+ enum impl_type type;
370
+};
371
+
372
+static const struct benchmark benchmarks[] = {
373
+ {
374
+ .name = "Lookup",
375
+ .op = OP_LOOKUP,
376
+ .fill_on_init = true,
377
+ },
378
+ {
379
+ .name = "Insert",
380
+ .op = OP_INSERT,
381
+ .fill_on_init = false,
382
+ },
383
+ {
384
+ .name = "Remove",
385
+ .op = OP_REMOVE,
386
+ .fill_on_init = true,
387
+ },
388
+ {
389
+ .name = "RemoveAll",
390
+ .op = OP_REMOVE_ALL,
391
+ .fill_on_init = true,
392
+ },
393
+ {
394
+ .name = "Traverse",
395
+ .op = OP_TRAVERSE,
396
+ .fill_on_init = true,
397
+ },
398
+};
399
+
400
+static const struct tree_implementation impls[] = {
401
+ {
402
+ .name = "GTree",
403
+ .type = IMPL_GTREE,
404
+ },
405
+ {
406
+ .name = "QTree",
407
+ .type = IMPL_QTREE,
408
+ },
409
+};
410
+
411
+static int compare_func(const void *ap, const void *bp)
412
+{
413
+ const size_t *a = ap;
414
+ const size_t *b = bp;
415
+
416
+ return *a - *b;
417
+}
418
+
419
+static void init_empty_tree_and_keys(enum impl_type impl,
420
+ void **ret_tree, size_t **ret_keys,
421
+ size_t n_elems)
422
+{
423
+ size_t *keys = g_malloc_n(n_elems, sizeof(*keys));
424
+ for (size_t i = 0; i < n_elems; i++) {
425
+ keys[i] = i;
426
+ }
427
+
428
+ void *tree;
429
+ switch (impl) {
430
+ case IMPL_GTREE:
431
+ tree = g_tree_new(compare_func);
68
+ break;
432
+ break;
69
+ case DISAS_NEXT:
433
+ case IMPL_QTREE:
70
+ case DISAS_TOO_MANY:
434
+ tree = q_tree_new(compare_func);
71
+ save_cpu_state(ctx, 0);
72
+ gen_goto_tb(ctx, 0, ctx->base.pc_next);
73
+ break;
74
+ case DISAS_EXIT:
75
+ tcg_gen_exit_tb(NULL, 0);
76
+ break;
77
+ case DISAS_NORETURN:
78
+ break;
435
+ break;
79
+ default:
436
+ default:
80
+ g_assert_not_reached();
437
+ g_assert_not_reached();
81
}
438
+ }
82
}
439
+
83
440
+ *ret_tree = tree;
441
+ *ret_keys = keys;
442
+}
443
+
444
+static gboolean traverse_func(gpointer key, gpointer value, gpointer data)
445
+{
446
+ return FALSE;
447
+}
448
+
449
+static inline void remove_all(void *tree, enum impl_type impl)
450
+{
451
+ switch (impl) {
452
+ case IMPL_GTREE:
453
+ g_tree_destroy(tree);
454
+ break;
455
+ case IMPL_QTREE:
456
+ q_tree_destroy(tree);
457
+ break;
458
+ default:
459
+ g_assert_not_reached();
460
+ }
461
+}
462
+
463
+static int64_t run_benchmark(const struct benchmark *bench,
464
+ enum impl_type impl,
465
+ size_t n_elems)
466
+{
467
+ void *tree;
468
+ size_t *keys;
469
+
470
+ init_empty_tree_and_keys(impl, &tree, &keys, n_elems);
471
+ if (bench->fill_on_init) {
472
+ for (size_t i = 0; i < n_elems; i++) {
473
+ switch (impl) {
474
+ case IMPL_GTREE:
475
+ g_tree_insert(tree, &keys[i], &keys[i]);
476
+ break;
477
+ case IMPL_QTREE:
478
+ q_tree_insert(tree, &keys[i], &keys[i]);
479
+ break;
480
+ default:
481
+ g_assert_not_reached();
482
+ }
483
+ }
484
+ }
485
+
486
+ int64_t start_ns = get_clock();
487
+ switch (bench->op) {
488
+ case OP_LOOKUP:
489
+ for (size_t i = 0; i < n_elems; i++) {
490
+ void *value;
491
+ switch (impl) {
492
+ case IMPL_GTREE:
493
+ value = g_tree_lookup(tree, &keys[i]);
494
+ break;
495
+ case IMPL_QTREE:
496
+ value = q_tree_lookup(tree, &keys[i]);
497
+ break;
498
+ default:
499
+ g_assert_not_reached();
500
+ }
501
+ (void)value;
502
+ }
503
+ break;
504
+ case OP_INSERT:
505
+ for (size_t i = 0; i < n_elems; i++) {
506
+ switch (impl) {
507
+ case IMPL_GTREE:
508
+ g_tree_insert(tree, &keys[i], &keys[i]);
509
+ break;
510
+ case IMPL_QTREE:
511
+ q_tree_insert(tree, &keys[i], &keys[i]);
512
+ break;
513
+ default:
514
+ g_assert_not_reached();
515
+ }
516
+ }
517
+ break;
518
+ case OP_REMOVE:
519
+ for (size_t i = 0; i < n_elems; i++) {
520
+ switch (impl) {
521
+ case IMPL_GTREE:
522
+ g_tree_remove(tree, &keys[i]);
523
+ break;
524
+ case IMPL_QTREE:
525
+ q_tree_remove(tree, &keys[i]);
526
+ break;
527
+ default:
528
+ g_assert_not_reached();
529
+ }
530
+ }
531
+ break;
532
+ case OP_REMOVE_ALL:
533
+ remove_all(tree, impl);
534
+ break;
535
+ case OP_TRAVERSE:
536
+ switch (impl) {
537
+ case IMPL_GTREE:
538
+ g_tree_foreach(tree, traverse_func, NULL);
539
+ break;
540
+ case IMPL_QTREE:
541
+ q_tree_foreach(tree, traverse_func, NULL);
542
+ break;
543
+ default:
544
+ g_assert_not_reached();
545
+ }
546
+ break;
547
+ default:
548
+ g_assert_not_reached();
549
+ }
550
+ int64_t ns = get_clock() - start_ns;
551
+
552
+ if (bench->op != OP_REMOVE_ALL) {
553
+ remove_all(tree, impl);
554
+ }
555
+ g_free(keys);
556
+
557
+ return ns;
558
+}
559
+
560
+int main(int argc, char *argv[])
561
+{
562
+ size_t sizes[] = {
563
+ 32,
564
+ 1024,
565
+ 1024 * 4,
566
+ 1024 * 128,
567
+ 1024 * 1024,
568
+ };
569
+
570
+ double res[ARRAY_SIZE(benchmarks)][ARRAY_SIZE(impls)][ARRAY_SIZE(sizes)];
571
+ for (int i = 0; i < ARRAY_SIZE(sizes); i++) {
572
+ size_t size = sizes[i];
573
+ for (int j = 0; j < ARRAY_SIZE(impls); j++) {
574
+ const struct tree_implementation *impl = &impls[j];
575
+ for (int k = 0; k < ARRAY_SIZE(benchmarks); k++) {
576
+ const struct benchmark *bench = &benchmarks[k];
577
+
578
+ /* warm-up run */
579
+ run_benchmark(bench, impl->type, size);
580
+
581
+ int64_t total_ns = 0;
582
+ int64_t n_runs = 0;
583
+ while (total_ns < 2e8 || n_runs < 5) {
584
+ total_ns += run_benchmark(bench, impl->type, size);
585
+ n_runs++;
586
+ }
587
+ double ns_per_run = (double)total_ns / n_runs;
588
+
589
+ /* Throughput, in Mops/s */
590
+ res[k][j][i] = size / ns_per_run * 1e3;
591
+ }
592
+ }
593
+ }
594
+
595
+ printf("# Results' breakdown: Tree, Op and #Elements. Units: Mops/s\n");
596
+ printf("%5s %10s ", "Tree", "Op");
597
+ for (int i = 0; i < ARRAY_SIZE(sizes); i++) {
598
+ printf("%7zu ", sizes[i]);
599
+ }
600
+ printf("\n");
601
+ char separator[97];
602
+ for (int i = 0; i < ARRAY_SIZE(separator) - 1; i++) {
603
+ separator[i] = '-';
604
+ }
605
+ separator[ARRAY_SIZE(separator) - 1] = '\0';
606
+ printf("%s\n", separator);
607
+ for (int i = 0; i < ARRAY_SIZE(benchmarks); i++) {
608
+ for (int j = 0; j < ARRAY_SIZE(impls); j++) {
609
+ printf("%5s %10s ", impls[j].name, benchmarks[i].name);
610
+ for (int k = 0; k < ARRAY_SIZE(sizes); k++) {
611
+ printf("%7.2f ", res[i][j][k]);
612
+ if (j == 0) {
613
+ printf(" ");
614
+ } else {
615
+ if (res[i][0][k] != 0) {
616
+ double speedup = res[i][j][k] / res[i][0][k];
617
+ printf("(%4.2fx) ", speedup);
618
+ } else {
619
+ printf("( ) ");
620
+ }
621
+ }
622
+ }
623
+ printf("\n");
624
+ }
625
+ }
626
+ printf("%s\n", separator);
627
+ return 0;
628
+}
629
diff --git a/tests/unit/test-qtree.c b/tests/unit/test-qtree.c
630
new file mode 100644
631
index XXXXXXX..XXXXXXX
632
--- /dev/null
633
+++ b/tests/unit/test-qtree.c
634
@@ -XXX,XX +XXX,XX @@
635
+/*
636
+ * SPDX-License-Identifier: LGPL-2.1-or-later
637
+ *
638
+ * Tests for QTree.
639
+ * Original source: glib
640
+ * https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/tests/tree.c
641
+ * LGPL license.
642
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
643
+ */
644
+
645
+#include "qemu/osdep.h"
646
+#include "qemu/qtree.h"
647
+
648
+static gint my_compare(gconstpointer a, gconstpointer b)
649
+{
650
+ const char *cha = a;
651
+ const char *chb = b;
652
+
653
+ return *cha - *chb;
654
+}
655
+
656
+static gint my_compare_with_data(gconstpointer a,
657
+ gconstpointer b,
658
+ gpointer user_data)
659
+{
660
+ const char *cha = a;
661
+ const char *chb = b;
662
+
663
+ /* just check that we got the right data */
664
+ g_assert(GPOINTER_TO_INT(user_data) == 123);
665
+
666
+ return *cha - *chb;
667
+}
668
+
669
+static gint my_search(gconstpointer a, gconstpointer b)
670
+{
671
+ return my_compare(b, a);
672
+}
673
+
674
+static gpointer destroyed_key;
675
+static gpointer destroyed_value;
676
+static guint destroyed_key_count;
677
+static guint destroyed_value_count;
678
+
679
+static void my_key_destroy(gpointer key)
680
+{
681
+ destroyed_key = key;
682
+ destroyed_key_count++;
683
+}
684
+
685
+static void my_value_destroy(gpointer value)
686
+{
687
+ destroyed_value = value;
688
+ destroyed_value_count++;
689
+}
690
+
691
+static gint my_traverse(gpointer key, gpointer value, gpointer data)
692
+{
693
+ char *ch = key;
694
+
695
+ g_assert((*ch) > 0);
696
+
697
+ if (*ch == 'd') {
698
+ return TRUE;
699
+ }
700
+
701
+ return FALSE;
702
+}
703
+
704
+char chars[] =
705
+ "0123456789"
706
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
707
+ "abcdefghijklmnopqrstuvwxyz";
708
+
709
+char chars2[] =
710
+ "0123456789"
711
+ "abcdefghijklmnopqrstuvwxyz";
712
+
713
+static gint check_order(gpointer key, gpointer value, gpointer data)
714
+{
715
+ char **p = data;
716
+ char *ch = key;
717
+
718
+ g_assert(**p == *ch);
719
+
720
+ (*p)++;
721
+
722
+ return FALSE;
723
+}
724
+
725
+static void test_tree_search(void)
726
+{
727
+ gint i;
728
+ QTree *tree;
729
+ gboolean removed;
730
+ gchar c;
731
+ gchar *p, *d;
732
+
733
+ tree = q_tree_new_with_data(my_compare_with_data, GINT_TO_POINTER(123));
734
+
735
+ for (i = 0; chars[i]; i++) {
736
+ q_tree_insert(tree, &chars[i], &chars[i]);
737
+ }
738
+
739
+ q_tree_foreach(tree, my_traverse, NULL);
740
+
741
+ g_assert(q_tree_nnodes(tree) == strlen(chars));
742
+ g_assert(q_tree_height(tree) == 6);
743
+
744
+ p = chars;
745
+ q_tree_foreach(tree, check_order, &p);
746
+
747
+ for (i = 0; i < 26; i++) {
748
+ removed = q_tree_remove(tree, &chars[i + 10]);
749
+ g_assert(removed);
750
+ }
751
+
752
+ c = '\0';
753
+ removed = q_tree_remove(tree, &c);
754
+ g_assert(!removed);
755
+
756
+ q_tree_foreach(tree, my_traverse, NULL);
757
+
758
+ g_assert(q_tree_nnodes(tree) == strlen(chars2));
759
+ g_assert(q_tree_height(tree) == 6);
760
+
761
+ p = chars2;
762
+ q_tree_foreach(tree, check_order, &p);
763
+
764
+ for (i = 25; i >= 0; i--) {
765
+ q_tree_insert(tree, &chars[i + 10], &chars[i + 10]);
766
+ }
767
+
768
+ p = chars;
769
+ q_tree_foreach(tree, check_order, &p);
770
+
771
+ c = '0';
772
+ p = q_tree_lookup(tree, &c);
773
+ g_assert(p && *p == c);
774
+ g_assert(q_tree_lookup_extended(tree, &c, (gpointer *)&d, (gpointer *)&p));
775
+ g_assert(c == *d && c == *p);
776
+
777
+ c = 'A';
778
+ p = q_tree_lookup(tree, &c);
779
+ g_assert(p && *p == c);
780
+
781
+ c = 'a';
782
+ p = q_tree_lookup(tree, &c);
783
+ g_assert(p && *p == c);
784
+
785
+ c = 'z';
786
+ p = q_tree_lookup(tree, &c);
787
+ g_assert(p && *p == c);
788
+
789
+ c = '!';
790
+ p = q_tree_lookup(tree, &c);
791
+ g_assert(p == NULL);
792
+
793
+ c = '=';
794
+ p = q_tree_lookup(tree, &c);
795
+ g_assert(p == NULL);
796
+
797
+ c = '|';
798
+ p = q_tree_lookup(tree, &c);
799
+ g_assert(p == NULL);
800
+
801
+ c = '0';
802
+ p = q_tree_search(tree, my_search, &c);
803
+ g_assert(p && *p == c);
804
+
805
+ c = 'A';
806
+ p = q_tree_search(tree, my_search, &c);
807
+ g_assert(p && *p == c);
808
+
809
+ c = 'a';
810
+ p = q_tree_search(tree, my_search, &c);
811
+ g_assert(p && *p == c);
812
+
813
+ c = 'z';
814
+ p = q_tree_search(tree, my_search, &c);
815
+ g_assert(p && *p == c);
816
+
817
+ c = '!';
818
+ p = q_tree_search(tree, my_search, &c);
819
+ g_assert(p == NULL);
820
+
821
+ c = '=';
822
+ p = q_tree_search(tree, my_search, &c);
823
+ g_assert(p == NULL);
824
+
825
+ c = '|';
826
+ p = q_tree_search(tree, my_search, &c);
827
+ g_assert(p == NULL);
828
+
829
+ q_tree_destroy(tree);
830
+}
831
+
832
+static void test_tree_remove(void)
833
+{
834
+ QTree *tree;
835
+ char c, d;
836
+ gint i;
837
+ gboolean removed;
838
+
839
+ tree = q_tree_new_full((GCompareDataFunc)my_compare, NULL,
840
+ my_key_destroy,
841
+ my_value_destroy);
842
+
843
+ for (i = 0; chars[i]; i++) {
844
+ q_tree_insert(tree, &chars[i], &chars[i]);
845
+ }
846
+
847
+ c = '0';
848
+ q_tree_insert(tree, &c, &c);
849
+ g_assert(destroyed_key == &c);
850
+ g_assert(destroyed_value == &chars[0]);
851
+ destroyed_key = NULL;
852
+ destroyed_value = NULL;
853
+
854
+ d = '1';
855
+ q_tree_replace(tree, &d, &d);
856
+ g_assert(destroyed_key == &chars[1]);
857
+ g_assert(destroyed_value == &chars[1]);
858
+ destroyed_key = NULL;
859
+ destroyed_value = NULL;
860
+
861
+ c = '2';
862
+ removed = q_tree_remove(tree, &c);
863
+ g_assert(removed);
864
+ g_assert(destroyed_key == &chars[2]);
865
+ g_assert(destroyed_value == &chars[2]);
866
+ destroyed_key = NULL;
867
+ destroyed_value = NULL;
868
+
869
+ c = '3';
870
+ removed = q_tree_steal(tree, &c);
871
+ g_assert(removed);
872
+ g_assert(destroyed_key == NULL);
873
+ g_assert(destroyed_value == NULL);
874
+
875
+ const gchar *remove = "omkjigfedba";
876
+ for (i = 0; remove[i]; i++) {
877
+ removed = q_tree_remove(tree, &remove[i]);
878
+ g_assert(removed);
879
+ }
880
+
881
+ q_tree_destroy(tree);
882
+}
883
+
884
+static void test_tree_destroy(void)
885
+{
886
+ QTree *tree;
887
+ gint i;
888
+
889
+ tree = q_tree_new(my_compare);
890
+
891
+ for (i = 0; chars[i]; i++) {
892
+ q_tree_insert(tree, &chars[i], &chars[i]);
893
+ }
894
+
895
+ g_assert(q_tree_nnodes(tree) == strlen(chars));
896
+
897
+ g_test_message("nnodes: %d", q_tree_nnodes(tree));
898
+ q_tree_ref(tree);
899
+ q_tree_destroy(tree);
900
+
901
+ g_test_message("nnodes: %d", q_tree_nnodes(tree));
902
+ g_assert(q_tree_nnodes(tree) == 0);
903
+
904
+ q_tree_unref(tree);
905
+}
906
+
907
+static void test_tree_insert(void)
908
+{
909
+ QTree *tree;
910
+ gchar *p;
911
+ gint i;
912
+ gchar *scrambled;
913
+
914
+ tree = q_tree_new(my_compare);
915
+
916
+ for (i = 0; chars[i]; i++) {
917
+ q_tree_insert(tree, &chars[i], &chars[i]);
918
+ }
919
+ p = chars;
920
+ q_tree_foreach(tree, check_order, &p);
921
+
922
+ q_tree_unref(tree);
923
+ tree = q_tree_new(my_compare);
924
+
925
+ for (i = strlen(chars) - 1; i >= 0; i--) {
926
+ q_tree_insert(tree, &chars[i], &chars[i]);
927
+ }
928
+ p = chars;
929
+ q_tree_foreach(tree, check_order, &p);
930
+
931
+ q_tree_unref(tree);
932
+ tree = q_tree_new(my_compare);
933
+
934
+ scrambled = g_strdup(chars);
935
+
936
+ for (i = 0; i < 30; i++) {
937
+ gchar tmp;
938
+ gint a, b;
939
+
940
+ a = g_random_int_range(0, strlen(scrambled));
941
+ b = g_random_int_range(0, strlen(scrambled));
942
+ tmp = scrambled[a];
943
+ scrambled[a] = scrambled[b];
944
+ scrambled[b] = tmp;
945
+ }
946
+
947
+ for (i = 0; scrambled[i]; i++) {
948
+ q_tree_insert(tree, &scrambled[i], &scrambled[i]);
949
+ }
950
+ p = chars;
951
+ q_tree_foreach(tree, check_order, &p);
952
+
953
+ g_free(scrambled);
954
+ q_tree_unref(tree);
955
+}
956
+
957
+int main(int argc, char *argv[])
958
+{
959
+ g_test_init(&argc, &argv, NULL);
960
+
961
+ g_test_add_func("/qtree/search", test_tree_search);
962
+ g_test_add_func("/qtree/remove", test_tree_remove);
963
+ g_test_add_func("/qtree/destroy", test_tree_destroy);
964
+ g_test_add_func("/qtree/insert", test_tree_insert);
965
+
966
+ return g_test_run();
967
+}
968
diff --git a/util/qtree.c b/util/qtree.c
969
new file mode 100644
970
index XXXXXXX..XXXXXXX
971
--- /dev/null
972
+++ b/util/qtree.c
973
@@ -XXX,XX +XXX,XX @@
974
+/*
975
+ * GLIB - Library of useful routines for C programming
976
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
977
+ *
978
+ * SPDX-License-Identifier: LGPL-2.1-or-later
979
+ *
980
+ * This library is free software; you can redistribute it and/or
981
+ * modify it under the terms of the GNU Lesser General Public
982
+ * License as published by the Free Software Foundation; either
983
+ * version 2.1 of the License, or (at your option) any later version.
984
+ *
985
+ * This library is distributed in the hope that it will be useful,
986
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
987
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
988
+ * Lesser General Public License for more details.
989
+ *
990
+ * You should have received a copy of the GNU Lesser General Public
991
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
992
+ */
993
+
994
+/*
995
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
996
+ * file for a list of people on the GLib Team. See the ChangeLog
997
+ * files for a list of changes. These files are distributed with
998
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
999
+ */
1000
+
1001
+/*
1002
+ * MT safe
1003
+ */
1004
+
1005
+#include "qemu/osdep.h"
1006
+#include "qemu/qtree.h"
1007
+
1008
+/**
1009
+ * SECTION:trees-binary
1010
+ * @title: Balanced Binary Trees
1011
+ * @short_description: a sorted collection of key/value pairs optimized
1012
+ * for searching and traversing in order
1013
+ *
1014
+ * The #QTree structure and its associated functions provide a sorted
1015
+ * collection of key/value pairs optimized for searching and traversing
1016
+ * in order. This means that most of the operations (access, search,
1017
+ * insertion, deletion, ...) on #QTree are O(log(n)) in average and O(n)
1018
+ * in worst case for time complexity. But, note that maintaining a
1019
+ * balanced sorted #QTree of n elements is done in time O(n log(n)).
1020
+ *
1021
+ * To create a new #QTree use q_tree_new().
1022
+ *
1023
+ * To insert a key/value pair into a #QTree use q_tree_insert()
1024
+ * (O(n log(n))).
1025
+ *
1026
+ * To remove a key/value pair use q_tree_remove() (O(n log(n))).
1027
+ *
1028
+ * To look up the value corresponding to a given key, use
1029
+ * q_tree_lookup() and q_tree_lookup_extended().
1030
+ *
1031
+ * To find out the number of nodes in a #QTree, use q_tree_nnodes(). To
1032
+ * get the height of a #QTree, use q_tree_height().
1033
+ *
1034
+ * To traverse a #QTree, calling a function for each node visited in
1035
+ * the traversal, use q_tree_foreach().
1036
+ *
1037
+ * To destroy a #QTree, use q_tree_destroy().
1038
+ **/
1039
+
1040
+#define MAX_GTREE_HEIGHT 40
1041
+
1042
+/**
1043
+ * QTree:
1044
+ *
1045
+ * The QTree struct is an opaque data structure representing a
1046
+ * [balanced binary tree][glib-Balanced-Binary-Trees]. It should be
1047
+ * accessed only by using the following functions.
1048
+ */
1049
+struct _QTree {
1050
+ QTreeNode *root;
1051
+ GCompareDataFunc key_compare;
1052
+ GDestroyNotify key_destroy_func;
1053
+ GDestroyNotify value_destroy_func;
1054
+ gpointer key_compare_data;
1055
+ guint nnodes;
1056
+ gint ref_count;
1057
+};
1058
+
1059
+struct _QTreeNode {
1060
+ gpointer key; /* key for this node */
1061
+ gpointer value; /* value stored at this node */
1062
+ QTreeNode *left; /* left subtree */
1063
+ QTreeNode *right; /* right subtree */
1064
+ gint8 balance; /* height (right) - height (left) */
1065
+ guint8 left_child;
1066
+ guint8 right_child;
1067
+};
1068
+
1069
+
1070
+static QTreeNode *q_tree_node_new(gpointer key,
1071
+ gpointer value);
1072
+static QTreeNode *q_tree_insert_internal(QTree *tree,
1073
+ gpointer key,
1074
+ gpointer value,
1075
+ gboolean replace);
1076
+static gboolean q_tree_remove_internal(QTree *tree,
1077
+ gconstpointer key,
1078
+ gboolean steal);
1079
+static QTreeNode *q_tree_node_balance(QTreeNode *node);
1080
+static QTreeNode *q_tree_find_node(QTree *tree,
1081
+ gconstpointer key);
1082
+static QTreeNode *q_tree_node_search(QTreeNode *node,
1083
+ GCompareFunc search_func,
1084
+ gconstpointer data);
1085
+static QTreeNode *q_tree_node_rotate_left(QTreeNode *node);
1086
+static QTreeNode *q_tree_node_rotate_right(QTreeNode *node);
1087
+#ifdef Q_TREE_DEBUG
1088
+static void q_tree_node_check(QTreeNode *node);
1089
+#endif
1090
+
1091
+static QTreeNode*
1092
+q_tree_node_new(gpointer key,
1093
+ gpointer value)
1094
+{
1095
+ QTreeNode *node = g_new(QTreeNode, 1);
1096
+
1097
+ node->balance = 0;
1098
+ node->left = NULL;
1099
+ node->right = NULL;
1100
+ node->left_child = FALSE;
1101
+ node->right_child = FALSE;
1102
+ node->key = key;
1103
+ node->value = value;
1104
+
1105
+ return node;
1106
+}
1107
+
1108
+/**
1109
+ * q_tree_new:
1110
+ * @key_compare_func: the function used to order the nodes in the #QTree.
1111
+ * It should return values similar to the standard strcmp() function -
1112
+ * 0 if the two arguments are equal, a negative value if the first argument
1113
+ * comes before the second, or a positive value if the first argument comes
1114
+ * after the second.
1115
+ *
1116
+ * Creates a new #QTree.
1117
+ *
1118
+ * Returns: a newly allocated #QTree
1119
+ */
1120
+QTree *
1121
+q_tree_new(GCompareFunc key_compare_func)
1122
+{
1123
+ g_return_val_if_fail(key_compare_func != NULL, NULL);
1124
+
1125
+ return q_tree_new_full((GCompareDataFunc) key_compare_func, NULL,
1126
+ NULL, NULL);
1127
+}
1128
+
1129
+/**
1130
+ * q_tree_new_with_data:
1131
+ * @key_compare_func: qsort()-style comparison function
1132
+ * @key_compare_data: data to pass to comparison function
1133
+ *
1134
+ * Creates a new #QTree with a comparison function that accepts user data.
1135
+ * See q_tree_new() for more details.
1136
+ *
1137
+ * Returns: a newly allocated #QTree
1138
+ */
1139
+QTree *
1140
+q_tree_new_with_data(GCompareDataFunc key_compare_func,
1141
+ gpointer key_compare_data)
1142
+{
1143
+ g_return_val_if_fail(key_compare_func != NULL, NULL);
1144
+
1145
+ return q_tree_new_full(key_compare_func, key_compare_data,
1146
+ NULL, NULL);
1147
+}
1148
+
1149
+/**
1150
+ * q_tree_new_full:
1151
+ * @key_compare_func: qsort()-style comparison function
1152
+ * @key_compare_data: data to pass to comparison function
1153
+ * @key_destroy_func: a function to free the memory allocated for the key
1154
+ * used when removing the entry from the #QTree or %NULL if you don't
1155
+ * want to supply such a function
1156
+ * @value_destroy_func: a function to free the memory allocated for the
1157
+ * value used when removing the entry from the #QTree or %NULL if you
1158
+ * don't want to supply such a function
1159
+ *
1160
+ * Creates a new #QTree like q_tree_new() and allows to specify functions
1161
+ * to free the memory allocated for the key and value that get called when
1162
+ * removing the entry from the #QTree.
1163
+ *
1164
+ * Returns: a newly allocated #QTree
1165
+ */
1166
+QTree *
1167
+q_tree_new_full(GCompareDataFunc key_compare_func,
1168
+ gpointer key_compare_data,
1169
+ GDestroyNotify key_destroy_func,
1170
+ GDestroyNotify value_destroy_func)
1171
+{
1172
+ QTree *tree;
1173
+
1174
+ g_return_val_if_fail(key_compare_func != NULL, NULL);
1175
+
1176
+ tree = g_new(QTree, 1);
1177
+ tree->root = NULL;
1178
+ tree->key_compare = key_compare_func;
1179
+ tree->key_destroy_func = key_destroy_func;
1180
+ tree->value_destroy_func = value_destroy_func;
1181
+ tree->key_compare_data = key_compare_data;
1182
+ tree->nnodes = 0;
1183
+ tree->ref_count = 1;
1184
+
1185
+ return tree;
1186
+}
1187
+
1188
+/**
1189
+ * q_tree_node_first:
1190
+ * @tree: a #QTree
1191
+ *
1192
+ * Returns the first in-order node of the tree, or %NULL
1193
+ * for an empty tree.
1194
+ *
1195
+ * Returns: (nullable) (transfer none): the first node in the tree
1196
+ *
1197
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1198
+ */
1199
+static QTreeNode *
1200
+q_tree_node_first(QTree *tree)
1201
+{
1202
+ QTreeNode *tmp;
1203
+
1204
+ g_return_val_if_fail(tree != NULL, NULL);
1205
+
1206
+ if (!tree->root) {
1207
+ return NULL;
1208
+ }
1209
+
1210
+ tmp = tree->root;
1211
+
1212
+ while (tmp->left_child) {
1213
+ tmp = tmp->left;
1214
+ }
1215
+
1216
+ return tmp;
1217
+}
1218
+
1219
+/**
1220
+ * q_tree_node_previous
1221
+ * @node: a #QTree node
1222
+ *
1223
+ * Returns the previous in-order node of the tree, or %NULL
1224
+ * if the passed node was already the first one.
1225
+ *
1226
+ * Returns: (nullable) (transfer none): the previous node in the tree
1227
+ *
1228
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1229
+ */
1230
+static QTreeNode *
1231
+q_tree_node_previous(QTreeNode *node)
1232
+{
1233
+ QTreeNode *tmp;
1234
+
1235
+ g_return_val_if_fail(node != NULL, NULL);
1236
+
1237
+ tmp = node->left;
1238
+
1239
+ if (node->left_child) {
1240
+ while (tmp->right_child) {
1241
+ tmp = tmp->right;
1242
+ }
1243
+ }
1244
+
1245
+ return tmp;
1246
+}
1247
+
1248
+/**
1249
+ * q_tree_node_next
1250
+ * @node: a #QTree node
1251
+ *
1252
+ * Returns the next in-order node of the tree, or %NULL
1253
+ * if the passed node was already the last one.
1254
+ *
1255
+ * Returns: (nullable) (transfer none): the next node in the tree
1256
+ *
1257
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1258
+ */
1259
+static QTreeNode *
1260
+q_tree_node_next(QTreeNode *node)
1261
+{
1262
+ QTreeNode *tmp;
1263
+
1264
+ g_return_val_if_fail(node != NULL, NULL);
1265
+
1266
+ tmp = node->right;
1267
+
1268
+ if (node->right_child) {
1269
+ while (tmp->left_child) {
1270
+ tmp = tmp->left;
1271
+ }
1272
+ }
1273
+
1274
+ return tmp;
1275
+}
1276
+
1277
+/**
1278
+ * q_tree_remove_all:
1279
+ * @tree: a #QTree
1280
+ *
1281
+ * Removes all nodes from a #QTree and destroys their keys and values,
1282
+ * then resets the #QTree’s root to %NULL.
1283
+ *
1284
+ * Since: 2.70 in GLib. Internal in Qtree, i.e. not in the public API.
1285
+ */
1286
+static void
1287
+q_tree_remove_all(QTree *tree)
1288
+{
1289
+ QTreeNode *node;
1290
+ QTreeNode *next;
1291
+
1292
+ g_return_if_fail(tree != NULL);
1293
+
1294
+ node = q_tree_node_first(tree);
1295
+
1296
+ while (node) {
1297
+ next = q_tree_node_next(node);
1298
+
1299
+ if (tree->key_destroy_func) {
1300
+ tree->key_destroy_func(node->key);
1301
+ }
1302
+ if (tree->value_destroy_func) {
1303
+ tree->value_destroy_func(node->value);
1304
+ }
1305
+ g_free(node);
1306
+
1307
+#ifdef Q_TREE_DEBUG
1308
+ g_assert(tree->nnodes > 0);
1309
+ tree->nnodes--;
1310
+#endif
1311
+
1312
+ node = next;
1313
+ }
1314
+
1315
+#ifdef Q_TREE_DEBUG
1316
+ g_assert(tree->nnodes == 0);
1317
+#endif
1318
+
1319
+ tree->root = NULL;
1320
+#ifndef Q_TREE_DEBUG
1321
+ tree->nnodes = 0;
1322
+#endif
1323
+}
1324
+
1325
+/**
1326
+ * q_tree_ref:
1327
+ * @tree: a #QTree
1328
+ *
1329
+ * Increments the reference count of @tree by one.
1330
+ *
1331
+ * It is safe to call this function from any thread.
1332
+ *
1333
+ * Returns: the passed in #QTree
1334
+ *
1335
+ * Since: 2.22
1336
+ */
1337
+QTree *
1338
+q_tree_ref(QTree *tree)
1339
+{
1340
+ g_return_val_if_fail(tree != NULL, NULL);
1341
+
1342
+ g_atomic_int_inc(&tree->ref_count);
1343
+
1344
+ return tree;
1345
+}
1346
+
1347
+/**
1348
+ * q_tree_unref:
1349
+ * @tree: a #QTree
1350
+ *
1351
+ * Decrements the reference count of @tree by one.
1352
+ * If the reference count drops to 0, all keys and values will
1353
+ * be destroyed (if destroy functions were specified) and all
1354
+ * memory allocated by @tree will be released.
1355
+ *
1356
+ * It is safe to call this function from any thread.
1357
+ *
1358
+ * Since: 2.22
1359
+ */
1360
+void
1361
+q_tree_unref(QTree *tree)
1362
+{
1363
+ g_return_if_fail(tree != NULL);
1364
+
1365
+ if (g_atomic_int_dec_and_test(&tree->ref_count)) {
1366
+ q_tree_remove_all(tree);
1367
+ g_free(tree);
1368
+ }
1369
+}
1370
+
1371
+/**
1372
+ * q_tree_destroy:
1373
+ * @tree: a #QTree
1374
+ *
1375
+ * Removes all keys and values from the #QTree and decreases its
1376
+ * reference count by one. If keys and/or values are dynamically
1377
+ * allocated, you should either free them first or create the #QTree
1378
+ * using q_tree_new_full(). In the latter case the destroy functions
1379
+ * you supplied will be called on all keys and values before destroying
1380
+ * the #QTree.
1381
+ */
1382
+void
1383
+q_tree_destroy(QTree *tree)
1384
+{
1385
+ g_return_if_fail(tree != NULL);
1386
+
1387
+ q_tree_remove_all(tree);
1388
+ q_tree_unref(tree);
1389
+}
1390
+
1391
+/**
1392
+ * q_tree_insert_node:
1393
+ * @tree: a #QTree
1394
+ * @key: the key to insert
1395
+ * @value: the value corresponding to the key
1396
+ *
1397
+ * Inserts a key/value pair into a #QTree.
1398
+ *
1399
+ * If the given key already exists in the #QTree its corresponding value
1400
+ * is set to the new value. If you supplied a @value_destroy_func when
1401
+ * creating the #QTree, the old value is freed using that function. If
1402
+ * you supplied a @key_destroy_func when creating the #QTree, the passed
1403
+ * key is freed using that function.
1404
+ *
1405
+ * The tree is automatically 'balanced' as new key/value pairs are added,
1406
+ * so that the distance from the root to every leaf is as small as possible.
1407
+ * The cost of maintaining a balanced tree while inserting new key/value
1408
+ * result in a O(n log(n)) operation where most of the other operations
1409
+ * are O(log(n)).
1410
+ *
1411
+ * Returns: (transfer none): the inserted (or set) node.
1412
+ *
1413
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1414
+ */
1415
+static QTreeNode *
1416
+q_tree_insert_node(QTree *tree,
1417
+ gpointer key,
1418
+ gpointer value)
1419
+{
1420
+ QTreeNode *node;
1421
+
1422
+ g_return_val_if_fail(tree != NULL, NULL);
1423
+
1424
+ node = q_tree_insert_internal(tree, key, value, FALSE);
1425
+
1426
+#ifdef Q_TREE_DEBUG
1427
+ q_tree_node_check(tree->root);
1428
+#endif
1429
+
1430
+ return node;
1431
+}
1432
+
1433
+/**
1434
+ * q_tree_insert:
1435
+ * @tree: a #QTree
1436
+ * @key: the key to insert
1437
+ * @value: the value corresponding to the key
1438
+ *
1439
+ * Inserts a key/value pair into a #QTree.
1440
+ *
1441
+ * Inserts a new key and value into a #QTree as q_tree_insert_node() does,
1442
+ * only this function does not return the inserted or set node.
1443
+ */
1444
+void
1445
+q_tree_insert(QTree *tree,
1446
+ gpointer key,
1447
+ gpointer value)
1448
+{
1449
+ q_tree_insert_node(tree, key, value);
1450
+}
1451
+
1452
+/**
1453
+ * q_tree_replace_node:
1454
+ * @tree: a #QTree
1455
+ * @key: the key to insert
1456
+ * @value: the value corresponding to the key
1457
+ *
1458
+ * Inserts a new key and value into a #QTree similar to q_tree_insert_node().
1459
+ * The difference is that if the key already exists in the #QTree, it gets
1460
+ * replaced by the new key. If you supplied a @value_destroy_func when
1461
+ * creating the #QTree, the old value is freed using that function. If you
1462
+ * supplied a @key_destroy_func when creating the #QTree, the old key is
1463
+ * freed using that function.
1464
+ *
1465
+ * The tree is automatically 'balanced' as new key/value pairs are added,
1466
+ * so that the distance from the root to every leaf is as small as possible.
1467
+ *
1468
+ * Returns: (transfer none): the inserted (or set) node.
1469
+ *
1470
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1471
+ */
1472
+static QTreeNode *
1473
+q_tree_replace_node(QTree *tree,
1474
+ gpointer key,
1475
+ gpointer value)
1476
+{
1477
+ QTreeNode *node;
1478
+
1479
+ g_return_val_if_fail(tree != NULL, NULL);
1480
+
1481
+ node = q_tree_insert_internal(tree, key, value, TRUE);
1482
+
1483
+#ifdef Q_TREE_DEBUG
1484
+ q_tree_node_check(tree->root);
1485
+#endif
1486
+
1487
+ return node;
1488
+}
1489
+
1490
+/**
1491
+ * q_tree_replace:
1492
+ * @tree: a #QTree
1493
+ * @key: the key to insert
1494
+ * @value: the value corresponding to the key
1495
+ *
1496
+ * Inserts a new key and value into a #QTree as q_tree_replace_node() does,
1497
+ * only this function does not return the inserted or set node.
1498
+ */
1499
+void
1500
+q_tree_replace(QTree *tree,
1501
+ gpointer key,
1502
+ gpointer value)
1503
+{
1504
+ q_tree_replace_node(tree, key, value);
1505
+}
1506
+
1507
+/* internal insert routine */
1508
+static QTreeNode *
1509
+q_tree_insert_internal(QTree *tree,
1510
+ gpointer key,
1511
+ gpointer value,
1512
+ gboolean replace)
1513
+{
1514
+ QTreeNode *node, *retnode;
1515
+ QTreeNode *path[MAX_GTREE_HEIGHT];
1516
+ int idx;
1517
+
1518
+ g_return_val_if_fail(tree != NULL, NULL);
1519
+
1520
+ if (!tree->root) {
1521
+ tree->root = q_tree_node_new(key, value);
1522
+ tree->nnodes++;
1523
+ return tree->root;
1524
+ }
1525
+
1526
+ idx = 0;
1527
+ path[idx++] = NULL;
1528
+ node = tree->root;
1529
+
1530
+ while (1) {
1531
+ int cmp = tree->key_compare(key, node->key, tree->key_compare_data);
1532
+
1533
+ if (cmp == 0) {
1534
+ if (tree->value_destroy_func) {
1535
+ tree->value_destroy_func(node->value);
1536
+ }
1537
+
1538
+ node->value = value;
1539
+
1540
+ if (replace) {
1541
+ if (tree->key_destroy_func) {
1542
+ tree->key_destroy_func(node->key);
1543
+ }
1544
+
1545
+ node->key = key;
1546
+ } else {
1547
+ /* free the passed key */
1548
+ if (tree->key_destroy_func) {
1549
+ tree->key_destroy_func(key);
1550
+ }
1551
+ }
1552
+
1553
+ return node;
1554
+ } else if (cmp < 0) {
1555
+ if (node->left_child) {
1556
+ path[idx++] = node;
1557
+ node = node->left;
1558
+ } else {
1559
+ QTreeNode *child = q_tree_node_new(key, value);
1560
+
1561
+ child->left = node->left;
1562
+ child->right = node;
1563
+ node->left = child;
1564
+ node->left_child = TRUE;
1565
+ node->balance -= 1;
1566
+
1567
+ tree->nnodes++;
1568
+
1569
+ retnode = child;
1570
+ break;
1571
+ }
1572
+ } else {
1573
+ if (node->right_child) {
1574
+ path[idx++] = node;
1575
+ node = node->right;
1576
+ } else {
1577
+ QTreeNode *child = q_tree_node_new(key, value);
1578
+
1579
+ child->right = node->right;
1580
+ child->left = node;
1581
+ node->right = child;
1582
+ node->right_child = TRUE;
1583
+ node->balance += 1;
1584
+
1585
+ tree->nnodes++;
1586
+
1587
+ retnode = child;
1588
+ break;
1589
+ }
1590
+ }
1591
+ }
1592
+
1593
+ /*
1594
+ * Restore balance. This is the goodness of a non-recursive
1595
+ * implementation, when we are done with balancing we 'break'
1596
+ * the loop and we are done.
1597
+ */
1598
+ while (1) {
1599
+ QTreeNode *bparent = path[--idx];
1600
+ gboolean left_node = (bparent && node == bparent->left);
1601
+ g_assert(!bparent || bparent->left == node || bparent->right == node);
1602
+
1603
+ if (node->balance < -1 || node->balance > 1) {
1604
+ node = q_tree_node_balance(node);
1605
+ if (bparent == NULL) {
1606
+ tree->root = node;
1607
+ } else if (left_node) {
1608
+ bparent->left = node;
1609
+ } else {
1610
+ bparent->right = node;
1611
+ }
1612
+ }
1613
+
1614
+ if (node->balance == 0 || bparent == NULL) {
1615
+ break;
1616
+ }
1617
+
1618
+ if (left_node) {
1619
+ bparent->balance -= 1;
1620
+ } else {
1621
+ bparent->balance += 1;
1622
+ }
1623
+
1624
+ node = bparent;
1625
+ }
1626
+
1627
+ return retnode;
1628
+}
1629
+
1630
+/**
1631
+ * q_tree_remove:
1632
+ * @tree: a #QTree
1633
+ * @key: the key to remove
1634
+ *
1635
+ * Removes a key/value pair from a #QTree.
1636
+ *
1637
+ * If the #QTree was created using q_tree_new_full(), the key and value
1638
+ * are freed using the supplied destroy functions, otherwise you have to
1639
+ * make sure that any dynamically allocated values are freed yourself.
1640
+ * If the key does not exist in the #QTree, the function does nothing.
1641
+ *
1642
+ * The cost of maintaining a balanced tree while removing a key/value
1643
+ * result in a O(n log(n)) operation where most of the other operations
1644
+ * are O(log(n)).
1645
+ *
1646
+ * Returns: %TRUE if the key was found (prior to 2.8, this function
1647
+ * returned nothing)
1648
+ */
1649
+gboolean
1650
+q_tree_remove(QTree *tree,
1651
+ gconstpointer key)
1652
+{
1653
+ gboolean removed;
1654
+
1655
+ g_return_val_if_fail(tree != NULL, FALSE);
1656
+
1657
+ removed = q_tree_remove_internal(tree, key, FALSE);
1658
+
1659
+#ifdef Q_TREE_DEBUG
1660
+ q_tree_node_check(tree->root);
1661
+#endif
1662
+
1663
+ return removed;
1664
+}
1665
+
1666
+/**
1667
+ * q_tree_steal:
1668
+ * @tree: a #QTree
1669
+ * @key: the key to remove
1670
+ *
1671
+ * Removes a key and its associated value from a #QTree without calling
1672
+ * the key and value destroy functions.
1673
+ *
1674
+ * If the key does not exist in the #QTree, the function does nothing.
1675
+ *
1676
+ * Returns: %TRUE if the key was found (prior to 2.8, this function
1677
+ * returned nothing)
1678
+ */
1679
+gboolean
1680
+q_tree_steal(QTree *tree,
1681
+ gconstpointer key)
1682
+{
1683
+ gboolean removed;
1684
+
1685
+ g_return_val_if_fail(tree != NULL, FALSE);
1686
+
1687
+ removed = q_tree_remove_internal(tree, key, TRUE);
1688
+
1689
+#ifdef Q_TREE_DEBUG
1690
+ q_tree_node_check(tree->root);
1691
+#endif
1692
+
1693
+ return removed;
1694
+}
1695
+
1696
+/* internal remove routine */
1697
+static gboolean
1698
+q_tree_remove_internal(QTree *tree,
1699
+ gconstpointer key,
1700
+ gboolean steal)
1701
+{
1702
+ QTreeNode *node, *parent, *balance;
1703
+ QTreeNode *path[MAX_GTREE_HEIGHT];
1704
+ int idx;
1705
+ gboolean left_node;
1706
+
1707
+ g_return_val_if_fail(tree != NULL, FALSE);
1708
+
1709
+ if (!tree->root) {
1710
+ return FALSE;
1711
+ }
1712
+
1713
+ idx = 0;
1714
+ path[idx++] = NULL;
1715
+ node = tree->root;
1716
+
1717
+ while (1) {
1718
+ int cmp = tree->key_compare(key, node->key, tree->key_compare_data);
1719
+
1720
+ if (cmp == 0) {
1721
+ break;
1722
+ } else if (cmp < 0) {
1723
+ if (!node->left_child) {
1724
+ return FALSE;
1725
+ }
1726
+
1727
+ path[idx++] = node;
1728
+ node = node->left;
1729
+ } else {
1730
+ if (!node->right_child) {
1731
+ return FALSE;
1732
+ }
1733
+
1734
+ path[idx++] = node;
1735
+ node = node->right;
1736
+ }
1737
+ }
1738
+
1739
+ /*
1740
+ * The following code is almost equal to q_tree_remove_node,
1741
+ * except that we do not have to call q_tree_node_parent.
1742
+ */
1743
+ balance = parent = path[--idx];
1744
+ g_assert(!parent || parent->left == node || parent->right == node);
1745
+ left_node = (parent && node == parent->left);
1746
+
1747
+ if (!node->left_child) {
1748
+ if (!node->right_child) {
1749
+ if (!parent) {
1750
+ tree->root = NULL;
1751
+ } else if (left_node) {
1752
+ parent->left_child = FALSE;
1753
+ parent->left = node->left;
1754
+ parent->balance += 1;
1755
+ } else {
1756
+ parent->right_child = FALSE;
1757
+ parent->right = node->right;
1758
+ parent->balance -= 1;
1759
+ }
1760
+ } else {
1761
+ /* node has a right child */
1762
+ QTreeNode *tmp = q_tree_node_next(node);
1763
+ tmp->left = node->left;
1764
+
1765
+ if (!parent) {
1766
+ tree->root = node->right;
1767
+ } else if (left_node) {
1768
+ parent->left = node->right;
1769
+ parent->balance += 1;
1770
+ } else {
1771
+ parent->right = node->right;
1772
+ parent->balance -= 1;
1773
+ }
1774
+ }
1775
+ } else {
1776
+ /* node has a left child */
1777
+ if (!node->right_child) {
1778
+ QTreeNode *tmp = q_tree_node_previous(node);
1779
+ tmp->right = node->right;
1780
+
1781
+ if (parent == NULL) {
1782
+ tree->root = node->left;
1783
+ } else if (left_node) {
1784
+ parent->left = node->left;
1785
+ parent->balance += 1;
1786
+ } else {
1787
+ parent->right = node->left;
1788
+ parent->balance -= 1;
1789
+ }
1790
+ } else {
1791
+ /* node has a both children (pant, pant!) */
1792
+ QTreeNode *prev = node->left;
1793
+ QTreeNode *next = node->right;
1794
+ QTreeNode *nextp = node;
1795
+ int old_idx = idx + 1;
1796
+ idx++;
1797
+
1798
+ /* path[idx] == parent */
1799
+ /* find the immediately next node (and its parent) */
1800
+ while (next->left_child) {
1801
+ path[++idx] = nextp = next;
1802
+ next = next->left;
1803
+ }
1804
+
1805
+ path[old_idx] = next;
1806
+ balance = path[idx];
1807
+
1808
+ /* remove 'next' from the tree */
1809
+ if (nextp != node) {
1810
+ if (next->right_child) {
1811
+ nextp->left = next->right;
1812
+ } else {
1813
+ nextp->left_child = FALSE;
1814
+ }
1815
+ nextp->balance += 1;
1816
+
1817
+ next->right_child = TRUE;
1818
+ next->right = node->right;
1819
+ } else {
1820
+ node->balance -= 1;
1821
+ }
1822
+
1823
+ /* set the prev to point to the right place */
1824
+ while (prev->right_child) {
1825
+ prev = prev->right;
1826
+ }
1827
+ prev->right = next;
1828
+
1829
+ /* prepare 'next' to replace 'node' */
1830
+ next->left_child = TRUE;
1831
+ next->left = node->left;
1832
+ next->balance = node->balance;
1833
+
1834
+ if (!parent) {
1835
+ tree->root = next;
1836
+ } else if (left_node) {
1837
+ parent->left = next;
1838
+ } else {
1839
+ parent->right = next;
1840
+ }
1841
+ }
1842
+ }
1843
+
1844
+ /* restore balance */
1845
+ if (balance) {
1846
+ while (1) {
1847
+ QTreeNode *bparent = path[--idx];
1848
+ g_assert(!bparent ||
1849
+ bparent->left == balance ||
1850
+ bparent->right == balance);
1851
+ left_node = (bparent && balance == bparent->left);
1852
+
1853
+ if (balance->balance < -1 || balance->balance > 1) {
1854
+ balance = q_tree_node_balance(balance);
1855
+ if (!bparent) {
1856
+ tree->root = balance;
1857
+ } else if (left_node) {
1858
+ bparent->left = balance;
1859
+ } else {
1860
+ bparent->right = balance;
1861
+ }
1862
+ }
1863
+
1864
+ if (balance->balance != 0 || !bparent) {
1865
+ break;
1866
+ }
1867
+
1868
+ if (left_node) {
1869
+ bparent->balance += 1;
1870
+ } else {
1871
+ bparent->balance -= 1;
1872
+ }
1873
+
1874
+ balance = bparent;
1875
+ }
1876
+ }
1877
+
1878
+ if (!steal) {
1879
+ if (tree->key_destroy_func) {
1880
+ tree->key_destroy_func(node->key);
1881
+ }
1882
+ if (tree->value_destroy_func) {
1883
+ tree->value_destroy_func(node->value);
1884
+ }
1885
+ }
1886
+
1887
+ g_free(node);
1888
+
1889
+ tree->nnodes--;
1890
+
1891
+ return TRUE;
1892
+}
1893
+
1894
+/**
1895
+ * q_tree_lookup_node:
1896
+ * @tree: a #QTree
1897
+ * @key: the key to look up
1898
+ *
1899
+ * Gets the tree node corresponding to the given key. Since a #QTree is
1900
+ * automatically balanced as key/value pairs are added, key lookup
1901
+ * is O(log n) (where n is the number of key/value pairs in the tree).
1902
+ *
1903
+ * Returns: (nullable) (transfer none): the tree node corresponding to
1904
+ * the key, or %NULL if the key was not found
1905
+ *
1906
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
1907
+ */
1908
+static QTreeNode *
1909
+q_tree_lookup_node(QTree *tree,
1910
+ gconstpointer key)
1911
+{
1912
+ g_return_val_if_fail(tree != NULL, NULL);
1913
+
1914
+ return q_tree_find_node(tree, key);
1915
+}
1916
+
1917
+/**
1918
+ * q_tree_lookup:
1919
+ * @tree: a #QTree
1920
+ * @key: the key to look up
1921
+ *
1922
+ * Gets the value corresponding to the given key. Since a #QTree is
1923
+ * automatically balanced as key/value pairs are added, key lookup
1924
+ * is O(log n) (where n is the number of key/value pairs in the tree).
1925
+ *
1926
+ * Returns: the value corresponding to the key, or %NULL
1927
+ * if the key was not found
1928
+ */
1929
+gpointer
1930
+q_tree_lookup(QTree *tree,
1931
+ gconstpointer key)
1932
+{
1933
+ QTreeNode *node;
1934
+
1935
+ node = q_tree_lookup_node(tree, key);
1936
+
1937
+ return node ? node->value : NULL;
1938
+}
1939
+
1940
+/**
1941
+ * q_tree_lookup_extended:
1942
+ * @tree: a #QTree
1943
+ * @lookup_key: the key to look up
1944
+ * @orig_key: (out) (optional) (nullable): returns the original key
1945
+ * @value: (out) (optional) (nullable): returns the value associated with
1946
+ * the key
1947
+ *
1948
+ * Looks up a key in the #QTree, returning the original key and the
1949
+ * associated value. This is useful if you need to free the memory
1950
+ * allocated for the original key, for example before calling
1951
+ * q_tree_remove().
1952
+ *
1953
+ * Returns: %TRUE if the key was found in the #QTree
1954
+ */
1955
+gboolean
1956
+q_tree_lookup_extended(QTree *tree,
1957
+ gconstpointer lookup_key,
1958
+ gpointer *orig_key,
1959
+ gpointer *value)
1960
+{
1961
+ QTreeNode *node;
1962
+
1963
+ g_return_val_if_fail(tree != NULL, FALSE);
1964
+
1965
+ node = q_tree_find_node(tree, lookup_key);
1966
+
1967
+ if (node) {
1968
+ if (orig_key) {
1969
+ *orig_key = node->key;
1970
+ }
1971
+ if (value) {
1972
+ *value = node->value;
1973
+ }
1974
+ return TRUE;
1975
+ } else {
1976
+ return FALSE;
1977
+ }
1978
+}
1979
+
1980
+/**
1981
+ * q_tree_foreach:
1982
+ * @tree: a #QTree
1983
+ * @func: the function to call for each node visited.
1984
+ * If this function returns %TRUE, the traversal is stopped.
1985
+ * @user_data: user data to pass to the function
1986
+ *
1987
+ * Calls the given function for each of the key/value pairs in the #QTree.
1988
+ * The function is passed the key and value of each pair, and the given
1989
+ * @data parameter. The tree is traversed in sorted order.
1990
+ *
1991
+ * The tree may not be modified while iterating over it (you can't
1992
+ * add/remove items). To remove all items matching a predicate, you need
1993
+ * to add each item to a list in your #GTraverseFunc as you walk over
1994
+ * the tree, then walk the list and remove each item.
1995
+ */
1996
+void
1997
+q_tree_foreach(QTree *tree,
1998
+ GTraverseFunc func,
1999
+ gpointer user_data)
2000
+{
2001
+ QTreeNode *node;
2002
+
2003
+ g_return_if_fail(tree != NULL);
2004
+
2005
+ if (!tree->root) {
2006
+ return;
2007
+ }
2008
+
2009
+ node = q_tree_node_first(tree);
2010
+
2011
+ while (node) {
2012
+ if ((*func)(node->key, node->value, user_data)) {
2013
+ break;
2014
+ }
2015
+
2016
+ node = q_tree_node_next(node);
2017
+ }
2018
+}
2019
+
2020
+/**
2021
+ * q_tree_search_node:
2022
+ * @tree: a #QTree
2023
+ * @search_func: a function used to search the #QTree
2024
+ * @user_data: the data passed as the second argument to @search_func
2025
+ *
2026
+ * Searches a #QTree using @search_func.
2027
+ *
2028
+ * The @search_func is called with a pointer to the key of a key/value
2029
+ * pair in the tree, and the passed in @user_data. If @search_func returns
2030
+ * 0 for a key/value pair, then the corresponding node is returned as
2031
+ * the result of q_tree_search(). If @search_func returns -1, searching
2032
+ * will proceed among the key/value pairs that have a smaller key; if
2033
+ * @search_func returns 1, searching will proceed among the key/value
2034
+ * pairs that have a larger key.
2035
+ *
2036
+ * Returns: (nullable) (transfer none): the node corresponding to the
2037
+ * found key, or %NULL if the key was not found
2038
+ *
2039
+ * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
2040
+ */
2041
+static QTreeNode *
2042
+q_tree_search_node(QTree *tree,
2043
+ GCompareFunc search_func,
2044
+ gconstpointer user_data)
2045
+{
2046
+ g_return_val_if_fail(tree != NULL, NULL);
2047
+
2048
+ if (!tree->root) {
2049
+ return NULL;
2050
+ }
2051
+
2052
+ return q_tree_node_search(tree->root, search_func, user_data);
2053
+}
2054
+
2055
+/**
2056
+ * q_tree_search:
2057
+ * @tree: a #QTree
2058
+ * @search_func: a function used to search the #QTree
2059
+ * @user_data: the data passed as the second argument to @search_func
2060
+ *
2061
+ * Searches a #QTree using @search_func.
2062
+ *
2063
+ * The @search_func is called with a pointer to the key of a key/value
2064
+ * pair in the tree, and the passed in @user_data. If @search_func returns
2065
+ * 0 for a key/value pair, then the corresponding value is returned as
2066
+ * the result of q_tree_search(). If @search_func returns -1, searching
2067
+ * will proceed among the key/value pairs that have a smaller key; if
2068
+ * @search_func returns 1, searching will proceed among the key/value
2069
+ * pairs that have a larger key.
2070
+ *
2071
+ * Returns: the value corresponding to the found key, or %NULL
2072
+ * if the key was not found
2073
+ */
2074
+gpointer
2075
+q_tree_search(QTree *tree,
2076
+ GCompareFunc search_func,
2077
+ gconstpointer user_data)
2078
+{
2079
+ QTreeNode *node;
2080
+
2081
+ node = q_tree_search_node(tree, search_func, user_data);
2082
+
2083
+ return node ? node->value : NULL;
2084
+}
2085
+
2086
+/**
2087
+ * q_tree_height:
2088
+ * @tree: a #QTree
2089
+ *
2090
+ * Gets the height of a #QTree.
2091
+ *
2092
+ * If the #QTree contains no nodes, the height is 0.
2093
+ * If the #QTree contains only one root node the height is 1.
2094
+ * If the root node has children the height is 2, etc.
2095
+ *
2096
+ * Returns: the height of @tree
2097
+ */
2098
+gint
2099
+q_tree_height(QTree *tree)
2100
+{
2101
+ QTreeNode *node;
2102
+ gint height;
2103
+
2104
+ g_return_val_if_fail(tree != NULL, 0);
2105
+
2106
+ if (!tree->root) {
2107
+ return 0;
2108
+ }
2109
+
2110
+ height = 0;
2111
+ node = tree->root;
2112
+
2113
+ while (1) {
2114
+ height += 1 + MAX(node->balance, 0);
2115
+
2116
+ if (!node->left_child) {
2117
+ return height;
2118
+ }
2119
+
2120
+ node = node->left;
2121
+ }
2122
+}
2123
+
2124
+/**
2125
+ * q_tree_nnodes:
2126
+ * @tree: a #QTree
2127
+ *
2128
+ * Gets the number of nodes in a #QTree.
2129
+ *
2130
+ * Returns: the number of nodes in @tree
2131
+ */
2132
+gint
2133
+q_tree_nnodes(QTree *tree)
2134
+{
2135
+ g_return_val_if_fail(tree != NULL, 0);
2136
+
2137
+ return tree->nnodes;
2138
+}
2139
+
2140
+static QTreeNode *
2141
+q_tree_node_balance(QTreeNode *node)
2142
+{
2143
+ if (node->balance < -1) {
2144
+ if (node->left->balance > 0) {
2145
+ node->left = q_tree_node_rotate_left(node->left);
2146
+ }
2147
+ node = q_tree_node_rotate_right(node);
2148
+ } else if (node->balance > 1) {
2149
+ if (node->right->balance < 0) {
2150
+ node->right = q_tree_node_rotate_right(node->right);
2151
+ }
2152
+ node = q_tree_node_rotate_left(node);
2153
+ }
2154
+
2155
+ return node;
2156
+}
2157
+
2158
+static QTreeNode *
2159
+q_tree_find_node(QTree *tree,
2160
+ gconstpointer key)
2161
+{
2162
+ QTreeNode *node;
2163
+ gint cmp;
2164
+
2165
+ node = tree->root;
2166
+ if (!node) {
2167
+ return NULL;
2168
+ }
2169
+
2170
+ while (1) {
2171
+ cmp = tree->key_compare(key, node->key, tree->key_compare_data);
2172
+ if (cmp == 0) {
2173
+ return node;
2174
+ } else if (cmp < 0) {
2175
+ if (!node->left_child) {
2176
+ return NULL;
2177
+ }
2178
+
2179
+ node = node->left;
2180
+ } else {
2181
+ if (!node->right_child) {
2182
+ return NULL;
2183
+ }
2184
+
2185
+ node = node->right;
2186
+ }
2187
+ }
2188
+}
2189
+
2190
+static QTreeNode *
2191
+q_tree_node_search(QTreeNode *node,
2192
+ GCompareFunc search_func,
2193
+ gconstpointer data)
2194
+{
2195
+ gint dir;
2196
+
2197
+ if (!node) {
2198
+ return NULL;
2199
+ }
2200
+
2201
+ while (1) {
2202
+ dir = (*search_func)(node->key, data);
2203
+ if (dir == 0) {
2204
+ return node;
2205
+ } else if (dir < 0) {
2206
+ if (!node->left_child) {
2207
+ return NULL;
2208
+ }
2209
+
2210
+ node = node->left;
2211
+ } else {
2212
+ if (!node->right_child) {
2213
+ return NULL;
2214
+ }
2215
+
2216
+ node = node->right;
2217
+ }
2218
+ }
2219
+}
2220
+
2221
+static QTreeNode *
2222
+q_tree_node_rotate_left(QTreeNode *node)
2223
+{
2224
+ QTreeNode *right;
2225
+ gint a_bal;
2226
+ gint b_bal;
2227
+
2228
+ right = node->right;
2229
+
2230
+ if (right->left_child) {
2231
+ node->right = right->left;
2232
+ } else {
2233
+ node->right_child = FALSE;
2234
+ right->left_child = TRUE;
2235
+ }
2236
+ right->left = node;
2237
+
2238
+ a_bal = node->balance;
2239
+ b_bal = right->balance;
2240
+
2241
+ if (b_bal <= 0) {
2242
+ if (a_bal >= 1) {
2243
+ right->balance = b_bal - 1;
2244
+ } else {
2245
+ right->balance = a_bal + b_bal - 2;
2246
+ }
2247
+ node->balance = a_bal - 1;
2248
+ } else {
2249
+ if (a_bal <= b_bal) {
2250
+ right->balance = a_bal - 2;
2251
+ } else {
2252
+ right->balance = b_bal - 1;
2253
+ }
2254
+ node->balance = a_bal - b_bal - 1;
2255
+ }
2256
+
2257
+ return right;
2258
+}
2259
+
2260
+static QTreeNode *
2261
+q_tree_node_rotate_right(QTreeNode *node)
2262
+{
2263
+ QTreeNode *left;
2264
+ gint a_bal;
2265
+ gint b_bal;
2266
+
2267
+ left = node->left;
2268
+
2269
+ if (left->right_child) {
2270
+ node->left = left->right;
2271
+ } else {
2272
+ node->left_child = FALSE;
2273
+ left->right_child = TRUE;
2274
+ }
2275
+ left->right = node;
2276
+
2277
+ a_bal = node->balance;
2278
+ b_bal = left->balance;
2279
+
2280
+ if (b_bal <= 0) {
2281
+ if (b_bal > a_bal) {
2282
+ left->balance = b_bal + 1;
2283
+ } else {
2284
+ left->balance = a_bal + 2;
2285
+ }
2286
+ node->balance = a_bal - b_bal + 1;
2287
+ } else {
2288
+ if (a_bal <= -1) {
2289
+ left->balance = b_bal + 1;
2290
+ } else {
2291
+ left->balance = a_bal + b_bal + 2;
2292
+ }
2293
+ node->balance = a_bal + 1;
2294
+ }
2295
+
2296
+ return left;
2297
+}
2298
+
2299
+#ifdef Q_TREE_DEBUG
2300
+static gint
2301
+q_tree_node_height(QTreeNode *node)
2302
+{
2303
+ gint left_height;
2304
+ gint right_height;
2305
+
2306
+ if (node) {
2307
+ left_height = 0;
2308
+ right_height = 0;
2309
+
2310
+ if (node->left_child) {
2311
+ left_height = q_tree_node_height(node->left);
2312
+ }
2313
+
2314
+ if (node->right_child) {
2315
+ right_height = q_tree_node_height(node->right);
2316
+ }
2317
+
2318
+ return MAX(left_height, right_height) + 1;
2319
+ }
2320
+
2321
+ return 0;
2322
+}
2323
+
2324
+static void q_tree_node_check(QTreeNode *node)
2325
+{
2326
+ gint left_height;
2327
+ gint right_height;
2328
+ gint balance;
2329
+ QTreeNode *tmp;
2330
+
2331
+ if (node) {
2332
+ if (node->left_child) {
2333
+ tmp = q_tree_node_previous(node);
2334
+ g_assert(tmp->right == node);
2335
+ }
2336
+
2337
+ if (node->right_child) {
2338
+ tmp = q_tree_node_next(node);
2339
+ g_assert(tmp->left == node);
2340
+ }
2341
+
2342
+ left_height = 0;
2343
+ right_height = 0;
2344
+
2345
+ if (node->left_child) {
2346
+ left_height = q_tree_node_height(node->left);
2347
+ }
2348
+ if (node->right_child) {
2349
+ right_height = q_tree_node_height(node->right);
2350
+ }
2351
+
2352
+ balance = right_height - left_height;
2353
+ g_assert(balance == node->balance);
2354
+
2355
+ if (node->left_child) {
2356
+ q_tree_node_check(node->left);
2357
+ }
2358
+ if (node->right_child) {
2359
+ q_tree_node_check(node->right);
2360
+ }
2361
+ }
2362
+}
2363
+#endif
2364
diff --git a/tests/bench/meson.build b/tests/bench/meson.build
2365
index XXXXXXX..XXXXXXX 100644
2366
--- a/tests/bench/meson.build
2367
+++ b/tests/bench/meson.build
2368
@@ -XXX,XX +XXX,XX @@ xbzrle_bench = executable('xbzrle-bench',
2369
dependencies: [qemuutil,migration])
2370
endif
2371
2372
+qtree_bench = executable('qtree-bench',
2373
+ sources: 'qtree-bench.c',
2374
+ dependencies: [qemuutil])
2375
+
2376
executable('atomic_add-bench',
2377
sources: files('atomic_add-bench.c'),
2378
dependencies: [qemuutil],
2379
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
2380
index XXXXXXX..XXXXXXX 100644
2381
--- a/tests/unit/meson.build
2382
+++ b/tests/unit/meson.build
2383
@@ -XXX,XX +XXX,XX @@ tests = {
2384
'test-rcu-slist': [],
2385
'test-qdist': [],
2386
'test-qht': [],
2387
+ 'test-qtree': [],
2388
'test-bitops': [],
2389
'test-bitcnt': [],
2390
'test-qgraph': ['../qtest/libqos/qgraph.c'],
2391
diff --git a/util/meson.build b/util/meson.build
2392
index XXXXXXX..XXXXXXX 100644
2393
--- a/util/meson.build
2394
+++ b/util/meson.build
2395
@@ -XXX,XX +XXX,XX @@ util_ss.add(when: 'CONFIG_WIN32', if_true: files('oslib-win32.c'))
2396
util_ss.add(when: 'CONFIG_WIN32', if_true: files('qemu-thread-win32.c'))
2397
util_ss.add(when: 'CONFIG_WIN32', if_true: winmm)
2398
util_ss.add(when: 'CONFIG_WIN32', if_true: pathcch)
2399
+util_ss.add(when: 'HAVE_GLIB_WITH_SLICE_ALLOCATOR', if_true: files('qtree.c'))
2400
util_ss.add(files('envlist.c', 'path.c', 'module.c'))
2401
util_ss.add(files('host-utils.c'))
2402
util_ss.add(files('bitmap.c', 'bitops.c'))
84
--
2403
--
85
2.25.1
2404
2.34.1
86
2405
87
2406
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
From: Emilio Cota <cota@braap.org>
2
2
3
qemu-user can hang in a multi-threaded fork. One common
4
reason is that when creating a TB, between fork and exec
5
we manipulate a GTree whose memory allocator (GSlice) is
6
not fork-safe.
7
8
Although POSIX does not mandate it, the system's allocator
9
(e.g. tcmalloc, libc malloc) is probably fork-safe.
10
11
Fix some of these hangs by using QTree, which uses the system's
12
allocator regardless of the Glib version that we used at
13
configuration time.
14
15
Tested with the test program in the original bug report, i.e.:
16
```
17
18
void garble() {
19
int pid = fork();
20
if (pid == 0) {
21
exit(0);
22
} else {
23
int wstatus;
24
waitpid(pid, &wstatus, 0);
25
}
26
}
27
28
void supragarble(unsigned depth) {
29
if (depth == 0)
30
return ;
31
32
std::thread a(supragarble, depth-1);
33
std::thread b(supragarble, depth-1);
34
garble();
35
a.join();
36
b.join();
37
}
38
39
int main() {
40
supragarble(10);
41
}
42
```
43
44
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/285
45
Reported-by: Valentin David <me@valentindavid.com>
46
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
47
Signed-off-by: Emilio Cota <cota@braap.org>
48
Message-Id: <20230205163758.416992-3-cota@braap.org>
49
[rth: Add QEMU_DISABLE_CFI for all callback using functions.]
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
50
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
51
---
5
target/s390x/tcg/translate.c | 8 ++------
52
accel/tcg/tb-maint.c | 17 +++++++++--------
6
1 file changed, 2 insertions(+), 6 deletions(-)
53
tcg/region.c | 19 ++++++++++---------
7
54
util/qtree.c | 8 ++++----
8
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
55
3 files changed, 23 insertions(+), 21 deletions(-)
56
57
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
9
index XXXXXXX..XXXXXXX 100644
58
index XXXXXXX..XXXXXXX 100644
10
--- a/target/s390x/tcg/translate.c
59
--- a/accel/tcg/tb-maint.c
11
+++ b/target/s390x/tcg/translate.c
60
+++ b/accel/tcg/tb-maint.c
12
@@ -XXX,XX +XXX,XX @@ struct DisasContext {
61
@@ -XXX,XX +XXX,XX @@
13
uint64_t pc_tmp;
62
14
uint32_t ilen;
63
#include "qemu/osdep.h"
15
enum cc_op cc_op;
64
#include "qemu/interval-tree.h"
16
- bool do_debug;
65
+#include "qemu/qtree.h"
66
#include "exec/cputlb.h"
67
#include "exec/log.h"
68
#include "exec/exec-all.h"
69
@@ -XXX,XX +XXX,XX @@ struct page_entry {
70
* See also: page_collection_lock().
71
*/
72
struct page_collection {
73
- GTree *tree;
74
+ QTree *tree;
75
struct page_entry *max;
17
};
76
};
18
77
19
/* Information carried about a condition to be evaluated. */
78
@@ -XXX,XX +XXX,XX @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
20
@@ -XXX,XX +XXX,XX @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
79
struct page_entry *pe;
21
80
PageDesc *pd;
22
dc->cc_op = CC_OP_DYNAMIC;
81
23
dc->ex_value = dc->base.tb->cs_base;
82
- pe = g_tree_lookup(set->tree, &index);
24
- dc->do_debug = dc->base.singlestep_enabled;
83
+ pe = q_tree_lookup(set->tree, &index);
25
}
84
if (pe) {
26
85
return false;
27
static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs)
86
}
28
@@ -XXX,XX +XXX,XX @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
87
@@ -XXX,XX +XXX,XX @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
29
/* FALLTHRU */
88
}
30
case DISAS_PC_CC_UPDATED:
89
31
/* Exit the TB, either by raising a debug exception or by return. */
90
pe = page_entry_new(pd, index);
32
- if (dc->do_debug) {
91
- g_tree_insert(set->tree, &pe->index, pe);
33
- gen_exception(EXCP_DEBUG);
92
+ q_tree_insert(set->tree, &pe->index, pe);
34
- } else if ((dc->base.tb->flags & FLAG_MASK_PER) ||
93
35
- dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) {
94
/*
36
+ if ((dc->base.tb->flags & FLAG_MASK_PER) ||
95
* If this is either (1) the first insertion or (2) a page whose index
37
+ dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) {
96
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
38
tcg_gen_exit_tb(NULL, 0);
97
end >>= TARGET_PAGE_BITS;
39
} else {
98
g_assert(start <= end);
40
tcg_gen_lookup_and_goto_ptr();
99
100
- set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL,
101
+ set->tree = q_tree_new_full(tb_page_addr_cmp, NULL, NULL,
102
page_entry_destroy);
103
set->max = NULL;
104
assert_no_pages_locked();
105
106
retry:
107
- g_tree_foreach(set->tree, page_entry_lock, NULL);
108
+ q_tree_foreach(set->tree, page_entry_lock, NULL);
109
110
for (index = start; index <= end; index++) {
111
TranslationBlock *tb;
112
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
113
continue;
114
}
115
if (page_trylock_add(set, index << TARGET_PAGE_BITS)) {
116
- g_tree_foreach(set->tree, page_entry_unlock, NULL);
117
+ q_tree_foreach(set->tree, page_entry_unlock, NULL);
118
goto retry;
119
}
120
assert_page_locked(pd);
121
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
122
(tb_page_addr1(tb) != -1 &&
123
page_trylock_add(set, tb_page_addr1(tb)))) {
124
/* drop all locks, and reacquire in order */
125
- g_tree_foreach(set->tree, page_entry_unlock, NULL);
126
+ q_tree_foreach(set->tree, page_entry_unlock, NULL);
127
goto retry;
128
}
129
}
130
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
131
static void page_collection_unlock(struct page_collection *set)
132
{
133
/* entries are unlocked and freed via page_entry_destroy */
134
- g_tree_destroy(set->tree);
135
+ q_tree_destroy(set->tree);
136
g_free(set);
137
}
138
139
diff --git a/tcg/region.c b/tcg/region.c
140
index XXXXXXX..XXXXXXX 100644
141
--- a/tcg/region.c
142
+++ b/tcg/region.c
143
@@ -XXX,XX +XXX,XX @@
144
#include "qemu/mprotect.h"
145
#include "qemu/memalign.h"
146
#include "qemu/cacheinfo.h"
147
+#include "qemu/qtree.h"
148
#include "qapi/error.h"
149
#include "exec/exec-all.h"
150
#include "tcg/tcg.h"
151
@@ -XXX,XX +XXX,XX @@
152
153
struct tcg_region_tree {
154
QemuMutex lock;
155
- GTree *tree;
156
+ QTree *tree;
157
/* padding to avoid false sharing is computed at run-time */
158
};
159
160
@@ -XXX,XX +XXX,XX @@ static void tcg_region_trees_init(void)
161
struct tcg_region_tree *rt = region_trees + i * tree_size;
162
163
qemu_mutex_init(&rt->lock);
164
- rt->tree = g_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy);
165
+ rt->tree = q_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy);
166
}
167
}
168
169
@@ -XXX,XX +XXX,XX @@ void tcg_tb_insert(TranslationBlock *tb)
170
171
g_assert(rt != NULL);
172
qemu_mutex_lock(&rt->lock);
173
- g_tree_insert(rt->tree, &tb->tc, tb);
174
+ q_tree_insert(rt->tree, &tb->tc, tb);
175
qemu_mutex_unlock(&rt->lock);
176
}
177
178
@@ -XXX,XX +XXX,XX @@ void tcg_tb_remove(TranslationBlock *tb)
179
180
g_assert(rt != NULL);
181
qemu_mutex_lock(&rt->lock);
182
- g_tree_remove(rt->tree, &tb->tc);
183
+ q_tree_remove(rt->tree, &tb->tc);
184
qemu_mutex_unlock(&rt->lock);
185
}
186
187
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr)
188
}
189
190
qemu_mutex_lock(&rt->lock);
191
- tb = g_tree_lookup(rt->tree, &s);
192
+ tb = q_tree_lookup(rt->tree, &s);
193
qemu_mutex_unlock(&rt->lock);
194
return tb;
195
}
196
@@ -XXX,XX +XXX,XX @@ void tcg_tb_foreach(GTraverseFunc func, gpointer user_data)
197
for (i = 0; i < region.n; i++) {
198
struct tcg_region_tree *rt = region_trees + i * tree_size;
199
200
- g_tree_foreach(rt->tree, func, user_data);
201
+ q_tree_foreach(rt->tree, func, user_data);
202
}
203
tcg_region_tree_unlock_all();
204
}
205
@@ -XXX,XX +XXX,XX @@ size_t tcg_nb_tbs(void)
206
for (i = 0; i < region.n; i++) {
207
struct tcg_region_tree *rt = region_trees + i * tree_size;
208
209
- nb_tbs += g_tree_nnodes(rt->tree);
210
+ nb_tbs += q_tree_nnodes(rt->tree);
211
}
212
tcg_region_tree_unlock_all();
213
return nb_tbs;
214
@@ -XXX,XX +XXX,XX @@ static void tcg_region_tree_reset_all(void)
215
struct tcg_region_tree *rt = region_trees + i * tree_size;
216
217
/* Increment the refcount first so that destroy acts as a reset */
218
- g_tree_ref(rt->tree);
219
- g_tree_destroy(rt->tree);
220
+ q_tree_ref(rt->tree);
221
+ q_tree_destroy(rt->tree);
222
}
223
tcg_region_tree_unlock_all();
224
}
225
diff --git a/util/qtree.c b/util/qtree.c
226
index XXXXXXX..XXXXXXX 100644
227
--- a/util/qtree.c
228
+++ b/util/qtree.c
229
@@ -XXX,XX +XXX,XX @@ q_tree_node_next(QTreeNode *node)
230
*
231
* Since: 2.70 in GLib. Internal in Qtree, i.e. not in the public API.
232
*/
233
-static void
234
+static void QEMU_DISABLE_CFI
235
q_tree_remove_all(QTree *tree)
236
{
237
QTreeNode *node;
238
@@ -XXX,XX +XXX,XX @@ q_tree_replace(QTree *tree,
239
}
240
241
/* internal insert routine */
242
-static QTreeNode *
243
+static QTreeNode * QEMU_DISABLE_CFI
244
q_tree_insert_internal(QTree *tree,
245
gpointer key,
246
gpointer value,
247
@@ -XXX,XX +XXX,XX @@ q_tree_steal(QTree *tree,
248
}
249
250
/* internal remove routine */
251
-static gboolean
252
+static gboolean QEMU_DISABLE_CFI
253
q_tree_remove_internal(QTree *tree,
254
gconstpointer key,
255
gboolean steal)
256
@@ -XXX,XX +XXX,XX @@ q_tree_node_balance(QTreeNode *node)
257
return node;
258
}
259
260
-static QTreeNode *
261
+static QTreeNode * QEMU_DISABLE_CFI
262
q_tree_find_node(QTree *tree,
263
gconstpointer key)
264
{
41
--
265
--
42
2.25.1
266
2.34.1
43
267
44
268
diff view generated by jsdifflib
1
This reverts commit 1b36e4f5a5de585210ea95f2257839c2312be28f.
1
We have been enforcing host page alignment for the non-R
2
fallback of MAX_RESERVED_VA, but failing to enforce for -R.
2
3
3
Despite a comment saying why cpu_common_props cannot be placed in
4
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
4
a file that is compiled once, it was moved anyway. Revert that.
5
6
Since then, Property is not defined in hw/core/cpu.h, so it is now
7
easier to declare a function to install the properties rather than
8
the Property array itself.
9
10
Cc: Eduardo Habkost <ehabkost@redhat.com>
11
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
12
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
13
---
6
---
14
include/hw/core/cpu.h | 1 +
7
linux-user/main.c | 6 ++++++
15
cpu.c | 21 +++++++++++++++++++++
8
1 file changed, 6 insertions(+)
16
hw/core/cpu-common.c | 17 +----------------
17
3 files changed, 23 insertions(+), 16 deletions(-)
18
9
19
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
10
diff --git a/linux-user/main.c b/linux-user/main.c
20
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
21
--- a/include/hw/core/cpu.h
12
--- a/linux-user/main.c
22
+++ b/include/hw/core/cpu.h
13
+++ b/linux-user/main.c
23
@@ -XXX,XX +XXX,XX @@ void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...)
14
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
24
GCC_FMT_ATTR(2, 3);
15
*/
25
16
max_reserved_va = MAX_RESERVED_VA(cpu);
26
/* $(top_srcdir)/cpu.c */
17
if (reserved_va != 0) {
27
+void cpu_class_init_props(DeviceClass *dc);
18
+ if (reserved_va % qemu_host_page_size) {
28
void cpu_exec_initfn(CPUState *cpu);
19
+ char *s = size_to_str(qemu_host_page_size);
29
void cpu_exec_realizefn(CPUState *cpu, Error **errp);
20
+ fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s);
30
void cpu_exec_unrealizefn(CPUState *cpu);
21
+ g_free(s);
31
diff --git a/cpu.c b/cpu.c
22
+ exit(EXIT_FAILURE);
32
index XXXXXXX..XXXXXXX 100644
23
+ }
33
--- a/cpu.c
24
if (max_reserved_va && reserved_va > max_reserved_va) {
34
+++ b/cpu.c
25
fprintf(stderr, "Reserved virtual address too big\n");
35
@@ -XXX,XX +XXX,XX @@ void cpu_exec_unrealizefn(CPUState *cpu)
26
exit(EXIT_FAILURE);
36
cpu_list_remove(cpu);
37
}
38
39
+static Property cpu_common_props[] = {
40
+#ifndef CONFIG_USER_ONLY
41
+ /*
42
+ * Create a memory property for softmmu CPU object,
43
+ * so users can wire up its memory. (This can't go in hw/core/cpu.c
44
+ * because that file is compiled only once for both user-mode
45
+ * and system builds.) The default if no link is set up is to use
46
+ * the system address space.
47
+ */
48
+ DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION,
49
+ MemoryRegion *),
50
+#endif
51
+ DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false),
52
+ DEFINE_PROP_END_OF_LIST(),
53
+};
54
+
55
+void cpu_class_init_props(DeviceClass *dc)
56
+{
57
+ device_class_set_props(dc, cpu_common_props);
58
+}
59
+
60
void cpu_exec_initfn(CPUState *cpu)
61
{
62
cpu->as = NULL;
63
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/hw/core/cpu-common.c
66
+++ b/hw/core/cpu-common.c
67
@@ -XXX,XX +XXX,XX @@ static int64_t cpu_common_get_arch_id(CPUState *cpu)
68
return cpu->cpu_index;
69
}
70
71
-static Property cpu_common_props[] = {
72
-#ifndef CONFIG_USER_ONLY
73
- /* Create a memory property for softmmu CPU object,
74
- * so users can wire up its memory. (This can't go in hw/core/cpu.c
75
- * because that file is compiled only once for both user-mode
76
- * and system builds.) The default if no link is set up is to use
77
- * the system address space.
78
- */
79
- DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION,
80
- MemoryRegion *),
81
-#endif
82
- DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false),
83
- DEFINE_PROP_END_OF_LIST(),
84
-};
85
-
86
static void cpu_class_init(ObjectClass *klass, void *data)
87
{
88
DeviceClass *dc = DEVICE_CLASS(klass);
89
@@ -XXX,XX +XXX,XX @@ static void cpu_class_init(ObjectClass *klass, void *data)
90
dc->realize = cpu_common_realizefn;
91
dc->unrealize = cpu_common_unrealizefn;
92
dc->reset = cpu_common_reset;
93
- device_class_set_props(dc, cpu_common_props);
94
+ cpu_class_init_props(dc);
95
/*
96
* Reason: CPUs still need special care by board code: wiring up
97
* IRQs, adding reset handlers, halting non-first CPUs, ...
98
--
27
--
99
2.25.1
28
2.34.1
100
29
101
30
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Pass the address of the last byte to be changed, rather than
2
2
the first address past the last byte. This avoids overflow
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
3
when the last page of the address space is involved.
4
5
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1528
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
7
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
8
---
6
target/openrisc/translate.c | 18 +++---------------
9
include/exec/cpu-all.h | 2 +-
7
1 file changed, 3 insertions(+), 15 deletions(-)
10
accel/tcg/user-exec.c | 16 +++++++---------
8
11
bsd-user/mmap.c | 6 +++---
9
diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
12
linux-user/elfload.c | 11 ++++++-----
10
index XXXXXXX..XXXXXXX 100644
13
linux-user/mmap.c | 16 ++++++++--------
11
--- a/target/openrisc/translate.c
14
linux-user/syscall.c | 4 ++--
12
+++ b/target/openrisc/translate.c
15
6 files changed, 27 insertions(+), 28 deletions(-)
13
@@ -XXX,XX +XXX,XX @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
16
14
/* The jump destination is indirect/computed; use jmp_pc. */
17
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
15
tcg_gen_mov_tl(cpu_pc, jmp_pc);
18
index XXXXXXX..XXXXXXX 100644
16
tcg_gen_discard_tl(jmp_pc);
19
--- a/include/exec/cpu-all.h
17
- if (unlikely(dc->base.singlestep_enabled)) {
20
+++ b/include/exec/cpu-all.h
18
- gen_exception(dc, EXCP_DEBUG);
21
@@ -XXX,XX +XXX,XX @@ typedef int (*walk_memory_regions_fn)(void *, target_ulong,
19
- } else {
22
int walk_memory_regions(void *, walk_memory_regions_fn);
20
- tcg_gen_lookup_and_goto_ptr();
23
21
- }
24
int page_get_flags(target_ulong address);
22
+ tcg_gen_lookup_and_goto_ptr();
25
-void page_set_flags(target_ulong start, target_ulong end, int flags);
26
+void page_set_flags(target_ulong start, target_ulong last, int flags);
27
void page_reset_target_data(target_ulong start, target_ulong end);
28
int page_check_range(target_ulong start, target_ulong len, int flags);
29
30
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/accel/tcg/user-exec.c
33
+++ b/accel/tcg/user-exec.c
34
@@ -XXX,XX +XXX,XX @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
35
* The flag PAGE_WRITE_ORG is positioned automatically depending
36
* on PAGE_WRITE. The mmap_lock should already be held.
37
*/
38
-void page_set_flags(target_ulong start, target_ulong end, int flags)
39
+void page_set_flags(target_ulong start, target_ulong last, int flags)
40
{
41
- target_ulong last;
42
bool reset = false;
43
bool inval_tb = false;
44
45
/* This function should never be called with addresses outside the
46
guest address space. If this assert fires, it probably indicates
47
a missing call to h2g_valid. */
48
- assert(start < end);
49
- assert(end - 1 <= GUEST_ADDR_MAX);
50
+ assert(start <= last);
51
+ assert(last <= GUEST_ADDR_MAX);
52
/* Only set PAGE_ANON with new mappings. */
53
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
54
assert_memory_lock();
55
56
- start = start & TARGET_PAGE_MASK;
57
- end = TARGET_PAGE_ALIGN(end);
58
- last = end - 1;
59
+ start &= TARGET_PAGE_MASK;
60
+ last |= ~TARGET_PAGE_MASK;
61
62
if (!(flags & PAGE_VALID)) {
63
flags = 0;
64
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
65
}
66
67
if (!flags || reset) {
68
- page_reset_target_data(start, end);
69
+ page_reset_target_data(start, last + 1);
70
inval_tb |= pageflags_unset(start, last);
71
}
72
if (flags) {
73
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
74
~(reset ? 0 : PAGE_STICKY));
75
}
76
if (inval_tb) {
77
- tb_invalidate_phys_range(start, end);
78
+ tb_invalidate_phys_range(start, last + 1);
79
}
80
}
81
82
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
83
index XXXXXXX..XXXXXXX 100644
84
--- a/bsd-user/mmap.c
85
+++ b/bsd-user/mmap.c
86
@@ -XXX,XX +XXX,XX @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
87
if (ret != 0)
88
goto error;
89
}
90
- page_set_flags(start, start + len, prot | PAGE_VALID);
91
+ page_set_flags(start, start + len - 1, prot | PAGE_VALID);
92
mmap_unlock();
93
return 0;
94
error:
95
@@ -XXX,XX +XXX,XX @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
96
}
97
}
98
the_end1:
99
- page_set_flags(start, start + len, prot | PAGE_VALID);
100
+ page_set_flags(start, start + len - 1, prot | PAGE_VALID);
101
the_end:
102
#ifdef DEBUG_MMAP
103
printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
104
@@ -XXX,XX +XXX,XX @@ int target_munmap(abi_ulong start, abi_ulong len)
105
}
106
107
if (ret == 0) {
108
- page_set_flags(start, start + len, 0);
109
+ page_set_flags(start, start + len - 1, 0);
110
}
111
mmap_unlock();
112
return ret;
113
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
114
index XXXXXXX..XXXXXXX 100644
115
--- a/linux-user/elfload.c
116
+++ b/linux-user/elfload.c
117
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
118
exit(EXIT_FAILURE);
119
}
120
page_set_flags(TARGET_VSYSCALL_PAGE,
121
- TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE,
122
+ TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK,
123
PAGE_EXEC | PAGE_VALID);
124
return true;
125
}
126
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
127
exit(EXIT_FAILURE);
128
}
129
130
- page_set_flags(commpage, commpage + qemu_host_page_size,
131
+ page_set_flags(commpage, commpage | ~qemu_host_page_mask,
132
PAGE_READ | PAGE_EXEC | PAGE_VALID);
133
return true;
134
}
135
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
136
exit(EXIT_FAILURE);
137
}
138
139
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE,
140
+ page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
141
PAGE_READ | PAGE_EXEC | PAGE_VALID);
142
return true;
143
}
144
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
145
* and implement syscalls. Here, simply mark the page executable.
146
* Special case the entry points during translation (see do_page_zero).
147
*/
148
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE,
149
+ page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
150
PAGE_EXEC | PAGE_VALID);
151
return true;
152
}
153
@@ -XXX,XX +XXX,XX @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
154
155
/* Ensure that the bss page(s) are valid */
156
if ((page_get_flags(last_bss-1) & prot) != prot) {
157
- page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot | PAGE_VALID);
158
+ page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss - 1,
159
+ prot | PAGE_VALID);
160
}
161
162
if (host_start < host_map_start) {
163
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/linux-user/mmap.c
166
+++ b/linux-user/mmap.c
167
@@ -XXX,XX +XXX,XX @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot)
168
}
169
}
170
171
- page_set_flags(start, start + len, page_flags);
172
+ page_set_flags(start, start + len - 1, page_flags);
173
ret = 0;
174
175
error:
176
@@ -XXX,XX +XXX,XX @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
177
}
178
page_flags |= PAGE_RESET;
179
if (passthrough_start == passthrough_end) {
180
- page_set_flags(start, start + len, page_flags);
181
+ page_set_flags(start, start + len - 1, page_flags);
182
} else {
183
if (start < passthrough_start) {
184
- page_set_flags(start, passthrough_start, page_flags);
185
+ page_set_flags(start, passthrough_start - 1, page_flags);
186
}
187
- page_set_flags(passthrough_start, passthrough_end,
188
+ page_set_flags(passthrough_start, passthrough_end - 1,
189
page_flags | PAGE_PASSTHROUGH);
190
if (passthrough_end < start + len) {
191
- page_set_flags(passthrough_end, start + len, page_flags);
192
+ page_set_flags(passthrough_end, start + len - 1, page_flags);
193
}
194
}
195
the_end:
196
@@ -XXX,XX +XXX,XX @@ int target_munmap(abi_ulong start, abi_ulong len)
197
}
198
199
if (ret == 0) {
200
- page_set_flags(start, start + len, 0);
201
+ page_set_flags(start, start + len - 1, 0);
202
}
203
mmap_unlock();
204
return ret;
205
@@ -XXX,XX +XXX,XX @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
206
} else {
207
new_addr = h2g(host_addr);
208
prot = page_get_flags(old_addr);
209
- page_set_flags(old_addr, old_addr + old_size, 0);
210
- page_set_flags(new_addr, new_addr + new_size,
211
+ page_set_flags(old_addr, old_addr + old_size - 1, 0);
212
+ page_set_flags(new_addr, new_addr + new_size - 1,
213
prot | PAGE_VALID | PAGE_RESET);
214
}
215
mmap_unlock();
216
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
217
index XXXXXXX..XXXXXXX 100644
218
--- a/linux-user/syscall.c
219
+++ b/linux-user/syscall.c
220
@@ -XXX,XX +XXX,XX @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env,
221
}
222
raddr=h2g((unsigned long)host_raddr);
223
224
- page_set_flags(raddr, raddr + shm_info.shm_segsz,
225
+ page_set_flags(raddr, raddr + shm_info.shm_segsz - 1,
226
PAGE_VALID | PAGE_RESET | PAGE_READ |
227
(shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
228
229
@@ -XXX,XX +XXX,XX @@ static inline abi_long do_shmdt(abi_ulong shmaddr)
230
for (i = 0; i < N_SHM_REGIONS; ++i) {
231
if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) {
232
shm_regions[i].in_use = false;
233
- page_set_flags(shmaddr, shmaddr + shm_regions[i].size, 0);
234
+ page_set_flags(shmaddr, shmaddr + shm_regions[i].size - 1, 0);
23
break;
235
break;
24
}
236
}
25
/* The jump destination is direct; use jmp_pc_imm.
237
}
26
@@ -XXX,XX +XXX,XX @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
27
break;
28
}
29
tcg_gen_movi_tl(cpu_pc, jmp_dest);
30
- if (unlikely(dc->base.singlestep_enabled)) {
31
- gen_exception(dc, EXCP_DEBUG);
32
- } else {
33
- tcg_gen_lookup_and_goto_ptr();
34
- }
35
+ tcg_gen_lookup_and_goto_ptr();
36
break;
37
38
case DISAS_EXIT:
39
- if (unlikely(dc->base.singlestep_enabled)) {
40
- gen_exception(dc, EXCP_DEBUG);
41
- } else {
42
- tcg_gen_exit_tb(NULL, 0);
43
- }
44
+ tcg_gen_exit_tb(NULL, 0);
45
break;
46
default:
47
g_assert_not_reached();
48
--
238
--
49
2.25.1
239
2.34.1
50
240
51
241
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Pass the address of the last byte to be changed, rather than
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
2
4
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
7
---
5
target/cris/translate.c | 16 ----------------
8
include/exec/cpu-all.h | 2 +-
6
1 file changed, 16 deletions(-)
9
accel/tcg/user-exec.c | 11 +++++------
10
linux-user/mmap.c | 2 +-
11
3 files changed, 7 insertions(+), 8 deletions(-)
7
12
8
diff --git a/target/cris/translate.c b/target/cris/translate.c
13
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
9
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
10
--- a/target/cris/translate.c
15
--- a/include/exec/cpu-all.h
11
+++ b/target/cris/translate.c
16
+++ b/include/exec/cpu-all.h
12
@@ -XXX,XX +XXX,XX @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
17
@@ -XXX,XX +XXX,XX @@ int walk_memory_regions(void *, walk_memory_regions_fn);
18
19
int page_get_flags(target_ulong address);
20
void page_set_flags(target_ulong start, target_ulong last, int flags);
21
-void page_reset_target_data(target_ulong start, target_ulong end);
22
+void page_reset_target_data(target_ulong start, target_ulong last);
23
int page_check_range(target_ulong start, target_ulong len, int flags);
24
25
/**
26
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/accel/tcg/user-exec.c
29
+++ b/accel/tcg/user-exec.c
30
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
31
}
32
33
if (!flags || reset) {
34
- page_reset_target_data(start, last + 1);
35
+ page_reset_target_data(start, last);
36
inval_tb |= pageflags_unset(start, last);
37
}
38
if (flags) {
39
@@ -XXX,XX +XXX,XX @@ typedef struct TargetPageDataNode {
40
41
static IntervalTreeRoot targetdata_root;
42
43
-void page_reset_target_data(target_ulong start, target_ulong end)
44
+void page_reset_target_data(target_ulong start, target_ulong last)
45
{
46
IntervalTreeNode *n, *next;
47
- target_ulong last;
48
49
assert_memory_lock();
50
51
- start = start & TARGET_PAGE_MASK;
52
- last = TARGET_PAGE_ALIGN(end) - 1;
53
+ start &= TARGET_PAGE_MASK;
54
+ last |= ~TARGET_PAGE_MASK;
55
56
for (n = interval_tree_iter_first(&targetdata_root, start, last),
57
next = n ? interval_tree_iter_next(n, start, last) : NULL;
58
@@ -XXX,XX +XXX,XX @@ void *page_get_target_data(target_ulong address)
59
return t->data[(page - region) >> TARGET_PAGE_BITS];
60
}
61
#else
62
-void page_reset_target_data(target_ulong start, target_ulong end) { }
63
+void page_reset_target_data(target_ulong start, target_ulong last) { }
64
#endif /* TARGET_PAGE_DATA_SIZE */
65
66
/* The softmmu versions of these helpers are in cputlb.c. */
67
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
68
index XXXXXXX..XXXXXXX 100644
69
--- a/linux-user/mmap.c
70
+++ b/linux-user/mmap.c
71
@@ -XXX,XX +XXX,XX @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice)
72
if (can_passthrough_madvise(start, end)) {
73
ret = get_errno(madvise(g2h_untagged(start), len, advice));
74
if ((advice == MADV_DONTNEED) && (ret == 0)) {
75
- page_reset_target_data(start, start + len);
76
+ page_reset_target_data(start, start + len - 1);
77
}
13
}
78
}
14
}
79
}
15
16
- if (unlikely(dc->base.singlestep_enabled)) {
17
- switch (is_jmp) {
18
- case DISAS_TOO_MANY:
19
- case DISAS_UPDATE_NEXT:
20
- tcg_gen_movi_tl(env_pc, npc);
21
- /* fall through */
22
- case DISAS_JUMP:
23
- case DISAS_UPDATE:
24
- t_gen_raise_exception(EXCP_DEBUG);
25
- return;
26
- default:
27
- break;
28
- }
29
- g_assert_not_reached();
30
- }
31
-
32
switch (is_jmp) {
33
case DISAS_TOO_MANY:
34
gen_goto_tb(dc, 0, npc);
35
--
80
--
36
2.25.1
81
2.34.1
37
82
38
83
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Pass the address of the last byte to be changed, rather than
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
2
4
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
7
---
6
target/tricore/helper.h | 1 -
8
accel/tcg/tb-maint.c | 28 ++++++++++++++++------------
7
target/tricore/op_helper.c | 7 -------
9
1 file changed, 16 insertions(+), 12 deletions(-)
8
target/tricore/translate.c | 14 +-------------
9
3 files changed, 1 insertion(+), 21 deletions(-)
10
10
11
diff --git a/target/tricore/helper.h b/target/tricore/helper.h
11
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
12
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
13
--- a/target/tricore/helper.h
13
--- a/accel/tcg/tb-maint.c
14
+++ b/target/tricore/helper.h
14
+++ b/accel/tcg/tb-maint.c
15
@@ -XXX,XX +XXX,XX @@ DEF_HELPER_2(psw_write, void, env, i32)
15
@@ -XXX,XX +XXX,XX @@ static void tb_remove(TranslationBlock *tb)
16
DEF_HELPER_1(psw_read, i32, env)
17
/* Exceptions */
18
DEF_HELPER_3(raise_exception_sync, noreturn, env, i32, i32)
19
-DEF_HELPER_2(qemu_excp, noreturn, env, i32)
20
diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/target/tricore/op_helper.c
23
+++ b/target/tricore/op_helper.c
24
@@ -XXX,XX +XXX,XX @@ static void raise_exception_sync_helper(CPUTriCoreState *env, uint32_t class,
25
raise_exception_sync_internal(env, class, tin, pc, 0);
26
}
16
}
27
17
28
-void helper_qemu_excp(CPUTriCoreState *env, uint32_t excp)
18
/* TODO: For now, still shared with translate-all.c for system mode. */
29
-{
19
-#define PAGE_FOR_EACH_TB(start, end, pagedesc, T, N) \
30
- CPUState *cs = env_cpu(env);
20
- for (T = foreach_tb_first(start, end), \
31
- cs->exception_index = excp;
21
- N = foreach_tb_next(T, start, end); \
32
- cpu_loop_exit(cs);
22
+#define PAGE_FOR_EACH_TB(start, last, pagedesc, T, N) \
33
-}
23
+ for (T = foreach_tb_first(start, last), \
34
-
24
+ N = foreach_tb_next(T, start, last); \
35
/* Addressing mode helper */
25
T != NULL; \
36
26
- T = N, N = foreach_tb_next(N, start, end))
37
static uint16_t reverse16(uint16_t val)
27
+ T = N, N = foreach_tb_next(N, start, last))
38
diff --git a/target/tricore/translate.c b/target/tricore/translate.c
28
39
index XXXXXXX..XXXXXXX 100644
29
typedef TranslationBlock *PageForEachNext;
40
--- a/target/tricore/translate.c
30
41
+++ b/target/tricore/translate.c
31
static PageForEachNext foreach_tb_first(tb_page_addr_t start,
42
@@ -XXX,XX +XXX,XX @@ static inline void gen_save_pc(target_ulong pc)
32
- tb_page_addr_t end)
43
tcg_gen_movi_tl(cpu_PC, pc);
33
+ tb_page_addr_t last)
34
{
35
- IntervalTreeNode *n = interval_tree_iter_first(&tb_root, start, end - 1);
36
+ IntervalTreeNode *n = interval_tree_iter_first(&tb_root, start, last);
37
return n ? container_of(n, TranslationBlock, itree) : NULL;
44
}
38
}
45
39
46
-static void generate_qemu_excp(DisasContext *ctx, int excp)
40
static PageForEachNext foreach_tb_next(PageForEachNext tb,
47
-{
41
tb_page_addr_t start,
48
- TCGv_i32 tmp = tcg_const_i32(excp);
42
- tb_page_addr_t end)
49
- gen_helper_qemu_excp(cpu_env, tmp);
43
+ tb_page_addr_t last)
50
- ctx->base.is_jmp = DISAS_NORETURN;
51
- tcg_temp_free(tmp);
52
-}
53
-
54
static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
55
{
44
{
56
if (translator_use_goto_tb(&ctx->base, dest)) {
45
IntervalTreeNode *n;
57
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
46
58
tcg_gen_exit_tb(ctx->base.tb, n);
47
if (tb) {
59
} else {
48
- n = interval_tree_iter_next(&tb->itree, start, end - 1);
60
gen_save_pc(dest);
49
+ n = interval_tree_iter_next(&tb->itree, start, last);
61
- if (ctx->base.singlestep_enabled) {
50
if (n) {
62
- generate_qemu_excp(ctx, EXCP_DEBUG);
51
return container_of(n, TranslationBlock, itree);
63
- } else {
52
}
64
- tcg_gen_lookup_and_goto_ptr();
53
@@ -XXX,XX +XXX,XX @@ struct page_collection {
65
- }
54
};
66
+ tcg_gen_lookup_and_goto_ptr();
55
56
typedef int PageForEachNext;
57
-#define PAGE_FOR_EACH_TB(start, end, pagedesc, tb, n) \
58
+#define PAGE_FOR_EACH_TB(start, last, pagedesc, tb, n) \
59
TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next)
60
61
#ifdef CONFIG_DEBUG_TCG
62
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
63
{
64
TranslationBlock *tb;
65
PageForEachNext n;
66
+ tb_page_addr_t last = end - 1;
67
68
assert_memory_lock();
69
70
- PAGE_FOR_EACH_TB(start, end, unused, tb, n) {
71
+ PAGE_FOR_EACH_TB(start, last, unused, tb, n) {
72
tb_phys_invalidate__locked(tb);
67
}
73
}
68
}
74
}
69
75
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
76
bool current_tb_modified;
77
TranslationBlock *tb;
78
PageForEachNext n;
79
+ tb_page_addr_t last;
80
81
/*
82
* Without precise smc semantics, or when outside of a TB,
83
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
84
assert_memory_lock();
85
current_tb = tcg_tb_lookup(pc);
86
87
+ last = addr | ~TARGET_PAGE_MASK;
88
addr &= TARGET_PAGE_MASK;
89
current_tb_modified = false;
90
91
- PAGE_FOR_EACH_TB(addr, addr + TARGET_PAGE_SIZE, unused, tb, n) {
92
+ PAGE_FOR_EACH_TB(addr, last, unused, tb, n) {
93
if (current_tb == tb &&
94
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
95
/*
96
@@ -XXX,XX +XXX,XX @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
97
bool current_tb_modified = false;
98
TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL;
99
#endif /* TARGET_HAS_PRECISE_SMC */
100
+ tb_page_addr_t last G_GNUC_UNUSED = end - 1;
101
102
/*
103
* We remove all the TBs in the range [start, end[.
104
* XXX: see if in some cases it could be faster to invalidate all the code
105
*/
106
- PAGE_FOR_EACH_TB(start, end, p, tb, n) {
107
+ PAGE_FOR_EACH_TB(start, last, p, tb, n) {
108
/* NOTE: this is subtle as a TB may span two physical pages */
109
if (n == 0) {
110
/* NOTE: tb_end may be after the end of the page, but
70
--
111
--
71
2.25.1
112
2.34.1
72
113
73
114
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically, which means
1
Pass the address of the last byte to be changed, rather than
2
we don't need to do anything in the wrappers.
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
3
4
4
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
5
Fixes a bug in the loop comparision where "<= end" would lock
6
one more page than required.
7
8
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
9
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
---
10
---
7
target/riscv/translate.c | 27 +------------------
11
accel/tcg/tb-maint.c | 22 +++++++++++-----------
8
.../riscv/insn_trans/trans_privileged.c.inc | 4 +--
12
1 file changed, 11 insertions(+), 11 deletions(-)
9
target/riscv/insn_trans/trans_rvi.c.inc | 8 +++---
10
target/riscv/insn_trans/trans_rvv.c.inc | 2 +-
11
4 files changed, 7 insertions(+), 34 deletions(-)
12
13
13
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
14
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
14
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
15
--- a/target/riscv/translate.c
16
--- a/accel/tcg/tb-maint.c
16
+++ b/target/riscv/translate.c
17
+++ b/accel/tcg/tb-maint.c
17
@@ -XXX,XX +XXX,XX @@ static void generate_exception_mtval(DisasContext *ctx, int excp)
18
@@ -XXX,XX +XXX,XX @@ static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata)
18
ctx->base.is_jmp = DISAS_NORETURN;
19
}
19
}
20
20
21
-static void gen_exception_debug(void)
21
/*
22
-{
22
- * Lock a range of pages ([@start,@end[) as well as the pages of all
23
- gen_helper_raise_exception(cpu_env, tcg_constant_i32(EXCP_DEBUG));
23
+ * Lock a range of pages ([@start,@last]) as well as the pages of all
24
-}
24
* intersecting TBs.
25
-
25
* Locking order: acquire locks in ascending order of page index.
26
-/* Wrapper around tcg_gen_exit_tb that handles single stepping */
26
*/
27
-static void exit_tb(DisasContext *ctx)
27
static struct page_collection *page_collection_lock(tb_page_addr_t start,
28
-{
28
- tb_page_addr_t end)
29
- if (ctx->base.singlestep_enabled) {
29
+ tb_page_addr_t last)
30
- gen_exception_debug();
31
- } else {
32
- tcg_gen_exit_tb(NULL, 0);
33
- }
34
-}
35
-
36
-/* Wrapper around tcg_gen_lookup_and_goto_ptr that handles single stepping */
37
-static void lookup_and_goto_ptr(DisasContext *ctx)
38
-{
39
- if (ctx->base.singlestep_enabled) {
40
- gen_exception_debug();
41
- } else {
42
- tcg_gen_lookup_and_goto_ptr();
43
- }
44
-}
45
-
46
static void gen_exception_illegal(DisasContext *ctx)
47
{
30
{
48
generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST);
31
struct page_collection *set = g_malloc(sizeof(*set));
49
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
32
tb_page_addr_t index;
50
tcg_gen_exit_tb(ctx->base.tb, n);
33
PageDesc *pd;
51
} else {
34
52
tcg_gen_movi_tl(cpu_pc, dest);
35
start >>= TARGET_PAGE_BITS;
53
- lookup_and_goto_ptr(ctx);
36
- end >>= TARGET_PAGE_BITS;
54
+ tcg_gen_lookup_and_goto_ptr();
37
- g_assert(start <= end);
38
+ last >>= TARGET_PAGE_BITS;
39
+ g_assert(start <= last);
40
41
set->tree = q_tree_new_full(tb_page_addr_cmp, NULL, NULL,
42
page_entry_destroy);
43
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
44
retry:
45
q_tree_foreach(set->tree, page_entry_lock, NULL);
46
47
- for (index = start; index <= end; index++) {
48
+ for (index = start; index <= last; index++) {
49
TranslationBlock *tb;
50
PageForEachNext n;
51
52
@@ -XXX,XX +XXX,XX @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
53
void tb_invalidate_phys_page(tb_page_addr_t addr)
54
{
55
struct page_collection *pages;
56
- tb_page_addr_t start, end;
57
+ tb_page_addr_t start, last;
58
PageDesc *p;
59
60
p = page_find(addr >> TARGET_PAGE_BITS);
61
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
55
}
62
}
63
64
start = addr & TARGET_PAGE_MASK;
65
- end = start + TARGET_PAGE_SIZE;
66
- pages = page_collection_lock(start, end);
67
- tb_invalidate_phys_page_range__locked(pages, p, start, end, 0);
68
+ last = addr | ~TARGET_PAGE_MASK;
69
+ pages = page_collection_lock(start, last);
70
+ tb_invalidate_phys_page_range__locked(pages, p, start, last + 1, 0);
71
page_collection_unlock(pages);
56
}
72
}
57
73
58
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
74
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
59
index XXXXXXX..XXXXXXX 100644
75
struct page_collection *pages;
60
--- a/target/riscv/insn_trans/trans_privileged.c.inc
76
tb_page_addr_t next;
61
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
77
62
@@ -XXX,XX +XXX,XX @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
78
- pages = page_collection_lock(start, end);
63
79
+ pages = page_collection_lock(start, end - 1);
64
if (has_ext(ctx, RVS)) {
80
for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
65
gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
81
start < end;
66
- exit_tb(ctx); /* no chaining */
82
start = next, next += TARGET_PAGE_SIZE) {
67
+ tcg_gen_exit_tb(NULL, 0); /* no chaining */
83
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
68
ctx->base.is_jmp = DISAS_NORETURN;
69
} else {
70
return false;
71
@@ -XXX,XX +XXX,XX @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
72
#ifndef CONFIG_USER_ONLY
73
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
74
gen_helper_mret(cpu_pc, cpu_env, cpu_pc);
75
- exit_tb(ctx); /* no chaining */
76
+ tcg_gen_exit_tb(NULL, 0); /* no chaining */
77
ctx->base.is_jmp = DISAS_NORETURN;
78
return true;
79
#else
80
diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
81
index XXXXXXX..XXXXXXX 100644
82
--- a/target/riscv/insn_trans/trans_rvi.c.inc
83
+++ b/target/riscv/insn_trans/trans_rvi.c.inc
84
@@ -XXX,XX +XXX,XX @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
85
if (a->rd != 0) {
86
tcg_gen_movi_tl(cpu_gpr[a->rd], ctx->pc_succ_insn);
87
}
88
-
89
- /* No chaining with JALR. */
90
- lookup_and_goto_ptr(ctx);
91
+ tcg_gen_lookup_and_goto_ptr();
92
93
if (misaligned) {
94
gen_set_label(misaligned);
95
@@ -XXX,XX +XXX,XX @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a)
96
* however we need to end the translation block
97
*/
98
tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
99
- exit_tb(ctx);
100
+ tcg_gen_exit_tb(NULL, 0);
101
ctx->base.is_jmp = DISAS_NORETURN;
102
return true;
103
}
104
@@ -XXX,XX +XXX,XX @@ static bool do_csr_post(DisasContext *ctx)
105
{
84
{
106
/* We may have changed important cpu state -- exit to main loop. */
85
struct page_collection *pages;
107
tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
86
108
- exit_tb(ctx);
87
- pages = page_collection_lock(ram_addr, ram_addr + size);
109
+ tcg_gen_exit_tb(NULL, 0);
88
+ pages = page_collection_lock(ram_addr, ram_addr + size - 1);
110
ctx->base.is_jmp = DISAS_NORETURN;
89
tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr);
111
return true;
90
page_collection_unlock(pages);
112
}
113
diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc
114
index XXXXXXX..XXXXXXX 100644
115
--- a/target/riscv/insn_trans/trans_rvv.c.inc
116
+++ b/target/riscv/insn_trans/trans_rvv.c.inc
117
@@ -XXX,XX +XXX,XX @@ static bool trans_vsetvl(DisasContext *ctx, arg_vsetvl *a)
118
gen_set_gpr(ctx, a->rd, dst);
119
120
tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
121
- lookup_and_goto_ptr(ctx);
122
+ tcg_gen_lookup_and_goto_ptr();
123
ctx->base.is_jmp = DISAS_NORETURN;
124
return true;
125
}
91
}
126
--
92
--
127
2.25.1
93
2.34.1
128
94
129
95
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Pass the address of the last byte to be changed, rather than
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
2
4
5
Properly truncate tb_last to the end of the page; the comment about
6
tb_end being past the end of the page being ok is not correct,
7
considering overflow.
8
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
10
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
11
---
5
target/arm/translate-a64.c | 10 ++--------
12
accel/tcg/tb-maint.c | 26 ++++++++++++--------------
6
target/arm/translate.c | 36 ++++++------------------------------
13
1 file changed, 12 insertions(+), 14 deletions(-)
7
2 files changed, 8 insertions(+), 38 deletions(-)
8
14
9
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
15
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
10
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
11
--- a/target/arm/translate-a64.c
17
--- a/accel/tcg/tb-maint.c
12
+++ b/target/arm/translate-a64.c
18
+++ b/accel/tcg/tb-maint.c
13
@@ -XXX,XX +XXX,XX @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
19
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
14
gen_a64_set_pc_im(dest);
20
static void
15
if (s->ss_active) {
21
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
16
gen_step_complete_exception(s);
22
PageDesc *p, tb_page_addr_t start,
17
- } else if (s->base.singlestep_enabled) {
23
- tb_page_addr_t end,
18
- gen_exception_internal(EXCP_DEBUG);
24
+ tb_page_addr_t last,
25
uintptr_t retaddr)
26
{
27
TranslationBlock *tb;
28
- tb_page_addr_t tb_start, tb_end;
29
PageForEachNext n;
30
#ifdef TARGET_HAS_PRECISE_SMC
31
bool current_tb_modified = false;
32
TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL;
33
#endif /* TARGET_HAS_PRECISE_SMC */
34
- tb_page_addr_t last G_GNUC_UNUSED = end - 1;
35
36
/*
37
- * We remove all the TBs in the range [start, end[.
38
+ * We remove all the TBs in the range [start, last].
39
* XXX: see if in some cases it could be faster to invalidate all the code
40
*/
41
PAGE_FOR_EACH_TB(start, last, p, tb, n) {
42
+ tb_page_addr_t tb_start, tb_last;
43
+
44
/* NOTE: this is subtle as a TB may span two physical pages */
45
+ tb_start = tb_page_addr0(tb);
46
+ tb_last = tb_start + tb->size - 1;
47
if (n == 0) {
48
- /* NOTE: tb_end may be after the end of the page, but
49
- it is not a problem */
50
- tb_start = tb_page_addr0(tb);
51
- tb_end = tb_start + tb->size;
52
+ tb_last = MIN(tb_last, tb_start | ~TARGET_PAGE_MASK);
19
} else {
53
} else {
20
tcg_gen_lookup_and_goto_ptr();
54
tb_start = tb_page_addr1(tb);
21
s->base.is_jmp = DISAS_NORETURN;
55
- tb_end = tb_start + ((tb_page_addr0(tb) + tb->size)
22
@@ -XXX,XX +XXX,XX @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
56
- & ~TARGET_PAGE_MASK);
23
{
57
+ tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK);
24
DisasContext *dc = container_of(dcbase, DisasContext, base);
58
}
25
59
- if (!(tb_end <= start || tb_start >= end)) {
26
- if (unlikely(dc->base.singlestep_enabled || dc->ss_active)) {
60
+ if (!(tb_last < start || tb_start > last)) {
27
+ if (unlikely(dc->ss_active)) {
61
#ifdef TARGET_HAS_PRECISE_SMC
28
/* Note that this means single stepping WFI doesn't halt the CPU.
62
if (current_tb == tb &&
29
* For conditional branch insns this is harmless unreachable code as
63
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
30
* gen_goto_tb() has already handled emitting the debug exception
64
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
31
@@ -XXX,XX +XXX,XX @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
65
start = addr & TARGET_PAGE_MASK;
32
/* fall through */
66
last = addr | ~TARGET_PAGE_MASK;
33
case DISAS_EXIT:
67
pages = page_collection_lock(start, last);
34
case DISAS_JUMP:
68
- tb_invalidate_phys_page_range__locked(pages, p, start, last + 1, 0);
35
- if (dc->base.singlestep_enabled) {
69
+ tb_invalidate_phys_page_range__locked(pages, p, start, last, 0);
36
- gen_exception_internal(EXCP_DEBUG);
70
page_collection_unlock(pages);
37
- } else {
38
- gen_step_complete_exception(dc);
39
- }
40
+ gen_step_complete_exception(dc);
41
break;
42
case DISAS_NORETURN:
43
break;
44
diff --git a/target/arm/translate.c b/target/arm/translate.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/target/arm/translate.c
47
+++ b/target/arm/translate.c
48
@@ -XXX,XX +XXX,XX @@ static void gen_exception_internal(int excp)
49
tcg_temp_free_i32(tcg_excp);
50
}
71
}
51
72
52
-static void gen_step_complete_exception(DisasContext *s)
73
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
53
+static void gen_singlestep_exception(DisasContext *s)
74
continue;
54
{
75
}
55
/* We just completed step of an insn. Move from Active-not-pending
76
assert_page_locked(pd);
56
* to Active-pending, and then also take the swstep exception.
77
- tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0);
57
@@ -XXX,XX +XXX,XX @@ static void gen_step_complete_exception(DisasContext *s)
78
+ tb_invalidate_phys_page_range__locked(pages, pd, start, bound - 1, 0);
58
s->base.is_jmp = DISAS_NORETURN;
79
}
80
page_collection_unlock(pages);
59
}
81
}
60
82
@@ -XXX,XX +XXX,XX @@ static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages,
61
-static void gen_singlestep_exception(DisasContext *s)
62
-{
63
- /* Generate the right kind of exception for singlestep, which is
64
- * either the architectural singlestep or EXCP_DEBUG for QEMU's
65
- * gdb singlestepping.
66
- */
67
- if (s->ss_active) {
68
- gen_step_complete_exception(s);
69
- } else {
70
- gen_exception_internal(EXCP_DEBUG);
71
- }
72
-}
73
-
74
-static inline bool is_singlestepping(DisasContext *s)
75
-{
76
- /* Return true if we are singlestepping either because of
77
- * architectural singlestep or QEMU gdbstub singlestep. This does
78
- * not include the command line '-singlestep' mode which is rather
79
- * misnamed as it only means "one instruction per TB" and doesn't
80
- * affect the code we generate.
81
- */
82
- return s->base.singlestep_enabled || s->ss_active;
83
-}
84
-
85
void clear_eci_state(DisasContext *s)
86
{
87
/*
88
@@ -XXX,XX +XXX,XX @@ static inline void gen_bx_excret_final_code(DisasContext *s)
89
/* Is the new PC value in the magic range indicating exception return? */
90
tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], min_magic, excret_label);
91
/* No: end the TB as we would for a DISAS_JMP */
92
- if (is_singlestepping(s)) {
93
+ if (s->ss_active) {
94
gen_singlestep_exception(s);
95
} else {
96
tcg_gen_exit_tb(NULL, 0);
97
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *s, int n, target_ulong dest)
98
/* Jump, specifying which TB number to use if we gen_goto_tb() */
99
static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno)
100
{
101
- if (unlikely(is_singlestepping(s))) {
102
+ if (unlikely(s->ss_active)) {
103
/* An indirect jump so that we still trigger the debug exception. */
104
gen_set_pc_im(s, dest);
105
s->base.is_jmp = DISAS_JUMP;
106
@@ -XXX,XX +XXX,XX @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
107
dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK;
108
109
/* If architectural single step active, limit to 1. */
110
- if (is_singlestepping(dc)) {
111
+ if (dc->ss_active) {
112
dc->base.max_insns = 1;
113
}
83
}
114
84
115
@@ -XXX,XX +XXX,XX @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
85
assert_page_locked(p);
116
* insn codepath itself.
86
- tb_invalidate_phys_page_range__locked(pages, p, start, start + len, ra);
117
*/
87
+ tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra);
118
gen_bx_excret_final_code(dc);
88
}
119
- } else if (unlikely(is_singlestepping(dc))) {
89
120
+ } else if (unlikely(dc->ss_active)) {
90
/*
121
/* Unconditional and "condition passed" instruction codepath. */
122
switch (dc->base.is_jmp) {
123
case DISAS_SWI:
124
@@ -XXX,XX +XXX,XX @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
125
/* "Condition failed" instruction codepath for the branch/trap insn */
126
gen_set_label(dc->condlabel);
127
gen_set_condexec(dc);
128
- if (unlikely(is_singlestepping(dc))) {
129
+ if (unlikely(dc->ss_active)) {
130
gen_set_pc_im(dc, dc->base.pc_next);
131
gen_singlestep_exception(dc);
132
} else {
133
--
91
--
134
2.25.1
92
2.34.1
135
93
136
94
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Pass the address of the last byte to be changed, rather than
2
Reuse gen_debug_exception to handle architectural debug exceptions.
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
3
4
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
7
---
6
target/ppc/translate.c | 38 ++++++++------------------------------
8
include/exec/exec-all.h | 2 +-
7
1 file changed, 8 insertions(+), 30 deletions(-)
9
accel/tcg/tb-maint.c | 31 ++++++++++++++++---------------
10
accel/tcg/translate-all.c | 2 +-
11
accel/tcg/user-exec.c | 2 +-
12
softmmu/physmem.c | 2 +-
13
5 files changed, 20 insertions(+), 19 deletions(-)
8
14
9
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
15
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
10
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
11
--- a/target/ppc/translate.c
17
--- a/include/exec/exec-all.h
12
+++ b/target/ppc/translate.c
18
+++ b/include/exec/exec-all.h
13
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_addr(target_ulong addr);
14
20
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
15
#define CPU_SINGLE_STEP 0x1
21
#endif
16
#define CPU_BRANCH_STEP 0x2
22
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
17
-#define GDBSTUB_SINGLE_STEP 0x4
23
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
18
24
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last);
19
/* Include definitions for instructions classes and implementations flags */
25
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
20
/* #define PPC_DEBUG_DISAS */
26
21
@@ -XXX,XX +XXX,XX @@ static uint32_t gen_prep_dbgex(DisasContext *ctx)
27
/* GETPC is the true target of the return instruction that we'll execute. */
22
28
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
23
static void gen_debug_exception(DisasContext *ctx)
29
index XXXXXXX..XXXXXXX 100644
30
--- a/accel/tcg/tb-maint.c
31
+++ b/accel/tcg/tb-maint.c
32
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
33
* Called with mmap_lock held for user-mode emulation.
34
* NOTE: this function must not be called while a TB is running.
35
*/
36
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
37
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
24
{
38
{
25
- gen_helper_raise_exception(cpu_env, tcg_constant_i32(EXCP_DEBUG));
39
TranslationBlock *tb;
26
+ gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx)));
40
PageForEachNext n;
27
ctx->base.is_jmp = DISAS_NORETURN;
41
- tb_page_addr_t last = end - 1;
42
43
assert_memory_lock();
44
45
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
46
*/
47
void tb_invalidate_phys_page(tb_page_addr_t addr)
48
{
49
- tb_page_addr_t start, end;
50
+ tb_page_addr_t start, last;
51
52
start = addr & TARGET_PAGE_MASK;
53
- end = start + TARGET_PAGE_SIZE;
54
- tb_invalidate_phys_range(start, end);
55
+ last = addr | ~TARGET_PAGE_MASK;
56
+ tb_invalidate_phys_range(start, last);
28
}
57
}
29
58
30
@@ -XXX,XX +XXX,XX @@ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
59
/*
31
60
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
32
static void gen_lookup_and_goto_ptr(DisasContext *ctx)
61
62
/*
63
* Invalidate all TBs which intersect with the target physical address range
64
- * [start;end[. NOTE: start and end may refer to *different* physical pages.
65
+ * [start;last]. NOTE: start and end may refer to *different* physical pages.
66
* 'is_cpu_write_access' should be true if called from a real cpu write
67
* access: the virtual CPU will exit the current TB if code is modified inside
68
* this TB.
69
*/
70
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
71
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
33
{
72
{
34
- int sse = ctx->singlestep_enabled;
73
struct page_collection *pages;
35
- if (unlikely(sse)) {
74
- tb_page_addr_t next;
36
- if (sse & GDBSTUB_SINGLE_STEP) {
75
+ tb_page_addr_t index, index_last;
37
- gen_debug_exception(ctx);
76
38
- } else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) {
77
- pages = page_collection_lock(start, end - 1);
39
- gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx)));
78
- for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
40
- } else {
79
- start < end;
41
- tcg_gen_exit_tb(NULL, 0);
80
- start = next, next += TARGET_PAGE_SIZE) {
42
- }
81
- PageDesc *pd = page_find(start >> TARGET_PAGE_BITS);
43
+ if (unlikely(ctx->singlestep_enabled)) {
82
- tb_page_addr_t bound = MIN(next, end);
44
+ gen_debug_exception(ctx);
83
+ pages = page_collection_lock(start, last);
45
} else {
84
+
46
tcg_gen_lookup_and_goto_ptr();
85
+ index_last = last >> TARGET_PAGE_BITS;
86
+ for (index = start >> TARGET_PAGE_BITS; index <= index_last; index++) {
87
+ PageDesc *pd = page_find(index);
88
+ tb_page_addr_t bound;
89
90
if (pd == NULL) {
91
continue;
92
}
93
assert_page_locked(pd);
94
- tb_invalidate_phys_page_range__locked(pages, pd, start, bound - 1, 0);
95
+ bound = (index << TARGET_PAGE_BITS) | ~TARGET_PAGE_MASK;
96
+ bound = MIN(bound, last);
97
+ tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0);
47
}
98
}
48
@@ -XXX,XX +XXX,XX @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
99
page_collection_unlock(pages);
49
ctx->singlestep_enabled = 0;
100
}
50
if ((hflags >> HFLAGS_SE) & 1) {
101
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
51
ctx->singlestep_enabled |= CPU_SINGLE_STEP;
102
index XXXXXXX..XXXXXXX 100644
52
+ ctx->base.max_insns = 1;
103
--- a/accel/tcg/translate-all.c
104
+++ b/accel/tcg/translate-all.c
105
@@ -XXX,XX +XXX,XX @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
106
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
107
addr = get_page_addr_code(env, pc);
108
if (addr != -1) {
109
- tb_invalidate_phys_range(addr, addr + 1);
110
+ tb_invalidate_phys_range(addr, addr);
111
}
53
}
112
}
54
if ((hflags >> HFLAGS_BE) & 1) {
113
}
55
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
114
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/accel/tcg/user-exec.c
117
+++ b/accel/tcg/user-exec.c
118
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
119
~(reset ? 0 : PAGE_STICKY));
56
}
120
}
57
- if (unlikely(ctx->base.singlestep_enabled)) {
121
if (inval_tb) {
58
- ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
122
- tb_invalidate_phys_range(start, last + 1);
59
- }
123
+ tb_invalidate_phys_range(start, last);
60
-
124
}
61
- if (ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP)) {
62
- ctx->base.max_insns = 1;
63
- }
64
}
125
}
65
126
66
static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs)
127
diff --git a/softmmu/physmem.c b/softmmu/physmem.c
67
@@ -XXX,XX +XXX,XX @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
128
index XXXXXXX..XXXXXXX 100644
68
DisasContext *ctx = container_of(dcbase, DisasContext, base);
129
--- a/softmmu/physmem.c
69
DisasJumpType is_jmp = ctx->base.is_jmp;
130
+++ b/softmmu/physmem.c
70
target_ulong nip = ctx->base.pc_next;
131
@@ -XXX,XX +XXX,XX @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
71
- int sse;
72
73
if (is_jmp == DISAS_NORETURN) {
74
/* We have already exited the TB. */
75
@@ -XXX,XX +XXX,XX @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
76
}
132
}
77
133
if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) {
78
/* Honor single stepping. */
134
assert(tcg_enabled());
79
- sse = ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP);
135
- tb_invalidate_phys_range(addr, addr + length);
80
- if (unlikely(sse)) {
136
+ tb_invalidate_phys_range(addr, addr + length - 1);
81
+ if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP)
137
dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
82
+ && (nip <= 0x100 || nip > 0xf00)) {
83
switch (is_jmp) {
84
case DISAS_TOO_MANY:
85
case DISAS_EXIT_UPDATE:
86
@@ -XXX,XX +XXX,XX @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
87
g_assert_not_reached();
88
}
89
90
- if (sse & GDBSTUB_SINGLE_STEP) {
91
- gen_debug_exception(ctx);
92
- return;
93
- }
94
- /* else CPU_SINGLE_STEP... */
95
- if (nip <= 0x100 || nip > 0xf00) {
96
- gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx)));
97
- return;
98
- }
99
+ gen_debug_exception(ctx);
100
+ return;
101
}
138
}
102
139
cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
103
switch (is_jmp) {
104
--
140
--
105
2.25.1
141
2.34.1
106
142
107
143
diff view generated by jsdifflib
1
We have already set DISAS_NORETURN in generate_exception,
1
Pass the address of the last byte of the image, rather than
2
which makes the exit_tb unreachable.
2
the first address past the last byte. This avoids overflow
3
when the last page of the address space is involved.
3
4
4
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
---
6
---
7
target/riscv/insn_trans/trans_privileged.c.inc | 6 +-----
7
linux-user/elfload.c | 24 ++++++++++++------------
8
1 file changed, 1 insertion(+), 5 deletions(-)
8
linux-user/flatload.c | 2 +-
9
2 files changed, 13 insertions(+), 13 deletions(-)
9
10
10
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
11
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
11
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
12
--- a/target/riscv/insn_trans/trans_privileged.c.inc
13
--- a/linux-user/elfload.c
13
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
14
+++ b/linux-user/elfload.c
14
@@ -XXX,XX +XXX,XX @@ static bool trans_ecall(DisasContext *ctx, arg_ecall *a)
15
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
15
{
16
if (guest_hiaddr > reserved_va) {
16
/* always generates U-level ECALL, fixed in do_interrupt handler */
17
error_report("%s: requires more than reserved virtual "
17
generate_exception(ctx, RISCV_EXCP_U_ECALL);
18
"address space (0x%" PRIx64 " > 0x%lx)",
18
- exit_tb(ctx); /* no chaining */
19
- image_name, (uint64_t)guest_hiaddr, reserved_va);
19
- ctx->base.is_jmp = DISAS_NORETURN;
20
+ image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
20
return true;
21
exit(EXIT_FAILURE);
22
}
23
} else {
24
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
25
if ((guest_hiaddr - guest_base) > ~(uintptr_t)0) {
26
error_report("%s: requires more virtual address space "
27
"than the host can provide (0x%" PRIx64 ")",
28
- image_name, (uint64_t)guest_hiaddr - guest_base);
29
+ image_name, (uint64_t)guest_hiaddr + 1 - guest_base);
30
exit(EXIT_FAILURE);
31
}
32
#endif
33
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
34
if (reserved_va) {
35
guest_loaddr = (guest_base >= mmap_min_addr ? 0
36
: mmap_min_addr - guest_base);
37
- guest_hiaddr = reserved_va;
38
+ guest_hiaddr = reserved_va - 1;
39
}
40
41
/* Reserve the address space for the binary, or reserved_va. */
42
test = g2h_untagged(guest_loaddr);
43
- addr = mmap(test, guest_hiaddr - guest_loaddr, PROT_NONE, flags, -1, 0);
44
+ addr = mmap(test, guest_hiaddr - guest_loaddr + 1, PROT_NONE, flags, -1, 0);
45
if (test != addr) {
46
pgb_fail_in_use(image_name);
47
}
48
qemu_log_mask(CPU_LOG_PAGE,
49
- "%s: base @ %p for " TARGET_ABI_FMT_ld " bytes\n",
50
- __func__, addr, guest_hiaddr - guest_loaddr);
51
+ "%s: base @ %p for %" PRIu64 " bytes\n",
52
+ __func__, addr, (uint64_t)guest_hiaddr - guest_loaddr + 1);
21
}
53
}
22
54
23
@@ -XXX,XX +XXX,XX @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
55
/**
24
post = opcode_at(&ctx->base, post_addr);
56
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
57
if (hiaddr != orig_hiaddr) {
58
error_report("%s: requires virtual address space that the "
59
"host cannot provide (0x%" PRIx64 ")",
60
- image_name, (uint64_t)orig_hiaddr);
61
+ image_name, (uint64_t)orig_hiaddr + 1);
62
exit(EXIT_FAILURE);
25
}
63
}
26
64
27
- if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
65
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
28
+ if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
66
* arithmetic wraps around.
29
generate_exception(ctx, RISCV_EXCP_SEMIHOST);
67
*/
30
} else {
68
if (sizeof(uintptr_t) == 8 || loaddr >= 0x80000000u) {
31
generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
69
- hiaddr = (uintptr_t) 4 << 30;
70
+ hiaddr = UINT32_MAX;
71
} else {
72
offset = -(HI_COMMPAGE & -align);
73
}
74
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
75
loaddr = MIN(loaddr, LO_COMMPAGE & -align);
32
}
76
}
33
- exit_tb(ctx); /* no chaining */
77
34
- ctx->base.is_jmp = DISAS_NORETURN;
78
- addr = pgb_find_hole(loaddr, hiaddr - loaddr, align, offset);
35
return true;
79
+ addr = pgb_find_hole(loaddr, hiaddr - loaddr + 1, align, offset);
36
}
80
if (addr == -1) {
37
81
/*
82
* If HI_COMMPAGE, there *might* be a non-consecutive allocation
83
@@ -XXX,XX +XXX,XX @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr,
84
if (guest_hiaddr > reserved_va) {
85
error_report("%s: requires more than reserved virtual "
86
"address space (0x%" PRIx64 " > 0x%lx)",
87
- image_name, (uint64_t)guest_hiaddr, reserved_va);
88
+ image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
89
exit(EXIT_FAILURE);
90
}
91
92
@@ -XXX,XX +XXX,XX @@ static void load_elf_image(const char *image_name, int image_fd,
93
if (a < loaddr) {
94
loaddr = a;
95
}
96
- a = eppnt->p_vaddr + eppnt->p_memsz;
97
+ a = eppnt->p_vaddr + eppnt->p_memsz - 1;
98
if (a > hiaddr) {
99
hiaddr = a;
100
}
101
@@ -XXX,XX +XXX,XX @@ static void load_elf_image(const char *image_name, int image_fd,
102
* In both cases, we will overwrite pages in this range with mappings
103
* from the executable.
104
*/
105
- load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
106
+ load_addr = target_mmap(loaddr, (size_t)hiaddr - loaddr + 1, PROT_NONE,
107
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE |
108
(ehdr->e_type == ET_EXEC ? MAP_FIXED : 0),
109
-1, 0);
110
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/linux-user/flatload.c
113
+++ b/linux-user/flatload.c
114
@@ -XXX,XX +XXX,XX @@ static int load_flat_file(struct linux_binprm * bprm,
115
* Allocate the address space.
116
*/
117
probe_guest_base(bprm->filename, 0,
118
- text_len + data_len + extra + indx_len);
119
+ text_len + data_len + extra + indx_len - 1);
120
121
/*
122
* there are a couple of cases here, the separate code/data
38
--
123
--
39
2.25.1
124
2.34.1
40
41
diff view generated by jsdifflib
1
GDB single-stepping is now handled generically.
1
Change the semantics to be the last byte of the guest va, rather
2
2
than the following byte. This avoids some overflow conditions.
3
Acked-by: Laurent Vivier <laurent@vivier.eu>
3
4
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
---
6
target/m68k/translate.c | 44 +++++++++--------------------------------
7
include/exec/cpu-all.h | 11 ++++++++++-
7
1 file changed, 9 insertions(+), 35 deletions(-)
8
linux-user/arm/target_cpu.h | 2 +-
8
9
bsd-user/main.c | 10 +++-------
9
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
10
bsd-user/mmap.c | 4 ++--
10
index XXXXXXX..XXXXXXX 100644
11
linux-user/elfload.c | 14 +++++++-------
11
--- a/target/m68k/translate.c
12
linux-user/main.c | 27 +++++++++++++--------------
12
+++ b/target/m68k/translate.c
13
linux-user/mmap.c | 4 ++--
13
@@ -XXX,XX +XXX,XX @@ static void do_writebacks(DisasContext *s)
14
7 files changed, 38 insertions(+), 34 deletions(-)
14
}
15
16
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/exec/cpu-all.h
19
+++ b/include/exec/cpu-all.h
20
@@ -XXX,XX +XXX,XX @@ static inline void tswap64s(uint64_t *s)
21
*/
22
extern uintptr_t guest_base;
23
extern bool have_guest_base;
24
+
25
+/*
26
+ * If non-zero, the guest virtual address space is a contiguous subset
27
+ * of the host virtual address space, i.e. '-R reserved_va' is in effect
28
+ * either from the command-line or by default. The value is the last
29
+ * byte of the guest address space e.g. UINT32_MAX.
30
+ *
31
+ * If zero, the host and guest virtual address spaces are intermingled.
32
+ */
33
extern unsigned long reserved_va;
34
35
/*
36
@@ -XXX,XX +XXX,XX @@ extern unsigned long reserved_va;
37
#define GUEST_ADDR_MAX_ \
38
((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \
39
UINT32_MAX : ~0ul)
40
-#define GUEST_ADDR_MAX (reserved_va ? reserved_va - 1 : GUEST_ADDR_MAX_)
41
+#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_)
42
43
#else
44
45
diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h
46
index XXXXXXX..XXXXXXX 100644
47
--- a/linux-user/arm/target_cpu.h
48
+++ b/linux-user/arm/target_cpu.h
49
@@ -XXX,XX +XXX,XX @@ static inline unsigned long arm_max_reserved_va(CPUState *cs)
50
* the high addresses. Restrict linux-user to the
51
* cached write-back RAM in the system map.
52
*/
53
- return 0x80000000ul;
54
+ return 0x7ffffffful;
55
} else {
56
/*
57
* We need to be able to map the commpage.
58
diff --git a/bsd-user/main.c b/bsd-user/main.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/bsd-user/main.c
61
+++ b/bsd-user/main.c
62
@@ -XXX,XX +XXX,XX @@ bool have_guest_base;
63
# if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
64
# if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \
65
(TARGET_LONG_BITS == 32 || defined(TARGET_ABI32))
66
-/*
67
- * There are a number of places where we assign reserved_va to a variable
68
- * of type abi_ulong and expect it to fit. Avoid the last page.
69
- */
70
-# define MAX_RESERVED_VA (0xfffffffful & TARGET_PAGE_MASK)
71
+# define MAX_RESERVED_VA 0xfffffffful
72
# else
73
-# define MAX_RESERVED_VA (1ul << TARGET_VIRT_ADDR_SPACE_BITS)
74
+# define MAX_RESERVED_VA ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
75
# endif
76
# else
77
# define MAX_RESERVED_VA 0
78
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
79
envlist_free(envlist);
80
81
if (reserved_va) {
82
- mmap_next_start = reserved_va;
83
+ mmap_next_start = reserved_va + 1;
84
}
85
86
{
87
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/bsd-user/mmap.c
90
+++ b/bsd-user/mmap.c
91
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
92
size = HOST_PAGE_ALIGN(size) + alignment;
93
end_addr = start + size;
94
if (end_addr > reserved_va) {
95
- end_addr = reserved_va;
96
+ end_addr = reserved_va + 1;
97
}
98
addr = end_addr - qemu_host_page_size;
99
100
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
101
if (looped) {
102
return (abi_ulong)-1;
103
}
104
- end_addr = reserved_va;
105
+ end_addr = reserved_va + 1;
106
addr = end_addr - qemu_host_page_size;
107
looped = 1;
108
continue;
109
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
110
index XXXXXXX..XXXXXXX 100644
111
--- a/linux-user/elfload.c
112
+++ b/linux-user/elfload.c
113
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
114
* has specified -R reserved_va, which would trigger an assert().
115
*/
116
if (reserved_va != 0 &&
117
- TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE >= reserved_va) {
118
+ TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) {
119
error_report("Cannot allocate vsyscall page");
120
exit(EXIT_FAILURE);
121
}
122
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
123
if (guest_hiaddr > reserved_va) {
124
error_report("%s: requires more than reserved virtual "
125
"address space (0x%" PRIx64 " > 0x%lx)",
126
- image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
127
+ image_name, (uint64_t)guest_hiaddr, reserved_va);
128
exit(EXIT_FAILURE);
129
}
130
} else {
131
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
132
if (reserved_va) {
133
guest_loaddr = (guest_base >= mmap_min_addr ? 0
134
: mmap_min_addr - guest_base);
135
- guest_hiaddr = reserved_va - 1;
136
+ guest_hiaddr = reserved_va;
137
}
138
139
/* Reserve the address space for the binary, or reserved_va. */
140
@@ -XXX,XX +XXX,XX @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr,
141
if (guest_hiaddr > reserved_va) {
142
error_report("%s: requires more than reserved virtual "
143
"address space (0x%" PRIx64 " > 0x%lx)",
144
- image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
145
+ image_name, (uint64_t)guest_hiaddr, reserved_va);
146
exit(EXIT_FAILURE);
147
}
148
149
@@ -XXX,XX +XXX,XX @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr,
150
/* Reserve the memory on the host. */
151
assert(guest_base != 0);
152
test = g2h_untagged(0);
153
- addr = mmap(test, reserved_va, PROT_NONE, flags, -1, 0);
154
+ addr = mmap(test, reserved_va + 1, PROT_NONE, flags, -1, 0);
155
if (addr == MAP_FAILED || addr != test) {
156
error_report("Unable to reserve 0x%lx bytes of virtual address "
157
"space at %p (%s) for use as guest address space (check your "
158
"virtual memory ulimit setting, min_mmap_addr or reserve less "
159
- "using -R option)", reserved_va, test, strerror(errno));
160
+ "using -R option)", reserved_va + 1, test, strerror(errno));
161
exit(EXIT_FAILURE);
162
}
163
164
qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %p for %lu bytes\n",
165
- __func__, addr, reserved_va);
166
+ __func__, addr, reserved_va + 1);
15
}
167
}
16
168
17
-static bool is_singlestepping(DisasContext *s)
169
void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
18
-{
170
diff --git a/linux-user/main.c b/linux-user/main.c
19
- /*
171
index XXXXXXX..XXXXXXX 100644
20
- * Return true if we are singlestepping either because of
172
--- a/linux-user/main.c
21
- * architectural singlestep or QEMU gdbstub singlestep. This does
173
+++ b/linux-user/main.c
22
- * not include the command line '-singlestep' mode which is rather
174
@@ -XXX,XX +XXX,XX @@ static const char *last_log_filename;
23
- * misnamed as it only means "one instruction per TB" and doesn't
175
# if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
24
- * affect the code we generate.
176
# if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \
25
- */
177
(TARGET_LONG_BITS == 32 || defined(TARGET_ABI32))
26
- return s->base.singlestep_enabled || s->ss_active;
178
-/* There are a number of places where we assign reserved_va to a variable
27
-}
179
- of type abi_ulong and expect it to fit. Avoid the last page. */
28
-
180
-# define MAX_RESERVED_VA(CPU) (0xfffffffful & TARGET_PAGE_MASK)
29
/* is_jmp field values */
181
+# define MAX_RESERVED_VA(CPU) 0xfffffffful
30
#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
182
# else
31
#define DISAS_EXIT DISAS_TARGET_1 /* cpu state was modified dynamically */
183
-# define MAX_RESERVED_VA(CPU) (1ul << TARGET_VIRT_ADDR_SPACE_BITS)
32
@@ -XXX,XX +XXX,XX @@ static void gen_exception(DisasContext *s, uint32_t dest, int nr)
184
+# define MAX_RESERVED_VA(CPU) ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
33
s->base.is_jmp = DISAS_NORETURN;
185
# endif
186
# else
187
# define MAX_RESERVED_VA(CPU) 0
188
@@ -XXX,XX +XXX,XX @@ static void handle_arg_reserved_va(const char *arg)
189
{
190
char *p;
191
int shift = 0;
192
- reserved_va = strtoul(arg, &p, 0);
193
+ unsigned long val;
194
+
195
+ val = strtoul(arg, &p, 0);
196
switch (*p) {
197
case 'k':
198
case 'K':
199
@@ -XXX,XX +XXX,XX @@ static void handle_arg_reserved_va(const char *arg)
200
break;
201
}
202
if (shift) {
203
- unsigned long unshifted = reserved_va;
204
+ unsigned long unshifted = val;
205
p++;
206
- reserved_va <<= shift;
207
- if (reserved_va >> shift != unshifted) {
208
+ val <<= shift;
209
+ if (val >> shift != unshifted) {
210
fprintf(stderr, "Reserved virtual address too big\n");
211
exit(EXIT_FAILURE);
212
}
213
@@ -XXX,XX +XXX,XX @@ static void handle_arg_reserved_va(const char *arg)
214
fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
215
exit(EXIT_FAILURE);
216
}
217
+ /* The representation is size - 1, with 0 remaining "default". */
218
+ reserved_va = val ? val - 1 : 0;
34
}
219
}
35
220
36
-static void gen_singlestep_exception(DisasContext *s)
221
static void handle_arg_singlestep(const char *arg)
37
-{
222
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
38
- /*
223
*/
39
- * Generate the right kind of exception for singlestep, which is
224
max_reserved_va = MAX_RESERVED_VA(cpu);
40
- * either the architectural singlestep or EXCP_DEBUG for QEMU's
225
if (reserved_va != 0) {
41
- * gdb singlestepping.
226
- if (reserved_va % qemu_host_page_size) {
42
- */
227
+ if ((reserved_va + 1) % qemu_host_page_size) {
43
- if (s->ss_active) {
228
char *s = size_to_str(qemu_host_page_size);
44
- gen_raise_exception(EXCP_TRACE);
229
fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s);
45
- } else {
230
g_free(s);
46
- gen_raise_exception(EXCP_DEBUG);
231
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
47
- }
232
exit(EXIT_FAILURE);
48
-}
233
}
49
-
234
} else if (HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32) {
50
static inline void gen_addr_fault(DisasContext *s)
235
- /*
51
{
236
- * reserved_va must be aligned with the host page size
52
gen_exception(s, s->base.pc_next, EXCP_ADDRESS);
237
- * as it is used with mmap()
53
@@ -XXX,XX +XXX,XX @@ static void gen_exit_tb(DisasContext *s)
238
- */
54
/* Generate a jump to an immediate address. */
239
- reserved_va = max_reserved_va & qemu_host_page_mask;
55
static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
240
+ /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */
56
{
241
+ reserved_va = max_reserved_va;
57
- if (unlikely(is_singlestepping(s))) {
242
}
58
+ if (unlikely(s->ss_active)) {
243
59
update_cc_op(s);
244
{
60
tcg_gen_movi_i32(QREG_PC, dest);
245
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
61
- gen_singlestep_exception(s);
246
index XXXXXXX..XXXXXXX 100644
62
+ gen_raise_exception(EXCP_TRACE);
247
--- a/linux-user/mmap.c
63
} else if (translator_use_goto_tb(&s->base, dest)) {
248
+++ b/linux-user/mmap.c
64
tcg_gen_goto_tb(n);
249
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
65
tcg_gen_movi_i32(QREG_PC, dest);
250
end_addr = start + size;
66
@@ -XXX,XX +XXX,XX @@ static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
251
if (start > reserved_va - size) {
67
252
/* Start at the top of the address space. */
68
dc->ss_active = (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS);
253
- end_addr = ((reserved_va - size) & -align) + size;
69
/* If architectural single step active, limit to 1 */
254
+ end_addr = ((reserved_va + 1 - size) & -align) + size;
70
- if (is_singlestepping(dc)) {
255
looped = true;
71
+ if (dc->ss_active) {
256
}
72
dc->base.max_insns = 1;
257
73
}
258
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
74
}
259
return (abi_ulong)-1;
75
@@ -XXX,XX +XXX,XX @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
260
}
76
break;
261
/* Re-start at the top of the address space. */
77
case DISAS_TOO_MANY:
262
- addr = end_addr = ((reserved_va - size) & -align) + size;
78
update_cc_op(dc);
263
+ addr = end_addr = ((reserved_va + 1 - size) & -align) + size;
79
- if (is_singlestepping(dc)) {
264
looped = true;
80
+ if (dc->ss_active) {
81
tcg_gen_movi_i32(QREG_PC, dc->pc);
82
- gen_singlestep_exception(dc);
83
+ gen_raise_exception(EXCP_TRACE);
84
} else {
265
} else {
85
gen_jmp_tb(dc, 0, dc->pc);
266
prot = page_get_flags(addr);
86
}
87
break;
88
case DISAS_JUMP:
89
/* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */
90
- if (is_singlestepping(dc)) {
91
- gen_singlestep_exception(dc);
92
+ if (dc->ss_active) {
93
+ gen_raise_exception(EXCP_TRACE);
94
} else {
95
tcg_gen_lookup_and_goto_ptr();
96
}
97
@@ -XXX,XX +XXX,XX @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
98
* We updated CC_OP and PC in gen_exit_tb, but also modified
99
* other state that may require returning to the main loop.
100
*/
101
- if (is_singlestepping(dc)) {
102
- gen_singlestep_exception(dc);
103
+ if (dc->ss_active) {
104
+ gen_raise_exception(EXCP_TRACE);
105
} else {
106
tcg_gen_exit_tb(NULL, 0);
107
}
108
--
267
--
109
2.25.1
268
2.34.1
110
269
111
270
diff view generated by jsdifflib
1
Currently the change in cpu_tb_exec is masked by the debug exception
1
User setting of -R reserved_va can lead to an assertion
2
being raised by the translators. But this allows us to remove that code.
2
failure in page_set_flags. Sanity check the value of
3
reserved_va and print an error message instead. Do not
4
allocate a commpage at all for m-profile cpus.
3
5
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
7
---
6
accel/tcg/cpu-exec.c | 11 +++++++++++
8
linux-user/elfload.c | 37 +++++++++++++++++++++++++++----------
7
1 file changed, 11 insertions(+)
9
1 file changed, 27 insertions(+), 10 deletions(-)
8
10
9
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
11
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
10
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
11
--- a/accel/tcg/cpu-exec.c
13
--- a/linux-user/elfload.c
12
+++ b/accel/tcg/cpu-exec.c
14
+++ b/linux-user/elfload.c
13
@@ -XXX,XX +XXX,XX @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
15
@@ -XXX,XX +XXX,XX @@ enum {
14
cc->set_pc(cpu, last_tb->pc);
16
15
}
17
static bool init_guest_commpage(void)
16
}
18
{
19
- abi_ptr commpage = HI_COMMPAGE & -qemu_host_page_size;
20
- void *want = g2h_untagged(commpage);
21
- void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE,
22
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
23
+ ARMCPU *cpu = ARM_CPU(thread_cpu);
24
+ abi_ptr want = HI_COMMPAGE & TARGET_PAGE_MASK;
25
+ abi_ptr addr;
26
27
- if (addr == MAP_FAILED) {
28
+ /*
29
+ * M-profile allocates maximum of 2GB address space, so can never
30
+ * allocate the commpage. Skip it.
31
+ */
32
+ if (arm_feature(&cpu->env, ARM_FEATURE_M)) {
33
+ return true;
34
+ }
17
+
35
+
18
+ /*
36
+ /*
19
+ * If gdb single-step, and we haven't raised another exception,
37
+ * If reserved_va does not cover the commpage, we get an assert
20
+ * raise a debug exception. Single-step with another exception
38
+ * in page_set_flags. Produce an intelligent error instead.
21
+ * is handled in cpu_handle_exception.
22
+ */
39
+ */
23
+ if (unlikely(cpu->singlestep_enabled) && cpu->exception_index == -1) {
40
+ if (reserved_va != 0 && want + TARGET_PAGE_SIZE - 1 > reserved_va) {
24
+ cpu->exception_index = EXCP_DEBUG;
41
+ error_report("Allocating guest commpage: -R 0x%" PRIx64 " too small",
25
+ cpu_loop_exit(cpu);
42
+ (uint64_t)reserved_va + 1);
43
+ exit(EXIT_FAILURE);
26
+ }
44
+ }
27
+
45
+
28
return last_tb;
46
+ addr = target_mmap(want, TARGET_PAGE_SIZE, PROT_READ | PROT_WRITE,
47
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
48
+
49
+ if (addr == -1) {
50
perror("Allocating guest commpage");
51
exit(EXIT_FAILURE);
52
}
53
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
54
}
55
56
/* Set kernel helper versions; rest of page is 0. */
57
- __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu));
58
+ put_user_u32(5, 0xffff0ffcu);
59
60
- if (mprotect(addr, qemu_host_page_size, PROT_READ)) {
61
+ if (target_mprotect(addr, qemu_host_page_size, PROT_READ | PROT_EXEC)) {
62
perror("Protecting guest commpage");
63
exit(EXIT_FAILURE);
64
}
65
-
66
- page_set_flags(commpage, commpage | ~qemu_host_page_mask,
67
- PAGE_READ | PAGE_EXEC | PAGE_VALID);
68
return true;
29
}
69
}
30
70
31
--
71
--
32
2.25.1
72
2.34.1
33
34
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/alpha/translate.c | 13 +++----------
7
1 file changed, 3 insertions(+), 10 deletions(-)
8
9
diff --git a/target/alpha/translate.c b/target/alpha/translate.c
10
index XXXXXXX..XXXXXXX 100644
11
--- a/target/alpha/translate.c
12
+++ b/target/alpha/translate.c
13
@@ -XXX,XX +XXX,XX @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
14
tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
15
/* FALLTHRU */
16
case DISAS_PC_UPDATED:
17
- if (!ctx->base.singlestep_enabled) {
18
- tcg_gen_lookup_and_goto_ptr();
19
- break;
20
- }
21
- /* FALLTHRU */
22
+ tcg_gen_lookup_and_goto_ptr();
23
+ break;
24
case DISAS_PC_UPDATED_NOCHAIN:
25
- if (ctx->base.singlestep_enabled) {
26
- gen_excp_1(EXCP_DEBUG, 0);
27
- } else {
28
- tcg_gen_exit_tb(NULL, 0);
29
- }
30
+ tcg_gen_exit_tb(NULL, 0);
31
break;
32
default:
33
g_assert_not_reached();
34
--
35
2.25.1
36
37
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Tested-by: Michael Rolnik <mrolnik@gmail.com>
4
Reviewed-by: Michael Rolnik <mrolnik@gmail.com>
5
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
7
---
8
target/avr/translate.c | 19 ++++---------------
9
1 file changed, 4 insertions(+), 15 deletions(-)
10
11
diff --git a/target/avr/translate.c b/target/avr/translate.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/target/avr/translate.c
14
+++ b/target/avr/translate.c
15
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
16
tcg_gen_exit_tb(tb, n);
17
} else {
18
tcg_gen_movi_i32(cpu_pc, dest);
19
- if (ctx->base.singlestep_enabled) {
20
- gen_helper_debug(cpu_env);
21
- } else {
22
- tcg_gen_lookup_and_goto_ptr();
23
- }
24
+ tcg_gen_lookup_and_goto_ptr();
25
}
26
ctx->base.is_jmp = DISAS_NORETURN;
27
}
28
@@ -XXX,XX +XXX,XX @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
29
tcg_gen_movi_tl(cpu_pc, ctx->npc);
30
/* fall through */
31
case DISAS_LOOKUP:
32
- if (!ctx->base.singlestep_enabled) {
33
- tcg_gen_lookup_and_goto_ptr();
34
- break;
35
- }
36
- /* fall through */
37
+ tcg_gen_lookup_and_goto_ptr();
38
+ break;
39
case DISAS_EXIT:
40
- if (ctx->base.singlestep_enabled) {
41
- gen_helper_debug(cpu_env);
42
- } else {
43
- tcg_gen_exit_tb(NULL, 0);
44
- }
45
+ tcg_gen_exit_tb(NULL, 0);
46
break;
47
default:
48
g_assert_not_reached();
49
--
50
2.25.1
51
52
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/hexagon/translate.c | 12 ++----------
7
1 file changed, 2 insertions(+), 10 deletions(-)
8
9
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
10
index XXXXXXX..XXXXXXX 100644
11
--- a/target/hexagon/translate.c
12
+++ b/target/hexagon/translate.c
13
@@ -XXX,XX +XXX,XX @@ static void gen_end_tb(DisasContext *ctx)
14
{
15
gen_exec_counters(ctx);
16
tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
17
- if (ctx->base.singlestep_enabled) {
18
- gen_exception_raw(EXCP_DEBUG);
19
- } else {
20
- tcg_gen_exit_tb(NULL, 0);
21
- }
22
+ tcg_gen_exit_tb(NULL, 0);
23
ctx->base.is_jmp = DISAS_NORETURN;
24
}
25
26
@@ -XXX,XX +XXX,XX @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
27
case DISAS_TOO_MANY:
28
gen_exec_counters(ctx);
29
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
30
- if (ctx->base.singlestep_enabled) {
31
- gen_exception_raw(EXCP_DEBUG);
32
- } else {
33
- tcg_gen_exit_tb(NULL, 0);
34
- }
35
+ tcg_gen_exit_tb(NULL, 0);
36
break;
37
case DISAS_NORETURN:
38
break;
39
--
40
2.25.1
41
42
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/hppa/translate.c | 17 ++++-------------
7
1 file changed, 4 insertions(+), 13 deletions(-)
8
9
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
10
index XXXXXXX..XXXXXXX 100644
11
--- a/target/hppa/translate.c
12
+++ b/target/hppa/translate.c
13
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int which,
14
} else {
15
copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b);
16
copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var);
17
- if (ctx->base.singlestep_enabled) {
18
- gen_excp_1(EXCP_DEBUG);
19
- } else {
20
- tcg_gen_lookup_and_goto_ptr();
21
- }
22
+ tcg_gen_lookup_and_goto_ptr();
23
}
24
}
25
26
@@ -XXX,XX +XXX,XX @@ static bool do_rfi(DisasContext *ctx, bool rfi_r)
27
gen_helper_rfi(cpu_env);
28
}
29
/* Exit the TB to recognize new interrupts. */
30
- if (ctx->base.singlestep_enabled) {
31
- gen_excp_1(EXCP_DEBUG);
32
- } else {
33
- tcg_gen_exit_tb(NULL, 0);
34
- }
35
+ tcg_gen_exit_tb(NULL, 0);
36
ctx->base.is_jmp = DISAS_NORETURN;
37
38
return nullify_end(ctx);
39
@@ -XXX,XX +XXX,XX @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
40
nullify_save(ctx);
41
/* FALLTHRU */
42
case DISAS_IAQ_N_UPDATED:
43
- if (ctx->base.singlestep_enabled) {
44
- gen_excp_1(EXCP_DEBUG);
45
- } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
46
+ if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
47
tcg_gen_lookup_and_goto_ptr();
48
+ break;
49
}
50
/* FALLTHRU */
51
case DISAS_EXIT:
52
--
53
2.25.1
54
55
diff view generated by jsdifflib
Deleted patch
1
We were using singlestep_enabled as a proxy for whether
2
translator_use_goto_tb would always return false.
3
1
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/i386/tcg/translate.c | 5 +++--
7
1 file changed, 3 insertions(+), 2 deletions(-)
8
9
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
10
index XXXXXXX..XXXXXXX 100644
11
--- a/target/i386/tcg/translate.c
12
+++ b/target/i386/tcg/translate.c
13
@@ -XXX,XX +XXX,XX @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
14
DisasContext *dc = container_of(dcbase, DisasContext, base);
15
CPUX86State *env = cpu->env_ptr;
16
uint32_t flags = dc->base.tb->flags;
17
+ uint32_t cflags = tb_cflags(dc->base.tb);
18
int cpl = (flags >> HF_CPL_SHIFT) & 3;
19
int iopl = (flags >> IOPL_SHIFT) & 3;
20
21
@@ -XXX,XX +XXX,XX @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
22
dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX];
23
dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX];
24
dc->cpuid_xsave_features = env->features[FEAT_XSAVE];
25
- dc->jmp_opt = !(dc->base.singlestep_enabled ||
26
+ dc->jmp_opt = !((cflags & CF_NO_GOTO_TB) ||
27
(flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)));
28
/*
29
* If jmp_opt, we want to handle each string instruction individually.
30
* For icount also disable repz optimization so that each iteration
31
* is accounted separately.
32
*/
33
- dc->repz_opt = !dc->jmp_opt && !(tb_cflags(dc->base.tb) & CF_USE_ICOUNT);
34
+ dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT);
35
36
dc->T0 = tcg_temp_new();
37
dc->T1 = tcg_temp_new();
38
--
39
2.25.1
40
41
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
5
target/i386/helper.h | 1 -
6
target/i386/tcg/misc_helper.c | 8 --------
7
target/i386/tcg/translate.c | 4 +---
8
3 files changed, 1 insertion(+), 12 deletions(-)
9
10
diff --git a/target/i386/helper.h b/target/i386/helper.h
11
index XXXXXXX..XXXXXXX 100644
12
--- a/target/i386/helper.h
13
+++ b/target/i386/helper.h
14
@@ -XXX,XX +XXX,XX @@ DEF_HELPER_2(syscall, void, env, int)
15
DEF_HELPER_2(sysret, void, env, int)
16
#endif
17
DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int)
18
-DEF_HELPER_FLAGS_1(debug, TCG_CALL_NO_WG, noreturn, env)
19
DEF_HELPER_1(reset_rf, void, env)
20
DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int)
21
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int)
22
diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/target/i386/tcg/misc_helper.c
25
+++ b/target/i386/tcg/misc_helper.c
26
@@ -XXX,XX +XXX,XX @@ void QEMU_NORETURN helper_pause(CPUX86State *env, int next_eip_addend)
27
do_pause(env);
28
}
29
30
-void QEMU_NORETURN helper_debug(CPUX86State *env)
31
-{
32
- CPUState *cs = env_cpu(env);
33
-
34
- cs->exception_index = EXCP_DEBUG;
35
- cpu_loop_exit(cs);
36
-}
37
-
38
uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx)
39
{
40
if ((env->cr[4] & CR4_PKE_MASK) == 0) {
41
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/target/i386/tcg/translate.c
44
+++ b/target/i386/tcg/translate.c
45
@@ -XXX,XX +XXX,XX @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr)
46
if (s->base.tb->flags & HF_RF_MASK) {
47
gen_helper_reset_rf(cpu_env);
48
}
49
- if (s->base.singlestep_enabled) {
50
- gen_helper_debug(cpu_env);
51
- } else if (recheck_tf) {
52
+ if (recheck_tf) {
53
gen_helper_rechecking_single_step(cpu_env);
54
tcg_gen_exit_tb(NULL, 0);
55
} else if (s->flags & HF_TF_MASK) {
56
--
57
2.25.1
58
59
diff view generated by jsdifflib
Deleted patch
1
We were using singlestep_enabled as a proxy for whether
2
translator_use_goto_tb would always return false.
3
1
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/microblaze/translate.c | 4 ++--
7
1 file changed, 2 insertions(+), 2 deletions(-)
8
9
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c
10
index XXXXXXX..XXXXXXX 100644
11
--- a/target/microblaze/translate.c
12
+++ b/target/microblaze/translate.c
13
@@ -XXX,XX +XXX,XX @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
14
break;
15
16
case DISAS_JUMP:
17
- if (dc->jmp_dest != -1 && !cs->singlestep_enabled) {
18
+ if (dc->jmp_dest != -1 && !(tb_cflags(dc->base.tb) & CF_NO_GOTO_TB)) {
19
/* Direct jump. */
20
tcg_gen_discard_i32(cpu_btarget);
21
22
@@ -XXX,XX +XXX,XX @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
23
return;
24
}
25
26
- /* Indirect jump (or direct jump w/ singlestep) */
27
+ /* Indirect jump (or direct jump w/ goto_tb disabled) */
28
tcg_gen_mov_i32(cpu_pc, cpu_btarget);
29
tcg_gen_discard_i32(cpu_btarget);
30
31
--
32
2.25.1
33
34
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
5
target/microblaze/translate.c | 14 ++------------
6
1 file changed, 2 insertions(+), 12 deletions(-)
7
8
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c
9
index XXXXXXX..XXXXXXX 100644
10
--- a/target/microblaze/translate.c
11
+++ b/target/microblaze/translate.c
12
@@ -XXX,XX +XXX,XX @@ static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec)
13
14
static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
15
{
16
- if (dc->base.singlestep_enabled) {
17
- TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG);
18
- tcg_gen_movi_i32(cpu_pc, dest);
19
- gen_helper_raise_exception(cpu_env, tmp);
20
- tcg_temp_free_i32(tmp);
21
- } else if (translator_use_goto_tb(&dc->base, dest)) {
22
+ if (translator_use_goto_tb(&dc->base, dest)) {
23
tcg_gen_goto_tb(n);
24
tcg_gen_movi_i32(cpu_pc, dest);
25
tcg_gen_exit_tb(dc->base.tb, n);
26
@@ -XXX,XX +XXX,XX @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
27
/* Indirect jump (or direct jump w/ goto_tb disabled) */
28
tcg_gen_mov_i32(cpu_pc, cpu_btarget);
29
tcg_gen_discard_i32(cpu_btarget);
30
-
31
- if (unlikely(cs->singlestep_enabled)) {
32
- gen_raise_exception(dc, EXCP_DEBUG);
33
- } else {
34
- tcg_gen_lookup_and_goto_ptr();
35
- }
36
+ tcg_gen_lookup_and_goto_ptr();
37
return;
38
39
default:
40
--
41
2.25.1
42
43
diff view generated by jsdifflib
Deleted patch
1
As per an ancient comment in mips_tr_translate_insn about the
2
expectations of gdb, when restarting the insn in a delay slot
3
we also re-execute the branch. Which means that we are
4
expected to execute two insns in this case.
5
1
6
This has been broken since 8b86d6d2580, where we forced max_insns
7
to 1 while single-stepping. This resulted in an exit from the
8
translator loop after the branch but before the delay slot is
9
translated.
10
11
Increase the max_insns to 2 for this case. In addition, bypass
12
the end-of-page check, for when the branch itself ends the page.
13
14
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
15
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
16
---
17
target/mips/tcg/translate.c | 25 ++++++++++++++++---------
18
1 file changed, 16 insertions(+), 9 deletions(-)
19
20
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/target/mips/tcg/translate.c
23
+++ b/target/mips/tcg/translate.c
24
@@ -XXX,XX +XXX,XX @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
25
ctx->default_tcg_memop_mask = (ctx->insn_flags & (ISA_MIPS_R6 |
26
INSN_LOONGSON3A)) ? MO_UNALN : MO_ALIGN;
27
28
+ /*
29
+ * Execute a branch and its delay slot as a single instruction.
30
+ * This is what GDB expects and is consistent with what the
31
+ * hardware does (e.g. if a delay slot instruction faults, the
32
+ * reported PC is the PC of the branch).
33
+ */
34
+ if (ctx->base.singlestep_enabled && (ctx->hflags & MIPS_HFLAG_BMASK)) {
35
+ ctx->base.max_insns = 2;
36
+ }
37
+
38
LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx,
39
ctx->hflags);
40
}
41
@@ -XXX,XX +XXX,XX @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
42
if (ctx->base.is_jmp != DISAS_NEXT) {
43
return;
44
}
45
+
46
/*
47
- * Execute a branch and its delay slot as a single instruction.
48
- * This is what GDB expects and is consistent with what the
49
- * hardware does (e.g. if a delay slot instruction faults, the
50
- * reported PC is the PC of the branch).
51
+ * End the TB on (most) page crossings.
52
+ * See mips_tr_init_disas_context about single-stepping a branch
53
+ * together with its delay slot.
54
*/
55
- if (ctx->base.singlestep_enabled &&
56
- (ctx->hflags & MIPS_HFLAG_BMASK) == 0) {
57
- ctx->base.is_jmp = DISAS_TOO_MANY;
58
- }
59
- if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE) {
60
+ if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE
61
+ && !ctx->base.singlestep_enabled) {
62
ctx->base.is_jmp = DISAS_TOO_MANY;
63
}
64
}
65
--
66
2.25.1
67
68
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/rx/helper.h | 1 -
7
target/rx/op_helper.c | 8 --------
8
target/rx/translate.c | 12 ++----------
9
3 files changed, 2 insertions(+), 19 deletions(-)
10
11
diff --git a/target/rx/helper.h b/target/rx/helper.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/target/rx/helper.h
14
+++ b/target/rx/helper.h
15
@@ -XXX,XX +XXX,XX @@ DEF_HELPER_1(raise_illegal_instruction, noreturn, env)
16
DEF_HELPER_1(raise_access_fault, noreturn, env)
17
DEF_HELPER_1(raise_privilege_violation, noreturn, env)
18
DEF_HELPER_1(wait, noreturn, env)
19
-DEF_HELPER_1(debug, noreturn, env)
20
DEF_HELPER_2(rxint, noreturn, env, i32)
21
DEF_HELPER_1(rxbrk, noreturn, env)
22
DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32)
23
diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/target/rx/op_helper.c
26
+++ b/target/rx/op_helper.c
27
@@ -XXX,XX +XXX,XX @@ void QEMU_NORETURN helper_wait(CPURXState *env)
28
raise_exception(env, EXCP_HLT, 0);
29
}
30
31
-void QEMU_NORETURN helper_debug(CPURXState *env)
32
-{
33
- CPUState *cs = env_cpu(env);
34
-
35
- cs->exception_index = EXCP_DEBUG;
36
- cpu_loop_exit(cs);
37
-}
38
-
39
void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec)
40
{
41
raise_exception(env, 0x100 + vec, 0);
42
diff --git a/target/rx/translate.c b/target/rx/translate.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/target/rx/translate.c
45
+++ b/target/rx/translate.c
46
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
47
tcg_gen_exit_tb(dc->base.tb, n);
48
} else {
49
tcg_gen_movi_i32(cpu_pc, dest);
50
- if (dc->base.singlestep_enabled) {
51
- gen_helper_debug(cpu_env);
52
- } else {
53
- tcg_gen_lookup_and_goto_ptr();
54
- }
55
+ tcg_gen_lookup_and_goto_ptr();
56
}
57
dc->base.is_jmp = DISAS_NORETURN;
58
}
59
@@ -XXX,XX +XXX,XX @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
60
gen_goto_tb(ctx, 0, dcbase->pc_next);
61
break;
62
case DISAS_JUMP:
63
- if (ctx->base.singlestep_enabled) {
64
- gen_helper_debug(cpu_env);
65
- } else {
66
- tcg_gen_lookup_and_goto_ptr();
67
- }
68
+ tcg_gen_lookup_and_goto_ptr();
69
break;
70
case DISAS_UPDATE:
71
tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next);
72
--
73
2.25.1
74
75
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
4
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
---
6
target/sh4/helper.h | 1 -
7
target/sh4/op_helper.c | 5 -----
8
target/sh4/translate.c | 14 +++-----------
9
3 files changed, 3 insertions(+), 17 deletions(-)
10
11
diff --git a/target/sh4/helper.h b/target/sh4/helper.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/target/sh4/helper.h
14
+++ b/target/sh4/helper.h
15
@@ -XXX,XX +XXX,XX @@ DEF_HELPER_1(raise_illegal_instruction, noreturn, env)
16
DEF_HELPER_1(raise_slot_illegal_instruction, noreturn, env)
17
DEF_HELPER_1(raise_fpu_disable, noreturn, env)
18
DEF_HELPER_1(raise_slot_fpu_disable, noreturn, env)
19
-DEF_HELPER_1(debug, noreturn, env)
20
DEF_HELPER_1(sleep, noreturn, env)
21
DEF_HELPER_2(trapa, noreturn, env, i32)
22
DEF_HELPER_1(exclusive, noreturn, env)
23
diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/target/sh4/op_helper.c
26
+++ b/target/sh4/op_helper.c
27
@@ -XXX,XX +XXX,XX @@ void helper_raise_slot_fpu_disable(CPUSH4State *env)
28
raise_exception(env, 0x820, 0);
29
}
30
31
-void helper_debug(CPUSH4State *env)
32
-{
33
- raise_exception(env, EXCP_DEBUG, 0);
34
-}
35
-
36
void helper_sleep(CPUSH4State *env)
37
{
38
CPUState *cs = env_cpu(env);
39
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/target/sh4/translate.c
42
+++ b/target/sh4/translate.c
43
@@ -XXX,XX +XXX,XX @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
44
tcg_gen_exit_tb(ctx->base.tb, n);
45
} else {
46
tcg_gen_movi_i32(cpu_pc, dest);
47
- if (ctx->base.singlestep_enabled) {
48
- gen_helper_debug(cpu_env);
49
- } else if (use_exit_tb(ctx)) {
50
+ if (use_exit_tb(ctx)) {
51
tcg_gen_exit_tb(NULL, 0);
52
} else {
53
tcg_gen_lookup_and_goto_ptr();
54
@@ -XXX,XX +XXX,XX @@ static void gen_jump(DisasContext * ctx)
55
     delayed jump as immediate jump are conditinal jumps */
56
    tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc);
57
tcg_gen_discard_i32(cpu_delayed_pc);
58
- if (ctx->base.singlestep_enabled) {
59
- gen_helper_debug(cpu_env);
60
- } else if (use_exit_tb(ctx)) {
61
+ if (use_exit_tb(ctx)) {
62
tcg_gen_exit_tb(NULL, 0);
63
} else {
64
tcg_gen_lookup_and_goto_ptr();
65
@@ -XXX,XX +XXX,XX @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
66
switch (ctx->base.is_jmp) {
67
case DISAS_STOP:
68
gen_save_cpu_state(ctx, true);
69
- if (ctx->base.singlestep_enabled) {
70
- gen_helper_debug(cpu_env);
71
- } else {
72
- tcg_gen_exit_tb(NULL, 0);
73
- }
74
+ tcg_gen_exit_tb(NULL, 0);
75
break;
76
case DISAS_NEXT:
77
case DISAS_TOO_MANY:
78
--
79
2.25.1
80
81
diff view generated by jsdifflib
Deleted patch
1
GDB single-stepping is now handled generically.
2
1
3
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
4
---
5
target/xtensa/translate.c | 25 ++++++++-----------------
6
1 file changed, 8 insertions(+), 17 deletions(-)
7
8
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
9
index XXXXXXX..XXXXXXX 100644
10
--- a/target/xtensa/translate.c
11
+++ b/target/xtensa/translate.c
12
@@ -XXX,XX +XXX,XX @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
13
if (dc->icount) {
14
tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount);
15
}
16
- if (dc->base.singlestep_enabled) {
17
- gen_exception(dc, EXCP_DEBUG);
18
+ if (dc->op_flags & XTENSA_OP_POSTPROCESS) {
19
+ slot = gen_postprocess(dc, slot);
20
+ }
21
+ if (slot >= 0) {
22
+ tcg_gen_goto_tb(slot);
23
+ tcg_gen_exit_tb(dc->base.tb, slot);
24
} else {
25
- if (dc->op_flags & XTENSA_OP_POSTPROCESS) {
26
- slot = gen_postprocess(dc, slot);
27
- }
28
- if (slot >= 0) {
29
- tcg_gen_goto_tb(slot);
30
- tcg_gen_exit_tb(dc->base.tb, slot);
31
- } else {
32
- tcg_gen_exit_tb(NULL, 0);
33
- }
34
+ tcg_gen_exit_tb(NULL, 0);
35
}
36
dc->base.is_jmp = DISAS_NORETURN;
37
}
38
@@ -XXX,XX +XXX,XX @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
39
case DISAS_NORETURN:
40
break;
41
case DISAS_TOO_MANY:
42
- if (dc->base.singlestep_enabled) {
43
- tcg_gen_movi_i32(cpu_pc, dc->pc);
44
- gen_exception(dc, EXCP_DEBUG);
45
- } else {
46
- gen_jumpi(dc, dc->pc, 0);
47
- }
48
+ gen_jumpi(dc, dc->pc, 0);
49
break;
50
default:
51
g_assert_not_reached();
52
--
53
2.25.1
54
55
diff view generated by jsdifflib