All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chao Liu <chao.liu@processmission.com>
To: Zephyr Li <fritchleybohrer@gmail.com>
Cc: qemu-devel@nongnu.org, qemu-riscv@nongnu.org,
	qemu-stable@nongnu.org,  pbonzini@redhat.com,
	marcandre.lureau@redhat.com, berrange@redhat.com,
	 pierrick.bouvier@oss.qualcomm.com, alex.bennee@linaro.org,
	palmer@dabbelt.com,  alistair.francis@wdc.com,
	liwei1518@gmail.com, daniel.barboza@oss.qualcomm.com,
	zhiwei_liu@linux.alibaba.com
Subject: Re: [PATCH v2] target/riscv: fix RV32 fixed counter accesses
Date: Fri, 11 Sep 2026 12:44:37 +0800	[thread overview]
Message-ID: <aqOHF2QVtK0UWM-e@MacBook-Pro-4.local> (raw)
In-Reply-To: <20260909072356.42784-1-fritchleybohrer@gmail.com>

On Wed, Sep 09, 2026 at 03:23:55PM +0800, Zephyr Li wrote:
> Since commit cfc96df65e01, 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: cfc96df65e01 ("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: Chao Liu <chao.liu@processmission.com>

Thanks,
Chao

> 
> ---
> Changes in v2:
> - Move the test to a new tests/tcg/riscv32 directory.
> - Convert the test registration to the Meson TCG test framework.
> - Add RV32 TCG cross-compiler options and cover the test directory in
>   MAINTAINERS.
> - Add a riscv32-local semihosting linker script.
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7183babd6a..5f75b2684f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -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
>  
> diff --git a/meson_options.txt b/meson_options.txt
> index 292625af08..2ceebe7a44 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -452,6 +452,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',
> diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
> index bd4b6dc114..65985efb22 100644
> --- a/target/riscv/tcg/csr.c
> +++ b/target/riscv/tcg/csr.c
> @@ -1337,23 +1337,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;
> @@ -1363,20 +1363,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);
>      }
> @@ -1407,12 +1409,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))) {
> @@ -1431,7 +1432,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;
> diff --git a/tests/tcg/meson.build b/tests/tcg/meson.build
> index d41a228fb3..60b5ce2529 100644
> --- a/tests/tcg/meson.build
> +++ b/tests/tcg/meson.build
> @@ -169,6 +169,7 @@ subdir('mips64el')
>  subdir('or1k')
>  subdir('ppc64')
>  subdir('ppc64le')
> +subdir('riscv32')
>  subdir('riscv64')
>  subdir('s390x')
>  subdir('sh4')
> diff --git a/tests/tcg/riscv32/meson.build b/tests/tcg/riscv32/meson.build
> new file mode 100644
> index 0000000000..c08dfd772d
> --- /dev/null
> +++ b/tests/tcg/riscv32/meson.build
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +cc = 'riscv64-linux-gnu-gcc'
> +cc_dockerfile = 'debian-all-test-cross'
> +cc_docker_host_arch = ['aarch64', 'x86_64']
> +
> +subdir('system')
> diff --git a/tests/tcg/riscv32/semihost.ld b/tests/tcg/riscv32/semihost.ld
> new file mode 100644
> index 0000000000..874838a865
> --- /dev/null
> +++ b/tests/tcg/riscv32/semihost.ld
> @@ -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)
> +    }
> +}
> diff --git a/tests/tcg/riscv32/system/meson.build b/tests/tcg/riscv32/system/meson.build
> new file mode 100644
> index 0000000000..16f9a06c94
> --- /dev/null
> +++ b/tests/tcg/riscv32/system/meson.build
> @@ -0,0 +1,37 @@
> +# 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,
> +      'cc_docker_host_arch': cc_docker_host_arch,
> +      'folder': 'riscv32',
> +      'gdb_arch': 'riscv32',
> +      'qemu': emulators['qemu-system-riscv32'],
> +      'tests': tests,
> +    }
> +  }
> +endif
> diff --git a/tests/tcg/riscv32/test-mcycle.S b/tests/tcg/riscv32/test-mcycle.S
> new file mode 100644
> index 0000000000..189d1e13a2
> --- /dev/null
> +++ b/tests/tcg/riscv32/test-mcycle.S
> @@ -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	.
> -- 
> 2.43.0


      parent reply	other threads:[~2026-09-11  4:45 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09  7:23 [PATCH v2] target/riscv: fix RV32 fixed counter accesses Zephyr Li
2026-09-09 19:58 ` Daniel Henrique Barboza
2026-09-11  4:44 ` Chao Liu [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aqOHF2QVtK0UWM-e@MacBook-Pro-4.local \
    --to=chao.liu@processmission.com \
    --cc=alex.bennee@linaro.org \
    --cc=alistair.francis@wdc.com \
    --cc=berrange@redhat.com \
    --cc=daniel.barboza@oss.qualcomm.com \
    --cc=fritchleybohrer@gmail.com \
    --cc=liwei1518@gmail.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=palmer@dabbelt.com \
    --cc=pbonzini@redhat.com \
    --cc=pierrick.bouvier@oss.qualcomm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-riscv@nongnu.org \
    --cc=qemu-stable@nongnu.org \
    --cc=zhiwei_liu@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.