1
TCG patch queue, plus one target/sh4 patch that
1
Changes from v1:
2
Yoshinori Sato asked me to process.
2
* Patch 10 is new, avoiding an overflow in probe_guest_base,
3
3
visible with aarch64 host, --static --disable-pie, exposed
4
by the placement of the host binary in the address space.
4
5
5
r~
6
r~
6
7
8
Emilio Cota (2):
9
util: import GTree as QTree
10
tcg: use QTree instead of GTree
7
11
8
The following changes since commit efbf38d73e5dcc4d5f8b98c6e7a12be1f3b91745:
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
9
23
10
Merge tag 'for-upstream' of git://repo.or.cz/qemu/kevin into staging (2022-10-03 15:06:07 -0400)
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
11
53
12
are available in the Git repository at:
54
--
13
55
2.34.1
14
https://gitlab.com/rth7680/qemu.git tags/pull-tcg-20221004
15
16
for you to fetch changes up to ab419fd8a035a65942de4e63effcd55ccbf1a9fe:
17
18
target/sh4: Fix TB_FLAG_UNALIGN (2022-10-04 12:33:05 -0700)
19
20
----------------------------------------------------------------
21
Cache CPUClass for use in hot code paths.
22
Add CPUTLBEntryFull, probe_access_full, tlb_set_page_full.
23
Add generic support for TARGET_TB_PCREL.
24
tcg/ppc: Optimize 26-bit jumps using STQ for POWER 2.07
25
target/sh4: Fix TB_FLAG_UNALIGN
26
27
----------------------------------------------------------------
28
Alex Bennée (3):
29
cpu: cache CPUClass in CPUState for hot code paths
30
hw/core/cpu-sysemu: used cached class in cpu_asidx_from_attrs
31
cputlb: used cached CPUClass in our hot-paths
32
33
Leandro Lupori (1):
34
tcg/ppc: Optimize 26-bit jumps
35
36
Richard Henderson (16):
37
accel/tcg: Rename CPUIOTLBEntry to CPUTLBEntryFull
38
accel/tcg: Drop addr member from SavedIOTLB
39
accel/tcg: Suppress auto-invalidate in probe_access_internal
40
accel/tcg: Introduce probe_access_full
41
accel/tcg: Introduce tlb_set_page_full
42
include/exec: Introduce TARGET_PAGE_ENTRY_EXTRA
43
accel/tcg: Remove PageDesc code_bitmap
44
accel/tcg: Use bool for page_find_alloc
45
accel/tcg: Use DisasContextBase in plugin_gen_tb_start
46
accel/tcg: Do not align tb->page_addr[0]
47
accel/tcg: Inline tb_flush_jmp_cache
48
include/hw/core: Create struct CPUJumpCache
49
hw/core: Add CPUClass.get_pc
50
accel/tcg: Introduce tb_pc and log_pc
51
accel/tcg: Introduce TARGET_TB_PCREL
52
target/sh4: Fix TB_FLAG_UNALIGN
53
54
accel/tcg/internal.h | 10 ++
55
accel/tcg/tb-hash.h | 1 +
56
accel/tcg/tb-jmp-cache.h | 65 ++++++++
57
include/exec/cpu-common.h | 1 +
58
include/exec/cpu-defs.h | 48 ++++--
59
include/exec/exec-all.h | 75 ++++++++-
60
include/exec/plugin-gen.h | 7 +-
61
include/hw/core/cpu.h | 28 ++--
62
include/qemu/typedefs.h | 2 +
63
include/tcg/tcg.h | 2 +-
64
target/sh4/cpu.h | 56 ++++---
65
accel/stubs/tcg-stub.c | 4 +
66
accel/tcg/cpu-exec.c | 80 +++++-----
67
accel/tcg/cputlb.c | 259 ++++++++++++++++++--------------
68
accel/tcg/plugin-gen.c | 22 +--
69
accel/tcg/translate-all.c | 214 ++++++++++++--------------
70
accel/tcg/translator.c | 2 +-
71
cpu.c | 9 +-
72
hw/core/cpu-common.c | 3 +-
73
hw/core/cpu-sysemu.c | 5 +-
74
linux-user/sh4/signal.c | 6 +-
75
plugins/core.c | 2 +-
76
target/alpha/cpu.c | 9 ++
77
target/arm/cpu.c | 17 ++-
78
target/arm/mte_helper.c | 14 +-
79
target/arm/sve_helper.c | 4 +-
80
target/arm/translate-a64.c | 2 +-
81
target/avr/cpu.c | 10 +-
82
target/cris/cpu.c | 8 +
83
target/hexagon/cpu.c | 10 +-
84
target/hppa/cpu.c | 12 +-
85
target/i386/cpu.c | 9 ++
86
target/i386/tcg/tcg-cpu.c | 2 +-
87
target/loongarch/cpu.c | 11 +-
88
target/m68k/cpu.c | 8 +
89
target/microblaze/cpu.c | 10 +-
90
target/mips/cpu.c | 8 +
91
target/mips/tcg/exception.c | 2 +-
92
target/mips/tcg/sysemu/special_helper.c | 2 +-
93
target/nios2/cpu.c | 9 ++
94
target/openrisc/cpu.c | 10 +-
95
target/ppc/cpu_init.c | 8 +
96
target/riscv/cpu.c | 17 ++-
97
target/rx/cpu.c | 10 +-
98
target/s390x/cpu.c | 8 +
99
target/s390x/tcg/mem_helper.c | 4 -
100
target/sh4/cpu.c | 18 ++-
101
target/sh4/helper.c | 6 +-
102
target/sh4/translate.c | 90 +++++------
103
target/sparc/cpu.c | 10 +-
104
target/tricore/cpu.c | 11 +-
105
target/xtensa/cpu.c | 8 +
106
tcg/tcg.c | 8 +-
107
trace/control-target.c | 2 +-
108
tcg/ppc/tcg-target.c.inc | 119 +++++++++++----
109
55 files changed, 915 insertions(+), 462 deletions(-)
110
create mode 100644 accel/tcg/tb-jmp-cache.h
111
diff view generated by jsdifflib
Deleted patch
1
From: Alex Bennée <alex.bennee@linaro.org>
2
1
3
The class cast checkers are quite expensive and always on (unlike the
4
dynamic case who's checks are gated by CONFIG_QOM_CAST_DEBUG). To
5
avoid the overhead of repeatedly checking something which should never
6
change we cache the CPUClass reference for use in the hot code paths.
7
8
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
9
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
10
Message-Id: <20220811151413.3350684-3-alex.bennee@linaro.org>
11
Signed-off-by: Cédric Le Goater <clg@kaod.org>
12
Message-Id: <20220923084803.498337-3-clg@kaod.org>
13
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
14
---
15
include/hw/core/cpu.h | 9 +++++++++
16
cpu.c | 9 ++++-----
17
2 files changed, 13 insertions(+), 5 deletions(-)
18
19
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/hw/core/cpu.h
22
+++ b/include/hw/core/cpu.h
23
@@ -XXX,XX +XXX,XX @@ typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size,
24
*/
25
#define CPU(obj) ((CPUState *)(obj))
26
27
+/*
28
+ * The class checkers bring in CPU_GET_CLASS() which is potentially
29
+ * expensive given the eventual call to
30
+ * object_class_dynamic_cast_assert(). Because of this the CPUState
31
+ * has a cached value for the class in cs->cc which is set up in
32
+ * cpu_exec_realizefn() for use in hot code paths.
33
+ */
34
typedef struct CPUClass CPUClass;
35
DECLARE_CLASS_CHECKERS(CPUClass, CPU,
36
TYPE_CPU)
37
@@ -XXX,XX +XXX,XX @@ struct qemu_work_item;
38
struct CPUState {
39
/*< private >*/
40
DeviceState parent_obj;
41
+ /* cache to avoid expensive CPU_GET_CLASS */
42
+ CPUClass *cc;
43
/*< public >*/
44
45
int nr_cores;
46
diff --git a/cpu.c b/cpu.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/cpu.c
49
+++ b/cpu.c
50
@@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_cpu_common = {
51
52
void cpu_exec_realizefn(CPUState *cpu, Error **errp)
53
{
54
-#ifndef CONFIG_USER_ONLY
55
- CPUClass *cc = CPU_GET_CLASS(cpu);
56
-#endif
57
+ /* cache the cpu class for the hotpath */
58
+ cpu->cc = CPU_GET_CLASS(cpu);
59
60
cpu_list_add(cpu);
61
if (!accel_cpu_realizefn(cpu, errp)) {
62
@@ -XXX,XX +XXX,XX @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
63
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
64
vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
65
}
66
- if (cc->sysemu_ops->legacy_vmsd != NULL) {
67
- vmstate_register(NULL, cpu->cpu_index, cc->sysemu_ops->legacy_vmsd, cpu);
68
+ if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) {
69
+ vmstate_register(NULL, cpu->cpu_index, cpu->cc->sysemu_ops->legacy_vmsd, cpu);
70
}
71
#endif /* CONFIG_USER_ONLY */
72
}
73
--
74
2.34.1
75
76
diff view generated by jsdifflib
Deleted patch
1
From: Alex Bennée <alex.bennee@linaro.org>
2
1
3
This is a heavily used function so lets avoid the cost of
4
CPU_GET_CLASS. On the romulus-bmc run it has a modest effect:
5
6
Before: 36.812 s ± 0.506 s
7
After: 35.912 s ± 0.168 s
8
9
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
10
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
11
Message-Id: <20220811151413.3350684-4-alex.bennee@linaro.org>
12
Signed-off-by: Cédric Le Goater <clg@kaod.org>
13
Message-Id: <20220923084803.498337-4-clg@kaod.org>
14
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
15
---
16
hw/core/cpu-sysemu.c | 5 ++---
17
1 file changed, 2 insertions(+), 3 deletions(-)
18
19
diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/core/cpu-sysemu.c
22
+++ b/hw/core/cpu-sysemu.c
23
@@ -XXX,XX +XXX,XX @@ hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr)
24
25
int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs)
26
{
27
- CPUClass *cc = CPU_GET_CLASS(cpu);
28
int ret = 0;
29
30
- if (cc->sysemu_ops->asidx_from_attrs) {
31
- ret = cc->sysemu_ops->asidx_from_attrs(cpu, attrs);
32
+ if (cpu->cc->sysemu_ops->asidx_from_attrs) {
33
+ ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs);
34
assert(ret < cpu->num_ases && ret >= 0);
35
}
36
return ret;
37
--
38
2.34.1
39
40
diff view generated by jsdifflib
Deleted patch
1
From: Alex Bennée <alex.bennee@linaro.org>
2
1
3
Before: 35.912 s ± 0.168 s
4
After: 35.565 s ± 0.087 s
5
6
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
7
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
8
Message-Id: <20220811151413.3350684-5-alex.bennee@linaro.org>
9
Signed-off-by: Cédric Le Goater <clg@kaod.org>
10
Message-Id: <20220923084803.498337-5-clg@kaod.org>
11
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
12
---
13
accel/tcg/cputlb.c | 15 ++++++---------
14
1 file changed, 6 insertions(+), 9 deletions(-)
15
16
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/accel/tcg/cputlb.c
19
+++ b/accel/tcg/cputlb.c
20
@@ -XXX,XX +XXX,XX @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
21
static void tlb_fill(CPUState *cpu, target_ulong addr, int size,
22
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
23
{
24
- CPUClass *cc = CPU_GET_CLASS(cpu);
25
bool ok;
26
27
/*
28
* This is not a probe, so only valid return is success; failure
29
* should result in exception + longjmp to the cpu loop.
30
*/
31
- ok = cc->tcg_ops->tlb_fill(cpu, addr, size,
32
- access_type, mmu_idx, false, retaddr);
33
+ ok = cpu->cc->tcg_ops->tlb_fill(cpu, addr, size,
34
+ access_type, mmu_idx, false, retaddr);
35
assert(ok);
36
}
37
38
@@ -XXX,XX +XXX,XX @@ static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr,
39
MMUAccessType access_type,
40
int mmu_idx, uintptr_t retaddr)
41
{
42
- CPUClass *cc = CPU_GET_CLASS(cpu);
43
-
44
- cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, mmu_idx, retaddr);
45
+ cpu->cc->tcg_ops->do_unaligned_access(cpu, addr, access_type,
46
+ mmu_idx, retaddr);
47
}
48
49
static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,
50
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
51
if (!tlb_hit_page(tlb_addr, page_addr)) {
52
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page_addr)) {
53
CPUState *cs = env_cpu(env);
54
- CPUClass *cc = CPU_GET_CLASS(cs);
55
56
- if (!cc->tcg_ops->tlb_fill(cs, addr, fault_size, access_type,
57
- mmu_idx, nonfault, retaddr)) {
58
+ if (!cs->cc->tcg_ops->tlb_fill(cs, addr, fault_size, access_type,
59
+ mmu_idx, nonfault, retaddr)) {
60
/* Non-faulting page table read failed. */
61
*phost = NULL;
62
return TLB_INVALID_MASK;
63
--
64
2.34.1
65
66
diff view generated by jsdifflib
1
Wrap the bare TranslationBlock pointer into a structure.
1
From: Emilio Cota <cota@braap.org>
2
2
3
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
3
The only reason to add this implementation is to control the memory allocator
4
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
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>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
58
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
---
59
---
7
accel/tcg/tb-hash.h | 1 +
60
configure | 15 +
8
accel/tcg/tb-jmp-cache.h | 24 ++++++++++++++++++++++++
61
meson.build | 4 +
9
include/exec/cpu-common.h | 1 +
62
include/qemu/qtree.h | 201 ++++++
10
include/hw/core/cpu.h | 15 +--------------
63
tests/bench/qtree-bench.c | 286 ++++++++
11
include/qemu/typedefs.h | 1 +
64
tests/unit/test-qtree.c | 333 +++++++++
12
accel/stubs/tcg-stub.c | 4 ++++
65
util/qtree.c | 1390 +++++++++++++++++++++++++++++++++++++
13
accel/tcg/cpu-exec.c | 10 +++++++---
66
tests/bench/meson.build | 4 +
14
accel/tcg/cputlb.c | 9 +++++----
67
tests/unit/meson.build | 1 +
15
accel/tcg/translate-all.c | 28 +++++++++++++++++++++++++---
68
util/meson.build | 1 +
16
hw/core/cpu-common.c | 3 +--
69
9 files changed, 2235 insertions(+)
17
plugins/core.c | 2 +-
70
create mode 100644 include/qemu/qtree.h
18
trace/control-target.c | 2 +-
71
create mode 100644 tests/bench/qtree-bench.c
19
12 files changed, 72 insertions(+), 28 deletions(-)
72
create mode 100644 tests/unit/test-qtree.c
20
create mode 100644 accel/tcg/tb-jmp-cache.h
73
create mode 100644 util/qtree.c
21
74
22
diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h
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
23
index XXXXXXX..XXXXXXX 100644
116
index XXXXXXX..XXXXXXX 100644
24
--- a/accel/tcg/tb-hash.h
117
--- a/meson.build
25
+++ b/accel/tcg/tb-hash.h
118
+++ b/meson.build
26
@@ -XXX,XX +XXX,XX @@
119
@@ -XXX,XX +XXX,XX @@ glib = declare_dependency(compile_args: config_host['GLIB_CFLAGS'].split(),
27
#include "exec/cpu-defs.h"
120
})
28
#include "exec/exec-all.h"
121
# override glib dep with the configure results (for subprojects)
29
#include "qemu/xxhash.h"
122
meson.override_dependency('glib-2.0', glib)
30
+#include "tb-jmp-cache.h"
123
+# pass down whether Glib has the slice allocator
31
124
+if config_host.has_key('HAVE_GLIB_WITH_SLICE_ALLOCATOR')
32
#ifdef CONFIG_SOFTMMU
125
+ config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', true)
33
126
+endif
34
diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h
127
128
gio = not_found
129
gdbus_codegen = not_found
130
diff --git a/include/qemu/qtree.h b/include/qemu/qtree.h
35
new file mode 100644
131
new file mode 100644
36
index XXXXXXX..XXXXXXX
132
index XXXXXXX..XXXXXXX
37
--- /dev/null
133
--- /dev/null
38
+++ b/accel/tcg/tb-jmp-cache.h
134
+++ b/include/qemu/qtree.h
39
@@ -XXX,XX +XXX,XX @@
135
@@ -XXX,XX +XXX,XX @@
40
+/*
136
+/*
41
+ * The per-CPU TranslationBlock jump cache.
137
+ * GLIB - Library of useful routines for C programming
42
+ *
138
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
43
+ * Copyright (c) 2003 Fabrice Bellard
139
+ *
44
+ *
140
+ * SPDX-License-Identifier: LGPL-2.1-or-later
45
+ * SPDX-License-Identifier: GPL-2.0-or-later
141
+ *
46
+ */
142
+ * This library is free software; you can redistribute it and/or
47
+
143
+ * modify it under the terms of the GNU Lesser General Public
48
+#ifndef ACCEL_TCG_TB_JMP_CACHE_H
144
+ * License as published by the Free Software Foundation; either
49
+#define ACCEL_TCG_TB_JMP_CACHE_H
145
+ * version 2.1 of the License, or (at your option) any later version.
50
+
146
+ *
51
+#define TB_JMP_CACHE_BITS 12
147
+ * This library is distributed in the hope that it will be useful,
52
+#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
148
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
149
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
150
+ * Lesser General Public License for more details.
151
+ *
152
+ * You should have received a copy of the GNU Lesser General Public
153
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
154
+ */
53
+
155
+
54
+/*
156
+/*
55
+ * Accessed in parallel; all accesses to 'tb' must be atomic.
157
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
56
+ */
158
+ * file for a list of people on the GLib Team. See the ChangeLog
57
+struct CPUJumpCache {
159
+ * files for a list of changes. These files are distributed with
58
+ struct {
160
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
59
+ TranslationBlock *tb;
161
+ */
60
+ } array[TB_JMP_CACHE_SIZE];
162
+
163
+/*
164
+ * QTree is a partial import of Glib's GTree. The parts excluded correspond
165
+ * to API calls either deprecated (e.g. g_tree_traverse) or recently added
166
+ * (e.g. g_tree_search_node, added in 2.68); neither have callers in QEMU.
167
+ *
168
+ * The reason for this import is to allow us to control the memory allocator
169
+ * used by the tree implementation. Until Glib 2.75.3, GTree uses Glib's
170
+ * slice allocator, which causes problems when forking in user-mode;
171
+ * see https://gitlab.com/qemu-project/qemu/-/issues/285 and glib's
172
+ * "45b5a6c1e gslice: Remove slice allocator and use malloc() instead".
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,
61
+};
354
+};
62
+
355
+
63
+#endif /* ACCEL_TCG_TB_JMP_CACHE_H */
356
+struct benchmark {
64
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
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);
432
+ break;
433
+ case IMPL_QTREE:
434
+ tree = q_tree_new(compare_func);
435
+ break;
436
+ default:
437
+ g_assert_not_reached();
438
+ }
439
+
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
65
index XXXXXXX..XXXXXXX 100644
2365
index XXXXXXX..XXXXXXX 100644
66
--- a/include/exec/cpu-common.h
2366
--- a/tests/bench/meson.build
67
+++ b/include/exec/cpu-common.h
2367
+++ b/tests/bench/meson.build
68
@@ -XXX,XX +XXX,XX @@ void cpu_list_unlock(void);
2368
@@ -XXX,XX +XXX,XX @@ xbzrle_bench = executable('xbzrle-bench',
69
unsigned int cpu_list_generation_id_get(void);
2369
dependencies: [qemuutil,migration])
70
2370
endif
71
void tcg_flush_softmmu_tlb(CPUState *cs);
2371
72
+void tcg_flush_jmp_cache(CPUState *cs);
2372
+qtree_bench = executable('qtree-bench',
73
2373
+ sources: 'qtree-bench.c',
74
void tcg_iommu_init_notifier_list(CPUState *cpu);
2374
+ dependencies: [qemuutil])
75
void tcg_iommu_free_notifier_list(CPUState *cpu);
2375
+
76
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
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
77
index XXXXXXX..XXXXXXX 100644
2380
index XXXXXXX..XXXXXXX 100644
78
--- a/include/hw/core/cpu.h
2381
--- a/tests/unit/meson.build
79
+++ b/include/hw/core/cpu.h
2382
+++ b/tests/unit/meson.build
80
@@ -XXX,XX +XXX,XX @@ struct kvm_run;
2383
@@ -XXX,XX +XXX,XX @@ tests = {
81
struct hax_vcpu_state;
2384
'test-rcu-slist': [],
82
struct hvf_vcpu_state;
2385
'test-qdist': [],
83
2386
'test-qht': [],
84
-#define TB_JMP_CACHE_BITS 12
2387
+ 'test-qtree': [],
85
-#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
2388
'test-bitops': [],
86
-
2389
'test-bitcnt': [],
87
/* work queue */
2390
'test-qgraph': ['../qtest/libqos/qgraph.c'],
88
2391
diff --git a/util/meson.build b/util/meson.build
89
/* The union type allows passing of 64 bit target pointers on 32 bit
90
@@ -XXX,XX +XXX,XX @@ struct CPUState {
91
CPUArchState *env_ptr;
92
IcountDecr *icount_decr_ptr;
93
94
- /* Accessed in parallel; all accesses must be atomic */
95
- TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE];
96
+ CPUJumpCache *tb_jmp_cache;
97
98
struct GDBRegisterState *gdb_regs;
99
int gdb_num_regs;
100
@@ -XXX,XX +XXX,XX @@ extern CPUTailQ cpus;
101
102
extern __thread CPUState *current_cpu;
103
104
-static inline void cpu_tb_jmp_cache_clear(CPUState *cpu)
105
-{
106
- unsigned int i;
107
-
108
- for (i = 0; i < TB_JMP_CACHE_SIZE; i++) {
109
- qatomic_set(&cpu->tb_jmp_cache[i], NULL);
110
- }
111
-}
112
-
113
/**
114
* qemu_tcg_mttcg_enabled:
115
* Check whether we are running MultiThread TCG or not.
116
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
117
index XXXXXXX..XXXXXXX 100644
2392
index XXXXXXX..XXXXXXX 100644
118
--- a/include/qemu/typedefs.h
2393
--- a/util/meson.build
119
+++ b/include/qemu/typedefs.h
2394
+++ b/util/meson.build
120
@@ -XXX,XX +XXX,XX @@ typedef struct CoMutex CoMutex;
2395
@@ -XXX,XX +XXX,XX @@ util_ss.add(when: 'CONFIG_WIN32', if_true: files('oslib-win32.c'))
121
typedef struct ConfidentialGuestSupport ConfidentialGuestSupport;
2396
util_ss.add(when: 'CONFIG_WIN32', if_true: files('qemu-thread-win32.c'))
122
typedef struct CPUAddressSpace CPUAddressSpace;
2397
util_ss.add(when: 'CONFIG_WIN32', if_true: winmm)
123
typedef struct CPUArchState CPUArchState;
2398
util_ss.add(when: 'CONFIG_WIN32', if_true: pathcch)
124
+typedef struct CPUJumpCache CPUJumpCache;
2399
+util_ss.add(when: 'HAVE_GLIB_WITH_SLICE_ALLOCATOR', if_true: files('qtree.c'))
125
typedef struct CPUState CPUState;
2400
util_ss.add(files('envlist.c', 'path.c', 'module.c'))
126
typedef struct CPUTLBEntryFull CPUTLBEntryFull;
2401
util_ss.add(files('host-utils.c'))
127
typedef struct DeviceListener DeviceListener;
2402
util_ss.add(files('bitmap.c', 'bitops.c'))
128
diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/accel/stubs/tcg-stub.c
131
+++ b/accel/stubs/tcg-stub.c
132
@@ -XXX,XX +XXX,XX @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
133
{
134
}
135
136
+void tcg_flush_jmp_cache(CPUState *cpu)
137
+{
138
+}
139
+
140
int probe_access_flags(CPUArchState *env, target_ulong addr,
141
MMUAccessType access_type, int mmu_idx,
142
bool nonfault, void **phost, uintptr_t retaddr)
143
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/accel/tcg/cpu-exec.c
146
+++ b/accel/tcg/cpu-exec.c
147
@@ -XXX,XX +XXX,XX @@
148
#include "sysemu/replay.h"
149
#include "sysemu/tcg.h"
150
#include "exec/helper-proto.h"
151
+#include "tb-jmp-cache.h"
152
#include "tb-hash.h"
153
#include "tb-context.h"
154
#include "internal.h"
155
@@ -XXX,XX +XXX,XX @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
156
tcg_debug_assert(!(cflags & CF_INVALID));
157
158
hash = tb_jmp_cache_hash_func(pc);
159
- tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]);
160
+ tb = qatomic_rcu_read(&cpu->tb_jmp_cache->array[hash].tb);
161
162
if (likely(tb &&
163
tb->pc == pc &&
164
@@ -XXX,XX +XXX,XX @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
165
if (tb == NULL) {
166
return NULL;
167
}
168
- qatomic_set(&cpu->tb_jmp_cache[hash], tb);
169
+ qatomic_set(&cpu->tb_jmp_cache->array[hash].tb, tb);
170
return tb;
171
}
172
173
@@ -XXX,XX +XXX,XX @@ int cpu_exec(CPUState *cpu)
174
175
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
176
if (tb == NULL) {
177
+ uint32_t h;
178
+
179
mmap_lock();
180
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
181
mmap_unlock();
182
@@ -XXX,XX +XXX,XX @@ int cpu_exec(CPUState *cpu)
183
* We add the TB in the virtual pc hash table
184
* for the fast lookup
185
*/
186
- qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
187
+ h = tb_jmp_cache_hash_func(pc);
188
+ qatomic_set(&cpu->tb_jmp_cache->array[h].tb, tb);
189
}
190
191
#ifndef CONFIG_USER_ONLY
192
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
193
index XXXXXXX..XXXXXXX 100644
194
--- a/accel/tcg/cputlb.c
195
+++ b/accel/tcg/cputlb.c
196
@@ -XXX,XX +XXX,XX @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
197
198
static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr)
199
{
200
- unsigned int i, i0 = tb_jmp_cache_hash_page(page_addr);
201
+ int i, i0 = tb_jmp_cache_hash_page(page_addr);
202
+ CPUJumpCache *jc = cpu->tb_jmp_cache;
203
204
for (i = 0; i < TB_JMP_PAGE_SIZE; i++) {
205
- qatomic_set(&cpu->tb_jmp_cache[i0 + i], NULL);
206
+ qatomic_set(&jc->array[i0 + i].tb, NULL);
207
}
208
}
209
210
@@ -XXX,XX +XXX,XX @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
211
212
qemu_spin_unlock(&env_tlb(env)->c.lock);
213
214
- cpu_tb_jmp_cache_clear(cpu);
215
+ tcg_flush_jmp_cache(cpu);
216
217
if (to_clean == ALL_MMUIDX_BITS) {
218
qatomic_set(&env_tlb(env)->c.full_flush_count,
219
@@ -XXX,XX +XXX,XX @@ static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
220
* longer to clear each entry individually than it will to clear it all.
221
*/
222
if (d.len >= (TARGET_PAGE_SIZE * TB_JMP_CACHE_SIZE)) {
223
- cpu_tb_jmp_cache_clear(cpu);
224
+ tcg_flush_jmp_cache(cpu);
225
return;
226
}
227
228
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
229
index XXXXXXX..XXXXXXX 100644
230
--- a/accel/tcg/translate-all.c
231
+++ b/accel/tcg/translate-all.c
232
@@ -XXX,XX +XXX,XX @@
233
#include "sysemu/tcg.h"
234
#include "qapi/error.h"
235
#include "hw/core/tcg-cpu-ops.h"
236
+#include "tb-jmp-cache.h"
237
#include "tb-hash.h"
238
#include "tb-context.h"
239
#include "internal.h"
240
@@ -XXX,XX +XXX,XX @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
241
}
242
243
CPU_FOREACH(cpu) {
244
- cpu_tb_jmp_cache_clear(cpu);
245
+ tcg_flush_jmp_cache(cpu);
246
}
247
248
qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
249
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
250
/* remove the TB from the hash list */
251
h = tb_jmp_cache_hash_func(tb->pc);
252
CPU_FOREACH(cpu) {
253
- if (qatomic_read(&cpu->tb_jmp_cache[h]) == tb) {
254
- qatomic_set(&cpu->tb_jmp_cache[h], NULL);
255
+ CPUJumpCache *jc = cpu->tb_jmp_cache;
256
+ if (qatomic_read(&jc->array[h].tb) == tb) {
257
+ qatomic_set(&jc->array[h].tb, NULL);
258
}
259
}
260
261
@@ -XXX,XX +XXX,XX @@ int page_unprotect(target_ulong address, uintptr_t pc)
262
}
263
#endif /* CONFIG_USER_ONLY */
264
265
+/*
266
+ * Called by generic code at e.g. cpu reset after cpu creation,
267
+ * therefore we must be prepared to allocate the jump cache.
268
+ */
269
+void tcg_flush_jmp_cache(CPUState *cpu)
270
+{
271
+ CPUJumpCache *jc = cpu->tb_jmp_cache;
272
+
273
+ if (likely(jc)) {
274
+ for (int i = 0; i < TB_JMP_CACHE_SIZE; i++) {
275
+ qatomic_set(&jc->array[i].tb, NULL);
276
+ }
277
+ } else {
278
+ /* This should happen once during realize, and thus never race. */
279
+ jc = g_new0(CPUJumpCache, 1);
280
+ jc = qatomic_xchg(&cpu->tb_jmp_cache, jc);
281
+ assert(jc == NULL);
282
+ }
283
+}
284
+
285
/* This is a wrapper for common code that can not use CONFIG_SOFTMMU */
286
void tcg_flush_softmmu_tlb(CPUState *cs)
287
{
288
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
289
index XXXXXXX..XXXXXXX 100644
290
--- a/hw/core/cpu-common.c
291
+++ b/hw/core/cpu-common.c
292
@@ -XXX,XX +XXX,XX @@ static void cpu_common_reset(DeviceState *dev)
293
cpu->cflags_next_tb = -1;
294
295
if (tcg_enabled()) {
296
- cpu_tb_jmp_cache_clear(cpu);
297
-
298
+ tcg_flush_jmp_cache(cpu);
299
tcg_flush_softmmu_tlb(cpu);
300
}
301
}
302
diff --git a/plugins/core.c b/plugins/core.c
303
index XXXXXXX..XXXXXXX 100644
304
--- a/plugins/core.c
305
+++ b/plugins/core.c
306
@@ -XXX,XX +XXX,XX @@ struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id)
307
static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data)
308
{
309
bitmap_copy(cpu->plugin_mask, &data.host_ulong, QEMU_PLUGIN_EV_MAX);
310
- cpu_tb_jmp_cache_clear(cpu);
311
+ tcg_flush_jmp_cache(cpu);
312
}
313
314
static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata)
315
diff --git a/trace/control-target.c b/trace/control-target.c
316
index XXXXXXX..XXXXXXX 100644
317
--- a/trace/control-target.c
318
+++ b/trace/control-target.c
319
@@ -XXX,XX +XXX,XX @@ static void trace_event_synchronize_vcpu_state_dynamic(
320
{
321
bitmap_copy(vcpu->trace_dstate, vcpu->trace_dstate_delayed,
322
CPU_TRACE_DSTATE_MAX_EVENTS);
323
- cpu_tb_jmp_cache_clear(vcpu);
324
+ tcg_flush_jmp_cache(vcpu);
325
}
326
327
void trace_event_set_vcpu_state_dynamic(CPUState *vcpu,
328
--
2403
--
329
2.34.1
2404
2.34.1
330
2405
331
2406
diff view generated by jsdifflib
1
Let tb->page_addr[0] contain the address of the first byte of the
1
From: Emilio Cota <cota@braap.org>
2
translated block, rather than the address of the page containing the
2
3
start of the translated block. We need to recover this value anyway
3
qemu-user can hang in a multi-threaded fork. One common
4
at various points, and it is easier to discard a page offset when it
4
reason is that when creating a TB, between fork and exec
5
is not needed, which happens naturally via the existing find_page shift.
5
we manipulate a GTree whose memory allocator (GSlice) is
6
6
not fork-safe.
7
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
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.]
8
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
50
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
9
---
51
---
10
accel/tcg/cpu-exec.c | 16 ++++++++--------
52
accel/tcg/tb-maint.c | 17 +++++++++--------
11
accel/tcg/cputlb.c | 3 ++-
53
tcg/region.c | 19 ++++++++++---------
12
accel/tcg/translate-all.c | 9 +++++----
54
util/qtree.c | 8 ++++----
13
3 files changed, 15 insertions(+), 13 deletions(-)
55
3 files changed, 23 insertions(+), 21 deletions(-)
14
56
15
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
57
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
16
index XXXXXXX..XXXXXXX 100644
58
index XXXXXXX..XXXXXXX 100644
17
--- a/accel/tcg/cpu-exec.c
59
--- a/accel/tcg/tb-maint.c
18
+++ b/accel/tcg/cpu-exec.c
60
+++ b/accel/tcg/tb-maint.c
19
@@ -XXX,XX +XXX,XX @@ struct tb_desc {
61
@@ -XXX,XX +XXX,XX @@
20
target_ulong pc;
62
21
target_ulong cs_base;
63
#include "qemu/osdep.h"
22
CPUArchState *env;
64
#include "qemu/interval-tree.h"
23
- tb_page_addr_t phys_page1;
65
+#include "qemu/qtree.h"
24
+ tb_page_addr_t page_addr0;
66
#include "exec/cputlb.h"
25
uint32_t flags;
67
#include "exec/log.h"
26
uint32_t cflags;
68
#include "exec/exec-all.h"
27
uint32_t trace_vcpu_dstate;
69
@@ -XXX,XX +XXX,XX @@ struct page_entry {
28
@@ -XXX,XX +XXX,XX @@ static bool tb_lookup_cmp(const void *p, const void *d)
70
* See also: page_collection_lock().
29
const struct tb_desc *desc = d;
71
*/
30
72
struct page_collection {
31
if (tb->pc == desc->pc &&
73
- GTree *tree;
32
- tb->page_addr[0] == desc->phys_page1 &&
74
+ QTree *tree;
33
+ tb->page_addr[0] == desc->page_addr0 &&
75
struct page_entry *max;
34
tb->cs_base == desc->cs_base &&
76
};
35
tb->flags == desc->flags &&
77
36
tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
78
@@ -XXX,XX +XXX,XX @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
37
@@ -XXX,XX +XXX,XX @@ static bool tb_lookup_cmp(const void *p, const void *d)
79
struct page_entry *pe;
38
if (tb->page_addr[1] == -1) {
80
PageDesc *pd;
39
return true;
81
40
} else {
82
- pe = g_tree_lookup(set->tree, &index);
41
- tb_page_addr_t phys_page2;
83
+ pe = q_tree_lookup(set->tree, &index);
42
- target_ulong virt_page2;
84
if (pe) {
43
+ tb_page_addr_t phys_page1;
85
return false;
44
+ target_ulong virt_page1;
86
}
45
87
@@ -XXX,XX +XXX,XX @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
46
/*
88
}
47
* We know that the first page matched, and an otherwise valid TB
89
48
@@ -XXX,XX +XXX,XX @@ static bool tb_lookup_cmp(const void *p, const void *d)
90
pe = page_entry_new(pd, index);
49
* is different for the new TB. Therefore any exception raised
91
- g_tree_insert(set->tree, &pe->index, pe);
50
* here by the faulting lookup is not premature.
92
+ q_tree_insert(set->tree, &pe->index, pe);
51
*/
93
52
- virt_page2 = TARGET_PAGE_ALIGN(desc->pc);
94
/*
53
- phys_page2 = get_page_addr_code(desc->env, virt_page2);
95
* If this is either (1) the first insertion or (2) a page whose index
54
- if (tb->page_addr[1] == phys_page2) {
96
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
55
+ virt_page1 = TARGET_PAGE_ALIGN(desc->pc);
97
end >>= TARGET_PAGE_BITS;
56
+ phys_page1 = get_page_addr_code(desc->env, virt_page1);
98
g_assert(start <= end);
57
+ if (tb->page_addr[1] == phys_page1) {
99
58
return true;
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;
59
}
128
}
60
}
129
}
61
@@ -XXX,XX +XXX,XX @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
130
@@ -XXX,XX +XXX,XX @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
62
if (phys_pc == -1) {
131
static void page_collection_unlock(struct page_collection *set)
63
return NULL;
132
{
64
}
133
/* entries are unlocked and freed via page_entry_destroy */
65
- desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
134
- g_tree_destroy(set->tree);
66
+ desc.page_addr0 = phys_pc;
135
+ q_tree_destroy(set->tree);
67
h = tb_hash_func(phys_pc, pc, flags, cflags, *cpu->trace_dstate);
136
g_free(set);
68
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
137
}
69
}
138
70
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
139
diff --git a/tcg/region.c b/tcg/region.c
71
index XXXXXXX..XXXXXXX 100644
140
index XXXXXXX..XXXXXXX 100644
72
--- a/accel/tcg/cputlb.c
141
--- a/tcg/region.c
73
+++ b/accel/tcg/cputlb.c
142
+++ b/tcg/region.c
74
@@ -XXX,XX +XXX,XX @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
143
@@ -XXX,XX +XXX,XX @@
75
can be detected */
144
#include "qemu/mprotect.h"
76
void tlb_protect_code(ram_addr_t ram_addr)
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)
77
{
236
{
78
- cpu_physical_memory_test_and_clear_dirty(ram_addr, TARGET_PAGE_SIZE,
237
QTreeNode *node;
79
+ cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
238
@@ -XXX,XX +XXX,XX @@ q_tree_replace(QTree *tree,
80
+ TARGET_PAGE_SIZE,
239
}
81
DIRTY_MEMORY_CODE);
240
82
}
241
/* internal insert routine */
83
242
-static QTreeNode *
84
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
243
+static QTreeNode * QEMU_DISABLE_CFI
85
index XXXXXXX..XXXXXXX 100644
244
q_tree_insert_internal(QTree *tree,
86
--- a/accel/tcg/translate-all.c
245
gpointer key,
87
+++ b/accel/tcg/translate-all.c
246
gpointer value,
88
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
247
@@ -XXX,XX +XXX,XX @@ q_tree_steal(QTree *tree,
89
qemu_spin_unlock(&tb->jmp_lock);
248
}
90
249
91
/* remove the TB from the hash list */
250
/* internal remove routine */
92
- phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
251
-static gboolean
93
+ phys_pc = tb->page_addr[0];
252
+static gboolean QEMU_DISABLE_CFI
94
h = tb_hash_func(phys_pc, tb->pc, tb->flags, orig_cflags,
253
q_tree_remove_internal(QTree *tree,
95
tb->trace_vcpu_dstate);
254
gconstpointer key,
96
if (!qht_remove(&tb_ctx.htable, tb, h)) {
255
gboolean steal)
97
@@ -XXX,XX +XXX,XX @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
256
@@ -XXX,XX +XXX,XX @@ q_tree_node_balance(QTreeNode *node)
98
* we can only insert TBs that are fully initialized.
257
return node;
99
*/
258
}
100
page_lock_pair(&p, phys_pc, &p2, phys_page2, true);
259
101
- tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK);
260
-static QTreeNode *
102
+ tb_page_add(p, tb, 0, phys_pc);
261
+static QTreeNode * QEMU_DISABLE_CFI
103
if (p2) {
262
q_tree_find_node(QTree *tree,
104
tb_page_add(p2, tb, 1, phys_page2);
263
gconstpointer key)
105
} else {
264
{
106
@@ -XXX,XX +XXX,XX @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
107
if (n == 0) {
108
/* NOTE: tb_end may be after the end of the page, but
109
it is not a problem */
110
- tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
111
+ tb_start = tb->page_addr[0];
112
tb_end = tb_start + tb->size;
113
} else {
114
tb_start = tb->page_addr[1];
115
- tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
116
+ tb_end = tb_start + ((tb->page_addr[0] + tb->size)
117
+ & ~TARGET_PAGE_MASK);
118
}
119
if (!(tb_end <= start || tb_start >= end)) {
120
#ifdef TARGET_HAS_PRECISE_SMC
121
--
265
--
122
2.34.1
266
2.34.1
123
267
124
268
diff view generated by jsdifflib
1
Populate this new method for all targets. Always match
1
We have been enforcing host page alignment for the non-R
2
the result that would be given by cpu_get_tb_cpu_state,
2
fallback of MAX_RESERVED_VA, but failing to enforce for -R.
3
as we will want these values to correspond in the logs.
4
3
5
Reviewed-by: Taylor Simpson <tsimpson@quicinc.com>
4
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
6
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
7
Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> (target/sparc)
8
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
9
---
6
---
10
Cc: Eduardo Habkost <eduardo@habkost.net> (supporter:Machine core)
7
linux-user/main.c | 6 ++++++
11
Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> (supporter:Machine core)
8
1 file changed, 6 insertions(+)
12
Cc: "Philippe Mathieu-Daudé" <f4bug@amsat.org> (reviewer:Machine core)
13
Cc: Yanan Wang <wangyanan55@huawei.com> (reviewer:Machine core)
14
Cc: Michael Rolnik <mrolnik@gmail.com> (maintainer:AVR TCG CPUs)
15
Cc: "Edgar E. Iglesias" <edgar.iglesias@gmail.com> (maintainer:CRIS TCG CPUs)
16
Cc: Taylor Simpson <tsimpson@quicinc.com> (supporter:Hexagon TCG CPUs)
17
Cc: Song Gao <gaosong@loongson.cn> (maintainer:LoongArch TCG CPUs)
18
Cc: Xiaojuan Yang <yangxiaojuan@loongson.cn> (maintainer:LoongArch TCG CPUs)
19
Cc: Laurent Vivier <laurent@vivier.eu> (maintainer:M68K TCG CPUs)
20
Cc: Jiaxun Yang <jiaxun.yang@flygoat.com> (reviewer:MIPS TCG CPUs)
21
Cc: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> (reviewer:MIPS TCG CPUs)
22
Cc: Chris Wulff <crwulff@gmail.com> (maintainer:NiosII TCG CPUs)
23
Cc: Marek Vasut <marex@denx.de> (maintainer:NiosII TCG CPUs)
24
Cc: Stafford Horne <shorne@gmail.com> (odd fixer:OpenRISC TCG CPUs)
25
Cc: Yoshinori Sato <ysato@users.sourceforge.jp> (reviewer:RENESAS RX CPUs)
26
Cc: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> (maintainer:SPARC TCG CPUs)
27
Cc: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> (maintainer:TriCore TCG CPUs)
28
Cc: Max Filippov <jcmvbkbc@gmail.com> (maintainer:Xtensa TCG CPUs)
29
Cc: qemu-arm@nongnu.org (open list:ARM TCG CPUs)
30
Cc: qemu-ppc@nongnu.org (open list:PowerPC TCG CPUs)
31
Cc: qemu-riscv@nongnu.org (open list:RISC-V TCG CPUs)
32
Cc: qemu-s390x@nongnu.org (open list:S390 TCG CPUs)
33
---
34
include/hw/core/cpu.h | 3 +++
35
target/alpha/cpu.c | 9 +++++++++
36
target/arm/cpu.c | 13 +++++++++++++
37
target/avr/cpu.c | 8 ++++++++
38
target/cris/cpu.c | 8 ++++++++
39
target/hexagon/cpu.c | 8 ++++++++
40
target/hppa/cpu.c | 8 ++++++++
41
target/i386/cpu.c | 9 +++++++++
42
target/loongarch/cpu.c | 9 +++++++++
43
target/m68k/cpu.c | 8 ++++++++
44
target/microblaze/cpu.c | 8 ++++++++
45
target/mips/cpu.c | 8 ++++++++
46
target/nios2/cpu.c | 9 +++++++++
47
target/openrisc/cpu.c | 8 ++++++++
48
target/ppc/cpu_init.c | 8 ++++++++
49
target/riscv/cpu.c | 13 +++++++++++++
50
target/rx/cpu.c | 8 ++++++++
51
target/s390x/cpu.c | 8 ++++++++
52
target/sh4/cpu.c | 8 ++++++++
53
target/sparc/cpu.c | 8 ++++++++
54
target/tricore/cpu.c | 9 +++++++++
55
target/xtensa/cpu.c | 8 ++++++++
56
22 files changed, 186 insertions(+)
57
9
58
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
59
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
60
--- a/include/hw/core/cpu.h
12
--- a/linux-user/main.c
61
+++ b/include/hw/core/cpu.h
13
+++ b/linux-user/main.c
62
@@ -XXX,XX +XXX,XX @@ struct SysemuCPUOps;
14
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
63
* If the target behaviour here is anything other than "set
15
*/
64
* the PC register to the value passed in" then the target must
16
max_reserved_va = MAX_RESERVED_VA(cpu);
65
* also implement the synchronize_from_tb hook.
17
if (reserved_va != 0) {
66
+ * @get_pc: Callback for getting the Program Counter register.
18
+ if (reserved_va % qemu_host_page_size) {
67
+ * As above, with the semantics of the target architecture.
19
+ char *s = size_to_str(qemu_host_page_size);
68
* @gdb_read_register: Callback for letting GDB read a register.
20
+ fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s);
69
* @gdb_write_register: Callback for letting GDB write a register.
21
+ g_free(s);
70
* @gdb_adjust_breakpoint: Callback for adjusting the address of a
22
+ exit(EXIT_FAILURE);
71
@@ -XXX,XX +XXX,XX @@ struct CPUClass {
23
+ }
72
void (*dump_state)(CPUState *cpu, FILE *, int flags);
24
if (max_reserved_va && reserved_va > max_reserved_va) {
73
int64_t (*get_arch_id)(CPUState *cpu);
25
fprintf(stderr, "Reserved virtual address too big\n");
74
void (*set_pc)(CPUState *cpu, vaddr value);
26
exit(EXIT_FAILURE);
75
+ vaddr (*get_pc)(CPUState *cpu);
76
int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg);
77
int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg);
78
vaddr (*gdb_adjust_breakpoint)(CPUState *cpu, vaddr addr);
79
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/target/alpha/cpu.c
82
+++ b/target/alpha/cpu.c
83
@@ -XXX,XX +XXX,XX @@ static void alpha_cpu_set_pc(CPUState *cs, vaddr value)
84
cpu->env.pc = value;
85
}
86
87
+static vaddr alpha_cpu_get_pc(CPUState *cs)
88
+{
89
+ AlphaCPU *cpu = ALPHA_CPU(cs);
90
+
91
+ return cpu->env.pc;
92
+}
93
+
94
+
95
static bool alpha_cpu_has_work(CPUState *cs)
96
{
97
/* Here we are checking to see if the CPU should wake up from HALT.
98
@@ -XXX,XX +XXX,XX @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data)
99
cc->has_work = alpha_cpu_has_work;
100
cc->dump_state = alpha_cpu_dump_state;
101
cc->set_pc = alpha_cpu_set_pc;
102
+ cc->get_pc = alpha_cpu_get_pc;
103
cc->gdb_read_register = alpha_cpu_gdb_read_register;
104
cc->gdb_write_register = alpha_cpu_gdb_write_register;
105
#ifndef CONFIG_USER_ONLY
106
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/target/arm/cpu.c
109
+++ b/target/arm/cpu.c
110
@@ -XXX,XX +XXX,XX @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value)
111
}
112
}
113
114
+static vaddr arm_cpu_get_pc(CPUState *cs)
115
+{
116
+ ARMCPU *cpu = ARM_CPU(cs);
117
+ CPUARMState *env = &cpu->env;
118
+
119
+ if (is_a64(env)) {
120
+ return env->pc;
121
+ } else {
122
+ return env->regs[15];
123
+ }
124
+}
125
+
126
#ifdef CONFIG_TCG
127
void arm_cpu_synchronize_from_tb(CPUState *cs,
128
const TranslationBlock *tb)
129
@@ -XXX,XX +XXX,XX @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
130
cc->has_work = arm_cpu_has_work;
131
cc->dump_state = arm_cpu_dump_state;
132
cc->set_pc = arm_cpu_set_pc;
133
+ cc->get_pc = arm_cpu_get_pc;
134
cc->gdb_read_register = arm_cpu_gdb_read_register;
135
cc->gdb_write_register = arm_cpu_gdb_write_register;
136
#ifndef CONFIG_USER_ONLY
137
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
138
index XXXXXXX..XXXXXXX 100644
139
--- a/target/avr/cpu.c
140
+++ b/target/avr/cpu.c
141
@@ -XXX,XX +XXX,XX @@ static void avr_cpu_set_pc(CPUState *cs, vaddr value)
142
cpu->env.pc_w = value / 2; /* internally PC points to words */
143
}
144
145
+static vaddr avr_cpu_get_pc(CPUState *cs)
146
+{
147
+ AVRCPU *cpu = AVR_CPU(cs);
148
+
149
+ return cpu->env.pc_w * 2;
150
+}
151
+
152
static bool avr_cpu_has_work(CPUState *cs)
153
{
154
AVRCPU *cpu = AVR_CPU(cs);
155
@@ -XXX,XX +XXX,XX @@ static void avr_cpu_class_init(ObjectClass *oc, void *data)
156
cc->has_work = avr_cpu_has_work;
157
cc->dump_state = avr_cpu_dump_state;
158
cc->set_pc = avr_cpu_set_pc;
159
+ cc->get_pc = avr_cpu_get_pc;
160
dc->vmsd = &vms_avr_cpu;
161
cc->sysemu_ops = &avr_sysemu_ops;
162
cc->disas_set_info = avr_cpu_disas_set_info;
163
diff --git a/target/cris/cpu.c b/target/cris/cpu.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/target/cris/cpu.c
166
+++ b/target/cris/cpu.c
167
@@ -XXX,XX +XXX,XX @@ static void cris_cpu_set_pc(CPUState *cs, vaddr value)
168
cpu->env.pc = value;
169
}
170
171
+static vaddr cris_cpu_get_pc(CPUState *cs)
172
+{
173
+ CRISCPU *cpu = CRIS_CPU(cs);
174
+
175
+ return cpu->env.pc;
176
+}
177
+
178
static bool cris_cpu_has_work(CPUState *cs)
179
{
180
return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
181
@@ -XXX,XX +XXX,XX @@ static void cris_cpu_class_init(ObjectClass *oc, void *data)
182
cc->has_work = cris_cpu_has_work;
183
cc->dump_state = cris_cpu_dump_state;
184
cc->set_pc = cris_cpu_set_pc;
185
+ cc->get_pc = cris_cpu_get_pc;
186
cc->gdb_read_register = cris_cpu_gdb_read_register;
187
cc->gdb_write_register = cris_cpu_gdb_write_register;
188
#ifndef CONFIG_USER_ONLY
189
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/target/hexagon/cpu.c
192
+++ b/target/hexagon/cpu.c
193
@@ -XXX,XX +XXX,XX @@ static void hexagon_cpu_set_pc(CPUState *cs, vaddr value)
194
env->gpr[HEX_REG_PC] = value;
195
}
196
197
+static vaddr hexagon_cpu_get_pc(CPUState *cs)
198
+{
199
+ HexagonCPU *cpu = HEXAGON_CPU(cs);
200
+ CPUHexagonState *env = &cpu->env;
201
+ return env->gpr[HEX_REG_PC];
202
+}
203
+
204
static void hexagon_cpu_synchronize_from_tb(CPUState *cs,
205
const TranslationBlock *tb)
206
{
207
@@ -XXX,XX +XXX,XX @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data)
208
cc->has_work = hexagon_cpu_has_work;
209
cc->dump_state = hexagon_dump_state;
210
cc->set_pc = hexagon_cpu_set_pc;
211
+ cc->get_pc = hexagon_cpu_get_pc;
212
cc->gdb_read_register = hexagon_gdb_read_register;
213
cc->gdb_write_register = hexagon_gdb_write_register;
214
cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS + NUM_VREGS + NUM_QREGS;
215
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
216
index XXXXXXX..XXXXXXX 100644
217
--- a/target/hppa/cpu.c
218
+++ b/target/hppa/cpu.c
219
@@ -XXX,XX +XXX,XX @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value)
220
cpu->env.iaoq_b = value + 4;
221
}
222
223
+static vaddr hppa_cpu_get_pc(CPUState *cs)
224
+{
225
+ HPPACPU *cpu = HPPA_CPU(cs);
226
+
227
+ return cpu->env.iaoq_f;
228
+}
229
+
230
static void hppa_cpu_synchronize_from_tb(CPUState *cs,
231
const TranslationBlock *tb)
232
{
233
@@ -XXX,XX +XXX,XX @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data)
234
cc->has_work = hppa_cpu_has_work;
235
cc->dump_state = hppa_cpu_dump_state;
236
cc->set_pc = hppa_cpu_set_pc;
237
+ cc->get_pc = hppa_cpu_get_pc;
238
cc->gdb_read_register = hppa_cpu_gdb_read_register;
239
cc->gdb_write_register = hppa_cpu_gdb_write_register;
240
#ifndef CONFIG_USER_ONLY
241
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
242
index XXXXXXX..XXXXXXX 100644
243
--- a/target/i386/cpu.c
244
+++ b/target/i386/cpu.c
245
@@ -XXX,XX +XXX,XX @@ static void x86_cpu_set_pc(CPUState *cs, vaddr value)
246
cpu->env.eip = value;
247
}
248
249
+static vaddr x86_cpu_get_pc(CPUState *cs)
250
+{
251
+ X86CPU *cpu = X86_CPU(cs);
252
+
253
+ /* Match cpu_get_tb_cpu_state. */
254
+ return cpu->env.eip + cpu->env.segs[R_CS].base;
255
+}
256
+
257
int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request)
258
{
259
X86CPU *cpu = X86_CPU(cs);
260
@@ -XXX,XX +XXX,XX @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
261
cc->has_work = x86_cpu_has_work;
262
cc->dump_state = x86_cpu_dump_state;
263
cc->set_pc = x86_cpu_set_pc;
264
+ cc->get_pc = x86_cpu_get_pc;
265
cc->gdb_read_register = x86_cpu_gdb_read_register;
266
cc->gdb_write_register = x86_cpu_gdb_write_register;
267
cc->get_arch_id = x86_cpu_get_arch_id;
268
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
269
index XXXXXXX..XXXXXXX 100644
270
--- a/target/loongarch/cpu.c
271
+++ b/target/loongarch/cpu.c
272
@@ -XXX,XX +XXX,XX @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
273
env->pc = value;
274
}
275
276
+static vaddr loongarch_cpu_get_pc(CPUState *cs)
277
+{
278
+ LoongArchCPU *cpu = LOONGARCH_CPU(cs);
279
+ CPULoongArchState *env = &cpu->env;
280
+
281
+ return env->pc;
282
+}
283
+
284
#ifndef CONFIG_USER_ONLY
285
#include "hw/loongarch/virt.h"
286
287
@@ -XXX,XX +XXX,XX @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
288
cc->has_work = loongarch_cpu_has_work;
289
cc->dump_state = loongarch_cpu_dump_state;
290
cc->set_pc = loongarch_cpu_set_pc;
291
+ cc->get_pc = loongarch_cpu_get_pc;
292
#ifndef CONFIG_USER_ONLY
293
dc->vmsd = &vmstate_loongarch_cpu;
294
cc->sysemu_ops = &loongarch_sysemu_ops;
295
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
296
index XXXXXXX..XXXXXXX 100644
297
--- a/target/m68k/cpu.c
298
+++ b/target/m68k/cpu.c
299
@@ -XXX,XX +XXX,XX @@ static void m68k_cpu_set_pc(CPUState *cs, vaddr value)
300
cpu->env.pc = value;
301
}
302
303
+static vaddr m68k_cpu_get_pc(CPUState *cs)
304
+{
305
+ M68kCPU *cpu = M68K_CPU(cs);
306
+
307
+ return cpu->env.pc;
308
+}
309
+
310
static bool m68k_cpu_has_work(CPUState *cs)
311
{
312
return cs->interrupt_request & CPU_INTERRUPT_HARD;
313
@@ -XXX,XX +XXX,XX @@ static void m68k_cpu_class_init(ObjectClass *c, void *data)
314
cc->has_work = m68k_cpu_has_work;
315
cc->dump_state = m68k_cpu_dump_state;
316
cc->set_pc = m68k_cpu_set_pc;
317
+ cc->get_pc = m68k_cpu_get_pc;
318
cc->gdb_read_register = m68k_cpu_gdb_read_register;
319
cc->gdb_write_register = m68k_cpu_gdb_write_register;
320
#if defined(CONFIG_SOFTMMU)
321
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
322
index XXXXXXX..XXXXXXX 100644
323
--- a/target/microblaze/cpu.c
324
+++ b/target/microblaze/cpu.c
325
@@ -XXX,XX +XXX,XX @@ static void mb_cpu_set_pc(CPUState *cs, vaddr value)
326
cpu->env.iflags = 0;
327
}
328
329
+static vaddr mb_cpu_get_pc(CPUState *cs)
330
+{
331
+ MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
332
+
333
+ return cpu->env.pc;
334
+}
335
+
336
static void mb_cpu_synchronize_from_tb(CPUState *cs,
337
const TranslationBlock *tb)
338
{
339
@@ -XXX,XX +XXX,XX @@ static void mb_cpu_class_init(ObjectClass *oc, void *data)
340
341
cc->dump_state = mb_cpu_dump_state;
342
cc->set_pc = mb_cpu_set_pc;
343
+ cc->get_pc = mb_cpu_get_pc;
344
cc->gdb_read_register = mb_cpu_gdb_read_register;
345
cc->gdb_write_register = mb_cpu_gdb_write_register;
346
347
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
348
index XXXXXXX..XXXXXXX 100644
349
--- a/target/mips/cpu.c
350
+++ b/target/mips/cpu.c
351
@@ -XXX,XX +XXX,XX @@ static void mips_cpu_set_pc(CPUState *cs, vaddr value)
352
mips_env_set_pc(&cpu->env, value);
353
}
354
355
+static vaddr mips_cpu_get_pc(CPUState *cs)
356
+{
357
+ MIPSCPU *cpu = MIPS_CPU(cs);
358
+
359
+ return cpu->env.active_tc.PC;
360
+}
361
+
362
static bool mips_cpu_has_work(CPUState *cs)
363
{
364
MIPSCPU *cpu = MIPS_CPU(cs);
365
@@ -XXX,XX +XXX,XX @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
366
cc->has_work = mips_cpu_has_work;
367
cc->dump_state = mips_cpu_dump_state;
368
cc->set_pc = mips_cpu_set_pc;
369
+ cc->get_pc = mips_cpu_get_pc;
370
cc->gdb_read_register = mips_cpu_gdb_read_register;
371
cc->gdb_write_register = mips_cpu_gdb_write_register;
372
#ifndef CONFIG_USER_ONLY
373
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
374
index XXXXXXX..XXXXXXX 100644
375
--- a/target/nios2/cpu.c
376
+++ b/target/nios2/cpu.c
377
@@ -XXX,XX +XXX,XX @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
378
env->pc = value;
379
}
380
381
+static vaddr nios2_cpu_get_pc(CPUState *cs)
382
+{
383
+ Nios2CPU *cpu = NIOS2_CPU(cs);
384
+ CPUNios2State *env = &cpu->env;
385
+
386
+ return env->pc;
387
+}
388
+
389
static bool nios2_cpu_has_work(CPUState *cs)
390
{
391
return cs->interrupt_request & CPU_INTERRUPT_HARD;
392
@@ -XXX,XX +XXX,XX @@ static void nios2_cpu_class_init(ObjectClass *oc, void *data)
393
cc->has_work = nios2_cpu_has_work;
394
cc->dump_state = nios2_cpu_dump_state;
395
cc->set_pc = nios2_cpu_set_pc;
396
+ cc->get_pc = nios2_cpu_get_pc;
397
cc->disas_set_info = nios2_cpu_disas_set_info;
398
#ifndef CONFIG_USER_ONLY
399
cc->sysemu_ops = &nios2_sysemu_ops;
400
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
401
index XXXXXXX..XXXXXXX 100644
402
--- a/target/openrisc/cpu.c
403
+++ b/target/openrisc/cpu.c
404
@@ -XXX,XX +XXX,XX @@ static void openrisc_cpu_set_pc(CPUState *cs, vaddr value)
405
cpu->env.dflag = 0;
406
}
407
408
+static vaddr openrisc_cpu_get_pc(CPUState *cs)
409
+{
410
+ OpenRISCCPU *cpu = OPENRISC_CPU(cs);
411
+
412
+ return cpu->env.pc;
413
+}
414
+
415
static void openrisc_cpu_synchronize_from_tb(CPUState *cs,
416
const TranslationBlock *tb)
417
{
418
@@ -XXX,XX +XXX,XX @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data)
419
cc->has_work = openrisc_cpu_has_work;
420
cc->dump_state = openrisc_cpu_dump_state;
421
cc->set_pc = openrisc_cpu_set_pc;
422
+ cc->get_pc = openrisc_cpu_get_pc;
423
cc->gdb_read_register = openrisc_cpu_gdb_read_register;
424
cc->gdb_write_register = openrisc_cpu_gdb_write_register;
425
#ifndef CONFIG_USER_ONLY
426
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
427
index XXXXXXX..XXXXXXX 100644
428
--- a/target/ppc/cpu_init.c
429
+++ b/target/ppc/cpu_init.c
430
@@ -XXX,XX +XXX,XX @@ static void ppc_cpu_set_pc(CPUState *cs, vaddr value)
431
cpu->env.nip = value;
432
}
433
434
+static vaddr ppc_cpu_get_pc(CPUState *cs)
435
+{
436
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
437
+
438
+ return cpu->env.nip;
439
+}
440
+
441
static bool ppc_cpu_has_work(CPUState *cs)
442
{
443
PowerPCCPU *cpu = POWERPC_CPU(cs);
444
@@ -XXX,XX +XXX,XX @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
445
cc->has_work = ppc_cpu_has_work;
446
cc->dump_state = ppc_cpu_dump_state;
447
cc->set_pc = ppc_cpu_set_pc;
448
+ cc->get_pc = ppc_cpu_get_pc;
449
cc->gdb_read_register = ppc_cpu_gdb_read_register;
450
cc->gdb_write_register = ppc_cpu_gdb_write_register;
451
#ifndef CONFIG_USER_ONLY
452
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
453
index XXXXXXX..XXXXXXX 100644
454
--- a/target/riscv/cpu.c
455
+++ b/target/riscv/cpu.c
456
@@ -XXX,XX +XXX,XX @@ static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
457
}
458
}
459
460
+static vaddr riscv_cpu_get_pc(CPUState *cs)
461
+{
462
+ RISCVCPU *cpu = RISCV_CPU(cs);
463
+ CPURISCVState *env = &cpu->env;
464
+
465
+ /* Match cpu_get_tb_cpu_state. */
466
+ if (env->xl == MXL_RV32) {
467
+ return env->pc & UINT32_MAX;
468
+ }
469
+ return env->pc;
470
+}
471
+
472
static void riscv_cpu_synchronize_from_tb(CPUState *cs,
473
const TranslationBlock *tb)
474
{
475
@@ -XXX,XX +XXX,XX @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
476
cc->has_work = riscv_cpu_has_work;
477
cc->dump_state = riscv_cpu_dump_state;
478
cc->set_pc = riscv_cpu_set_pc;
479
+ cc->get_pc = riscv_cpu_get_pc;
480
cc->gdb_read_register = riscv_cpu_gdb_read_register;
481
cc->gdb_write_register = riscv_cpu_gdb_write_register;
482
cc->gdb_num_core_regs = 33;
483
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
484
index XXXXXXX..XXXXXXX 100644
485
--- a/target/rx/cpu.c
486
+++ b/target/rx/cpu.c
487
@@ -XXX,XX +XXX,XX @@ static void rx_cpu_set_pc(CPUState *cs, vaddr value)
488
cpu->env.pc = value;
489
}
490
491
+static vaddr rx_cpu_get_pc(CPUState *cs)
492
+{
493
+ RXCPU *cpu = RX_CPU(cs);
494
+
495
+ return cpu->env.pc;
496
+}
497
+
498
static void rx_cpu_synchronize_from_tb(CPUState *cs,
499
const TranslationBlock *tb)
500
{
501
@@ -XXX,XX +XXX,XX @@ static void rx_cpu_class_init(ObjectClass *klass, void *data)
502
cc->has_work = rx_cpu_has_work;
503
cc->dump_state = rx_cpu_dump_state;
504
cc->set_pc = rx_cpu_set_pc;
505
+ cc->get_pc = rx_cpu_get_pc;
506
507
#ifndef CONFIG_USER_ONLY
508
cc->sysemu_ops = &rx_sysemu_ops;
509
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
510
index XXXXXXX..XXXXXXX 100644
511
--- a/target/s390x/cpu.c
512
+++ b/target/s390x/cpu.c
513
@@ -XXX,XX +XXX,XX @@ static void s390_cpu_set_pc(CPUState *cs, vaddr value)
514
cpu->env.psw.addr = value;
515
}
516
517
+static vaddr s390_cpu_get_pc(CPUState *cs)
518
+{
519
+ S390CPU *cpu = S390_CPU(cs);
520
+
521
+ return cpu->env.psw.addr;
522
+}
523
+
524
static bool s390_cpu_has_work(CPUState *cs)
525
{
526
S390CPU *cpu = S390_CPU(cs);
527
@@ -XXX,XX +XXX,XX @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
528
cc->has_work = s390_cpu_has_work;
529
cc->dump_state = s390_cpu_dump_state;
530
cc->set_pc = s390_cpu_set_pc;
531
+ cc->get_pc = s390_cpu_get_pc;
532
cc->gdb_read_register = s390_cpu_gdb_read_register;
533
cc->gdb_write_register = s390_cpu_gdb_write_register;
534
#ifndef CONFIG_USER_ONLY
535
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
536
index XXXXXXX..XXXXXXX 100644
537
--- a/target/sh4/cpu.c
538
+++ b/target/sh4/cpu.c
539
@@ -XXX,XX +XXX,XX @@ static void superh_cpu_set_pc(CPUState *cs, vaddr value)
540
cpu->env.pc = value;
541
}
542
543
+static vaddr superh_cpu_get_pc(CPUState *cs)
544
+{
545
+ SuperHCPU *cpu = SUPERH_CPU(cs);
546
+
547
+ return cpu->env.pc;
548
+}
549
+
550
static void superh_cpu_synchronize_from_tb(CPUState *cs,
551
const TranslationBlock *tb)
552
{
553
@@ -XXX,XX +XXX,XX @@ static void superh_cpu_class_init(ObjectClass *oc, void *data)
554
cc->has_work = superh_cpu_has_work;
555
cc->dump_state = superh_cpu_dump_state;
556
cc->set_pc = superh_cpu_set_pc;
557
+ cc->get_pc = superh_cpu_get_pc;
558
cc->gdb_read_register = superh_cpu_gdb_read_register;
559
cc->gdb_write_register = superh_cpu_gdb_write_register;
560
#ifndef CONFIG_USER_ONLY
561
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
562
index XXXXXXX..XXXXXXX 100644
563
--- a/target/sparc/cpu.c
564
+++ b/target/sparc/cpu.c
565
@@ -XXX,XX +XXX,XX @@ static void sparc_cpu_set_pc(CPUState *cs, vaddr value)
566
cpu->env.npc = value + 4;
567
}
568
569
+static vaddr sparc_cpu_get_pc(CPUState *cs)
570
+{
571
+ SPARCCPU *cpu = SPARC_CPU(cs);
572
+
573
+ return cpu->env.pc;
574
+}
575
+
576
static void sparc_cpu_synchronize_from_tb(CPUState *cs,
577
const TranslationBlock *tb)
578
{
579
@@ -XXX,XX +XXX,XX @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
580
cc->memory_rw_debug = sparc_cpu_memory_rw_debug;
581
#endif
582
cc->set_pc = sparc_cpu_set_pc;
583
+ cc->get_pc = sparc_cpu_get_pc;
584
cc->gdb_read_register = sparc_cpu_gdb_read_register;
585
cc->gdb_write_register = sparc_cpu_gdb_write_register;
586
#ifndef CONFIG_USER_ONLY
587
diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c
588
index XXXXXXX..XXXXXXX 100644
589
--- a/target/tricore/cpu.c
590
+++ b/target/tricore/cpu.c
591
@@ -XXX,XX +XXX,XX @@ static void tricore_cpu_set_pc(CPUState *cs, vaddr value)
592
env->PC = value & ~(target_ulong)1;
593
}
594
595
+static vaddr tricore_cpu_get_pc(CPUState *cs)
596
+{
597
+ TriCoreCPU *cpu = TRICORE_CPU(cs);
598
+ CPUTriCoreState *env = &cpu->env;
599
+
600
+ return env->PC;
601
+}
602
+
603
static void tricore_cpu_synchronize_from_tb(CPUState *cs,
604
const TranslationBlock *tb)
605
{
606
@@ -XXX,XX +XXX,XX @@ static void tricore_cpu_class_init(ObjectClass *c, void *data)
607
608
cc->dump_state = tricore_cpu_dump_state;
609
cc->set_pc = tricore_cpu_set_pc;
610
+ cc->get_pc = tricore_cpu_get_pc;
611
cc->sysemu_ops = &tricore_sysemu_ops;
612
cc->tcg_ops = &tricore_tcg_ops;
613
}
614
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
615
index XXXXXXX..XXXXXXX 100644
616
--- a/target/xtensa/cpu.c
617
+++ b/target/xtensa/cpu.c
618
@@ -XXX,XX +XXX,XX @@ static void xtensa_cpu_set_pc(CPUState *cs, vaddr value)
619
cpu->env.pc = value;
620
}
621
622
+static vaddr xtensa_cpu_get_pc(CPUState *cs)
623
+{
624
+ XtensaCPU *cpu = XTENSA_CPU(cs);
625
+
626
+ return cpu->env.pc;
627
+}
628
+
629
static bool xtensa_cpu_has_work(CPUState *cs)
630
{
631
#ifndef CONFIG_USER_ONLY
632
@@ -XXX,XX +XXX,XX @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
633
cc->has_work = xtensa_cpu_has_work;
634
cc->dump_state = xtensa_cpu_dump_state;
635
cc->set_pc = xtensa_cpu_set_pc;
636
+ cc->get_pc = xtensa_cpu_get_pc;
637
cc->gdb_read_register = xtensa_cpu_gdb_read_register;
638
cc->gdb_write_register = xtensa_cpu_gdb_write_register;
639
cc->gdb_stop_before_watchpoint = true;
640
--
27
--
641
2.34.1
28
2.34.1
642
29
643
30
diff view generated by jsdifflib
1
Add an interface to return the CPUTLBEntryFull struct
1
Pass the address of the last byte to be changed, rather than
2
that goes with the lookup. The result is not intended
2
the first address past the last byte. This avoids overflow
3
to be valid across multiple lookups, so the user must
3
when the last page of the address space is involved.
4
use the results immediately.
4
5
5
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1528
6
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
7
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
8
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
9
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
7
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
10
---
8
---
11
include/exec/exec-all.h | 15 +++++++++++++
9
include/exec/cpu-all.h | 2 +-
12
include/qemu/typedefs.h | 1 +
10
accel/tcg/user-exec.c | 16 +++++++---------
13
accel/tcg/cputlb.c | 47 +++++++++++++++++++++++++----------------
11
bsd-user/mmap.c | 6 +++---
14
3 files changed, 45 insertions(+), 18 deletions(-)
12
linux-user/elfload.c | 11 ++++++-----
15
13
linux-user/mmap.c | 16 ++++++++--------
16
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
14
linux-user/syscall.c | 4 ++--
17
index XXXXXXX..XXXXXXX 100644
15
6 files changed, 27 insertions(+), 28 deletions(-)
18
--- a/include/exec/exec-all.h
16
19
+++ b/include/exec/exec-all.h
17
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
20
@@ -XXX,XX +XXX,XX @@ int probe_access_flags(CPUArchState *env, target_ulong addr,
18
index XXXXXXX..XXXXXXX 100644
21
MMUAccessType access_type, int mmu_idx,
19
--- a/include/exec/cpu-all.h
22
bool nonfault, void **phost, uintptr_t retaddr);
20
+++ b/include/exec/cpu-all.h
23
21
@@ -XXX,XX +XXX,XX @@ typedef int (*walk_memory_regions_fn)(void *, target_ulong,
24
+#ifndef CONFIG_USER_ONLY
22
int walk_memory_regions(void *, walk_memory_regions_fn);
25
+/**
23
26
+ * probe_access_full:
24
int page_get_flags(target_ulong address);
27
+ * Like probe_access_flags, except also return into @pfull.
25
-void page_set_flags(target_ulong start, target_ulong end, int flags);
28
+ *
26
+void page_set_flags(target_ulong start, target_ulong last, int flags);
29
+ * The CPUTLBEntryFull structure returned via @pfull is transient
27
void page_reset_target_data(target_ulong start, target_ulong end);
30
+ * and must be consumed or copied immediately, before any further
28
int page_check_range(target_ulong start, target_ulong len, int flags);
31
+ * access or changes to TLB @mmu_idx.
29
32
+ */
30
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
33
+int probe_access_full(CPUArchState *env, target_ulong addr,
31
index XXXXXXX..XXXXXXX 100644
34
+ MMUAccessType access_type, int mmu_idx,
32
--- a/accel/tcg/user-exec.c
35
+ bool nonfault, void **phost,
33
+++ b/accel/tcg/user-exec.c
36
+ CPUTLBEntryFull **pfull, uintptr_t retaddr);
34
@@ -XXX,XX +XXX,XX @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
37
+#endif
35
* The flag PAGE_WRITE_ORG is positioned automatically depending
38
+
36
* on PAGE_WRITE. The mmap_lock should already be held.
39
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
37
*/
40
38
-void page_set_flags(target_ulong start, target_ulong end, int flags)
41
/* Estimated block size for TB allocation. */
39
+void page_set_flags(target_ulong start, target_ulong last, int flags)
42
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/qemu/typedefs.h
45
+++ b/include/qemu/typedefs.h
46
@@ -XXX,XX +XXX,XX @@ typedef struct ConfidentialGuestSupport ConfidentialGuestSupport;
47
typedef struct CPUAddressSpace CPUAddressSpace;
48
typedef struct CPUArchState CPUArchState;
49
typedef struct CPUState CPUState;
50
+typedef struct CPUTLBEntryFull CPUTLBEntryFull;
51
typedef struct DeviceListener DeviceListener;
52
typedef struct DeviceState DeviceState;
53
typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
54
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/accel/tcg/cputlb.c
57
+++ b/accel/tcg/cputlb.c
58
@@ -XXX,XX +XXX,XX @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
59
static int probe_access_internal(CPUArchState *env, target_ulong addr,
60
int fault_size, MMUAccessType access_type,
61
int mmu_idx, bool nonfault,
62
- void **phost, uintptr_t retaddr)
63
+ void **phost, CPUTLBEntryFull **pfull,
64
+ uintptr_t retaddr)
65
{
40
{
66
uintptr_t index = tlb_index(env, mmu_idx, addr);
41
- target_ulong last;
67
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
42
bool reset = false;
68
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
43
bool inval_tb = false;
69
mmu_idx, nonfault, retaddr)) {
44
70
/* Non-faulting page table read failed. */
45
/* This function should never be called with addresses outside the
71
*phost = NULL;
46
guest address space. If this assert fires, it probably indicates
72
+ *pfull = NULL;
47
a missing call to h2g_valid. */
73
return TLB_INVALID_MASK;
48
- assert(start < end);
74
}
49
- assert(end - 1 <= GUEST_ADDR_MAX);
75
50
+ assert(start <= last);
76
/* TLB resize via tlb_fill may have moved the entry. */
51
+ assert(last <= GUEST_ADDR_MAX);
77
+ index = tlb_index(env, mmu_idx, addr);
52
/* Only set PAGE_ANON with new mappings. */
78
entry = tlb_entry(env, mmu_idx, addr);
53
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
79
54
assert_memory_lock();
80
/*
55
81
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
56
- start = start & TARGET_PAGE_MASK;
82
}
57
- end = TARGET_PAGE_ALIGN(end);
83
flags &= tlb_addr;
58
- last = end - 1;
84
59
+ start &= TARGET_PAGE_MASK;
85
+ *pfull = &env_tlb(env)->d[mmu_idx].fulltlb[index];
60
+ last |= ~TARGET_PAGE_MASK;
86
+
61
87
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
62
if (!(flags & PAGE_VALID)) {
88
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
63
flags = 0;
89
*phost = NULL;
64
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
90
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
65
}
91
return flags;
66
92
}
67
if (!flags || reset) {
93
68
- page_reset_target_data(start, end);
94
-int probe_access_flags(CPUArchState *env, target_ulong addr,
69
+ page_reset_target_data(start, last + 1);
95
- MMUAccessType access_type, int mmu_idx,
70
inval_tb |= pageflags_unset(start, last);
96
- bool nonfault, void **phost, uintptr_t retaddr)
71
}
97
+int probe_access_full(CPUArchState *env, target_ulong addr,
72
if (flags) {
98
+ MMUAccessType access_type, int mmu_idx,
73
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
99
+ bool nonfault, void **phost, CPUTLBEntryFull **pfull,
74
~(reset ? 0 : PAGE_STICKY));
100
+ uintptr_t retaddr)
75
}
101
{
76
if (inval_tb) {
102
- int flags;
77
- tb_invalidate_phys_range(start, end);
103
-
78
+ tb_invalidate_phys_range(start, last + 1);
104
- flags = probe_access_internal(env, addr, 0, access_type, mmu_idx,
79
}
105
- nonfault, phost, retaddr);
80
}
106
+ int flags = probe_access_internal(env, addr, 0, access_type, mmu_idx,
81
107
+ nonfault, phost, pfull, retaddr);
82
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
108
83
index XXXXXXX..XXXXXXX 100644
109
/* Handle clean RAM pages. */
84
--- a/bsd-user/mmap.c
110
if (unlikely(flags & TLB_NOTDIRTY)) {
85
+++ b/bsd-user/mmap.c
111
- uintptr_t index = tlb_index(env, mmu_idx, addr);
86
@@ -XXX,XX +XXX,XX @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
112
- CPUTLBEntryFull *full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
87
if (ret != 0)
113
-
88
goto error;
114
- notdirty_write(env_cpu(env), addr, 1, full, retaddr);
89
}
115
+ notdirty_write(env_cpu(env), addr, 1, *pfull, retaddr);
90
- page_set_flags(start, start + len, prot | PAGE_VALID);
116
flags &= ~TLB_NOTDIRTY;
91
+ page_set_flags(start, start + len - 1, prot | PAGE_VALID);
117
}
92
mmap_unlock();
118
93
return 0;
119
return flags;
94
error:
120
}
95
@@ -XXX,XX +XXX,XX @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
121
96
}
122
+int probe_access_flags(CPUArchState *env, target_ulong addr,
97
}
123
+ MMUAccessType access_type, int mmu_idx,
98
the_end1:
124
+ bool nonfault, void **phost, uintptr_t retaddr)
99
- page_set_flags(start, start + len, prot | PAGE_VALID);
125
+{
100
+ page_set_flags(start, start + len - 1, prot | PAGE_VALID);
126
+ CPUTLBEntryFull *full;
101
the_end:
127
+
102
#ifdef DEBUG_MMAP
128
+ return probe_access_full(env, addr, access_type, mmu_idx,
103
printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
129
+ nonfault, phost, &full, retaddr);
104
@@ -XXX,XX +XXX,XX @@ int target_munmap(abi_ulong start, abi_ulong len)
130
+}
105
}
131
+
106
132
void *probe_access(CPUArchState *env, target_ulong addr, int size,
107
if (ret == 0) {
133
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
108
- page_set_flags(start, start + len, 0);
134
{
109
+ page_set_flags(start, start + len - 1, 0);
135
+ CPUTLBEntryFull *full;
110
}
136
void *host;
111
mmap_unlock();
137
int flags;
112
return ret;
138
113
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
139
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
114
index XXXXXXX..XXXXXXX 100644
140
115
--- a/linux-user/elfload.c
141
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
116
+++ b/linux-user/elfload.c
142
- false, &host, retaddr);
117
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
143
+ false, &host, &full, retaddr);
118
exit(EXIT_FAILURE);
144
119
}
145
/* Per the interface, size == 0 merely faults the access. */
120
page_set_flags(TARGET_VSYSCALL_PAGE,
146
if (size == 0) {
121
- TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE,
147
@@ -XXX,XX +XXX,XX @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
122
+ TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK,
148
}
123
PAGE_EXEC | PAGE_VALID);
149
124
return true;
150
if (unlikely(flags & (TLB_NOTDIRTY | TLB_WATCHPOINT))) {
125
}
151
- uintptr_t index = tlb_index(env, mmu_idx, addr);
126
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
152
- CPUTLBEntryFull *full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
127
exit(EXIT_FAILURE);
153
-
128
}
154
/* Handle watchpoints. */
129
155
if (flags & TLB_WATCHPOINT) {
130
- page_set_flags(commpage, commpage + qemu_host_page_size,
156
int wp_access = (access_type == MMU_DATA_STORE
131
+ page_set_flags(commpage, commpage | ~qemu_host_page_mask,
157
@@ -XXX,XX +XXX,XX @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
132
PAGE_READ | PAGE_EXEC | PAGE_VALID);
158
void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
133
return true;
159
MMUAccessType access_type, int mmu_idx)
134
}
160
{
135
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
161
+ CPUTLBEntryFull *full;
136
exit(EXIT_FAILURE);
162
void *host;
137
}
163
int flags;
138
164
139
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE,
165
flags = probe_access_internal(env, addr, 0, access_type,
140
+ page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
166
- mmu_idx, true, &host, 0);
141
PAGE_READ | PAGE_EXEC | PAGE_VALID);
167
+ mmu_idx, true, &host, &full, 0);
142
return true;
168
143
}
169
/* No combination of flags are expected by the caller. */
144
@@ -XXX,XX +XXX,XX @@ static bool init_guest_commpage(void)
170
return flags ? NULL : host;
145
* and implement syscalls. Here, simply mark the page executable.
171
@@ -XXX,XX +XXX,XX @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
146
* Special case the entry points during translation (see do_page_zero).
172
tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
147
*/
173
void **hostp)
148
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE,
174
{
149
+ page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
175
+ CPUTLBEntryFull *full;
150
PAGE_EXEC | PAGE_VALID);
176
void *p;
151
return true;
177
152
}
178
(void)probe_access_internal(env, addr, 1, MMU_INST_FETCH,
153
@@ -XXX,XX +XXX,XX @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
179
- cpu_mmu_index(env, true), false, &p, 0);
154
180
+ cpu_mmu_index(env, true), false, &p, &full, 0);
155
/* Ensure that the bss page(s) are valid */
181
if (p == NULL) {
156
if ((page_get_flags(last_bss-1) & prot) != prot) {
182
return -1;
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);
235
break;
236
}
183
}
237
}
184
--
238
--
185
2.34.1
239
2.34.1
186
240
187
241
diff view generated by jsdifflib
1
This structure will shortly contain more than just
1
Pass the address of the last byte to be changed, rather than
2
data for accessing MMIO. Rename the 'addr' member
2
the first address past the last byte. This avoids overflow
3
to 'xlat_section' to more clearly indicate its purpose.
3
when the last page of the address space is involved.
4
4
5
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
6
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
7
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
8
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
9
---
7
---
10
include/exec/cpu-defs.h | 22 ++++----
8
include/exec/cpu-all.h | 2 +-
11
accel/tcg/cputlb.c | 102 +++++++++++++++++++------------------
9
accel/tcg/user-exec.c | 11 +++++------
12
target/arm/mte_helper.c | 14 ++---
10
linux-user/mmap.c | 2 +-
13
target/arm/sve_helper.c | 4 +-
11
3 files changed, 7 insertions(+), 8 deletions(-)
14
target/arm/translate-a64.c | 2 +-
15
5 files changed, 73 insertions(+), 71 deletions(-)
16
12
17
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
13
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
18
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
19
--- a/include/exec/cpu-defs.h
15
--- a/include/exec/cpu-all.h
20
+++ b/include/exec/cpu-defs.h
16
+++ b/include/exec/cpu-all.h
21
@@ -XXX,XX +XXX,XX @@ typedef uint64_t target_ulong;
17
@@ -XXX,XX +XXX,XX @@ int walk_memory_regions(void *, walk_memory_regions_fn);
22
# endif
18
23
# endif
19
int page_get_flags(target_ulong address);
24
20
void page_set_flags(target_ulong start, target_ulong last, int flags);
25
+/* Minimalized TLB entry for use by TCG fast path. */
21
-void page_reset_target_data(target_ulong start, target_ulong end);
26
typedef struct CPUTLBEntry {
22
+void page_reset_target_data(target_ulong start, target_ulong last);
27
/* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address
23
int page_check_range(target_ulong start, target_ulong len, int flags);
28
bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not
24
29
@@ -XXX,XX +XXX,XX @@ typedef struct CPUTLBEntry {
25
/**
30
26
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
31
QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS));
32
33
-/* The IOTLB is not accessed directly inline by generated TCG code,
34
- * so the CPUIOTLBEntry layout is not as critical as that of the
35
- * CPUTLBEntry. (This is also why we don't want to combine the two
36
- * structs into one.)
37
+/*
38
+ * The full TLB entry, which is not accessed by generated TCG code,
39
+ * so the layout is not as critical as that of CPUTLBEntry. This is
40
+ * also why we don't want to combine the two structs.
41
*/
42
-typedef struct CPUIOTLBEntry {
43
+typedef struct CPUTLBEntryFull {
44
/*
45
- * @addr contains:
46
+ * @xlat_section contains:
47
* - in the lower TARGET_PAGE_BITS, a physical section number
48
* - with the lower TARGET_PAGE_BITS masked off, an offset which
49
* must be added to the virtual address to obtain:
50
@@ -XXX,XX +XXX,XX @@ typedef struct CPUIOTLBEntry {
51
* number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM)
52
* + the offset within the target MemoryRegion (otherwise)
53
*/
54
- hwaddr addr;
55
+ hwaddr xlat_section;
56
MemTxAttrs attrs;
57
-} CPUIOTLBEntry;
58
+} CPUTLBEntryFull;
59
60
/*
61
* Data elements that are per MMU mode, minus the bits accessed by
62
@@ -XXX,XX +XXX,XX @@ typedef struct CPUTLBDesc {
63
size_t vindex;
64
/* The tlb victim table, in two parts. */
65
CPUTLBEntry vtable[CPU_VTLB_SIZE];
66
- CPUIOTLBEntry viotlb[CPU_VTLB_SIZE];
67
- /* The iotlb. */
68
- CPUIOTLBEntry *iotlb;
69
+ CPUTLBEntryFull vfulltlb[CPU_VTLB_SIZE];
70
+ CPUTLBEntryFull *fulltlb;
71
} CPUTLBDesc;
72
73
/*
74
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
75
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
76
--- a/accel/tcg/cputlb.c
28
--- a/accel/tcg/user-exec.c
77
+++ b/accel/tcg/cputlb.c
29
+++ b/accel/tcg/user-exec.c
78
@@ -XXX,XX +XXX,XX @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
30
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
79
}
31
}
80
32
81
g_free(fast->table);
33
if (!flags || reset) {
82
- g_free(desc->iotlb);
34
- page_reset_target_data(start, last + 1);
83
+ g_free(desc->fulltlb);
35
+ page_reset_target_data(start, last);
84
36
inval_tb |= pageflags_unset(start, last);
85
tlb_window_reset(desc, now, 0);
86
/* desc->n_used_entries is cleared by the caller */
87
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
88
fast->table = g_try_new(CPUTLBEntry, new_size);
89
- desc->iotlb = g_try_new(CPUIOTLBEntry, new_size);
90
+ desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size);
91
92
/*
93
* If the allocations fail, try smaller sizes. We just freed some
94
@@ -XXX,XX +XXX,XX @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
95
* allocations to fail though, so we progressively reduce the allocation
96
* size, aborting if we cannot even allocate the smallest TLB we support.
97
*/
98
- while (fast->table == NULL || desc->iotlb == NULL) {
99
+ while (fast->table == NULL || desc->fulltlb == NULL) {
100
if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) {
101
error_report("%s: %s", __func__, strerror(errno));
102
abort();
103
@@ -XXX,XX +XXX,XX @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
104
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
105
106
g_free(fast->table);
107
- g_free(desc->iotlb);
108
+ g_free(desc->fulltlb);
109
fast->table = g_try_new(CPUTLBEntry, new_size);
110
- desc->iotlb = g_try_new(CPUIOTLBEntry, new_size);
111
+ desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size);
112
}
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];
113
}
60
}
114
61
#else
115
@@ -XXX,XX +XXX,XX @@ static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now)
62
-void page_reset_target_data(target_ulong start, target_ulong end) { }
116
desc->n_used_entries = 0;
63
+void page_reset_target_data(target_ulong start, target_ulong last) { }
117
fast->mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS;
64
#endif /* TARGET_PAGE_DATA_SIZE */
118
fast->table = g_new(CPUTLBEntry, n_entries);
65
119
- desc->iotlb = g_new(CPUIOTLBEntry, n_entries);
66
/* The softmmu versions of these helpers are in cputlb.c. */
120
+ desc->fulltlb = g_new(CPUTLBEntryFull, n_entries);
67
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
121
tlb_mmu_flush_locked(desc, fast);
68
index XXXXXXX..XXXXXXX 100644
122
}
69
--- a/linux-user/mmap.c
123
70
+++ b/linux-user/mmap.c
124
@@ -XXX,XX +XXX,XX @@ void tlb_destroy(CPUState *cpu)
71
@@ -XXX,XX +XXX,XX @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice)
125
CPUTLBDescFast *fast = &env_tlb(env)->f[i];
72
if (can_passthrough_madvise(start, end)) {
126
73
ret = get_errno(madvise(g2h_untagged(start), len, advice));
127
g_free(fast->table);
74
if ((advice == MADV_DONTNEED) && (ret == 0)) {
128
- g_free(desc->iotlb);
75
- page_reset_target_data(start, start + len);
129
+ g_free(desc->fulltlb);
76
+ page_reset_target_data(start, start + len - 1);
130
}
77
}
131
}
132
133
@@ -XXX,XX +XXX,XX @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
134
135
/* Evict the old entry into the victim tlb. */
136
copy_tlb_helper_locked(tv, te);
137
- desc->viotlb[vidx] = desc->iotlb[index];
138
+ desc->vfulltlb[vidx] = desc->fulltlb[index];
139
tlb_n_used_entries_dec(env, mmu_idx);
140
}
141
142
@@ -XXX,XX +XXX,XX @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
143
* subtract here is that of the page base, and not the same as the
144
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
145
*/
146
- desc->iotlb[index].addr = iotlb - vaddr_page;
147
- desc->iotlb[index].attrs = attrs;
148
+ desc->fulltlb[index].xlat_section = iotlb - vaddr_page;
149
+ desc->fulltlb[index].attrs = attrs;
150
151
/* Now calculate the new entry */
152
tn.addend = addend - vaddr_page;
153
@@ -XXX,XX +XXX,XX @@ static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,
154
}
155
}
156
157
-static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
158
+static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
159
int mmu_idx, target_ulong addr, uintptr_t retaddr,
160
MMUAccessType access_type, MemOp op)
161
{
162
@@ -XXX,XX +XXX,XX @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
163
bool locked = false;
164
MemTxResult r;
165
166
- section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
167
+ section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
168
mr = section->mr;
169
- mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
170
+ mr_offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
171
cpu->mem_io_pc = retaddr;
172
if (!cpu->can_do_io) {
173
cpu_io_recompile(cpu, retaddr);
174
@@ -XXX,XX +XXX,XX @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
175
qemu_mutex_lock_iothread();
176
locked = true;
177
}
178
- r = memory_region_dispatch_read(mr, mr_offset, &val, op, iotlbentry->attrs);
179
+ r = memory_region_dispatch_read(mr, mr_offset, &val, op, full->attrs);
180
if (r != MEMTX_OK) {
181
hwaddr physaddr = mr_offset +
182
section->offset_within_address_space -
183
section->offset_within_region;
184
185
cpu_transaction_failed(cpu, physaddr, addr, memop_size(op), access_type,
186
- mmu_idx, iotlbentry->attrs, r, retaddr);
187
+ mmu_idx, full->attrs, r, retaddr);
188
}
189
if (locked) {
190
qemu_mutex_unlock_iothread();
191
@@ -XXX,XX +XXX,XX @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
192
}
193
194
/*
195
- * Save a potentially trashed IOTLB entry for later lookup by plugin.
196
- * This is read by tlb_plugin_lookup if the iotlb entry doesn't match
197
+ * Save a potentially trashed CPUTLBEntryFull for later lookup by plugin.
198
+ * This is read by tlb_plugin_lookup if the fulltlb entry doesn't match
199
* because of the side effect of io_writex changing memory layout.
200
*/
201
static void save_iotlb_data(CPUState *cs, hwaddr addr,
202
@@ -XXX,XX +XXX,XX @@ static void save_iotlb_data(CPUState *cs, hwaddr addr,
203
#endif
204
}
205
206
-static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
207
+static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
208
int mmu_idx, uint64_t val, target_ulong addr,
209
uintptr_t retaddr, MemOp op)
210
{
211
@@ -XXX,XX +XXX,XX @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
212
bool locked = false;
213
MemTxResult r;
214
215
- section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
216
+ section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
217
mr = section->mr;
218
- mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
219
+ mr_offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
220
if (!cpu->can_do_io) {
221
cpu_io_recompile(cpu, retaddr);
222
}
223
@@ -XXX,XX +XXX,XX @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
224
* The memory_region_dispatch may trigger a flush/resize
225
* so for plugins we save the iotlb_data just in case.
226
*/
227
- save_iotlb_data(cpu, iotlbentry->addr, section, mr_offset);
228
+ save_iotlb_data(cpu, full->xlat_section, section, mr_offset);
229
230
if (!qemu_mutex_iothread_locked()) {
231
qemu_mutex_lock_iothread();
232
locked = true;
233
}
234
- r = memory_region_dispatch_write(mr, mr_offset, val, op, iotlbentry->attrs);
235
+ r = memory_region_dispatch_write(mr, mr_offset, val, op, full->attrs);
236
if (r != MEMTX_OK) {
237
hwaddr physaddr = mr_offset +
238
section->offset_within_address_space -
239
section->offset_within_region;
240
241
cpu_transaction_failed(cpu, physaddr, addr, memop_size(op),
242
- MMU_DATA_STORE, mmu_idx, iotlbentry->attrs, r,
243
+ MMU_DATA_STORE, mmu_idx, full->attrs, r,
244
retaddr);
245
}
246
if (locked) {
247
@@ -XXX,XX +XXX,XX @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
248
copy_tlb_helper_locked(vtlb, &tmptlb);
249
qemu_spin_unlock(&env_tlb(env)->c.lock);
250
251
- CPUIOTLBEntry tmpio, *io = &env_tlb(env)->d[mmu_idx].iotlb[index];
252
- CPUIOTLBEntry *vio = &env_tlb(env)->d[mmu_idx].viotlb[vidx];
253
- tmpio = *io; *io = *vio; *vio = tmpio;
254
+ CPUTLBEntryFull *f1 = &env_tlb(env)->d[mmu_idx].fulltlb[index];
255
+ CPUTLBEntryFull *f2 = &env_tlb(env)->d[mmu_idx].vfulltlb[vidx];
256
+ CPUTLBEntryFull tmpf;
257
+ tmpf = *f1; *f1 = *f2; *f2 = tmpf;
258
return true;
259
}
78
}
260
}
79
}
261
@@ -XXX,XX +XXX,XX @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
262
(ADDR) & TARGET_PAGE_MASK)
263
264
static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
265
- CPUIOTLBEntry *iotlbentry, uintptr_t retaddr)
266
+ CPUTLBEntryFull *full, uintptr_t retaddr)
267
{
268
- ram_addr_t ram_addr = mem_vaddr + iotlbentry->addr;
269
+ ram_addr_t ram_addr = mem_vaddr + full->xlat_section;
270
271
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
272
273
@@ -XXX,XX +XXX,XX @@ int probe_access_flags(CPUArchState *env, target_ulong addr,
274
/* Handle clean RAM pages. */
275
if (unlikely(flags & TLB_NOTDIRTY)) {
276
uintptr_t index = tlb_index(env, mmu_idx, addr);
277
- CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
278
+ CPUTLBEntryFull *full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
279
280
- notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr);
281
+ notdirty_write(env_cpu(env), addr, 1, full, retaddr);
282
flags &= ~TLB_NOTDIRTY;
283
}
284
285
@@ -XXX,XX +XXX,XX @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
286
287
if (unlikely(flags & (TLB_NOTDIRTY | TLB_WATCHPOINT))) {
288
uintptr_t index = tlb_index(env, mmu_idx, addr);
289
- CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
290
+ CPUTLBEntryFull *full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
291
292
/* Handle watchpoints. */
293
if (flags & TLB_WATCHPOINT) {
294
int wp_access = (access_type == MMU_DATA_STORE
295
? BP_MEM_WRITE : BP_MEM_READ);
296
cpu_check_watchpoint(env_cpu(env), addr, size,
297
- iotlbentry->attrs, wp_access, retaddr);
298
+ full->attrs, wp_access, retaddr);
299
}
300
301
/* Handle clean RAM pages. */
302
if (flags & TLB_NOTDIRTY) {
303
- notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr);
304
+ notdirty_write(env_cpu(env), addr, 1, full, retaddr);
305
}
306
}
307
308
@@ -XXX,XX +XXX,XX @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
309
* should have just filled the TLB. The one corner case is io_writex
310
* which can cause TLB flushes and potential resizing of the TLBs
311
* losing the information we need. In those cases we need to recover
312
- * data from a copy of the iotlbentry. As long as this always occurs
313
+ * data from a copy of the CPUTLBEntryFull. As long as this always occurs
314
* from the same thread (which a mem callback will be) this is safe.
315
*/
316
317
@@ -XXX,XX +XXX,XX @@ bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
318
if (likely(tlb_hit(tlb_addr, addr))) {
319
/* We must have an iotlb entry for MMIO */
320
if (tlb_addr & TLB_MMIO) {
321
- CPUIOTLBEntry *iotlbentry;
322
- iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
323
+ CPUTLBEntryFull *full;
324
+ full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
325
data->is_io = true;
326
- data->v.io.section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
327
- data->v.io.offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
328
+ data->v.io.section =
329
+ iotlb_to_section(cpu, full->xlat_section, full->attrs);
330
+ data->v.io.offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
331
} else {
332
data->is_io = false;
333
data->v.ram.hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
334
@@ -XXX,XX +XXX,XX @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
335
336
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
337
notdirty_write(env_cpu(env), addr, size,
338
- &env_tlb(env)->d[mmu_idx].iotlb[index], retaddr);
339
+ &env_tlb(env)->d[mmu_idx].fulltlb[index], retaddr);
340
}
341
342
return hostaddr;
343
@@ -XXX,XX +XXX,XX @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi,
344
345
/* Handle anything that isn't just a straight memory access. */
346
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
347
- CPUIOTLBEntry *iotlbentry;
348
+ CPUTLBEntryFull *full;
349
bool need_swap;
350
351
/* For anything that is unaligned, recurse through full_load. */
352
@@ -XXX,XX +XXX,XX @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi,
353
goto do_unaligned_access;
354
}
355
356
- iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
357
+ full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
358
359
/* Handle watchpoints. */
360
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
361
/* On watchpoint hit, this will longjmp out. */
362
cpu_check_watchpoint(env_cpu(env), addr, size,
363
- iotlbentry->attrs, BP_MEM_READ, retaddr);
364
+ full->attrs, BP_MEM_READ, retaddr);
365
}
366
367
need_swap = size > 1 && (tlb_addr & TLB_BSWAP);
368
369
/* Handle I/O access. */
370
if (likely(tlb_addr & TLB_MMIO)) {
371
- return io_readx(env, iotlbentry, mmu_idx, addr, retaddr,
372
+ return io_readx(env, full, mmu_idx, addr, retaddr,
373
access_type, op ^ (need_swap * MO_BSWAP));
374
}
375
376
@@ -XXX,XX +XXX,XX @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val,
377
*/
378
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
379
cpu_check_watchpoint(env_cpu(env), addr, size - size2,
380
- env_tlb(env)->d[mmu_idx].iotlb[index].attrs,
381
+ env_tlb(env)->d[mmu_idx].fulltlb[index].attrs,
382
BP_MEM_WRITE, retaddr);
383
}
384
if (unlikely(tlb_addr2 & TLB_WATCHPOINT)) {
385
cpu_check_watchpoint(env_cpu(env), page2, size2,
386
- env_tlb(env)->d[mmu_idx].iotlb[index2].attrs,
387
+ env_tlb(env)->d[mmu_idx].fulltlb[index2].attrs,
388
BP_MEM_WRITE, retaddr);
389
}
390
391
@@ -XXX,XX +XXX,XX @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
392
393
/* Handle anything that isn't just a straight memory access. */
394
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
395
- CPUIOTLBEntry *iotlbentry;
396
+ CPUTLBEntryFull *full;
397
bool need_swap;
398
399
/* For anything that is unaligned, recurse through byte stores. */
400
@@ -XXX,XX +XXX,XX @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
401
goto do_unaligned_access;
402
}
403
404
- iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
405
+ full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
406
407
/* Handle watchpoints. */
408
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
409
/* On watchpoint hit, this will longjmp out. */
410
cpu_check_watchpoint(env_cpu(env), addr, size,
411
- iotlbentry->attrs, BP_MEM_WRITE, retaddr);
412
+ full->attrs, BP_MEM_WRITE, retaddr);
413
}
414
415
need_swap = size > 1 && (tlb_addr & TLB_BSWAP);
416
417
/* Handle I/O access. */
418
if (tlb_addr & TLB_MMIO) {
419
- io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
420
+ io_writex(env, full, mmu_idx, val, addr, retaddr,
421
op ^ (need_swap * MO_BSWAP));
422
return;
423
}
424
@@ -XXX,XX +XXX,XX @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
425
426
/* Handle clean RAM pages. */
427
if (tlb_addr & TLB_NOTDIRTY) {
428
- notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr);
429
+ notdirty_write(env_cpu(env), addr, size, full, retaddr);
430
}
431
432
haddr = (void *)((uintptr_t)addr + entry->addend);
433
diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c
434
index XXXXXXX..XXXXXXX 100644
435
--- a/target/arm/mte_helper.c
436
+++ b/target/arm/mte_helper.c
437
@@ -XXX,XX +XXX,XX @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
438
return tags + index;
439
#else
440
uintptr_t index;
441
- CPUIOTLBEntry *iotlbentry;
442
+ CPUTLBEntryFull *full;
443
int in_page, flags;
444
ram_addr_t ptr_ra;
445
hwaddr ptr_paddr, tag_paddr, xlat;
446
@@ -XXX,XX +XXX,XX @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
447
assert(!(flags & TLB_INVALID_MASK));
448
449
/*
450
- * Find the iotlbentry for ptr. This *must* be present in the TLB
451
+ * Find the CPUTLBEntryFull for ptr. This *must* be present in the TLB
452
* because we just found the mapping.
453
* TODO: Perhaps there should be a cputlb helper that returns a
454
* matching tlb entry + iotlb entry.
455
@@ -XXX,XX +XXX,XX @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
456
g_assert(tlb_hit(comparator, ptr));
457
}
458
# endif
459
- iotlbentry = &env_tlb(env)->d[ptr_mmu_idx].iotlb[index];
460
+ full = &env_tlb(env)->d[ptr_mmu_idx].fulltlb[index];
461
462
/* If the virtual page MemAttr != Tagged, access unchecked. */
463
- if (!arm_tlb_mte_tagged(&iotlbentry->attrs)) {
464
+ if (!arm_tlb_mte_tagged(&full->attrs)) {
465
return NULL;
466
}
467
468
@@ -XXX,XX +XXX,XX @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
469
int wp = ptr_access == MMU_DATA_LOAD ? BP_MEM_READ : BP_MEM_WRITE;
470
assert(ra != 0);
471
cpu_check_watchpoint(env_cpu(env), ptr, ptr_size,
472
- iotlbentry->attrs, wp, ra);
473
+ full->attrs, wp, ra);
474
}
475
476
/*
477
@@ -XXX,XX +XXX,XX @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
478
tag_paddr = ptr_paddr >> (LOG2_TAG_GRANULE + 1);
479
480
/* Look up the address in tag space. */
481
- tag_asi = iotlbentry->attrs.secure ? ARMASIdx_TagS : ARMASIdx_TagNS;
482
+ tag_asi = full->attrs.secure ? ARMASIdx_TagS : ARMASIdx_TagNS;
483
tag_as = cpu_get_address_space(env_cpu(env), tag_asi);
484
mr = address_space_translate(tag_as, tag_paddr, &xlat, NULL,
485
tag_access == MMU_DATA_STORE,
486
- iotlbentry->attrs);
487
+ full->attrs);
488
489
/*
490
* Note that @mr will never be NULL. If there is nothing in the address
491
diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c
492
index XXXXXXX..XXXXXXX 100644
493
--- a/target/arm/sve_helper.c
494
+++ b/target/arm/sve_helper.c
495
@@ -XXX,XX +XXX,XX @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env,
496
g_assert(tlb_hit(comparator, addr));
497
# endif
498
499
- CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
500
- info->attrs = iotlbentry->attrs;
501
+ CPUTLBEntryFull *full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
502
+ info->attrs = full->attrs;
503
}
504
#endif
505
506
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
507
index XXXXXXX..XXXXXXX 100644
508
--- a/target/arm/translate-a64.c
509
+++ b/target/arm/translate-a64.c
510
@@ -XXX,XX +XXX,XX @@ static bool is_guarded_page(CPUARMState *env, DisasContext *s)
511
* table entry even for that case.
512
*/
513
return (tlb_hit(entry->addr_code, addr) &&
514
- arm_tlb_bti_gp(&env_tlb(env)->d[mmu_idx].iotlb[index].attrs));
515
+ arm_tlb_bti_gp(&env_tlb(env)->d[mmu_idx].fulltlb[index].attrs));
516
#endif
517
}
518
519
--
80
--
520
2.34.1
81
2.34.1
521
82
522
83
diff view generated by jsdifflib
Deleted patch
1
This field is only written, not read; remove it.
2
1
3
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
4
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
5
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
7
---
8
include/hw/core/cpu.h | 1 -
9
accel/tcg/cputlb.c | 7 +++----
10
2 files changed, 3 insertions(+), 5 deletions(-)
11
12
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
13
index XXXXXXX..XXXXXXX 100644
14
--- a/include/hw/core/cpu.h
15
+++ b/include/hw/core/cpu.h
16
@@ -XXX,XX +XXX,XX @@ struct CPUWatchpoint {
17
* the memory regions get moved around by io_writex.
18
*/
19
typedef struct SavedIOTLB {
20
- hwaddr addr;
21
MemoryRegionSection *section;
22
hwaddr mr_offset;
23
} SavedIOTLB;
24
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/accel/tcg/cputlb.c
27
+++ b/accel/tcg/cputlb.c
28
@@ -XXX,XX +XXX,XX @@ static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
29
* This is read by tlb_plugin_lookup if the fulltlb entry doesn't match
30
* because of the side effect of io_writex changing memory layout.
31
*/
32
-static void save_iotlb_data(CPUState *cs, hwaddr addr,
33
- MemoryRegionSection *section, hwaddr mr_offset)
34
+static void save_iotlb_data(CPUState *cs, MemoryRegionSection *section,
35
+ hwaddr mr_offset)
36
{
37
#ifdef CONFIG_PLUGIN
38
SavedIOTLB *saved = &cs->saved_iotlb;
39
- saved->addr = addr;
40
saved->section = section;
41
saved->mr_offset = mr_offset;
42
#endif
43
@@ -XXX,XX +XXX,XX @@ static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
44
* The memory_region_dispatch may trigger a flush/resize
45
* so for plugins we save the iotlb_data just in case.
46
*/
47
- save_iotlb_data(cpu, full->xlat_section, section, mr_offset);
48
+ save_iotlb_data(cpu, section, mr_offset);
49
50
if (!qemu_mutex_iothread_locked()) {
51
qemu_mutex_lock_iothread();
52
--
53
2.34.1
54
55
diff view generated by jsdifflib
Deleted patch
1
When PAGE_WRITE_INV is set when calling tlb_set_page,
2
we immediately set TLB_INVALID_MASK in order to force
3
tlb_fill to be called on the next lookup. Here in
4
probe_access_internal, we have just called tlb_fill
5
and eliminated true misses, thus the lookup must be valid.
6
1
7
This allows us to remove a warning comment from s390x.
8
There doesn't seem to be a reason to change the code though.
9
10
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
11
Reviewed-by: David Hildenbrand <david@redhat.com>
12
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
13
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
14
---
15
accel/tcg/cputlb.c | 10 +++++++++-
16
target/s390x/tcg/mem_helper.c | 4 ----
17
2 files changed, 9 insertions(+), 5 deletions(-)
18
19
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/accel/tcg/cputlb.c
22
+++ b/accel/tcg/cputlb.c
23
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
24
}
25
tlb_addr = tlb_read_ofs(entry, elt_ofs);
26
27
+ flags = TLB_FLAGS_MASK;
28
page_addr = addr & TARGET_PAGE_MASK;
29
if (!tlb_hit_page(tlb_addr, page_addr)) {
30
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page_addr)) {
31
@@ -XXX,XX +XXX,XX @@ static int probe_access_internal(CPUArchState *env, target_ulong addr,
32
33
/* TLB resize via tlb_fill may have moved the entry. */
34
entry = tlb_entry(env, mmu_idx, addr);
35
+
36
+ /*
37
+ * With PAGE_WRITE_INV, we set TLB_INVALID_MASK immediately,
38
+ * to force the next access through tlb_fill. We've just
39
+ * called tlb_fill, so we know that this entry *is* valid.
40
+ */
41
+ flags &= ~TLB_INVALID_MASK;
42
}
43
tlb_addr = tlb_read_ofs(entry, elt_ofs);
44
}
45
- flags = tlb_addr & TLB_FLAGS_MASK;
46
+ flags &= tlb_addr;
47
48
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
49
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
50
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/target/s390x/tcg/mem_helper.c
53
+++ b/target/s390x/tcg/mem_helper.c
54
@@ -XXX,XX +XXX,XX @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size,
55
#else
56
int flags;
57
58
- /*
59
- * For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL
60
- * to detect if there was an exception during tlb_fill().
61
- */
62
env->tlb_fill_exc = 0;
63
flags = probe_access_flags(env, addr, access_type, mmu_idx, nonfault, phost,
64
ra);
65
--
66
2.34.1
67
68
diff view generated by jsdifflib
Deleted patch
1
Now that we have collected all of the page data into
2
CPUTLBEntryFull, provide an interface to record that
3
all in one go, instead of using 4 arguments. This interface
4
allows CPUTLBEntryFull to be extended without having to
5
change the number of arguments.
6
1
7
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
8
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
11
---
12
include/exec/cpu-defs.h | 14 +++++++++++
13
include/exec/exec-all.h | 22 ++++++++++++++++++
14
accel/tcg/cputlb.c | 51 ++++++++++++++++++++++++++---------------
15
3 files changed, 69 insertions(+), 18 deletions(-)
16
17
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/exec/cpu-defs.h
20
+++ b/include/exec/cpu-defs.h
21
@@ -XXX,XX +XXX,XX @@ typedef struct CPUTLBEntryFull {
22
* + the offset within the target MemoryRegion (otherwise)
23
*/
24
hwaddr xlat_section;
25
+
26
+ /*
27
+ * @phys_addr contains the physical address in the address space
28
+ * given by cpu_asidx_from_attrs(cpu, @attrs).
29
+ */
30
+ hwaddr phys_addr;
31
+
32
+ /* @attrs contains the memory transaction attributes for the page. */
33
MemTxAttrs attrs;
34
+
35
+ /* @prot contains the complete protections for the page. */
36
+ uint8_t prot;
37
+
38
+ /* @lg_page_size contains the log2 of the page size. */
39
+ uint8_t lg_page_size;
40
} CPUTLBEntryFull;
41
42
/*
43
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
44
index XXXXXXX..XXXXXXX 100644
45
--- a/include/exec/exec-all.h
46
+++ b/include/exec/exec-all.h
47
@@ -XXX,XX +XXX,XX @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu,
48
uint16_t idxmap,
49
unsigned bits);
50
51
+/**
52
+ * tlb_set_page_full:
53
+ * @cpu: CPU context
54
+ * @mmu_idx: mmu index of the tlb to modify
55
+ * @vaddr: virtual address of the entry to add
56
+ * @full: the details of the tlb entry
57
+ *
58
+ * Add an entry to @cpu tlb index @mmu_idx. All of the fields of
59
+ * @full must be filled, except for xlat_section, and constitute
60
+ * the complete description of the translated page.
61
+ *
62
+ * This is generally called by the target tlb_fill function after
63
+ * having performed a successful page table walk to find the physical
64
+ * address and attributes for the translation.
65
+ *
66
+ * At most one entry for a given virtual address is permitted. Only a
67
+ * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only
68
+ * used by tlb_flush_page.
69
+ */
70
+void tlb_set_page_full(CPUState *cpu, int mmu_idx, target_ulong vaddr,
71
+ CPUTLBEntryFull *full);
72
+
73
/**
74
* tlb_set_page_with_attrs:
75
* @cpu: CPU to add this TLB entry for
76
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
77
index XXXXXXX..XXXXXXX 100644
78
--- a/accel/tcg/cputlb.c
79
+++ b/accel/tcg/cputlb.c
80
@@ -XXX,XX +XXX,XX @@ static void tlb_add_large_page(CPUArchState *env, int mmu_idx,
81
env_tlb(env)->d[mmu_idx].large_page_mask = lp_mask;
82
}
83
84
-/* Add a new TLB entry. At most one entry for a given virtual address
85
+/*
86
+ * Add a new TLB entry. At most one entry for a given virtual address
87
* is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
88
* supplied size is only used by tlb_flush_page.
89
*
90
* Called from TCG-generated code, which is under an RCU read-side
91
* critical section.
92
*/
93
-void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
94
- hwaddr paddr, MemTxAttrs attrs, int prot,
95
- int mmu_idx, target_ulong size)
96
+void tlb_set_page_full(CPUState *cpu, int mmu_idx,
97
+ target_ulong vaddr, CPUTLBEntryFull *full)
98
{
99
CPUArchState *env = cpu->env_ptr;
100
CPUTLB *tlb = env_tlb(env);
101
@@ -XXX,XX +XXX,XX @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
102
CPUTLBEntry *te, tn;
103
hwaddr iotlb, xlat, sz, paddr_page;
104
target_ulong vaddr_page;
105
- int asidx = cpu_asidx_from_attrs(cpu, attrs);
106
- int wp_flags;
107
+ int asidx, wp_flags, prot;
108
bool is_ram, is_romd;
109
110
assert_cpu_is_self(cpu);
111
112
- if (size <= TARGET_PAGE_SIZE) {
113
+ if (full->lg_page_size <= TARGET_PAGE_BITS) {
114
sz = TARGET_PAGE_SIZE;
115
} else {
116
- tlb_add_large_page(env, mmu_idx, vaddr, size);
117
- sz = size;
118
+ sz = (hwaddr)1 << full->lg_page_size;
119
+ tlb_add_large_page(env, mmu_idx, vaddr, sz);
120
}
121
vaddr_page = vaddr & TARGET_PAGE_MASK;
122
- paddr_page = paddr & TARGET_PAGE_MASK;
123
+ paddr_page = full->phys_addr & TARGET_PAGE_MASK;
124
125
+ prot = full->prot;
126
+ asidx = cpu_asidx_from_attrs(cpu, full->attrs);
127
section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
128
- &xlat, &sz, attrs, &prot);
129
+ &xlat, &sz, full->attrs, &prot);
130
assert(sz >= TARGET_PAGE_SIZE);
131
132
tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
133
" prot=%x idx=%d\n",
134
- vaddr, paddr, prot, mmu_idx);
135
+ vaddr, full->phys_addr, prot, mmu_idx);
136
137
address = vaddr_page;
138
- if (size < TARGET_PAGE_SIZE) {
139
+ if (full->lg_page_size < TARGET_PAGE_BITS) {
140
/* Repeat the MMU check and TLB fill on every access. */
141
address |= TLB_INVALID_MASK;
142
}
143
- if (attrs.byte_swap) {
144
+ if (full->attrs.byte_swap) {
145
address |= TLB_BSWAP;
146
}
147
148
@@ -XXX,XX +XXX,XX @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
149
* subtract here is that of the page base, and not the same as the
150
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
151
*/
152
+ desc->fulltlb[index] = *full;
153
desc->fulltlb[index].xlat_section = iotlb - vaddr_page;
154
- desc->fulltlb[index].attrs = attrs;
155
+ desc->fulltlb[index].phys_addr = paddr_page;
156
+ desc->fulltlb[index].prot = prot;
157
158
/* Now calculate the new entry */
159
tn.addend = addend - vaddr_page;
160
@@ -XXX,XX +XXX,XX @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
161
qemu_spin_unlock(&tlb->c.lock);
162
}
163
164
-/* Add a new TLB entry, but without specifying the memory
165
- * transaction attributes to be used.
166
- */
167
+void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
168
+ hwaddr paddr, MemTxAttrs attrs, int prot,
169
+ int mmu_idx, target_ulong size)
170
+{
171
+ CPUTLBEntryFull full = {
172
+ .phys_addr = paddr,
173
+ .attrs = attrs,
174
+ .prot = prot,
175
+ .lg_page_size = ctz64(size)
176
+ };
177
+
178
+ assert(is_power_of_2(size));
179
+ tlb_set_page_full(cpu, mmu_idx, vaddr, &full);
180
+}
181
+
182
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
183
hwaddr paddr, int prot,
184
int mmu_idx, target_ulong size)
185
--
186
2.34.1
187
188
diff view generated by jsdifflib
1
Prepare for targets to be able to produce TBs that can
1
Pass the address of the last byte to be changed, rather than
2
run in more than one virtual context.
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: Alex Bennée <alex.bennee@linaro.org>
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
---
7
---
7
accel/tcg/internal.h | 4 +++
8
accel/tcg/tb-maint.c | 28 ++++++++++++++++------------
8
accel/tcg/tb-jmp-cache.h | 41 +++++++++++++++++++++++++
9
1 file changed, 16 insertions(+), 12 deletions(-)
9
include/exec/cpu-defs.h | 3 ++
10
include/exec/exec-all.h | 32 ++++++++++++++++++--
11
accel/tcg/cpu-exec.c | 16 ++++++----
12
accel/tcg/translate-all.c | 64 ++++++++++++++++++++++++++-------------
13
6 files changed, 131 insertions(+), 29 deletions(-)
14
10
15
diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h
11
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
16
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
17
--- a/accel/tcg/internal.h
13
--- a/accel/tcg/tb-maint.c
18
+++ b/accel/tcg/internal.h
14
+++ b/accel/tcg/tb-maint.c
19
@@ -XXX,XX +XXX,XX @@ void tb_htable_init(void);
15
@@ -XXX,XX +XXX,XX @@ static void tb_remove(TranslationBlock *tb)
20
/* Return the current PC from CPU, which may be cached in TB. */
16
}
21
static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb)
17
18
/* TODO: For now, still shared with translate-all.c for system mode. */
19
-#define PAGE_FOR_EACH_TB(start, end, pagedesc, T, N) \
20
- for (T = foreach_tb_first(start, end), \
21
- N = foreach_tb_next(T, start, end); \
22
+#define PAGE_FOR_EACH_TB(start, last, pagedesc, T, N) \
23
+ for (T = foreach_tb_first(start, last), \
24
+ N = foreach_tb_next(T, start, last); \
25
T != NULL; \
26
- T = N, N = foreach_tb_next(N, start, end))
27
+ T = N, N = foreach_tb_next(N, start, last))
28
29
typedef TranslationBlock *PageForEachNext;
30
31
static PageForEachNext foreach_tb_first(tb_page_addr_t start,
32
- tb_page_addr_t end)
33
+ tb_page_addr_t last)
22
{
34
{
23
+#if TARGET_TB_PCREL
35
- IntervalTreeNode *n = interval_tree_iter_first(&tb_root, start, end - 1);
24
+ return cpu->cc->get_pc(cpu);
36
+ IntervalTreeNode *n = interval_tree_iter_first(&tb_root, start, last);
25
+#else
37
return n ? container_of(n, TranslationBlock, itree) : NULL;
26
return tb_pc(tb);
27
+#endif
28
}
38
}
29
39
30
#endif /* ACCEL_TCG_INTERNAL_H */
40
static PageForEachNext foreach_tb_next(PageForEachNext tb,
31
diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h
41
tb_page_addr_t start,
32
index XXXXXXX..XXXXXXX 100644
42
- tb_page_addr_t end)
33
--- a/accel/tcg/tb-jmp-cache.h
43
+ tb_page_addr_t last)
34
+++ b/accel/tcg/tb-jmp-cache.h
44
{
35
@@ -XXX,XX +XXX,XX @@
45
IntervalTreeNode *n;
36
46
37
/*
47
if (tb) {
38
* Accessed in parallel; all accesses to 'tb' must be atomic.
48
- n = interval_tree_iter_next(&tb->itree, start, end - 1);
39
+ * For TARGET_TB_PCREL, accesses to 'pc' must be protected by
49
+ n = interval_tree_iter_next(&tb->itree, start, last);
40
+ * a load_acquire/store_release to 'tb'.
50
if (n) {
41
*/
51
return container_of(n, TranslationBlock, itree);
42
struct CPUJumpCache {
52
}
43
struct {
53
@@ -XXX,XX +XXX,XX @@ struct page_collection {
44
TranslationBlock *tb;
45
+#if TARGET_TB_PCREL
46
+ target_ulong pc;
47
+#endif
48
} array[TB_JMP_CACHE_SIZE];
49
};
54
};
50
55
51
+static inline TranslationBlock *
56
typedef int PageForEachNext;
52
+tb_jmp_cache_get_tb(CPUJumpCache *jc, uint32_t hash)
57
-#define PAGE_FOR_EACH_TB(start, end, pagedesc, tb, n) \
53
+{
58
+#define PAGE_FOR_EACH_TB(start, last, pagedesc, tb, n) \
54
+#if TARGET_TB_PCREL
59
TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next)
55
+ /* Use acquire to ensure current load of pc from jc. */
60
56
+ return qatomic_load_acquire(&jc->array[hash].tb);
61
#ifdef CONFIG_DEBUG_TCG
57
+#else
62
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
58
+ /* Use rcu_read to ensure current load of pc from *tb. */
59
+ return qatomic_rcu_read(&jc->array[hash].tb);
60
+#endif
61
+}
62
+
63
+static inline target_ulong
64
+tb_jmp_cache_get_pc(CPUJumpCache *jc, uint32_t hash, TranslationBlock *tb)
65
+{
66
+#if TARGET_TB_PCREL
67
+ return jc->array[hash].pc;
68
+#else
69
+ return tb_pc(tb);
70
+#endif
71
+}
72
+
73
+static inline void
74
+tb_jmp_cache_set(CPUJumpCache *jc, uint32_t hash,
75
+ TranslationBlock *tb, target_ulong pc)
76
+{
77
+#if TARGET_TB_PCREL
78
+ jc->array[hash].pc = pc;
79
+ /* Use store_release on tb to ensure pc is written first. */
80
+ qatomic_store_release(&jc->array[hash].tb, tb);
81
+#else
82
+ /* Use the pc value already stored in tb->pc. */
83
+ qatomic_set(&jc->array[hash].tb, tb);
84
+#endif
85
+}
86
+
87
#endif /* ACCEL_TCG_TB_JMP_CACHE_H */
88
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
89
index XXXXXXX..XXXXXXX 100644
90
--- a/include/exec/cpu-defs.h
91
+++ b/include/exec/cpu-defs.h
92
@@ -XXX,XX +XXX,XX @@
93
# error TARGET_PAGE_BITS must be defined in cpu-param.h
94
# endif
95
#endif
96
+#ifndef TARGET_TB_PCREL
97
+# define TARGET_TB_PCREL 0
98
+#endif
99
100
#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
101
102
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
103
index XXXXXXX..XXXXXXX 100644
104
--- a/include/exec/exec-all.h
105
+++ b/include/exec/exec-all.h
106
@@ -XXX,XX +XXX,XX @@ struct tb_tc {
107
};
108
109
struct TranslationBlock {
110
- target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
111
- target_ulong cs_base; /* CS base for this block */
112
+#if !TARGET_TB_PCREL
113
+ /*
114
+ * Guest PC corresponding to this block. This must be the true
115
+ * virtual address. Therefore e.g. x86 stores EIP + CS_BASE, and
116
+ * targets like Arm, MIPS, HP-PA, which reuse low bits for ISA or
117
+ * privilege, must store those bits elsewhere.
118
+ *
119
+ * If TARGET_TB_PCREL, the opcodes for the TranslationBlock are
120
+ * written such that the TB is associated only with the physical
121
+ * page and may be run in any virtual address context. In this case,
122
+ * PC must always be taken from ENV in a target-specific manner.
123
+ * Unwind information is taken as offsets from the page, to be
124
+ * deposited into the "current" PC.
125
+ */
126
+ target_ulong pc;
127
+#endif
128
+
129
+ /*
130
+ * Target-specific data associated with the TranslationBlock, e.g.:
131
+ * x86: the original user, the Code Segment virtual base,
132
+ * arm: an extension of tb->flags,
133
+ * s390x: instruction data for EXECUTE,
134
+ * sparc: the next pc of the instruction queue (for delay slots).
135
+ */
136
+ target_ulong cs_base;
137
+
138
uint32_t flags; /* flags defining in which context the code was generated */
139
uint32_t cflags; /* compile flags */
140
141
@@ -XXX,XX +XXX,XX @@ struct TranslationBlock {
142
/* Hide the read to avoid ifdefs for TARGET_TB_PCREL. */
143
static inline target_ulong tb_pc(const TranslationBlock *tb)
144
{
145
+#if TARGET_TB_PCREL
146
+ qemu_build_not_reached();
147
+#else
148
return tb->pc;
149
+#endif
150
}
151
152
/* Hide the qatomic_read to make code a little easier on the eyes */
153
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
154
index XXXXXXX..XXXXXXX 100644
155
--- a/accel/tcg/cpu-exec.c
156
+++ b/accel/tcg/cpu-exec.c
157
@@ -XXX,XX +XXX,XX @@ static bool tb_lookup_cmp(const void *p, const void *d)
158
const TranslationBlock *tb = p;
159
const struct tb_desc *desc = d;
160
161
- if (tb_pc(tb) == desc->pc &&
162
+ if ((TARGET_TB_PCREL || tb_pc(tb) == desc->pc) &&
163
tb->page_addr[0] == desc->page_addr0 &&
164
tb->cs_base == desc->cs_base &&
165
tb->flags == desc->flags &&
166
@@ -XXX,XX +XXX,XX @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
167
return NULL;
168
}
169
desc.page_addr0 = phys_pc;
170
- h = tb_hash_func(phys_pc, pc, flags, cflags, *cpu->trace_dstate);
171
+ h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc),
172
+ flags, cflags, *cpu->trace_dstate);
173
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
174
}
175
176
@@ -XXX,XX +XXX,XX @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
177
uint32_t flags, uint32_t cflags)
178
{
63
{
179
TranslationBlock *tb;
64
TranslationBlock *tb;
180
+ CPUJumpCache *jc;
65
PageForEachNext n;
181
uint32_t hash;
66
+ tb_page_addr_t last = end - 1;
182
67
183
/* we should never be trying to look up an INVALID tb */
68
assert_memory_lock();
184
tcg_debug_assert(!(cflags & CF_INVALID));
69
185
70
- PAGE_FOR_EACH_TB(start, end, unused, tb, n) {
186
hash = tb_jmp_cache_hash_func(pc);
71
+ PAGE_FOR_EACH_TB(start, last, unused, tb, n) {
187
- tb = qatomic_rcu_read(&cpu->tb_jmp_cache->array[hash].tb);
72
tb_phys_invalidate__locked(tb);
188
+ jc = cpu->tb_jmp_cache;
189
+ tb = tb_jmp_cache_get_tb(jc, hash);
190
191
if (likely(tb &&
192
- tb->pc == pc &&
193
+ tb_jmp_cache_get_pc(jc, hash, tb) == pc &&
194
tb->cs_base == cs_base &&
195
tb->flags == flags &&
196
tb->trace_vcpu_dstate == *cpu->trace_dstate &&
197
@@ -XXX,XX +XXX,XX @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
198
if (tb == NULL) {
199
return NULL;
200
}
73
}
201
- qatomic_set(&cpu->tb_jmp_cache->array[hash].tb, tb);
202
+ tb_jmp_cache_set(jc, hash, tb, pc);
203
return tb;
204
}
74
}
205
75
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
206
@@ -XXX,XX +XXX,XX @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
76
bool current_tb_modified;
207
if (cc->tcg_ops->synchronize_from_tb) {
77
TranslationBlock *tb;
208
cc->tcg_ops->synchronize_from_tb(cpu, last_tb);
78
PageForEachNext n;
209
} else {
79
+ tb_page_addr_t last;
210
+ assert(!TARGET_TB_PCREL);
80
211
assert(cc->set_pc);
81
/*
212
cc->set_pc(cpu, tb_pc(last_tb));
82
* Without precise smc semantics, or when outside of a TB,
213
}
83
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
214
@@ -XXX,XX +XXX,XX @@ int cpu_exec(CPUState *cpu)
84
assert_memory_lock();
215
* for the fast lookup
85
current_tb = tcg_tb_lookup(pc);
216
*/
86
217
h = tb_jmp_cache_hash_func(pc);
87
+ last = addr | ~TARGET_PAGE_MASK;
218
- qatomic_set(&cpu->tb_jmp_cache->array[h].tb, tb);
88
addr &= TARGET_PAGE_MASK;
219
+ tb_jmp_cache_set(cpu->tb_jmp_cache, h, tb, pc);
89
current_tb_modified = false;
220
}
90
221
91
- PAGE_FOR_EACH_TB(addr, addr + TARGET_PAGE_SIZE, unused, tb, n) {
222
#ifndef CONFIG_USER_ONLY
92
+ PAGE_FOR_EACH_TB(addr, last, unused, tb, n) {
223
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
93
if (current_tb == tb &&
224
index XXXXXXX..XXXXXXX 100644
94
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
225
--- a/accel/tcg/translate-all.c
95
/*
226
+++ b/accel/tcg/translate-all.c
96
@@ -XXX,XX +XXX,XX @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
227
@@ -XXX,XX +XXX,XX @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
97
bool current_tb_modified = false;
228
98
TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL;
229
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
99
#endif /* TARGET_HAS_PRECISE_SMC */
230
if (i == 0) {
100
+ tb_page_addr_t last G_GNUC_UNUSED = end - 1;
231
- prev = (j == 0 ? tb_pc(tb) : 0);
101
232
+ prev = (!TARGET_TB_PCREL && j == 0 ? tb_pc(tb) : 0);
102
/*
233
} else {
103
* We remove all the TBs in the range [start, end[.
234
prev = tcg_ctx->gen_insn_data[i - 1][j];
104
* XXX: see if in some cases it could be faster to invalidate all the code
235
}
105
*/
236
@@ -XXX,XX +XXX,XX @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
106
- PAGE_FOR_EACH_TB(start, end, p, tb, n) {
237
static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
107
+ PAGE_FOR_EACH_TB(start, last, p, tb, n) {
238
uintptr_t searched_pc, bool reset_icount)
108
/* NOTE: this is subtle as a TB may span two physical pages */
239
{
109
if (n == 0) {
240
- target_ulong data[TARGET_INSN_START_WORDS] = { tb_pc(tb) };
110
/* NOTE: tb_end may be after the end of the page, but
241
+ target_ulong data[TARGET_INSN_START_WORDS];
242
uintptr_t host_pc = (uintptr_t)tb->tc.ptr;
243
CPUArchState *env = cpu->env_ptr;
244
const uint8_t *p = tb->tc.ptr + tb->tc.size;
245
@@ -XXX,XX +XXX,XX @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
246
return -1;
247
}
248
249
+ memset(data, 0, sizeof(data));
250
+ if (!TARGET_TB_PCREL) {
251
+ data[0] = tb_pc(tb);
252
+ }
253
+
254
/* Reconstruct the stored insn data while looking for the point at
255
which the end of the insn exceeds the searched_pc. */
256
for (i = 0; i < num_insns; ++i) {
257
@@ -XXX,XX +XXX,XX @@ static bool tb_cmp(const void *ap, const void *bp)
258
const TranslationBlock *a = ap;
259
const TranslationBlock *b = bp;
260
261
- return tb_pc(a) == tb_pc(b) &&
262
- a->cs_base == b->cs_base &&
263
- a->flags == b->flags &&
264
- (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
265
- a->trace_vcpu_dstate == b->trace_vcpu_dstate &&
266
- a->page_addr[0] == b->page_addr[0] &&
267
- a->page_addr[1] == b->page_addr[1];
268
+ return ((TARGET_TB_PCREL || tb_pc(a) == tb_pc(b)) &&
269
+ a->cs_base == b->cs_base &&
270
+ a->flags == b->flags &&
271
+ (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
272
+ a->trace_vcpu_dstate == b->trace_vcpu_dstate &&
273
+ a->page_addr[0] == b->page_addr[0] &&
274
+ a->page_addr[1] == b->page_addr[1]);
275
}
276
277
void tb_htable_init(void)
278
@@ -XXX,XX +XXX,XX @@ static inline void tb_jmp_unlink(TranslationBlock *dest)
279
qemu_spin_unlock(&dest->jmp_lock);
280
}
281
282
+static void tb_jmp_cache_inval_tb(TranslationBlock *tb)
283
+{
284
+ CPUState *cpu;
285
+
286
+ if (TARGET_TB_PCREL) {
287
+ /* A TB may be at any virtual address */
288
+ CPU_FOREACH(cpu) {
289
+ tcg_flush_jmp_cache(cpu);
290
+ }
291
+ } else {
292
+ uint32_t h = tb_jmp_cache_hash_func(tb_pc(tb));
293
+
294
+ CPU_FOREACH(cpu) {
295
+ CPUJumpCache *jc = cpu->tb_jmp_cache;
296
+
297
+ if (qatomic_read(&jc->array[h].tb) == tb) {
298
+ qatomic_set(&jc->array[h].tb, NULL);
299
+ }
300
+ }
301
+ }
302
+}
303
+
304
/*
305
* In user-mode, call with mmap_lock held.
306
* In !user-mode, if @rm_from_page_list is set, call with the TB's pages'
307
@@ -XXX,XX +XXX,XX @@ static inline void tb_jmp_unlink(TranslationBlock *dest)
308
*/
309
static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
310
{
311
- CPUState *cpu;
312
PageDesc *p;
313
uint32_t h;
314
tb_page_addr_t phys_pc;
315
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
316
317
/* remove the TB from the hash list */
318
phys_pc = tb->page_addr[0];
319
- h = tb_hash_func(phys_pc, tb_pc(tb), tb->flags, orig_cflags,
320
- tb->trace_vcpu_dstate);
321
+ h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
322
+ tb->flags, orig_cflags, tb->trace_vcpu_dstate);
323
if (!qht_remove(&tb_ctx.htable, tb, h)) {
324
return;
325
}
326
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
327
}
328
329
/* remove the TB from the hash list */
330
- h = tb_jmp_cache_hash_func(tb->pc);
331
- CPU_FOREACH(cpu) {
332
- CPUJumpCache *jc = cpu->tb_jmp_cache;
333
- if (qatomic_read(&jc->array[h].tb) == tb) {
334
- qatomic_set(&jc->array[h].tb, NULL);
335
- }
336
- }
337
+ tb_jmp_cache_inval_tb(tb);
338
339
/* suppress this TB from the two jump lists */
340
tb_remove_from_jmp_list(tb, 0);
341
@@ -XXX,XX +XXX,XX @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
342
}
343
344
/* add in the hash table */
345
- h = tb_hash_func(phys_pc, tb_pc(tb), tb->flags, tb->cflags,
346
- tb->trace_vcpu_dstate);
347
+ h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
348
+ tb->flags, tb->cflags, tb->trace_vcpu_dstate);
349
qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
350
351
/* remove TB from the page(s) if we couldn't insert it */
352
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tb_gen_code(CPUState *cpu,
353
354
gen_code_buf = tcg_ctx->code_gen_ptr;
355
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
356
+#if !TARGET_TB_PCREL
357
tb->pc = pc;
358
+#endif
359
tb->cs_base = cs_base;
360
tb->flags = flags;
361
tb->cflags = cflags;
362
--
111
--
363
2.34.1
112
2.34.1
364
113
365
114
diff view generated by jsdifflib
1
Bool is more appropriate type for the alloc parameter.
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: Alex Bennée <alex.bennee@linaro.org>
5
Fixes a bug in the loop comparision where "<= end" would lock
4
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
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
accel/tcg/translate-all.c | 14 +++++++-------
11
accel/tcg/tb-maint.c | 22 +++++++++++-----------
8
1 file changed, 7 insertions(+), 7 deletions(-)
12
1 file changed, 11 insertions(+), 11 deletions(-)
9
13
10
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
14
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
11
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
12
--- a/accel/tcg/translate-all.c
16
--- a/accel/tcg/tb-maint.c
13
+++ b/accel/tcg/translate-all.c
17
+++ b/accel/tcg/tb-maint.c
14
@@ -XXX,XX +XXX,XX @@ void page_init(void)
18
@@ -XXX,XX +XXX,XX @@ static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata)
15
#endif
16
}
19
}
17
20
18
-static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
21
/*
19
+static PageDesc *page_find_alloc(tb_page_addr_t index, bool alloc)
22
- * Lock a range of pages ([@start,@end[) as well as the pages of all
23
+ * Lock a range of pages ([@start,@last]) as well as the pages of all
24
* intersecting TBs.
25
* Locking order: acquire locks in ascending order of page index.
26
*/
27
static struct page_collection *page_collection_lock(tb_page_addr_t start,
28
- tb_page_addr_t end)
29
+ tb_page_addr_t last)
20
{
30
{
31
struct page_collection *set = g_malloc(sizeof(*set));
32
tb_page_addr_t index;
21
PageDesc *pd;
33
PageDesc *pd;
22
void **lp;
34
23
@@ -XXX,XX +XXX,XX @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
35
start >>= TARGET_PAGE_BITS;
24
36
- end >>= TARGET_PAGE_BITS;
25
static inline PageDesc *page_find(tb_page_addr_t index)
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)
26
{
54
{
27
- return page_find_alloc(index, 0);
55
struct page_collection *pages;
28
+ return page_find_alloc(index, false);
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)
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);
29
}
72
}
30
73
31
static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
74
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
32
- PageDesc **ret_p2, tb_page_addr_t phys2, int alloc);
75
struct page_collection *pages;
33
+ PageDesc **ret_p2, tb_page_addr_t phys2, bool alloc);
76
tb_page_addr_t next;
34
77
35
/* In user-mode page locks aren't used; mmap_lock is enough */
78
- pages = page_collection_lock(start, end);
36
#ifdef CONFIG_USER_ONLY
79
+ pages = page_collection_lock(start, end - 1);
37
@@ -XXX,XX +XXX,XX @@ static inline void page_unlock(PageDesc *pd)
80
for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
38
/* lock the page(s) of a TB in the correct acquisition order */
81
start < end;
39
static inline void page_lock_tb(const TranslationBlock *tb)
82
start = next, next += TARGET_PAGE_SIZE) {
83
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
40
{
84
{
41
- page_lock_pair(NULL, tb->page_addr[0], NULL, tb->page_addr[1], 0);
85
struct page_collection *pages;
42
+ page_lock_pair(NULL, tb->page_addr[0], NULL, tb->page_addr[1], false);
86
87
- pages = page_collection_lock(ram_addr, ram_addr + size);
88
+ pages = page_collection_lock(ram_addr, ram_addr + size - 1);
89
tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr);
90
page_collection_unlock(pages);
43
}
91
}
44
45
static inline void page_unlock_tb(const TranslationBlock *tb)
46
@@ -XXX,XX +XXX,XX @@ void page_collection_unlock(struct page_collection *set)
47
#endif /* !CONFIG_USER_ONLY */
48
49
static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
50
- PageDesc **ret_p2, tb_page_addr_t phys2, int alloc)
51
+ PageDesc **ret_p2, tb_page_addr_t phys2, bool alloc)
52
{
53
PageDesc *p1, *p2;
54
tb_page_addr_t page1;
55
@@ -XXX,XX +XXX,XX @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
56
* Note that inserting into the hash table first isn't an option, since
57
* we can only insert TBs that are fully initialized.
58
*/
59
- page_lock_pair(&p, phys_pc, &p2, phys_page2, 1);
60
+ page_lock_pair(&p, phys_pc, &p2, phys_page2, true);
61
tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK);
62
if (p2) {
63
tb_page_add(p2, tb, 1, phys_page2);
64
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
65
for (addr = start, len = end - start;
66
len != 0;
67
len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) {
68
- PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
69
+ PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, true);
70
71
/* If the write protection bit is set, then we invalidate
72
the code inside. */
73
--
92
--
74
2.34.1
93
2.34.1
75
94
76
95
diff view generated by jsdifflib
1
Allow the target to cache items from the guest page tables.
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: Alex Bennée <alex.bennee@linaro.org>
5
Properly truncate tb_last to the end of the page; the comment about
4
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
6
tb_end being past the end of the page being ok is not correct,
5
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
7
considering overflow.
8
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
10
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
7
---
11
---
8
include/exec/cpu-defs.h | 9 +++++++++
12
accel/tcg/tb-maint.c | 26 ++++++++++++--------------
9
1 file changed, 9 insertions(+)
13
1 file changed, 12 insertions(+), 14 deletions(-)
10
14
11
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
15
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
12
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
13
--- a/include/exec/cpu-defs.h
17
--- a/accel/tcg/tb-maint.c
14
+++ b/include/exec/cpu-defs.h
18
+++ b/accel/tcg/tb-maint.c
15
@@ -XXX,XX +XXX,XX @@ typedef struct CPUTLBEntryFull {
19
@@ -XXX,XX +XXX,XX @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
16
20
static void
17
/* @lg_page_size contains the log2 of the page size. */
21
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
18
uint8_t lg_page_size;
22
PageDesc *p, tb_page_addr_t start,
23
- tb_page_addr_t end,
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;
19
+
43
+
20
+ /*
44
/* NOTE: this is subtle as a TB may span two physical pages */
21
+ * Allow target-specific additions to this structure.
45
+ tb_start = tb_page_addr0(tb);
22
+ * This may be used to cache items from the guest cpu
46
+ tb_last = tb_start + tb->size - 1;
23
+ * page tables for later use by the implementation.
47
if (n == 0) {
24
+ */
48
- /* NOTE: tb_end may be after the end of the page, but
25
+#ifdef TARGET_PAGE_ENTRY_EXTRA
49
- it is not a problem */
26
+ TARGET_PAGE_ENTRY_EXTRA
50
- tb_start = tb_page_addr0(tb);
27
+#endif
51
- tb_end = tb_start + tb->size;
28
} CPUTLBEntryFull;
52
+ tb_last = MIN(tb_last, tb_start | ~TARGET_PAGE_MASK);
53
} else {
54
tb_start = tb_page_addr1(tb);
55
- tb_end = tb_start + ((tb_page_addr0(tb) + tb->size)
56
- & ~TARGET_PAGE_MASK);
57
+ tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK);
58
}
59
- if (!(tb_end <= start || tb_start >= end)) {
60
+ if (!(tb_last < start || tb_start > last)) {
61
#ifdef TARGET_HAS_PRECISE_SMC
62
if (current_tb == tb &&
63
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
64
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
65
start = addr & TARGET_PAGE_MASK;
66
last = addr | ~TARGET_PAGE_MASK;
67
pages = page_collection_lock(start, last);
68
- tb_invalidate_phys_page_range__locked(pages, p, start, last + 1, 0);
69
+ tb_invalidate_phys_page_range__locked(pages, p, start, last, 0);
70
page_collection_unlock(pages);
71
}
72
73
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
74
continue;
75
}
76
assert_page_locked(pd);
77
- tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0);
78
+ tb_invalidate_phys_page_range__locked(pages, pd, start, bound - 1, 0);
79
}
80
page_collection_unlock(pages);
81
}
82
@@ -XXX,XX +XXX,XX @@ static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages,
83
}
84
85
assert_page_locked(p);
86
- tb_invalidate_phys_page_range__locked(pages, p, start, start + len, ra);
87
+ tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra);
88
}
29
89
30
/*
90
/*
31
--
91
--
32
2.34.1
92
2.34.1
33
93
34
94
diff view generated by jsdifflib
Deleted patch
1
This bitmap is created and discarded immediately.
2
We gain nothing by its existence.
3
1
4
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Message-Id: <20220822232338.1727934-2-richard.henderson@linaro.org>
7
---
8
accel/tcg/translate-all.c | 78 ++-------------------------------------
9
1 file changed, 4 insertions(+), 74 deletions(-)
10
11
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/accel/tcg/translate-all.c
14
+++ b/accel/tcg/translate-all.c
15
@@ -XXX,XX +XXX,XX @@
16
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
17
#endif
18
19
-#define SMC_BITMAP_USE_THRESHOLD 10
20
-
21
typedef struct PageDesc {
22
/* list of TBs intersecting this ram page */
23
uintptr_t first_tb;
24
-#ifdef CONFIG_SOFTMMU
25
- /* in order to optimize self modifying code, we count the number
26
- of lookups we do to a given page to use a bitmap */
27
- unsigned long *code_bitmap;
28
- unsigned int code_write_count;
29
-#else
30
+#ifdef CONFIG_USER_ONLY
31
unsigned long flags;
32
void *target_data;
33
#endif
34
-#ifndef CONFIG_USER_ONLY
35
+#ifdef CONFIG_SOFTMMU
36
QemuSpin lock;
37
#endif
38
} PageDesc;
39
@@ -XXX,XX +XXX,XX @@ void tb_htable_init(void)
40
qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode);
41
}
42
43
-/* call with @p->lock held */
44
-static inline void invalidate_page_bitmap(PageDesc *p)
45
-{
46
- assert_page_locked(p);
47
-#ifdef CONFIG_SOFTMMU
48
- g_free(p->code_bitmap);
49
- p->code_bitmap = NULL;
50
- p->code_write_count = 0;
51
-#endif
52
-}
53
-
54
/* Set to NULL all the 'first_tb' fields in all PageDescs. */
55
static void page_flush_tb_1(int level, void **lp)
56
{
57
@@ -XXX,XX +XXX,XX @@ static void page_flush_tb_1(int level, void **lp)
58
for (i = 0; i < V_L2_SIZE; ++i) {
59
page_lock(&pd[i]);
60
pd[i].first_tb = (uintptr_t)NULL;
61
- invalidate_page_bitmap(pd + i);
62
page_unlock(&pd[i]);
63
}
64
} else {
65
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
66
if (rm_from_page_list) {
67
p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
68
tb_page_remove(p, tb);
69
- invalidate_page_bitmap(p);
70
if (tb->page_addr[1] != -1) {
71
p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
72
tb_page_remove(p, tb);
73
- invalidate_page_bitmap(p);
74
}
75
}
76
77
@@ -XXX,XX +XXX,XX @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
78
}
79
}
80
81
-#ifdef CONFIG_SOFTMMU
82
-/* call with @p->lock held */
83
-static void build_page_bitmap(PageDesc *p)
84
-{
85
- int n, tb_start, tb_end;
86
- TranslationBlock *tb;
87
-
88
- assert_page_locked(p);
89
- p->code_bitmap = bitmap_new(TARGET_PAGE_SIZE);
90
-
91
- PAGE_FOR_EACH_TB(p, tb, n) {
92
- /* NOTE: this is subtle as a TB may span two physical pages */
93
- if (n == 0) {
94
- /* NOTE: tb_end may be after the end of the page, but
95
- it is not a problem */
96
- tb_start = tb->pc & ~TARGET_PAGE_MASK;
97
- tb_end = tb_start + tb->size;
98
- if (tb_end > TARGET_PAGE_SIZE) {
99
- tb_end = TARGET_PAGE_SIZE;
100
- }
101
- } else {
102
- tb_start = 0;
103
- tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
104
- }
105
- bitmap_set(p->code_bitmap, tb_start, tb_end - tb_start);
106
- }
107
-}
108
-#endif
109
-
110
/* add the tb in the target page and protect it if necessary
111
*
112
* Called with mmap_lock held for user-mode emulation.
113
@@ -XXX,XX +XXX,XX @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb,
114
page_already_protected = p->first_tb != (uintptr_t)NULL;
115
#endif
116
p->first_tb = (uintptr_t)tb | n;
117
- invalidate_page_bitmap(p);
118
119
#if defined(CONFIG_USER_ONLY)
120
/* translator_loop() must have made all TB pages non-writable */
121
@@ -XXX,XX +XXX,XX @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
122
/* remove TB from the page(s) if we couldn't insert it */
123
if (unlikely(existing_tb)) {
124
tb_page_remove(p, tb);
125
- invalidate_page_bitmap(p);
126
if (p2) {
127
tb_page_remove(p2, tb);
128
- invalidate_page_bitmap(p2);
129
}
130
tb = existing_tb;
131
}
132
@@ -XXX,XX +XXX,XX @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
133
#if !defined(CONFIG_USER_ONLY)
134
/* if no code remaining, no need to continue to use slow writes */
135
if (!p->first_tb) {
136
- invalidate_page_bitmap(p);
137
tlb_unprotect_code(start);
138
}
139
#endif
140
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
141
}
142
143
assert_page_locked(p);
144
- if (!p->code_bitmap &&
145
- ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) {
146
- build_page_bitmap(p);
147
- }
148
- if (p->code_bitmap) {
149
- unsigned int nr;
150
- unsigned long b;
151
-
152
- nr = start & ~TARGET_PAGE_MASK;
153
- b = p->code_bitmap[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG - 1));
154
- if (b & ((1 << len) - 1)) {
155
- goto do_invalidate;
156
- }
157
- } else {
158
- do_invalidate:
159
- tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
160
- retaddr);
161
- }
162
+ tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
163
+ retaddr);
164
}
165
#else
166
/* Called with mmap_lock held. If pc is not 0 then it indicates the
167
--
168
2.34.1
169
170
diff view generated by jsdifflib
Deleted patch
1
Use the pc coming from db->pc_first rather than the TB.
2
1
3
Use the cached host_addr rather than re-computing for the
4
first page. We still need a separate lookup for the second
5
page because it won't be computed for DisasContextBase until
6
the translator actually performs a read from the page.
7
8
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
9
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
10
---
11
include/exec/plugin-gen.h | 7 ++++---
12
accel/tcg/plugin-gen.c | 22 +++++++++++-----------
13
accel/tcg/translator.c | 2 +-
14
3 files changed, 16 insertions(+), 15 deletions(-)
15
16
diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/exec/plugin-gen.h
19
+++ b/include/exec/plugin-gen.h
20
@@ -XXX,XX +XXX,XX @@ struct DisasContextBase;
21
22
#ifdef CONFIG_PLUGIN
23
24
-bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool supress);
25
+bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db,
26
+ bool supress);
27
void plugin_gen_tb_end(CPUState *cpu);
28
void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db);
29
void plugin_gen_insn_end(void);
30
@@ -XXX,XX +XXX,XX @@ static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size)
31
32
#else /* !CONFIG_PLUGIN */
33
34
-static inline
35
-bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool supress)
36
+static inline bool
37
+plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, bool sup)
38
{
39
return false;
40
}
41
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/accel/tcg/plugin-gen.c
44
+++ b/accel/tcg/plugin-gen.c
45
@@ -XXX,XX +XXX,XX @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
46
pr_ops();
47
}
48
49
-bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only)
50
+bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
51
+ bool mem_only)
52
{
53
bool ret = false;
54
55
@@ -XXX,XX +XXX,XX @@ bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_onl
56
57
ret = true;
58
59
- ptb->vaddr = tb->pc;
60
+ ptb->vaddr = db->pc_first;
61
ptb->vaddr2 = -1;
62
- get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
63
+ ptb->haddr1 = db->host_addr[0];
64
ptb->haddr2 = NULL;
65
ptb->mem_only = mem_only;
66
67
@@ -XXX,XX +XXX,XX @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
68
* Note that we skip this when haddr1 == NULL, e.g. when we're
69
* fetching instructions from a region not backed by RAM.
70
*/
71
- if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
72
- unlikely((db->pc_next & TARGET_PAGE_MASK) !=
73
- (db->pc_first & TARGET_PAGE_MASK))) {
74
- get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
75
- &ptb->haddr2);
76
- ptb->vaddr2 = db->pc_next;
77
- }
78
- if (likely(ptb->vaddr2 == -1)) {
79
+ if (ptb->haddr1 == NULL) {
80
+ pinsn->haddr = NULL;
81
+ } else if (is_same_page(db, db->pc_next)) {
82
pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
83
} else {
84
+ if (ptb->vaddr2 == -1) {
85
+ ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
86
+ get_page_addr_code_hostp(cpu->env_ptr, ptb->vaddr2, &ptb->haddr2);
87
+ }
88
pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
89
}
90
}
91
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
92
index XXXXXXX..XXXXXXX 100644
93
--- a/accel/tcg/translator.c
94
+++ b/accel/tcg/translator.c
95
@@ -XXX,XX +XXX,XX @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns,
96
ops->tb_start(db, cpu);
97
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
98
99
- plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);
100
+ plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
101
102
while (true) {
103
db->num_insns++;
104
--
105
2.34.1
106
107
diff view generated by jsdifflib
1
The availability of tb->pc will shortly be conditional.
1
Pass the address of the last byte to be changed, rather than
2
Introduce accessor functions to minimize ifdefs.
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
Pass around a known pc to places like tcg_gen_code,
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
5
where the caller must already have the value.
6
7
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
8
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
9
---
7
---
10
accel/tcg/internal.h | 6 ++++
8
include/exec/exec-all.h | 2 +-
11
include/exec/exec-all.h | 6 ++++
9
accel/tcg/tb-maint.c | 31 ++++++++++++++++---------------
12
include/tcg/tcg.h | 2 +-
10
accel/tcg/translate-all.c | 2 +-
13
accel/tcg/cpu-exec.c | 46 ++++++++++++++-----------
11
accel/tcg/user-exec.c | 2 +-
14
accel/tcg/translate-all.c | 37 +++++++++++---------
12
softmmu/physmem.c | 2 +-
15
target/arm/cpu.c | 4 +--
13
5 files changed, 20 insertions(+), 19 deletions(-)
16
target/avr/cpu.c | 2 +-
17
target/hexagon/cpu.c | 2 +-
18
target/hppa/cpu.c | 4 +--
19
target/i386/tcg/tcg-cpu.c | 2 +-
20
target/loongarch/cpu.c | 2 +-
21
target/microblaze/cpu.c | 2 +-
22
target/mips/tcg/exception.c | 2 +-
23
target/mips/tcg/sysemu/special_helper.c | 2 +-
24
target/openrisc/cpu.c | 2 +-
25
target/riscv/cpu.c | 4 +--
26
target/rx/cpu.c | 2 +-
27
target/sh4/cpu.c | 4 +--
28
target/sparc/cpu.c | 2 +-
29
target/tricore/cpu.c | 2 +-
30
tcg/tcg.c | 8 ++---
31
21 files changed, 82 insertions(+), 61 deletions(-)
32
14
33
diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h
34
index XXXXXXX..XXXXXXX 100644
35
--- a/accel/tcg/internal.h
36
+++ b/accel/tcg/internal.h
37
@@ -XXX,XX +XXX,XX @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
38
void page_init(void);
39
void tb_htable_init(void);
40
41
+/* Return the current PC from CPU, which may be cached in TB. */
42
+static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb)
43
+{
44
+ return tb_pc(tb);
45
+}
46
+
47
#endif /* ACCEL_TCG_INTERNAL_H */
48
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
15
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
49
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
50
--- a/include/exec/exec-all.h
17
--- a/include/exec/exec-all.h
51
+++ b/include/exec/exec-all.h
18
+++ b/include/exec/exec-all.h
52
@@ -XXX,XX +XXX,XX @@ struct TranslationBlock {
19
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_addr(target_ulong addr);
53
uintptr_t jmp_dest[2];
20
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
54
};
21
#endif
55
22
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
56
+/* Hide the read to avoid ifdefs for TARGET_TB_PCREL. */
23
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
57
+static inline target_ulong tb_pc(const TranslationBlock *tb)
24
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last);
58
+{
25
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
59
+ return tb->pc;
26
60
+}
27
/* GETPC is the true target of the return instruction that we'll execute. */
28
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
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)
38
{
39
TranslationBlock *tb;
40
PageForEachNext n;
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);
57
}
58
59
/*
60
@@ -XXX,XX +XXX,XX @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
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)
72
{
73
struct page_collection *pages;
74
- tb_page_addr_t next;
75
+ tb_page_addr_t index, index_last;
76
77
- pages = page_collection_lock(start, end - 1);
78
- for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
79
- start < end;
80
- start = next, next += TARGET_PAGE_SIZE) {
81
- PageDesc *pd = page_find(start >> TARGET_PAGE_BITS);
82
- tb_page_addr_t bound = MIN(next, end);
83
+ pages = page_collection_lock(start, last);
61
+
84
+
62
/* Hide the qatomic_read to make code a little easier on the eyes */
85
+ index_last = last >> TARGET_PAGE_BITS;
63
static inline uint32_t tb_cflags(const TranslationBlock *tb)
86
+ for (index = start >> TARGET_PAGE_BITS; index <= index_last; index++) {
64
{
87
+ PageDesc *pd = page_find(index);
65
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
88
+ tb_page_addr_t bound;
66
index XXXXXXX..XXXXXXX 100644
89
67
--- a/include/tcg/tcg.h
90
if (pd == NULL) {
68
+++ b/include/tcg/tcg.h
91
continue;
69
@@ -XXX,XX +XXX,XX @@ void tcg_register_thread(void);
92
}
70
void tcg_prologue_init(TCGContext *s);
93
assert_page_locked(pd);
71
void tcg_func_start(TCGContext *s);
94
- tb_invalidate_phys_page_range__locked(pages, pd, start, bound - 1, 0);
72
95
+ bound = (index << TARGET_PAGE_BITS) | ~TARGET_PAGE_MASK;
73
-int tcg_gen_code(TCGContext *s, TranslationBlock *tb);
96
+ bound = MIN(bound, last);
74
+int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start);
97
+ tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0);
75
98
}
76
void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size);
99
page_collection_unlock(pages);
77
78
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
79
index XXXXXXX..XXXXXXX 100644
80
--- a/accel/tcg/cpu-exec.c
81
+++ b/accel/tcg/cpu-exec.c
82
@@ -XXX,XX +XXX,XX @@ static bool tb_lookup_cmp(const void *p, const void *d)
83
const TranslationBlock *tb = p;
84
const struct tb_desc *desc = d;
85
86
- if (tb->pc == desc->pc &&
87
+ if (tb_pc(tb) == desc->pc &&
88
tb->page_addr[0] == desc->page_addr0 &&
89
tb->cs_base == desc->cs_base &&
90
tb->flags == desc->flags &&
91
@@ -XXX,XX +XXX,XX @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
92
return tb;
93
}
100
}
94
95
-static inline void log_cpu_exec(target_ulong pc, CPUState *cpu,
96
- const TranslationBlock *tb)
97
+static void log_cpu_exec(target_ulong pc, CPUState *cpu,
98
+ const TranslationBlock *tb)
99
{
100
- if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC))
101
- && qemu_log_in_addr_range(pc)) {
102
-
103
+ if (qemu_log_in_addr_range(pc)) {
104
qemu_log_mask(CPU_LOG_EXEC,
105
"Trace %d: %p [" TARGET_FMT_lx
106
"/" TARGET_FMT_lx "/%08x/%08x] %s\n",
107
@@ -XXX,XX +XXX,XX @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
108
return tcg_code_gen_epilogue;
109
}
110
111
- log_cpu_exec(pc, cpu, tb);
112
+ if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
113
+ log_cpu_exec(pc, cpu, tb);
114
+ }
115
116
return tb->tc.ptr;
117
}
118
@@ -XXX,XX +XXX,XX @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
119
TranslationBlock *last_tb;
120
const void *tb_ptr = itb->tc.ptr;
121
122
- log_cpu_exec(itb->pc, cpu, itb);
123
+ if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
124
+ log_cpu_exec(log_pc(cpu, itb), cpu, itb);
125
+ }
126
127
qemu_thread_jit_execute();
128
ret = tcg_qemu_tb_exec(env, tb_ptr);
129
@@ -XXX,XX +XXX,XX @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
130
* of the start of the TB.
131
*/
132
CPUClass *cc = CPU_GET_CLASS(cpu);
133
- qemu_log_mask_and_addr(CPU_LOG_EXEC, last_tb->pc,
134
- "Stopped execution of TB chain before %p ["
135
- TARGET_FMT_lx "] %s\n",
136
- last_tb->tc.ptr, last_tb->pc,
137
- lookup_symbol(last_tb->pc));
138
+
139
if (cc->tcg_ops->synchronize_from_tb) {
140
cc->tcg_ops->synchronize_from_tb(cpu, last_tb);
141
} else {
142
assert(cc->set_pc);
143
- cc->set_pc(cpu, last_tb->pc);
144
+ cc->set_pc(cpu, tb_pc(last_tb));
145
+ }
146
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
147
+ target_ulong pc = log_pc(cpu, last_tb);
148
+ if (qemu_log_in_addr_range(pc)) {
149
+ qemu_log("Stopped execution of TB chain before %p ["
150
+ TARGET_FMT_lx "] %s\n",
151
+ last_tb->tc.ptr, pc, lookup_symbol(pc));
152
+ }
153
}
154
}
155
156
@@ -XXX,XX +XXX,XX @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
157
158
qemu_spin_unlock(&tb_next->jmp_lock);
159
160
- qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
161
- "Linking TBs %p [" TARGET_FMT_lx
162
- "] index %d -> %p [" TARGET_FMT_lx "]\n",
163
- tb->tc.ptr, tb->pc, n,
164
- tb_next->tc.ptr, tb_next->pc);
165
+ qemu_log_mask(CPU_LOG_EXEC, "Linking TBs %p index %d -> %p\n",
166
+ tb->tc.ptr, n, tb_next->tc.ptr);
167
return;
168
169
out_unlock_next:
170
@@ -XXX,XX +XXX,XX @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
171
}
172
173
static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
174
+ target_ulong pc,
175
TranslationBlock **last_tb, int *tb_exit)
176
{
177
int32_t insns_left;
178
179
- trace_exec_tb(tb, tb->pc);
180
+ trace_exec_tb(tb, pc);
181
tb = cpu_tb_exec(cpu, tb, tb_exit);
182
if (*tb_exit != TB_EXIT_REQUESTED) {
183
*last_tb = tb;
184
@@ -XXX,XX +XXX,XX @@ int cpu_exec(CPUState *cpu)
185
tb_add_jump(last_tb, tb_exit, tb);
186
}
187
188
- cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit);
189
+ cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit);
190
191
/* Try to align the host and virtual clocks
192
if the guest is in advance */
193
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
101
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
194
index XXXXXXX..XXXXXXX 100644
102
index XXXXXXX..XXXXXXX 100644
195
--- a/accel/tcg/translate-all.c
103
--- a/accel/tcg/translate-all.c
196
+++ b/accel/tcg/translate-all.c
104
+++ b/accel/tcg/translate-all.c
197
@@ -XXX,XX +XXX,XX @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
105
@@ -XXX,XX +XXX,XX @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
198
106
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
199
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
107
addr = get_page_addr_code(env, pc);
200
if (i == 0) {
108
if (addr != -1) {
201
- prev = (j == 0 ? tb->pc : 0);
109
- tb_invalidate_phys_range(addr, addr + 1);
202
+ prev = (j == 0 ? tb_pc(tb) : 0);
110
+ tb_invalidate_phys_range(addr, addr);
203
} else {
111
}
204
prev = tcg_ctx->gen_insn_data[i - 1][j];
205
}
206
@@ -XXX,XX +XXX,XX @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
207
static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
208
uintptr_t searched_pc, bool reset_icount)
209
{
210
- target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc };
211
+ target_ulong data[TARGET_INSN_START_WORDS] = { tb_pc(tb) };
212
uintptr_t host_pc = (uintptr_t)tb->tc.ptr;
213
CPUArchState *env = cpu->env_ptr;
214
const uint8_t *p = tb->tc.ptr + tb->tc.size;
215
@@ -XXX,XX +XXX,XX @@ static bool tb_cmp(const void *ap, const void *bp)
216
const TranslationBlock *a = ap;
217
const TranslationBlock *b = bp;
218
219
- return a->pc == b->pc &&
220
+ return tb_pc(a) == tb_pc(b) &&
221
a->cs_base == b->cs_base &&
222
a->flags == b->flags &&
223
(tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
224
@@ -XXX,XX +XXX,XX @@ static void do_tb_invalidate_check(void *p, uint32_t hash, void *userp)
225
TranslationBlock *tb = p;
226
target_ulong addr = *(target_ulong *)userp;
227
228
- if (!(addr + TARGET_PAGE_SIZE <= tb->pc || addr >= tb->pc + tb->size)) {
229
+ if (!(addr + TARGET_PAGE_SIZE <= tb_pc(tb) ||
230
+ addr >= tb_pc(tb) + tb->size)) {
231
printf("ERROR invalidate: address=" TARGET_FMT_lx
232
- " PC=%08lx size=%04x\n", addr, (long)tb->pc, tb->size);
233
+ " PC=%08lx size=%04x\n", addr, (long)tb_pc(tb), tb->size);
234
}
112
}
235
}
113
}
236
114
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
237
@@ -XXX,XX +XXX,XX @@ static void do_tb_page_check(void *p, uint32_t hash, void *userp)
115
index XXXXXXX..XXXXXXX 100644
238
TranslationBlock *tb = p;
116
--- a/accel/tcg/user-exec.c
239
int flags1, flags2;
117
+++ b/accel/tcg/user-exec.c
240
118
@@ -XXX,XX +XXX,XX @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
241
- flags1 = page_get_flags(tb->pc);
119
~(reset ? 0 : PAGE_STICKY));
242
- flags2 = page_get_flags(tb->pc + tb->size - 1);
120
}
243
+ flags1 = page_get_flags(tb_pc(tb));
121
if (inval_tb) {
244
+ flags2 = page_get_flags(tb_pc(tb) + tb->size - 1);
122
- tb_invalidate_phys_range(start, last + 1);
245
if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
123
+ tb_invalidate_phys_range(start, last);
246
printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
247
- (long)tb->pc, tb->size, flags1, flags2);
248
+ (long)tb_pc(tb), tb->size, flags1, flags2);
249
}
124
}
250
}
125
}
251
126
252
@@ -XXX,XX +XXX,XX @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
127
diff --git a/softmmu/physmem.c b/softmmu/physmem.c
253
128
index XXXXXXX..XXXXXXX 100644
254
/* remove the TB from the hash list */
129
--- a/softmmu/physmem.c
255
phys_pc = tb->page_addr[0];
130
+++ b/softmmu/physmem.c
256
- h = tb_hash_func(phys_pc, tb->pc, tb->flags, orig_cflags,
131
@@ -XXX,XX +XXX,XX @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
257
+ h = tb_hash_func(phys_pc, tb_pc(tb), tb->flags, orig_cflags,
258
tb->trace_vcpu_dstate);
259
if (!qht_remove(&tb_ctx.htable, tb, h)) {
260
return;
261
@@ -XXX,XX +XXX,XX @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
262
}
132
}
263
133
if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) {
264
/* add in the hash table */
134
assert(tcg_enabled());
265
- h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags,
135
- tb_invalidate_phys_range(addr, addr + length);
266
+ h = tb_hash_func(phys_pc, tb_pc(tb), tb->flags, tb->cflags,
136
+ tb_invalidate_phys_range(addr, addr + length - 1);
267
tb->trace_vcpu_dstate);
137
dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
268
qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
269
270
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tb_gen_code(CPUState *cpu,
271
tcg_ctx->cpu = NULL;
272
max_insns = tb->icount;
273
274
- trace_translate_block(tb, tb->pc, tb->tc.ptr);
275
+ trace_translate_block(tb, pc, tb->tc.ptr);
276
277
/* generate machine code */
278
tb->jmp_reset_offset[0] = TB_JMP_RESET_OFFSET_INVALID;
279
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tb_gen_code(CPUState *cpu,
280
ti = profile_getclock();
281
#endif
282
283
- gen_code_size = tcg_gen_code(tcg_ctx, tb);
284
+ gen_code_size = tcg_gen_code(tcg_ctx, tb, pc);
285
if (unlikely(gen_code_size < 0)) {
286
error_return:
287
switch (gen_code_size) {
288
@@ -XXX,XX +XXX,XX @@ TranslationBlock *tb_gen_code(CPUState *cpu,
289
290
#ifdef DEBUG_DISAS
291
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
292
- qemu_log_in_addr_range(tb->pc)) {
293
+ qemu_log_in_addr_range(pc)) {
294
FILE *logfile = qemu_log_trylock();
295
if (logfile) {
296
int code_size, data_size;
297
@@ -XXX,XX +XXX,XX @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
298
*/
299
cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n;
300
301
- qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
302
- "cpu_io_recompile: rewound execution of TB to "
303
- TARGET_FMT_lx "\n", tb->pc);
304
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
305
+ target_ulong pc = log_pc(cpu, tb);
306
+ if (qemu_log_in_addr_range(pc)) {
307
+ qemu_log("cpu_io_recompile: rewound execution of TB to "
308
+ TARGET_FMT_lx "\n", pc);
309
+ }
310
+ }
311
312
cpu_loop_exit_noexc(cpu);
313
}
314
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
315
index XXXXXXX..XXXXXXX 100644
316
--- a/target/arm/cpu.c
317
+++ b/target/arm/cpu.c
318
@@ -XXX,XX +XXX,XX @@ void arm_cpu_synchronize_from_tb(CPUState *cs,
319
* never possible for an AArch64 TB to chain to an AArch32 TB.
320
*/
321
if (is_a64(env)) {
322
- env->pc = tb->pc;
323
+ env->pc = tb_pc(tb);
324
} else {
325
- env->regs[15] = tb->pc;
326
+ env->regs[15] = tb_pc(tb);
327
}
138
}
328
}
139
cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
329
#endif /* CONFIG_TCG */
330
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
331
index XXXXXXX..XXXXXXX 100644
332
--- a/target/avr/cpu.c
333
+++ b/target/avr/cpu.c
334
@@ -XXX,XX +XXX,XX @@ static void avr_cpu_synchronize_from_tb(CPUState *cs,
335
AVRCPU *cpu = AVR_CPU(cs);
336
CPUAVRState *env = &cpu->env;
337
338
- env->pc_w = tb->pc / 2; /* internally PC points to words */
339
+ env->pc_w = tb_pc(tb) / 2; /* internally PC points to words */
340
}
341
342
static void avr_cpu_reset(DeviceState *ds)
343
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
344
index XXXXXXX..XXXXXXX 100644
345
--- a/target/hexagon/cpu.c
346
+++ b/target/hexagon/cpu.c
347
@@ -XXX,XX +XXX,XX @@ static void hexagon_cpu_synchronize_from_tb(CPUState *cs,
348
{
349
HexagonCPU *cpu = HEXAGON_CPU(cs);
350
CPUHexagonState *env = &cpu->env;
351
- env->gpr[HEX_REG_PC] = tb->pc;
352
+ env->gpr[HEX_REG_PC] = tb_pc(tb);
353
}
354
355
static bool hexagon_cpu_has_work(CPUState *cs)
356
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
357
index XXXXXXX..XXXXXXX 100644
358
--- a/target/hppa/cpu.c
359
+++ b/target/hppa/cpu.c
360
@@ -XXX,XX +XXX,XX @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs,
361
HPPACPU *cpu = HPPA_CPU(cs);
362
363
#ifdef CONFIG_USER_ONLY
364
- cpu->env.iaoq_f = tb->pc;
365
+ cpu->env.iaoq_f = tb_pc(tb);
366
cpu->env.iaoq_b = tb->cs_base;
367
#else
368
/* Recover the IAOQ values from the GVA + PRIV. */
369
@@ -XXX,XX +XXX,XX @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs,
370
int32_t diff = cs_base;
371
372
cpu->env.iasq_f = iasq_f;
373
- cpu->env.iaoq_f = (tb->pc & ~iasq_f) + priv;
374
+ cpu->env.iaoq_f = (tb_pc(tb) & ~iasq_f) + priv;
375
if (diff) {
376
cpu->env.iaoq_b = cpu->env.iaoq_f + diff;
377
}
378
diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
379
index XXXXXXX..XXXXXXX 100644
380
--- a/target/i386/tcg/tcg-cpu.c
381
+++ b/target/i386/tcg/tcg-cpu.c
382
@@ -XXX,XX +XXX,XX @@ static void x86_cpu_synchronize_from_tb(CPUState *cs,
383
{
384
X86CPU *cpu = X86_CPU(cs);
385
386
- cpu->env.eip = tb->pc - tb->cs_base;
387
+ cpu->env.eip = tb_pc(tb) - tb->cs_base;
388
}
389
390
#ifndef CONFIG_USER_ONLY
391
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
392
index XXXXXXX..XXXXXXX 100644
393
--- a/target/loongarch/cpu.c
394
+++ b/target/loongarch/cpu.c
395
@@ -XXX,XX +XXX,XX @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
396
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
397
CPULoongArchState *env = &cpu->env;
398
399
- env->pc = tb->pc;
400
+ env->pc = tb_pc(tb);
401
}
402
#endif /* CONFIG_TCG */
403
404
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
405
index XXXXXXX..XXXXXXX 100644
406
--- a/target/microblaze/cpu.c
407
+++ b/target/microblaze/cpu.c
408
@@ -XXX,XX +XXX,XX @@ static void mb_cpu_synchronize_from_tb(CPUState *cs,
409
{
410
MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
411
412
- cpu->env.pc = tb->pc;
413
+ cpu->env.pc = tb_pc(tb);
414
cpu->env.iflags = tb->flags & IFLAGS_TB_MASK;
415
}
416
417
diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c
418
index XXXXXXX..XXXXXXX 100644
419
--- a/target/mips/tcg/exception.c
420
+++ b/target/mips/tcg/exception.c
421
@@ -XXX,XX +XXX,XX @@ void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb)
422
MIPSCPU *cpu = MIPS_CPU(cs);
423
CPUMIPSState *env = &cpu->env;
424
425
- env->active_tc.PC = tb->pc;
426
+ env->active_tc.PC = tb_pc(tb);
427
env->hflags &= ~MIPS_HFLAG_BMASK;
428
env->hflags |= tb->flags & MIPS_HFLAG_BMASK;
429
}
430
diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c
431
index XXXXXXX..XXXXXXX 100644
432
--- a/target/mips/tcg/sysemu/special_helper.c
433
+++ b/target/mips/tcg/sysemu/special_helper.c
434
@@ -XXX,XX +XXX,XX @@ bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb)
435
CPUMIPSState *env = &cpu->env;
436
437
if ((env->hflags & MIPS_HFLAG_BMASK) != 0
438
- && env->active_tc.PC != tb->pc) {
439
+ && env->active_tc.PC != tb_pc(tb)) {
440
env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
441
env->hflags &= ~MIPS_HFLAG_BMASK;
442
return true;
443
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
444
index XXXXXXX..XXXXXXX 100644
445
--- a/target/openrisc/cpu.c
446
+++ b/target/openrisc/cpu.c
447
@@ -XXX,XX +XXX,XX @@ static void openrisc_cpu_synchronize_from_tb(CPUState *cs,
448
{
449
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
450
451
- cpu->env.pc = tb->pc;
452
+ cpu->env.pc = tb_pc(tb);
453
}
454
455
456
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
457
index XXXXXXX..XXXXXXX 100644
458
--- a/target/riscv/cpu.c
459
+++ b/target/riscv/cpu.c
460
@@ -XXX,XX +XXX,XX @@ static void riscv_cpu_synchronize_from_tb(CPUState *cs,
461
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
462
463
if (xl == MXL_RV32) {
464
- env->pc = (int32_t)tb->pc;
465
+ env->pc = (int32_t)tb_pc(tb);
466
} else {
467
- env->pc = tb->pc;
468
+ env->pc = tb_pc(tb);
469
}
470
}
471
472
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
473
index XXXXXXX..XXXXXXX 100644
474
--- a/target/rx/cpu.c
475
+++ b/target/rx/cpu.c
476
@@ -XXX,XX +XXX,XX @@ static void rx_cpu_synchronize_from_tb(CPUState *cs,
477
{
478
RXCPU *cpu = RX_CPU(cs);
479
480
- cpu->env.pc = tb->pc;
481
+ cpu->env.pc = tb_pc(tb);
482
}
483
484
static bool rx_cpu_has_work(CPUState *cs)
485
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
486
index XXXXXXX..XXXXXXX 100644
487
--- a/target/sh4/cpu.c
488
+++ b/target/sh4/cpu.c
489
@@ -XXX,XX +XXX,XX @@ static void superh_cpu_synchronize_from_tb(CPUState *cs,
490
{
491
SuperHCPU *cpu = SUPERH_CPU(cs);
492
493
- cpu->env.pc = tb->pc;
494
+ cpu->env.pc = tb_pc(tb);
495
cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK;
496
}
497
498
@@ -XXX,XX +XXX,XX @@ static bool superh_io_recompile_replay_branch(CPUState *cs,
499
CPUSH4State *env = &cpu->env;
500
501
if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
502
- && env->pc != tb->pc) {
503
+ && env->pc != tb_pc(tb)) {
504
env->pc -= 2;
505
env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
506
return true;
507
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
508
index XXXXXXX..XXXXXXX 100644
509
--- a/target/sparc/cpu.c
510
+++ b/target/sparc/cpu.c
511
@@ -XXX,XX +XXX,XX @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs,
512
{
513
SPARCCPU *cpu = SPARC_CPU(cs);
514
515
- cpu->env.pc = tb->pc;
516
+ cpu->env.pc = tb_pc(tb);
517
cpu->env.npc = tb->cs_base;
518
}
519
520
diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c
521
index XXXXXXX..XXXXXXX 100644
522
--- a/target/tricore/cpu.c
523
+++ b/target/tricore/cpu.c
524
@@ -XXX,XX +XXX,XX @@ static void tricore_cpu_synchronize_from_tb(CPUState *cs,
525
TriCoreCPU *cpu = TRICORE_CPU(cs);
526
CPUTriCoreState *env = &cpu->env;
527
528
- env->PC = tb->pc;
529
+ env->PC = tb_pc(tb);
530
}
531
532
static void tricore_cpu_reset(DeviceState *dev)
533
diff --git a/tcg/tcg.c b/tcg/tcg.c
534
index XXXXXXX..XXXXXXX 100644
535
--- a/tcg/tcg.c
536
+++ b/tcg/tcg.c
537
@@ -XXX,XX +XXX,XX @@ int64_t tcg_cpu_exec_time(void)
538
#endif
539
540
541
-int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
542
+int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start)
543
{
544
#ifdef CONFIG_PROFILER
545
TCGProfile *prof = &s->prof;
546
@@ -XXX,XX +XXX,XX @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
547
548
#ifdef DEBUG_DISAS
549
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)
550
- && qemu_log_in_addr_range(tb->pc))) {
551
+ && qemu_log_in_addr_range(pc_start))) {
552
FILE *logfile = qemu_log_trylock();
553
if (logfile) {
554
fprintf(logfile, "OP:\n");
555
@@ -XXX,XX +XXX,XX @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
556
if (s->nb_indirects > 0) {
557
#ifdef DEBUG_DISAS
558
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_IND)
559
- && qemu_log_in_addr_range(tb->pc))) {
560
+ && qemu_log_in_addr_range(pc_start))) {
561
FILE *logfile = qemu_log_trylock();
562
if (logfile) {
563
fprintf(logfile, "OP before indirect lowering:\n");
564
@@ -XXX,XX +XXX,XX @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
565
566
#ifdef DEBUG_DISAS
567
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT)
568
- && qemu_log_in_addr_range(tb->pc))) {
569
+ && qemu_log_in_addr_range(pc_start))) {
570
FILE *logfile = qemu_log_trylock();
571
if (logfile) {
572
fprintf(logfile, "OP after optimization and liveness analysis:\n");
573
--
140
--
574
2.34.1
141
2.34.1
575
142
576
143
diff view generated by jsdifflib
1
The value previously chosen overlaps GUSA_MASK.
1
Pass the address of the last byte of the image, 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
Rename all DELAY_SLOT_* and GUSA_* defines to emphasize
4
that they are included in TB_FLAGs. Add aliases for the
5
FPSCR and SR bits that are included in TB_FLAGS, so that
6
we don't accidentally reassign those bits.
7
8
Fixes: 4da06fb3062 ("target/sh4: Implement prctl_unalign_sigbus")
9
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/856
10
Reviewed-by: Yoshinori Sato <ysato@users.sourceforge.jp>
11
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
12
---
6
---
13
target/sh4/cpu.h | 56 +++++++++++++------------
7
linux-user/elfload.c | 24 ++++++++++++------------
14
linux-user/sh4/signal.c | 6 +--
8
linux-user/flatload.c | 2 +-
15
target/sh4/cpu.c | 6 +--
9
2 files changed, 13 insertions(+), 13 deletions(-)
16
target/sh4/helper.c | 6 +--
17
target/sh4/translate.c | 90 ++++++++++++++++++++++-------------------
18
5 files changed, 88 insertions(+), 76 deletions(-)
19
10
20
diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h
11
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
21
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
22
--- a/target/sh4/cpu.h
13
--- a/linux-user/elfload.c
23
+++ b/target/sh4/cpu.h
14
+++ b/linux-user/elfload.c
24
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
25
#define FPSCR_RM_NEAREST (0 << 0)
16
if (guest_hiaddr > reserved_va) {
26
#define FPSCR_RM_ZERO (1 << 0)
17
error_report("%s: requires more than reserved virtual "
27
18
"address space (0x%" PRIx64 " > 0x%lx)",
28
-#define DELAY_SLOT_MASK 0x7
19
- image_name, (uint64_t)guest_hiaddr, reserved_va);
29
-#define DELAY_SLOT (1 << 0)
20
+ image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
30
-#define DELAY_SLOT_CONDITIONAL (1 << 1)
21
exit(EXIT_FAILURE);
31
-#define DELAY_SLOT_RTE (1 << 2)
22
}
32
+#define TB_FLAG_DELAY_SLOT (1 << 0)
33
+#define TB_FLAG_DELAY_SLOT_COND (1 << 1)
34
+#define TB_FLAG_DELAY_SLOT_RTE (1 << 2)
35
+#define TB_FLAG_PENDING_MOVCA (1 << 3)
36
+#define TB_FLAG_GUSA_SHIFT 4 /* [11:4] */
37
+#define TB_FLAG_GUSA_EXCLUSIVE (1 << 12)
38
+#define TB_FLAG_UNALIGN (1 << 13)
39
+#define TB_FLAG_SR_FD (1 << SR_FD) /* 15 */
40
+#define TB_FLAG_FPSCR_PR FPSCR_PR /* 19 */
41
+#define TB_FLAG_FPSCR_SZ FPSCR_SZ /* 20 */
42
+#define TB_FLAG_FPSCR_FR FPSCR_FR /* 21 */
43
+#define TB_FLAG_SR_RB (1 << SR_RB) /* 29 */
44
+#define TB_FLAG_SR_MD (1 << SR_MD) /* 30 */
45
46
-#define TB_FLAG_PENDING_MOVCA (1 << 3)
47
-#define TB_FLAG_UNALIGN (1 << 4)
48
-
49
-#define GUSA_SHIFT 4
50
-#ifdef CONFIG_USER_ONLY
51
-#define GUSA_EXCLUSIVE (1 << 12)
52
-#define GUSA_MASK ((0xff << GUSA_SHIFT) | GUSA_EXCLUSIVE)
53
-#else
54
-/* Provide dummy versions of the above to allow tests against tbflags
55
- to be elided while avoiding ifdefs. */
56
-#define GUSA_EXCLUSIVE 0
57
-#define GUSA_MASK 0
58
-#endif
59
-
60
-#define TB_FLAG_ENVFLAGS_MASK (DELAY_SLOT_MASK | GUSA_MASK)
61
+#define TB_FLAG_DELAY_SLOT_MASK (TB_FLAG_DELAY_SLOT | \
62
+ TB_FLAG_DELAY_SLOT_COND | \
63
+ TB_FLAG_DELAY_SLOT_RTE)
64
+#define TB_FLAG_GUSA_MASK ((0xff << TB_FLAG_GUSA_SHIFT) | \
65
+ TB_FLAG_GUSA_EXCLUSIVE)
66
+#define TB_FLAG_FPSCR_MASK (TB_FLAG_FPSCR_PR | \
67
+ TB_FLAG_FPSCR_SZ | \
68
+ TB_FLAG_FPSCR_FR)
69
+#define TB_FLAG_SR_MASK (TB_FLAG_SR_FD | \
70
+ TB_FLAG_SR_RB | \
71
+ TB_FLAG_SR_MD)
72
+#define TB_FLAG_ENVFLAGS_MASK (TB_FLAG_DELAY_SLOT_MASK | \
73
+ TB_FLAG_GUSA_MASK)
74
75
typedef struct tlb_t {
76
uint32_t vpn;        /* virtual page number */
77
@@ -XXX,XX +XXX,XX @@ static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch)
78
{
79
/* The instruction in a RTE delay slot is fetched in privileged
80
mode, but executed in user mode. */
81
- if (ifetch && (env->flags & DELAY_SLOT_RTE)) {
82
+ if (ifetch && (env->flags & TB_FLAG_DELAY_SLOT_RTE)) {
83
return 0;
84
} else {
23
} else {
85
return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0;
24
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
86
@@ -XXX,XX +XXX,XX @@ static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc,
25
if ((guest_hiaddr - guest_base) > ~(uintptr_t)0) {
87
{
26
error_report("%s: requires more virtual address space "
88
*pc = env->pc;
27
"than the host can provide (0x%" PRIx64 ")",
89
/* For a gUSA region, notice the end of the region. */
28
- image_name, (uint64_t)guest_hiaddr - guest_base);
90
- *cs_base = env->flags & GUSA_MASK ? env->gregs[0] : 0;
29
+ image_name, (uint64_t)guest_hiaddr + 1 - guest_base);
91
- *flags = env->flags /* TB_FLAG_ENVFLAGS_MASK: bits 0-2, 4-12 */
30
exit(EXIT_FAILURE);
92
- | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */
93
- | (env->sr & ((1u << SR_MD) | (1u << SR_RB))) /* Bits 29-30 */
94
- | (env->sr & (1u << SR_FD)) /* Bit 15 */
95
+ *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0;
96
+ *flags = env->flags
97
+ | (env->fpscr & TB_FLAG_FPSCR_MASK)
98
+ | (env->sr & TB_FLAG_SR_MASK)
99
| (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */
100
#ifdef CONFIG_USER_ONLY
101
*flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus;
102
diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/linux-user/sh4/signal.c
105
+++ b/linux-user/sh4/signal.c
106
@@ -XXX,XX +XXX,XX @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc)
107
__get_user(regs->fpul, &sc->sc_fpul);
108
109
regs->tra = -1; /* disable syscall checks */
110
- regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
111
+ regs->flags = 0;
112
}
113
114
void setup_frame(int sig, struct target_sigaction *ka,
115
@@ -XXX,XX +XXX,XX @@ void setup_frame(int sig, struct target_sigaction *ka,
116
regs->gregs[5] = 0;
117
regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc);
118
regs->pc = (unsigned long) ka->_sa_handler;
119
- regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
120
+ regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK);
121
122
unlock_user_struct(frame, frame_addr, 1);
123
return;
124
@@ -XXX,XX +XXX,XX @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
125
regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info);
126
regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc);
127
regs->pc = (unsigned long) ka->_sa_handler;
128
- regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
129
+ regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK);
130
131
unlock_user_struct(frame, frame_addr, 1);
132
return;
133
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
134
index XXXXXXX..XXXXXXX 100644
135
--- a/target/sh4/cpu.c
136
+++ b/target/sh4/cpu.c
137
@@ -XXX,XX +XXX,XX @@ static void superh_cpu_synchronize_from_tb(CPUState *cs,
138
SuperHCPU *cpu = SUPERH_CPU(cs);
139
140
cpu->env.pc = tb_pc(tb);
141
- cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK;
142
+ cpu->env.flags = tb->flags;
143
}
144
145
#ifndef CONFIG_USER_ONLY
146
@@ -XXX,XX +XXX,XX @@ static bool superh_io_recompile_replay_branch(CPUState *cs,
147
SuperHCPU *cpu = SUPERH_CPU(cs);
148
CPUSH4State *env = &cpu->env;
149
150
- if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
151
+ if ((env->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND))
152
&& env->pc != tb_pc(tb)) {
153
env->pc -= 2;
154
- env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
155
+ env->flags &= ~(TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND);
156
return true;
157
}
158
return false;
159
diff --git a/target/sh4/helper.c b/target/sh4/helper.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/target/sh4/helper.c
162
+++ b/target/sh4/helper.c
163
@@ -XXX,XX +XXX,XX @@ void superh_cpu_do_interrupt(CPUState *cs)
164
env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB);
165
env->lock_addr = -1;
166
167
- if (env->flags & DELAY_SLOT_MASK) {
168
+ if (env->flags & TB_FLAG_DELAY_SLOT_MASK) {
169
/* Branch instruction should be executed again before delay slot. */
170
    env->spc -= 2;
171
    /* Clear flags for exception/interrupt routine. */
172
- env->flags &= ~DELAY_SLOT_MASK;
173
+ env->flags &= ~TB_FLAG_DELAY_SLOT_MASK;
174
}
175
176
if (do_exp) {
177
@@ -XXX,XX +XXX,XX @@ bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
178
CPUSH4State *env = &cpu->env;
179
180
/* Delay slots are indivisible, ignore interrupts */
181
- if (env->flags & DELAY_SLOT_MASK) {
182
+ if (env->flags & TB_FLAG_DELAY_SLOT_MASK) {
183
return false;
184
} else {
185
superh_cpu_do_interrupt(cs);
186
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/target/sh4/translate.c
189
+++ b/target/sh4/translate.c
190
@@ -XXX,XX +XXX,XX @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, int flags)
191
         i, env->gregs[i], i + 1, env->gregs[i + 1],
192
         i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]);
193
}
194
- if (env->flags & DELAY_SLOT) {
195
+ if (env->flags & TB_FLAG_DELAY_SLOT) {
196
qemu_printf("in delay slot (delayed_pc=0x%08x)\n",
197
         env->delayed_pc);
198
- } else if (env->flags & DELAY_SLOT_CONDITIONAL) {
199
+ } else if (env->flags & TB_FLAG_DELAY_SLOT_COND) {
200
qemu_printf("in conditional delay slot (delayed_pc=0x%08x)\n",
201
         env->delayed_pc);
202
- } else if (env->flags & DELAY_SLOT_RTE) {
203
+ } else if (env->flags & TB_FLAG_DELAY_SLOT_RTE) {
204
qemu_fprintf(f, "in rte delay slot (delayed_pc=0x%08x)\n",
205
env->delayed_pc);
206
}
207
@@ -XXX,XX +XXX,XX @@ static inline void gen_save_cpu_state(DisasContext *ctx, bool save_pc)
208
209
static inline bool use_exit_tb(DisasContext *ctx)
210
{
211
- return (ctx->tbflags & GUSA_EXCLUSIVE) != 0;
212
+ return (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) != 0;
213
}
214
215
static bool use_goto_tb(DisasContext *ctx, target_ulong dest)
216
@@ -XXX,XX +XXX,XX @@ static void gen_conditional_jump(DisasContext *ctx, target_ulong dest,
217
TCGLabel *l1 = gen_new_label();
218
TCGCond cond_not_taken = jump_if_true ? TCG_COND_EQ : TCG_COND_NE;
219
220
- if (ctx->tbflags & GUSA_EXCLUSIVE) {
221
+ if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
222
/* When in an exclusive region, we must continue to the end.
223
Therefore, exit the region on a taken branch, but otherwise
224
fall through to the next instruction. */
225
tcg_gen_brcondi_i32(cond_not_taken, cpu_sr_t, 0, l1);
226
- tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~GUSA_MASK);
227
+ tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~TB_FLAG_GUSA_MASK);
228
/* Note that this won't actually use a goto_tb opcode because we
229
disallow it in use_goto_tb, but it handles exit + singlestep. */
230
gen_goto_tb(ctx, 0, dest);
231
@@ -XXX,XX +XXX,XX @@ static void gen_delayed_conditional_jump(DisasContext * ctx)
232
tcg_gen_mov_i32(ds, cpu_delayed_cond);
233
tcg_gen_discard_i32(cpu_delayed_cond);
234
235
- if (ctx->tbflags & GUSA_EXCLUSIVE) {
236
+ if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
237
/* When in an exclusive region, we must continue to the end.
238
Therefore, exit the region on a taken branch, but otherwise
239
fall through to the next instruction. */
240
tcg_gen_brcondi_i32(TCG_COND_EQ, ds, 0, l1);
241
242
/* Leave the gUSA region. */
243
- tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~GUSA_MASK);
244
+ tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~TB_FLAG_GUSA_MASK);
245
gen_jump(ctx);
246
247
gen_set_label(l1);
248
@@ -XXX,XX +XXX,XX @@ static inline void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg)
249
#define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe))
250
251
#define CHECK_NOT_DELAY_SLOT \
252
- if (ctx->envflags & DELAY_SLOT_MASK) { \
253
- goto do_illegal_slot; \
254
+ if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) { \
255
+ goto do_illegal_slot; \
256
}
257
258
#define CHECK_PRIVILEGED \
259
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
260
case 0x000b:        /* rts */
261
    CHECK_NOT_DELAY_SLOT
262
    tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr);
263
- ctx->envflags |= DELAY_SLOT;
264
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
265
    ctx->delayed_pc = (uint32_t) - 1;
266
    return;
267
case 0x0028:        /* clrmac */
268
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
269
    CHECK_NOT_DELAY_SLOT
270
gen_write_sr(cpu_ssr);
271
    tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc);
272
- ctx->envflags |= DELAY_SLOT_RTE;
273
+ ctx->envflags |= TB_FLAG_DELAY_SLOT_RTE;
274
    ctx->delayed_pc = (uint32_t) - 1;
275
ctx->base.is_jmp = DISAS_STOP;
276
    return;
277
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
278
    return;
279
case 0xe000:        /* mov #imm,Rn */
280
#ifdef CONFIG_USER_ONLY
281
- /* Detect the start of a gUSA region. If so, update envflags
282
- and end the TB. This will allow us to see the end of the
283
- region (stored in R0) in the next TB. */
284
+ /*
285
+ * Detect the start of a gUSA region (mov #-n, r15).
286
+ * If so, update envflags and end the TB. This will allow us
287
+ * to see the end of the region (stored in R0) in the next TB.
288
+ */
289
if (B11_8 == 15 && B7_0s < 0 &&
290
(tb_cflags(ctx->base.tb) & CF_PARALLEL)) {
291
- ctx->envflags = deposit32(ctx->envflags, GUSA_SHIFT, 8, B7_0s);
292
+ ctx->envflags =
293
+ deposit32(ctx->envflags, TB_FLAG_GUSA_SHIFT, 8, B7_0s);
294
ctx->base.is_jmp = DISAS_STOP;
295
}
31
}
296
#endif
32
#endif
297
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
33
@@ -XXX,XX +XXX,XX @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
298
case 0xa000:        /* bra disp */
34
if (reserved_va) {
299
    CHECK_NOT_DELAY_SLOT
35
guest_loaddr = (guest_base >= mmap_min_addr ? 0
300
ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2;
36
: mmap_min_addr - guest_base);
301
- ctx->envflags |= DELAY_SLOT;
37
- guest_hiaddr = reserved_va;
302
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
38
+ guest_hiaddr = reserved_va - 1;
303
    return;
304
case 0xb000:        /* bsr disp */
305
    CHECK_NOT_DELAY_SLOT
306
tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
307
ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2;
308
- ctx->envflags |= DELAY_SLOT;
309
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
310
    return;
311
}
39
}
312
40
313
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
41
/* Reserve the address space for the binary, or reserved_va. */
314
    CHECK_NOT_DELAY_SLOT
42
test = g2h_untagged(guest_loaddr);
315
tcg_gen_xori_i32(cpu_delayed_cond, cpu_sr_t, 1);
43
- addr = mmap(test, guest_hiaddr - guest_loaddr, PROT_NONE, flags, -1, 0);
316
ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2;
44
+ addr = mmap(test, guest_hiaddr - guest_loaddr + 1, PROT_NONE, flags, -1, 0);
317
- ctx->envflags |= DELAY_SLOT_CONDITIONAL;
45
if (test != addr) {
318
+ ctx->envflags |= TB_FLAG_DELAY_SLOT_COND;
46
pgb_fail_in_use(image_name);
319
    return;
47
}
320
case 0x8900:        /* bt label */
48
qemu_log_mask(CPU_LOG_PAGE,
321
    CHECK_NOT_DELAY_SLOT
49
- "%s: base @ %p for " TARGET_ABI_FMT_ld " bytes\n",
322
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
50
- __func__, addr, guest_hiaddr - guest_loaddr);
323
    CHECK_NOT_DELAY_SLOT
51
+ "%s: base @ %p for %" PRIu64 " bytes\n",
324
tcg_gen_mov_i32(cpu_delayed_cond, cpu_sr_t);
52
+ __func__, addr, (uint64_t)guest_hiaddr - guest_loaddr + 1);
325
ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2;
53
}
326
- ctx->envflags |= DELAY_SLOT_CONDITIONAL;
54
327
+ ctx->envflags |= TB_FLAG_DELAY_SLOT_COND;
55
/**
328
    return;
56
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
329
case 0x8800:        /* cmp/eq #imm,R0 */
57
if (hiaddr != orig_hiaddr) {
330
tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s);
58
error_report("%s: requires virtual address space that the "
331
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
59
"host cannot provide (0x%" PRIx64 ")",
332
case 0x0023:        /* braf Rn */
60
- image_name, (uint64_t)orig_hiaddr);
333
    CHECK_NOT_DELAY_SLOT
61
+ image_name, (uint64_t)orig_hiaddr + 1);
334
tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->base.pc_next + 4);
62
exit(EXIT_FAILURE);
335
- ctx->envflags |= DELAY_SLOT;
63
}
336
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
64
337
    ctx->delayed_pc = (uint32_t) - 1;
65
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
338
    return;
66
* arithmetic wraps around.
339
case 0x0003:        /* bsrf Rn */
67
*/
340
    CHECK_NOT_DELAY_SLOT
68
if (sizeof(uintptr_t) == 8 || loaddr >= 0x80000000u) {
341
tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
69
- hiaddr = (uintptr_t) 4 << 30;
342
    tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr);
70
+ hiaddr = UINT32_MAX;
343
- ctx->envflags |= DELAY_SLOT;
71
} else {
344
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
72
offset = -(HI_COMMPAGE & -align);
345
    ctx->delayed_pc = (uint32_t) - 1;
346
    return;
347
case 0x4015:        /* cmp/pl Rn */
348
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
349
case 0x402b:        /* jmp @Rn */
350
    CHECK_NOT_DELAY_SLOT
351
    tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
352
- ctx->envflags |= DELAY_SLOT;
353
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
354
    ctx->delayed_pc = (uint32_t) - 1;
355
    return;
356
case 0x400b:        /* jsr @Rn */
357
    CHECK_NOT_DELAY_SLOT
358
tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
359
    tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
360
- ctx->envflags |= DELAY_SLOT;
361
+ ctx->envflags |= TB_FLAG_DELAY_SLOT;
362
    ctx->delayed_pc = (uint32_t) - 1;
363
    return;
364
case 0x400e:        /* ldc Rm,SR */
365
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
366
fflush(stderr);
367
#endif
368
do_illegal:
369
- if (ctx->envflags & DELAY_SLOT_MASK) {
370
+ if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) {
371
do_illegal_slot:
372
gen_save_cpu_state(ctx, true);
373
gen_helper_raise_slot_illegal_instruction(cpu_env);
374
@@ -XXX,XX +XXX,XX @@ static void _decode_opc(DisasContext * ctx)
375
376
do_fpu_disabled:
377
gen_save_cpu_state(ctx, true);
378
- if (ctx->envflags & DELAY_SLOT_MASK) {
379
+ if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) {
380
gen_helper_raise_slot_fpu_disable(cpu_env);
381
} else {
382
gen_helper_raise_fpu_disable(cpu_env);
383
@@ -XXX,XX +XXX,XX @@ static void decode_opc(DisasContext * ctx)
384
385
_decode_opc(ctx);
386
387
- if (old_flags & DELAY_SLOT_MASK) {
388
+ if (old_flags & TB_FLAG_DELAY_SLOT_MASK) {
389
/* go out of the delay slot */
390
- ctx->envflags &= ~DELAY_SLOT_MASK;
391
+ ctx->envflags &= ~TB_FLAG_DELAY_SLOT_MASK;
392
393
/* When in an exclusive region, we must continue to the end
394
for conditional branches. */
395
- if (ctx->tbflags & GUSA_EXCLUSIVE
396
- && old_flags & DELAY_SLOT_CONDITIONAL) {
397
+ if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE
398
+ && old_flags & TB_FLAG_DELAY_SLOT_COND) {
399
gen_delayed_conditional_jump(ctx);
400
return;
401
}
73
}
402
/* Otherwise this is probably an invalid gUSA region.
74
@@ -XXX,XX +XXX,XX @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr,
403
Drop the GUSA bits so the next TB doesn't see them. */
75
loaddr = MIN(loaddr, LO_COMMPAGE & -align);
404
- ctx->envflags &= ~GUSA_MASK;
405
+ ctx->envflags &= ~TB_FLAG_GUSA_MASK;
406
407
tcg_gen_movi_i32(cpu_flags, ctx->envflags);
408
- if (old_flags & DELAY_SLOT_CONDITIONAL) {
409
+ if (old_flags & TB_FLAG_DELAY_SLOT_COND) {
410
     gen_delayed_conditional_jump(ctx);
411
} else {
412
gen_jump(ctx);
413
@@ -XXX,XX +XXX,XX @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env)
414
}
76
}
415
77
416
/* The entire region has been translated. */
78
- addr = pgb_find_hole(loaddr, hiaddr - loaddr, align, offset);
417
- ctx->envflags &= ~GUSA_MASK;
79
+ addr = pgb_find_hole(loaddr, hiaddr - loaddr + 1, align, offset);
418
+ ctx->envflags &= ~TB_FLAG_GUSA_MASK;
80
if (addr == -1) {
419
ctx->base.pc_next = pc_end;
81
/*
420
ctx->base.num_insns += max_insns - 1;
82
* If HI_COMMPAGE, there *might* be a non-consecutive allocation
421
return;
83
@@ -XXX,XX +XXX,XX @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr,
422
@@ -XXX,XX +XXX,XX @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env)
84
if (guest_hiaddr > reserved_va) {
423
85
error_report("%s: requires more than reserved virtual "
424
/* Restart with the EXCLUSIVE bit set, within a TB run via
86
"address space (0x%" PRIx64 " > 0x%lx)",
425
cpu_exec_step_atomic holding the exclusive lock. */
87
- image_name, (uint64_t)guest_hiaddr, reserved_va);
426
- ctx->envflags |= GUSA_EXCLUSIVE;
88
+ image_name, (uint64_t)guest_hiaddr + 1, reserved_va);
427
+ ctx->envflags |= TB_FLAG_GUSA_EXCLUSIVE;
89
exit(EXIT_FAILURE);
428
gen_save_cpu_state(ctx, false);
429
gen_helper_exclusive(cpu_env);
430
ctx->base.is_jmp = DISAS_NORETURN;
431
@@ -XXX,XX +XXX,XX @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
432
(tbflags & (1 << SR_RB))) * 0x10;
433
ctx->fbank = tbflags & FPSCR_FR ? 0x10 : 0;
434
435
- if (tbflags & GUSA_MASK) {
436
+#ifdef CONFIG_USER_ONLY
437
+ if (tbflags & TB_FLAG_GUSA_MASK) {
438
+ /* In gUSA exclusive region. */
439
uint32_t pc = ctx->base.pc_next;
440
uint32_t pc_end = ctx->base.tb->cs_base;
441
- int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8);
442
+ int backup = sextract32(ctx->tbflags, TB_FLAG_GUSA_SHIFT, 8);
443
int max_insns = (pc_end - pc) / 2;
444
445
if (pc != pc_end + backup || max_insns < 2) {
446
/* This is a malformed gUSA region. Don't do anything special,
447
since the interpreter is likely to get confused. */
448
- ctx->envflags &= ~GUSA_MASK;
449
- } else if (tbflags & GUSA_EXCLUSIVE) {
450
+ ctx->envflags &= ~TB_FLAG_GUSA_MASK;
451
+ } else if (tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
452
/* Regardless of single-stepping or the end of the page,
453
we must complete execution of the gUSA region while
454
holding the exclusive lock. */
455
@@ -XXX,XX +XXX,XX @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
456
return;
457
}
458
}
90
}
459
+#endif
91
460
92
@@ -XXX,XX +XXX,XX @@ static void load_elf_image(const char *image_name, int image_fd,
461
/* Since the ISA is fixed-width, we can bound by the number
93
if (a < loaddr) {
462
of instructions remaining on the page. */
94
loaddr = a;
463
@@ -XXX,XX +XXX,XX @@ static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
95
}
464
DisasContext *ctx = container_of(dcbase, DisasContext, base);
96
- a = eppnt->p_vaddr + eppnt->p_memsz;
465
97
+ a = eppnt->p_vaddr + eppnt->p_memsz - 1;
466
#ifdef CONFIG_USER_ONLY
98
if (a > hiaddr) {
467
- if (unlikely(ctx->envflags & GUSA_MASK)
99
hiaddr = a;
468
- && !(ctx->envflags & GUSA_EXCLUSIVE)) {
100
}
469
+ if (unlikely(ctx->envflags & TB_FLAG_GUSA_MASK)
101
@@ -XXX,XX +XXX,XX @@ static void load_elf_image(const char *image_name, int image_fd,
470
+ && !(ctx->envflags & TB_FLAG_GUSA_EXCLUSIVE)) {
102
* In both cases, we will overwrite pages in this range with mappings
471
/* We're in an gUSA region, and we have not already fallen
103
* from the executable.
472
back on using an exclusive region. Attempt to parse the
104
*/
473
region into a single supported atomic operation. Failure
105
- load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
474
@@ -XXX,XX +XXX,XX @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
106
+ load_addr = target_mmap(loaddr, (size_t)hiaddr - loaddr + 1, PROT_NONE,
475
{
107
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE |
476
DisasContext *ctx = container_of(dcbase, DisasContext, base);
108
(ehdr->e_type == ET_EXEC ? MAP_FIXED : 0),
477
109
-1, 0);
478
- if (ctx->tbflags & GUSA_EXCLUSIVE) {
110
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
479
+ if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
111
index XXXXXXX..XXXXXXX 100644
480
/* Ending the region of exclusivity. Clear the bits. */
112
--- a/linux-user/flatload.c
481
- ctx->envflags &= ~GUSA_MASK;
113
+++ b/linux-user/flatload.c
482
+ ctx->envflags &= ~TB_FLAG_GUSA_MASK;
114
@@ -XXX,XX +XXX,XX @@ static int load_flat_file(struct linux_binprm * bprm,
483
}
115
* Allocate the address space.
484
116
*/
485
switch (ctx->base.is_jmp) {
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
486
--
123
--
487
2.34.1
124
2.34.1
diff view generated by jsdifflib
1
This function has two users, who use it incompatibly.
1
Change the semantics to be the last byte of the guest va, rather
2
In tlb_flush_page_by_mmuidx_async_0, when flushing a
2
than the following byte. This avoids some overflow conditions.
3
single page, we need to flush exactly two pages.
3
4
In tlb_flush_range_by_mmuidx_async_0, when flushing a
4
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
5
range of pages, we need to flush N+1 pages.
6
7
This avoids double-flushing of jmp cache pages in a range.
8
9
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
10
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
5
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
11
---
6
---
12
accel/tcg/cputlb.c | 25 ++++++++++++++-----------
7
include/exec/cpu-all.h | 11 ++++++++++-
13
1 file changed, 14 insertions(+), 11 deletions(-)
8
linux-user/arm/target_cpu.h | 2 +-
14
9
bsd-user/main.c | 10 +++-------
15
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
10
bsd-user/mmap.c | 4 ++--
16
index XXXXXXX..XXXXXXX 100644
11
linux-user/elfload.c | 14 +++++++-------
17
--- a/accel/tcg/cputlb.c
12
linux-user/main.c | 27 +++++++++++++--------------
18
+++ b/accel/tcg/cputlb.c
13
linux-user/mmap.c | 4 ++--
19
@@ -XXX,XX +XXX,XX @@ static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr)
14
7 files changed, 38 insertions(+), 34 deletions(-)
20
}
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);
21
}
167
}
22
168
23
-static void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr)
169
void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
24
-{
170
diff --git a/linux-user/main.c b/linux-user/main.c
25
- /* Discard jump cache entries for any tb which might potentially
171
index XXXXXXX..XXXXXXX 100644
26
- overlap the flushed page. */
172
--- a/linux-user/main.c
27
- tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE);
173
+++ b/linux-user/main.c
28
- tb_jmp_cache_clear_page(cpu, addr);
174
@@ -XXX,XX +XXX,XX @@ static const char *last_log_filename;
29
-}
175
# if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
30
-
176
# if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \
31
/**
177
(TARGET_LONG_BITS == 32 || defined(TARGET_ABI32))
32
* tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary
178
-/* There are a number of places where we assign reserved_va to a variable
33
* @desc: The CPUTLBDesc portion of the TLB
179
- of type abi_ulong and expect it to fit. Avoid the last page. */
34
@@ -XXX,XX +XXX,XX @@ static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
180
-# define MAX_RESERVED_VA(CPU) (0xfffffffful & TARGET_PAGE_MASK)
35
}
181
+# define MAX_RESERVED_VA(CPU) 0xfffffffful
36
qemu_spin_unlock(&env_tlb(env)->c.lock);
182
# else
37
183
-# define MAX_RESERVED_VA(CPU) (1ul << TARGET_VIRT_ADDR_SPACE_BITS)
38
- tb_flush_jmp_cache(cpu, addr);
184
+# define MAX_RESERVED_VA(CPU) ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
39
+ /*
185
# endif
40
+ * Discard jump cache entries for any tb which might potentially
186
# else
41
+ * overlap the flushed page, which includes the previous.
187
# define MAX_RESERVED_VA(CPU) 0
42
+ */
188
@@ -XXX,XX +XXX,XX @@ static void handle_arg_reserved_va(const char *arg)
43
+ tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE);
189
{
44
+ tb_jmp_cache_clear_page(cpu, addr);
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;
45
}
219
}
46
220
47
/**
221
static void handle_arg_singlestep(const char *arg)
48
@@ -XXX,XX +XXX,XX @@ static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
222
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
49
return;
223
*/
50
}
224
max_reserved_va = MAX_RESERVED_VA(cpu);
51
225
if (reserved_va != 0) {
52
- for (target_ulong i = 0; i < d.len; i += TARGET_PAGE_SIZE) {
226
- if (reserved_va % qemu_host_page_size) {
53
- tb_flush_jmp_cache(cpu, d.addr + i);
227
+ if ((reserved_va + 1) % qemu_host_page_size) {
54
+ /*
228
char *s = size_to_str(qemu_host_page_size);
55
+ * Discard jump cache entries for any tb which might potentially
229
fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s);
56
+ * overlap the flushed pages, which includes the previous.
230
g_free(s);
57
+ */
231
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
58
+ d.addr -= TARGET_PAGE_SIZE;
232
exit(EXIT_FAILURE);
59
+ for (target_ulong i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) {
233
}
60
+ tb_jmp_cache_clear_page(cpu, d.addr);
234
} else if (HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32) {
61
+ d.addr += TARGET_PAGE_SIZE;
235
- /*
62
}
236
- * reserved_va must be aligned with the host page size
63
}
237
- * as it is used with mmap()
64
238
- */
239
- reserved_va = max_reserved_va & qemu_host_page_mask;
240
+ /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */
241
+ reserved_va = max_reserved_va;
242
}
243
244
{
245
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
246
index XXXXXXX..XXXXXXX 100644
247
--- a/linux-user/mmap.c
248
+++ b/linux-user/mmap.c
249
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
250
end_addr = start + size;
251
if (start > reserved_va - size) {
252
/* Start at the top of the address space. */
253
- end_addr = ((reserved_va - size) & -align) + size;
254
+ end_addr = ((reserved_va + 1 - size) & -align) + size;
255
looped = true;
256
}
257
258
@@ -XXX,XX +XXX,XX @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
259
return (abi_ulong)-1;
260
}
261
/* Re-start at the top of the address space. */
262
- addr = end_addr = ((reserved_va - size) & -align) + size;
263
+ addr = end_addr = ((reserved_va + 1 - size) & -align) + size;
264
looped = true;
265
} else {
266
prot = page_get_flags(addr);
65
--
267
--
66
2.34.1
268
2.34.1
67
269
68
270
diff view generated by jsdifflib
1
From: Leandro Lupori <leandro.lupori@eldorado.org.br>
1
User setting of -R reserved_va can lead to an assertion
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.
2
5
3
PowerPC64 processors handle direct branches better than indirect
4
ones, resulting in less stalled cycles and branch misses.
5
6
However, PPC's tb_target_set_jmp_target() was only using direct
7
branches for 16-bit jumps, while PowerPC64's unconditional branch
8
instructions are able to handle displacements of up to 26 bits.
9
To take advantage of this, now jumps whose displacements fit in
10
between 17 and 26 bits are also converted to direct branches.
11
12
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
13
Signed-off-by: Leandro Lupori <leandro.lupori@eldorado.org.br>
14
[rth: Expanded some commentary.]
15
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
6
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
16
---
7
---
17
tcg/ppc/tcg-target.c.inc | 119 +++++++++++++++++++++++++++++----------
8
linux-user/elfload.c | 37 +++++++++++++++++++++++++++----------
18
1 file changed, 88 insertions(+), 31 deletions(-)
9
1 file changed, 27 insertions(+), 10 deletions(-)
19
10
20
diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc
11
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
21
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
22
--- a/tcg/ppc/tcg-target.c.inc
13
--- a/linux-user/elfload.c
23
+++ b/tcg/ppc/tcg-target.c.inc
14
+++ b/linux-user/elfload.c
24
@@ -XXX,XX +XXX,XX @@ static void tcg_out_mb(TCGContext *s, TCGArg a0)
15
@@ -XXX,XX +XXX,XX @@ enum {
25
tcg_out32(s, insn);
16
26
}
17
static bool init_guest_commpage(void)
27
18
{
28
+static inline uint64_t make_pair(tcg_insn_unit i1, tcg_insn_unit i2)
19
- abi_ptr commpage = HI_COMMPAGE & -qemu_host_page_size;
29
+{
20
- void *want = g2h_untagged(commpage);
30
+ if (HOST_BIG_ENDIAN) {
21
- void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE,
31
+ return (uint64_t)i1 << 32 | i2;
22
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
32
+ }
23
+ ARMCPU *cpu = ARM_CPU(thread_cpu);
33
+ return (uint64_t)i2 << 32 | i1;
24
+ abi_ptr want = HI_COMMPAGE & TARGET_PAGE_MASK;
34
+}
25
+ abi_ptr addr;
35
+
26
36
+static inline void ppc64_replace2(uintptr_t rx, uintptr_t rw,
27
- if (addr == MAP_FAILED) {
37
+ tcg_insn_unit i0, tcg_insn_unit i1)
38
+{
39
+#if TCG_TARGET_REG_BITS == 64
40
+ qatomic_set((uint64_t *)rw, make_pair(i0, i1));
41
+ flush_idcache_range(rx, rw, 8);
42
+#else
43
+ qemu_build_not_reached();
44
+#endif
45
+}
46
+
47
+static inline void ppc64_replace4(uintptr_t rx, uintptr_t rw,
48
+ tcg_insn_unit i0, tcg_insn_unit i1,
49
+ tcg_insn_unit i2, tcg_insn_unit i3)
50
+{
51
+ uint64_t p[2];
52
+
53
+ p[!HOST_BIG_ENDIAN] = make_pair(i0, i1);
54
+ p[HOST_BIG_ENDIAN] = make_pair(i2, i3);
55
+
56
+ /*
28
+ /*
57
+ * There's no convenient way to get the compiler to allocate a pair
29
+ * M-profile allocates maximum of 2GB address space, so can never
58
+ * of registers at an even index, so copy into r6/r7 and clobber.
30
+ * allocate the commpage. Skip it.
59
+ */
31
+ */
60
+ asm("mr %%r6, %1\n\t"
32
+ if (arm_feature(&cpu->env, ARM_FEATURE_M)) {
61
+ "mr %%r7, %2\n\t"
33
+ return true;
62
+ "stq %%r6, %0"
63
+ : "=Q"(*(__int128 *)rw) : "r"(p[0]), "r"(p[1]) : "r6", "r7");
64
+ flush_idcache_range(rx, rw, 16);
65
+}
66
+
67
void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx,
68
uintptr_t jmp_rw, uintptr_t addr)
69
{
70
- if (TCG_TARGET_REG_BITS == 64) {
71
- tcg_insn_unit i1, i2;
72
- intptr_t tb_diff = addr - tc_ptr;
73
- intptr_t br_diff = addr - (jmp_rx + 4);
74
- uint64_t pair;
75
+ tcg_insn_unit i0, i1, i2, i3;
76
+ intptr_t tb_diff = addr - tc_ptr;
77
+ intptr_t br_diff = addr - (jmp_rx + 4);
78
+ intptr_t lo, hi;
79
80
- /* This does not exercise the range of the branch, but we do
81
- still need to be able to load the new value of TCG_REG_TB.
82
- But this does still happen quite often. */
83
- if (tb_diff == (int16_t)tb_diff) {
84
- i1 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, tb_diff);
85
- i2 = B | (br_diff & 0x3fffffc);
86
- } else {
87
- intptr_t lo = (int16_t)tb_diff;
88
- intptr_t hi = (int32_t)(tb_diff - lo);
89
- assert(tb_diff == hi + lo);
90
- i1 = ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, hi >> 16);
91
- i2 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, lo);
92
- }
93
-#if HOST_BIG_ENDIAN
94
- pair = (uint64_t)i1 << 32 | i2;
95
-#else
96
- pair = (uint64_t)i2 << 32 | i1;
97
-#endif
98
-
99
- /* As per the enclosing if, this is ppc64. Avoid the _Static_assert
100
- within qatomic_set that would fail to build a ppc32 host. */
101
- qatomic_set__nocheck((uint64_t *)jmp_rw, pair);
102
- flush_idcache_range(jmp_rx, jmp_rw, 8);
103
- } else {
104
+ if (TCG_TARGET_REG_BITS == 32) {
105
intptr_t diff = addr - jmp_rx;
106
tcg_debug_assert(in_range_b(diff));
107
qatomic_set((uint32_t *)jmp_rw, B | (diff & 0x3fffffc));
108
flush_idcache_range(jmp_rx, jmp_rw, 4);
109
+ return;
110
}
111
+
112
+ /*
113
+ * For 16-bit displacements, we can use a single add + branch.
114
+ * This happens quite often.
115
+ */
116
+ if (tb_diff == (int16_t)tb_diff) {
117
+ i0 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, tb_diff);
118
+ i1 = B | (br_diff & 0x3fffffc);
119
+ ppc64_replace2(jmp_rx, jmp_rw, i0, i1);
120
+ return;
121
+ }
122
+
123
+ lo = (int16_t)tb_diff;
124
+ hi = (int32_t)(tb_diff - lo);
125
+ assert(tb_diff == hi + lo);
126
+ i0 = ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, hi >> 16);
127
+ i1 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, lo);
128
+
129
+ /*
130
+ * Without stq from 2.07, we can only update two insns,
131
+ * and those must be the ones that load the target address.
132
+ */
133
+ if (!have_isa_2_07) {
134
+ ppc64_replace2(jmp_rx, jmp_rw, i0, i1);
135
+ return;
136
+ }
34
+ }
137
+
35
+
138
+ /*
36
+ /*
139
+ * For 26-bit displacements, we can use a direct branch.
37
+ * If reserved_va does not cover the commpage, we get an assert
140
+ * Otherwise we still need the indirect branch, which we
38
+ * in page_set_flags. Produce an intelligent error instead.
141
+ * must restore after a potential direct branch write.
142
+ */
39
+ */
143
+ br_diff -= 4;
40
+ if (reserved_va != 0 && want + TARGET_PAGE_SIZE - 1 > reserved_va) {
144
+ if (in_range_b(br_diff)) {
41
+ error_report("Allocating guest commpage: -R 0x%" PRIx64 " too small",
145
+ i2 = B | (br_diff & 0x3fffffc);
42
+ (uint64_t)reserved_va + 1);
146
+ i3 = NOP;
43
+ exit(EXIT_FAILURE);
147
+ } else {
148
+ i2 = MTSPR | RS(TCG_REG_TB) | CTR;
149
+ i3 = BCCTR | BO_ALWAYS;
150
+ }
44
+ }
151
+ ppc64_replace4(jmp_rx, jmp_rw, i0, i1, i2, i3);
45
+
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;
152
}
69
}
153
70
154
static void tcg_out_call_int(TCGContext *s, int lk,
155
@@ -XXX,XX +XXX,XX @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc,
156
if (s->tb_jmp_insn_offset) {
157
/* Direct jump. */
158
if (TCG_TARGET_REG_BITS == 64) {
159
- /* Ensure the next insns are 8-byte aligned. */
160
- if ((uintptr_t)s->code_ptr & 7) {
161
+ /* Ensure the next insns are 8 or 16-byte aligned. */
162
+ while ((uintptr_t)s->code_ptr & (have_isa_2_07 ? 15 : 7)) {
163
tcg_out32(s, NOP);
164
}
165
s->tb_jmp_insn_offset[args[0]] = tcg_current_code_size(s);
166
--
71
--
167
2.34.1
72
2.34.1
diff view generated by jsdifflib