mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-10-28 11:54:41 +08:00
target/riscv: fix RV32 fixed counter accesses
Since commitcfc96df65e, riscv_pmu_ctr_get_fixed_counters_val() returns the complete 64-bit fixed-counter value. The RV32 counter access paths, however, still perform parts of the offset calculation on separately extracted 32-bit halves. In particular, riscv_pmu_write_ctrh() deposits the low 32 bits of the complete fixed-counter value into the high half of mhpmcounter_prev. riscv_pmu_read_ctr() also subtracts a 32-bit half of the previous value from the complete 64-bit fixed-counter value. Consequently, writes to mcycleh can be lost and carries between the low and high halves are not handled correctly. Keep the fixed-counter offset calculation entirely in 64 bits. Before a running counter is partially written, materialize its current architectural value and reset the fixed-counter baseline. On reads, calculate the complete 64-bit counter value before extracting the half requested by RV32. Register RV32 system TCG tests with the Meson build. Add a test for high-half writes, low-to-high carry, and preserving the carried high half across a subsequent low-half write. Fixes:cfc96df65e("target/riscv: Remove upper_half from riscv_pmu_ctr_get_fixed_counters_val") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4219 Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Chao Liu <chao.liu@processmission.com> Message-ID: <20261008035150.2223-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
1 parent
3ba3371db5
commit
57b484e2d2
9 files changed
+135
-15
No files matched your search
@@ -377,6 +377,7 @@ F: include/hw/riscv/
|
||||
F: common-user/host/riscv*
|
||||
F: tests/functional/riscv32
|
||||
F: tests/functional/riscv64
|
||||
F: tests/tcg/riscv32/
|
||||
F: tests/tcg/riscv64/
|
||||
F: tests/qtest/iommu-riscv-test.c
|
||||
|
||||
|
||||
@@ -473,6 +473,10 @@ option('tcg_tests_cross_cc_ppc64le', type: 'string',
|
||||
description: 'cc for ppc64le tcg tests')
|
||||
option('tcg_tests_cross_cflags_ppc64le', type: 'string',
|
||||
description: 'cflags for ppc64le tcg tests')
|
||||
option('tcg_tests_cross_cc_riscv32', type: 'string',
|
||||
description: 'cc for riscv32 tcg tests')
|
||||
option('tcg_tests_cross_cflags_riscv32', type: 'string',
|
||||
description: 'cflags for riscv32 tcg tests')
|
||||
option('tcg_tests_cross_cc_riscv64', type: 'string',
|
||||
description: 'cc for riscv64 tcg tests')
|
||||
option('tcg_tests_cross_cflags_riscv64', type: 'string',
|
||||
|
||||
@@ -547,6 +547,7 @@ _meson_option_parse() {
|
||||
--tcg-tests-cross-cc-or1k=*) quote_sh "-Dtcg_tests_cross_cc_or1k=$2" ;;
|
||||
--tcg-tests-cross-cc-ppc64=*) quote_sh "-Dtcg_tests_cross_cc_ppc64=$2" ;;
|
||||
--tcg-tests-cross-cc-ppc64le=*) quote_sh "-Dtcg_tests_cross_cc_ppc64le=$2" ;;
|
||||
--tcg-tests-cross-cc-riscv32=*) quote_sh "-Dtcg_tests_cross_cc_riscv32=$2" ;;
|
||||
--tcg-tests-cross-cc-riscv64=*) quote_sh "-Dtcg_tests_cross_cc_riscv64=$2" ;;
|
||||
--tcg-tests-cross-cc-s390x=*) quote_sh "-Dtcg_tests_cross_cc_s390x=$2" ;;
|
||||
--tcg-tests-cross-cc-sh4=*) quote_sh "-Dtcg_tests_cross_cc_sh4=$2" ;;
|
||||
@@ -573,6 +574,7 @@ _meson_option_parse() {
|
||||
--tcg-tests-cross-cflags-or1k=*) quote_sh "-Dtcg_tests_cross_cflags_or1k=$2" ;;
|
||||
--tcg-tests-cross-cflags-ppc64=*) quote_sh "-Dtcg_tests_cross_cflags_ppc64=$2" ;;
|
||||
--tcg-tests-cross-cflags-ppc64le=*) quote_sh "-Dtcg_tests_cross_cflags_ppc64le=$2" ;;
|
||||
--tcg-tests-cross-cflags-riscv32=*) quote_sh "-Dtcg_tests_cross_cflags_riscv32=$2" ;;
|
||||
--tcg-tests-cross-cflags-riscv64=*) quote_sh "-Dtcg_tests_cross_cflags_riscv64=$2" ;;
|
||||
--tcg-tests-cross-cflags-s390x=*) quote_sh "-Dtcg_tests_cross_cflags_s390x=$2" ;;
|
||||
--tcg-tests-cross-cflags-sh4=*) quote_sh "-Dtcg_tests_cross_cflags_sh4=$2" ;;
|
||||
|
||||
+17
-15
@@ -1340,23 +1340,23 @@ static RISCVException riscv_pmu_write_ctr(CPURISCVState *env, target_ulong val,
|
||||
int deposit_size = rv32 ? 32 : 64;
|
||||
uint64_t ctr;
|
||||
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
0, deposit_size, val);
|
||||
|
||||
if (!get_field(env->mcountinhibit, BIT(ctr_idx)) &&
|
||||
(riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
|
||||
riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) {
|
||||
ctr = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx);
|
||||
counter->mhpmcounter_prev = deposit64(counter->mhpmcounter_prev,
|
||||
0, deposit_size, ctr);
|
||||
counter->mhpmcounter_val += ctr - counter->mhpmcounter_prev;
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
0, deposit_size, val);
|
||||
counter->mhpmcounter_prev = ctr;
|
||||
if (ctr_idx > 2) {
|
||||
riscv_pmu_setup_timer(env, counter->mhpmcounter_val, ctr_idx);
|
||||
}
|
||||
} else {
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
0, deposit_size, val);
|
||||
/* Other counters can keep incrementing from the given value */
|
||||
counter->mhpmcounter_prev = deposit64(counter->mhpmcounter_prev,
|
||||
0, deposit_size, val);
|
||||
|
||||
}
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
@@ -1366,20 +1366,22 @@ static RISCVException riscv_pmu_write_ctrh(CPURISCVState *env, target_ulong val,
|
||||
uint32_t ctr_idx)
|
||||
{
|
||||
PMUCTRState *counter = &env->pmu_ctrs[ctr_idx];
|
||||
uint64_t ctrh;
|
||||
uint64_t ctr;
|
||||
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
32, 32, val);
|
||||
if (!get_field(env->mcountinhibit, BIT(ctr_idx)) &&
|
||||
(riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
|
||||
riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) {
|
||||
ctrh = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx);
|
||||
counter->mhpmcounter_prev = deposit64(counter->mhpmcounter_prev,
|
||||
32, 32, ctrh);
|
||||
ctr = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx);
|
||||
counter->mhpmcounter_val += ctr - counter->mhpmcounter_prev;
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
32, 32, val);
|
||||
counter->mhpmcounter_prev = ctr;
|
||||
if (ctr_idx > 2) {
|
||||
riscv_pmu_setup_timer(env, counter->mhpmcounter_val, ctr_idx);
|
||||
}
|
||||
} else {
|
||||
counter->mhpmcounter_val = deposit64(counter->mhpmcounter_val,
|
||||
32, 32, val);
|
||||
counter->mhpmcounter_prev = deposit64(counter->mhpmcounter_prev,
|
||||
32, 32, val);
|
||||
}
|
||||
@@ -1410,12 +1412,11 @@ RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
|
||||
bool rv32 = riscv_cpu_mxl(env) == MXL_RV32;
|
||||
int start = upper_half ? 32 : 0;
|
||||
int length = rv32 ? 32 : 64;
|
||||
uint64_t ctr_prev, ctr_val;
|
||||
uint64_t ctr_val;
|
||||
|
||||
/* Ensure upper_half is only set for XLEN == 32 */
|
||||
g_assert(rv32 || !upper_half);
|
||||
|
||||
ctr_prev = extract64(counter->mhpmcounter_prev, start, length);
|
||||
ctr_val = extract64(counter->mhpmcounter_val, start, length);
|
||||
|
||||
if (get_field(env->mcountinhibit, BIT(ctr_idx))) {
|
||||
@@ -1434,7 +1435,8 @@ RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
|
||||
if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
|
||||
riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) {
|
||||
uint64_t cntr = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx) -
|
||||
ctr_prev + ctr_val;
|
||||
counter->mhpmcounter_prev +
|
||||
counter->mhpmcounter_val;
|
||||
*val = extract64(cntr, start, length);
|
||||
} else {
|
||||
*val = ctr_val;
|
||||
|
||||
@@ -186,6 +186,7 @@ subdir('mips64el')
|
||||
subdir('or1k')
|
||||
subdir('ppc64')
|
||||
subdir('ppc64le')
|
||||
subdir('riscv32')
|
||||
subdir('riscv64')
|
||||
subdir('s390x')
|
||||
subdir('sh4')
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
cc = 'riscv64-linux-gnu-gcc'
|
||||
cc_dockerfile = 'debian-all-test-cross'
|
||||
|
||||
subdir('system')
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* virt machine, RAM starts at 2gb */
|
||||
. = 0x80000000;
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
/* align r/w section to next 2mb */
|
||||
. = ALIGN(1 << 21);
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
tests = []
|
||||
|
||||
link_script = files('../semihost.ld')[0]
|
||||
cflags = ['-march=rv32im_zicsr',
|
||||
'-mabi=ilp32',
|
||||
'-nostdlib',
|
||||
'-ffreestanding',
|
||||
'-Wa,--noexecstack',
|
||||
'-Wl,-T', link_script]
|
||||
qemu_args = ['-M', 'virt',
|
||||
'-display', 'none',
|
||||
'-serial', 'stdio',
|
||||
'-semihosting',
|
||||
'-bios']
|
||||
|
||||
tests += {
|
||||
'test-mcycle.S': {
|
||||
'cflags': cflags,
|
||||
'qemu_args': ['-icount', 'shift=1', qemu_args],
|
||||
},
|
||||
}
|
||||
|
||||
if 'qemu-system-riscv32' in emulators
|
||||
tcg_tests += {
|
||||
'riscv32-softmmu': {
|
||||
'cc': cc,
|
||||
'cc_dockerfile': cc_dockerfile,
|
||||
'folder': 'riscv32',
|
||||
'gdb_arch': 'riscv32',
|
||||
'qemu': emulators['qemu-system-riscv32'],
|
||||
'tests': tests,
|
||||
}
|
||||
}
|
||||
endif
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
.option norvc
|
||||
|
||||
.text
|
||||
.global _start
|
||||
_start:
|
||||
/* Exercise writes while mcycle is running. */
|
||||
csrw mcountinhibit, zero
|
||||
csrw mcycle, zero
|
||||
|
||||
/* A write to the high half must be immediately observable. */
|
||||
li s0, 0x1234ffff
|
||||
csrw mcycleh, s0
|
||||
csrr t0, mcycleh
|
||||
bne t0, s0, fail
|
||||
|
||||
/* Check carry from the low half into the high half. */
|
||||
li s0, 0x12345678
|
||||
csrw mcycleh, s0
|
||||
li t0, 0xfffffff0
|
||||
csrw mcycle, t0
|
||||
.rept 32
|
||||
nop
|
||||
.endr
|
||||
csrr t0, mcycleh
|
||||
addi s0, s0, 1
|
||||
bne t0, s0, fail
|
||||
|
||||
/* A low-half write must preserve the carried high half. */
|
||||
li t0, 0x22222222
|
||||
csrw mcycle, t0
|
||||
csrr t0, mcycleh
|
||||
bne t0, s0, fail
|
||||
|
||||
li t0, 0x100000
|
||||
li t1, 0x5555 /* FINISHER_PASS */
|
||||
sw t1, 0(t0)
|
||||
j .
|
||||
|
||||
fail:
|
||||
li t0, 0x100000
|
||||
li t1, 0x13333 /* status = FINISHER_FAIL, code = 1 */
|
||||
sw t1, 0(t0)
|
||||
j .
|
||||
Reference in new issue
Block a user